source: mainline/uspace/lib/riff/src/rwave.c

Last change on this file was d145ecb, checked in by Jiri Svoboda <jiri@…>, 5 years ago

Libriff needs unit tests

  • Property mode set to 100644
File size: 8.3 KB
Line 
1/*
2 * Copyright (c) 2015 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libriff
30 * @{
31 */
32/**
33 * @file Waveform Audio File Format (WAVE).
34 */
35
36#include <assert.h>
37#include <byteorder.h>
38#include <errno.h>
39#include <macros.h>
40#include <mem.h>
41#include <riff/chunk.h>
42#include <riff/rwave.h>
43#include <stdlib.h>
44
45/** Encode format chunk data.
46 *
47 * @params params WAVE parameters
48 * @params fmt Pointer to fmt chunk data structure to fill in
49 */
50static void rwave_encode_fmt(rwave_params_t *params, rwave_fmt_t *fmt)
51{
52 int bytes_smp;
53
54 bytes_smp = (params->bits_smp + 7) / 8;
55
56 fmt->format_tag = host2uint16_t_le(WFMT_PCM);
57 fmt->channels = host2uint16_t_le(params->channels);
58 fmt->smp_sec = host2uint32_t_le(params->smp_freq);
59 fmt->avg_bytes_sec = host2uint32_t_le(bytes_smp * params->smp_freq *
60 params->channels);
61 fmt->block_align = host2uint16_t_le(bytes_smp);
62 fmt->bits_smp = host2uint16_t_le(params->bits_smp);
63}
64
65/** Decode format chunk data.
66 *
67 * @param fmt Pointer to format chunk data
68 * @param params WAVE parameters to fill in
69 *
70 * @return EOK on success, EINVAL if format is not supported.
71 */
72static int rwave_decode_fmt(rwave_fmt_t *fmt, rwave_params_t *params)
73{
74 uint16_t fmt_tag;
75
76 fmt_tag = uint16_t_le2host(fmt->format_tag);
77 printf("fmt_tag=0x%x\n", fmt_tag);
78 if (fmt_tag != WFMT_PCM)
79 return EINVAL;
80
81 params->channels = uint16_t_le2host(fmt->channels);
82 params->smp_freq = uint32_t_le2host(fmt->smp_sec);
83 params->bits_smp = uint16_t_le2host(fmt->bits_smp);
84
85 return EOK;
86}
87
88/** Open WAVE file for writing.
89 *
90 * @param fname File name
91 * @param params WAVE file parameters
92 * @param rww Place to store pointer to WAVE writer
93 *
94 * @return EOK on success, EIO on I/O error, ENOMEM if out of memory.
95 */
96errno_t rwave_wopen(const char *fname, rwave_params_t *params, rwavew_t **rww)
97{
98 riff_wchunk_t fmt;
99 rwave_fmt_t rwfmt;
100 errno_t rc;
101 rwavew_t *ww;
102
103 rwave_encode_fmt(params, &rwfmt);
104
105 ww = calloc(1, sizeof(rwavew_t));
106 if (ww == NULL) {
107 rc = ENOMEM;
108 goto error;
109 }
110
111 ww->bufsize = 4096;
112 ww->buf = calloc(1, ww->bufsize);
113 if (ww->buf == NULL) {
114 rc = ENOMEM;
115 goto error;
116 }
117
118 /* Make a copy of parameters */
119 ww->params = *params;
120
121 rc = riff_wopen(fname, &ww->rw);
122 if (rc != EOK) {
123 assert(rc == EIO || rc == ENOMEM);
124 goto error;
125 }
126
127 rc = riff_wchunk_start(ww->rw, CKID_RIFF, &ww->wave);
128 if (rc != EOK)
129 goto error;
130
131 rc = riff_write_uint32(ww->rw, FORM_WAVE);
132 if (rc != EOK)
133 goto error;
134
135 rc = riff_wchunk_start(ww->rw, CKID_fmt, &fmt);
136 if (rc != EOK)
137 goto error;
138
139 rc = riff_write(ww->rw, &rwfmt, sizeof(rwfmt));
140 if (rc != EOK)
141 goto error;
142
143 rc = riff_wchunk_end(ww->rw, &fmt);
144 if (rc != EOK)
145 goto error;
146
147 rc = riff_wchunk_start(ww->rw, CKID_data, &ww->data);
148 if (rc != EOK)
149 goto error;
150
151 *rww = ww;
152 return EOK;
153error:
154 if (ww != NULL)
155 free(ww->buf);
156 if (ww->rw != NULL)
157 riff_wclose(ww->rw);
158 free(ww);
159 return rc;
160}
161
162/** Write samples to WAVE file.
163 *
164 * @param ww WAVE writer
165 * @param data Pointer to data
166 * @param bytes Number of bytes to write
167 *
168 * @return EOK on success, EIO on I/O error, ENOTSUP if sample format is
169 * not supported.
170 */
171errno_t rwave_write_samples(rwavew_t *ww, void *data, size_t bytes)
172{
173 size_t i;
174 uint16_t *d16, *b16;
175 size_t now;
176 errno_t rc;
177
178 /* Convert sample data to little endian */
179
180 while (bytes > 0) {
181 now = min(bytes, ww->bufsize);
182
183 switch (ww->params.bits_smp / 8) {
184 case 1:
185 memcpy(ww->buf, data, now);
186 break;
187 case 2:
188 b16 = (uint16_t *)ww->buf;
189 d16 = (uint16_t *)data;
190 for (i = 0; i < now / 2; i++) {
191 b16[i] = host2uint16_t_le(d16[i]);
192 }
193 break;
194 default:
195 return ENOTSUP;
196 }
197
198 rc = riff_write(ww->rw, ww->buf, now);
199 if (rc != EOK) {
200 assert(rc == EIO);
201 return rc;
202 }
203
204 bytes -= now;
205 data += now;
206 }
207
208 return EOK;
209}
210
211/** Close WAVE file for writing.
212 *
213 * @param ww WAVE writer
214 * @return EOK on success, EIO on I/O error - in which case @a ww is destroyed
215 * anyway.
216 */
217errno_t rwave_wclose(rwavew_t *ww)
218{
219 errno_t rc;
220
221 rc = riff_wchunk_end(ww->rw, &ww->wave);
222 if (rc == EOK)
223 rc = riff_wchunk_end(ww->rw, &ww->data);
224
225 rc = riff_wclose(ww->rw);
226
227 ww->rw = NULL;
228 free(ww->buf);
229 free(ww);
230
231 return rc;
232}
233
234/** Open WAVE file for reading.
235 *
236 * @param fname File name
237 * @param params WAVE file parameters
238 * @param rwaver Place to store pointer to new wave reader
239 *
240 * @return EOK on success, EIO on I/O error, ENOMEM if out of memory
241 */
242errno_t rwave_ropen(const char *fname, rwave_params_t *params, rwaver_t **rwr)
243{
244 rwaver_t *wr = NULL;
245 uint32_t form_id;
246 riff_rchunk_t fmt;
247 rwave_fmt_t wfmt;
248 size_t nread;
249 errno_t rc;
250
251 wr = calloc(1, sizeof(rwaver_t));
252 if (wr == NULL) {
253 rc = ENOMEM;
254 goto error;
255 }
256
257 rc = riff_ropen(fname, &wr->wave, &wr->rr);
258 if (rc != EOK) {
259 assert(rc == EIO || rc == ENOMEM);
260 goto error;
261 }
262
263 if (wr->wave.ckid != CKID_RIFF) {
264 printf("Not RIFF file\n");
265 rc = ENOMEM;
266 goto error;
267 }
268
269 rc = riff_read_uint32(&wr->wave, &form_id);
270 if (rc != EOK) {
271 assert(rc == EIO);
272 goto error;
273 }
274
275 if (form_id != FORM_WAVE) {
276 printf("wrong form ID\n");
277 rc = EIO;
278 goto error;
279 }
280
281 rc = riff_rchunk_start(&wr->wave, &fmt);
282 if (rc != EOK) {
283 assert(rc == EIO);
284 goto error;
285 }
286
287 if (fmt.ckid != CKID_fmt) {
288 printf("not fmt chunk\n");
289 rc = ENOMEM;
290 goto error;
291 }
292
293 rc = riff_read(&fmt, &wfmt, sizeof(rwave_fmt_t), &nread);
294 if (rc != EOK) {
295 printf("error reading fmt chunk\n");
296 assert(rc == EIO || rc == ELIMIT);
297 rc = EIO;
298 goto error;
299 }
300
301 if (nread < sizeof(rwave_fmt_t)) {
302 rc = EIO;
303 goto error;
304 }
305
306 rc = riff_rchunk_end(&fmt);
307 if (rc != EOK) {
308 assert(rc == EIO);
309 goto error;
310 }
311
312 rc = rwave_decode_fmt(&wfmt, params);
313 if (rc != EOK) {
314 printf("decode fmt fail\n");
315 assert(rc == EINVAL);
316 rc = EIO;
317 goto error;
318 }
319
320 rc = riff_rchunk_start(&wr->wave, &wr->data);
321 if (rc != EOK) {
322 assert(rc == EIO);
323 goto error;
324 }
325
326 if (wr->data.ckid != CKID_data) {
327 printf("not data ckid\n");
328 rc = EIO;
329 goto error;
330 }
331
332 *rwr = wr;
333 return EOK;
334error:
335 if (wr != NULL && wr->rr != NULL)
336 riff_rclose(wr->rr);
337 free(wr);
338 return rc;
339}
340
341/** Read samples from WAVE file.
342 *
343 * @param wr WAVE reader
344 * @param buf Buffer for storing the data
345 * @param bytes Number of bytes to read
346 * @param nread Place to store number of bytes actually read (>= 0).
347 *
348 * @return EOK if zero or more bytes successfully read and @a *nread is set,
349 * EIO on I/O error.
350 */
351errno_t rwave_read_samples(rwaver_t *wr, void *buf, size_t bytes, size_t *nread)
352{
353 errno_t rc;
354
355 rc = riff_read(&wr->data, buf, bytes, nread);
356 if (rc != EOK) {
357 assert(rc == EIO || rc == ELIMIT);
358 return EIO;
359 }
360
361 return EOK;
362}
363
364/** Close WAVE file for reading.
365 *
366 * @param wr WAVE reader
367 *
368 * @return EOK on success, EIO on I/O error in which case @a wr is destroyed
369 * anyway.
370 */
371errno_t rwave_rclose(rwaver_t *wr)
372{
373 errno_t rc;
374
375 rc = riff_rchunk_end(&wr->wave);
376 if (rc != EOK) {
377 assert(rc == EIO);
378 goto error;
379 }
380
381 riff_rclose(wr->rr);
382 free(wr);
383 return EOK;
384error:
385 return rc;
386}
387
388/** @}
389 */
Note: See TracBrowser for help on using the repository browser.