Index: uspace/lib/riff/src/rwave.c
===================================================================
--- uspace/lib/riff/src/rwave.c	(revision d53af3c8e49cf53bc3b0b68fb0fcadb78291e77f)
+++ uspace/lib/riff/src/rwave.c	(revision d53af3c8e49cf53bc3b0b68fb0fcadb78291e77f)
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 2015 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libriff
+ * @{
+ */
+/**
+ * @file Waveform Audio File Format (WAVE).
+ */
+
+#include <assert.h>
+#include <byteorder.h>
+#include <errno.h>
+#include <macros.h>
+#include <mem.h>
+#include <riff/chunk.h>
+#include <riff/rwave.h>
+#include <stdlib.h>
+
+/** Encode format chunk data.
+ *
+ * @params params	WAVE parameters
+ * @params fmt		Pointer to fmt chunk data structure to fill in
+ */
+static void rwave_encode_fmt(rwave_params_t *params, rwave_fmt_t *fmt)
+{
+	int bytes_smp;
+
+	bytes_smp = (params->bits_smp + 7) / 8;
+
+	fmt->format_tag = host2uint16_t_le(WFMT_PCM);
+	fmt->channels = host2uint16_t_le(params->channels);
+	fmt->smp_sec = host2uint32_t_le(params->smp_freq);
+	fmt->avg_bytes_sec = host2uint32_t_le(bytes_smp * params->smp_freq *
+	    params->channels);
+	fmt->block_align = host2uint16_t_le(bytes_smp);
+	fmt->bits_smp = host2uint16_t_le(params->bits_smp);
+}
+
+/** Decode format chunk data.
+ *
+ * @param fmt	 Pointer to format chunk data
+ * @param params WAVE parameters to fill in
+ *
+ * @return EOK on success, EINVAL if format is not supported.
+ */
+static int rwave_decode_fmt(rwave_fmt_t *fmt, rwave_params_t *params)
+{
+	uint16_t fmt_tag;
+
+	fmt_tag = uint16_t_le2host(fmt->format_tag);
+	printf("fmt_tag=0x%x\n", fmt_tag);
+	if (fmt_tag != WFMT_PCM)
+		return EINVAL;
+
+	params->channels = uint16_t_le2host(fmt->channels);
+	params->smp_freq = uint32_t_le2host(fmt->smp_sec);
+	params->bits_smp = uint16_t_le2host(fmt->bits_smp);
+
+	return EOK;
+}
+
+/** Open WAVE file for writing.
+ *
+ * @param fname  File name
+ * @param params WAVE file parameters
+ * @param rww    Place to store pointer to WAVE writer
+ *
+ * @return EOK on success, EIO on I/O error, ENOMEM if out of memory.
+ */
+int rwave_wopen(const char *fname, rwave_params_t *params, rwavew_t **rww)
+{
+	riff_wchunk_t fmt;
+	rwave_fmt_t rwfmt;
+	int rc;
+	rwavew_t *ww;
+
+	rwave_encode_fmt(params, &rwfmt);
+
+	ww = calloc(1, sizeof(rwavew_t));
+	if (ww == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	ww->bufsize = 4096;
+	ww->buf = calloc(1, ww->bufsize);
+	if (ww->buf == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	/* Make a copy of parameters */
+	ww->params = *params;
+
+	rc = riff_wopen(fname, &ww->rw);
+	if (rc != EOK) {
+		assert(rc == EIO || rc == ENOMEM);
+		goto error;
+	}
+
+	rc = riff_wchunk_start(ww->rw, CKID_RIFF, &ww->wave);
+	if (rc != EOK)
+		goto error;
+
+	rc = riff_write_uint32(ww->rw, FORM_WAVE);
+	if (rc != EOK)
+		goto error;
+
+	rc = riff_wchunk_start(ww->rw, CKID_fmt, &fmt);
+	if (rc != EOK)
+		goto error;
+
+	rc = riff_wchunk_write(ww->rw, &rwfmt, sizeof(rwfmt));
+	if (rc != EOK)
+		goto error;
+
+	rc = riff_wchunk_end(ww->rw, &fmt);
+	if (rc != EOK)
+		goto error;
+
+	rc = riff_wchunk_start(ww->rw, CKID_data, &ww->data);
+	if (rc != EOK)
+		goto error;
+
+	*rww = ww;
+	return EOK;
+error:
+	if (ww != NULL)
+		free(ww->buf);
+	if (ww->rw != NULL)
+		riff_wclose(ww->rw);
+	free(ww);
+	return rc;
+}
+
+/** Write samples to WAVE file.
+ *
+ * @param ww    WAVE writer
+ * @param data  Pointer to data
+ * @param bytes Number of bytes to write
+ *
+ * @return EOK on success, EIO on I/O error, ENOTSUP if sample format is
+ *         not supported.
+ */
+int rwave_write_samples(rwavew_t *ww, void *data, size_t bytes)
+{
+	size_t i;
+	uint16_t *d16, *b16;
+	size_t now;
+	int rc;
+
+	/* Convert sample data to little endian */
+
+	while (bytes > 0) {
+		now = min(bytes, ww->bufsize);
+
+		switch (ww->params.bits_smp / 8) {
+		case 1:
+			memcpy(ww->buf, data, now);
+			break;
+		case 2:
+			b16 = (uint16_t *)ww->buf;
+			d16 = (uint16_t *)data;
+			for (i = 0; i < now / 2; i++) {
+				b16[i] = host2uint16_t_le(d16[i]);
+			}
+			break;
+		default:
+			return ENOTSUP;
+		}
+
+		rc = riff_wchunk_write(ww->rw, ww->buf, now);
+		if (rc != EOK) {
+			assert(rc == EIO);
+			return rc;
+		}
+
+		bytes -= now;
+		data += now;
+	}
+
+	return EOK;
+}
+
+/** Close WAVE file for writing.
+ *
+ * @param ww WAVE writer
+ * @return EOK on success, EIO on I/O error - in which case @a ww is destroyed
+ *         anyway.
+ */
+int rwave_wclose(rwavew_t *ww)
+{
+	int rc;
+
+	rc = riff_wchunk_end(ww->rw, &ww->wave);
+	if (rc == EOK)
+		rc = riff_wchunk_end(ww->rw, &ww->data);
+
+	rc = riff_wclose(ww->rw);
+
+	ww->rw = NULL;
+	free(ww->buf);
+	free(ww);
+
+	return rc;
+}
+
+/** Open WAVE file for reading.
+ *
+ * @param fname  File name
+ * @param params WAVE file parameters
+ * @param rwaver Place to store pointer to new wave reader
+ *
+ * @return EOK on success, EIO on I/O error, ENOMEM if out of memory
+ */
+int rwave_ropen(const char *fname, rwave_params_t *params, rwaver_t **rwr)
+{
+	rwaver_t *wr = NULL;
+	uint32_t form_id;
+	riff_rchunk_t fmt;
+	rwave_fmt_t wfmt;
+	size_t nread;
+	int rc;
+
+	wr = calloc(1, sizeof(rwaver_t));
+	if (wr == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	rc = riff_ropen(fname, &wr->rr);
+	if (rc != EOK) {
+		assert(rc == EIO || rc == ENOMEM);
+		goto error;
+	}
+
+	rc = riff_rchunk_start(wr->rr, &wr->wave);
+	if (rc != EOK) {
+		assert(rc == EIO);
+		goto error;
+	}
+
+	rc = riff_read_uint32(wr->rr, &form_id);
+	if (rc != EOK) {
+		assert(rc == EIO);
+		goto error;
+	}
+
+	if (form_id != FORM_WAVE) {
+		printf("wrong form ID\n");
+		rc = EIO;
+		goto error;
+	}
+
+	rc = riff_rchunk_start(wr->rr, &fmt);
+	if (rc != EOK) {
+		assert(rc == EIO);
+		goto error;
+	}
+
+	if (fmt.ckid != CKID_fmt) {
+		printf("not fmt chunk\n");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	rc = riff_rchunk_read(wr->rr, &fmt, &wfmt, sizeof(rwave_fmt_t), &nread);
+	if (rc != EOK) {
+		printf("error reading fmt chunk\n");
+		assert(rc == EIO || rc == ELIMIT);
+		rc = EIO;
+		goto error;
+	}
+
+	if (nread < sizeof(rwave_fmt_t)) {
+		rc = EIO;
+		goto error;
+	}
+
+	rc = riff_rchunk_end(wr->rr, &fmt);
+	if (rc != EOK) {
+		assert(rc == EIO);
+		goto error;
+	}
+
+	rc = rwave_decode_fmt(&wfmt, params);
+	if (rc != EOK) {
+		printf("decode fmt fail\n");
+		assert(rc == EINVAL);
+		rc = EIO;
+		goto error;
+	}
+
+	rc = riff_rchunk_start(wr->rr, &wr->data);
+	if (rc != EOK) {
+		assert(rc == EIO);
+		goto error;
+	}
+
+	if (wr->data.ckid != CKID_data) {
+		printf("not data ckid\n");
+		rc = EIO;
+		goto error;
+	}
+
+	*rwr = wr;
+	return EOK;
+error:
+	if (wr != NULL && wr->rr != NULL)
+		riff_rclose(wr->rr);
+	free(wr);
+	return rc;
+}
+
+/** Read samples from WAVE file.
+ *
+ * @param wr    WAVE reader
+ * @param buf   Buffer for storing the data
+ * @param bytes Number of bytes to read
+ * @param nread Place to store number of bytes actually read (>= 0).
+ *
+ * @return EOK if zero or more bytes successfully read and @a *nread is set,
+ *         EIO on I/O error.
+ */
+int rwave_read_samples(rwaver_t *wr, void *buf, size_t bytes, size_t *nread)
+{
+	int rc;
+
+	rc = riff_rchunk_read(wr->rr, &wr->data, buf, bytes, nread);
+	if (rc != EOK) {
+		assert(rc == EIO || rc == ELIMIT);
+		return EIO;
+	}
+
+	return EOK;
+}
+
+/** Close WAVE file for reading.
+ *
+ * @param wr WAVE reader
+ *
+ * @return EOK on success, EIO on I/O error in which case @a wr is destroyed
+ *         anyway.
+ */
+int rwave_rclose(rwaver_t *wr)
+{
+	int rc;
+
+	rc = riff_rchunk_end(wr->rr, &wr->wave);
+	if (rc != EOK) {
+		assert(rc == EIO);
+		goto error;
+	}
+
+	riff_rclose(wr->rr);
+	free(wr);
+	return EOK;
+error:
+	return rc;
+}
+
+/** @}
+ */
