1#include <stdlib.h>
2#include <string.h>
3
4#include "wuff_config.h"
5#include "wuff.h"
6#include "wuff_internal.h"
7#include "wuff_convert.h"
8
9
10wuff_sint32 wuff_setup(struct wuff_handle * handle)
11{
12 wuff_sint32 wuff_status;
13
14 if (handle == NULL)
15 return WUFF_INVALID_PARAM;
16
17 wuff_status = wuff_init_stream(handle);
18 WUFF_STATUS_BAIL()
19
20 /* Allocating the buffer for the handle requires information from the stream. */
21 wuff_status = wuff_buffer_alloc(handle);
22 WUFF_STATUS_BAIL()
23
24 /* The output format defaults to the stream format. */
25 wuff_status = wuff_format(handle, handle->stream.format);
26 WUFF_STATUS_BAIL()
27
28 return WUFF_SUCCESS;
29}
30
31wuff_sint32 wuff_cleanup(struct wuff_handle * handle)
32{
33 if (handle == NULL)
34 return WUFF_INVALID_PARAM;
35
36 if (handle->buffer.data != NULL)
37 wuff_free(handle->buffer.data);
38 wuff_free(handle);
39
40 return WUFF_SUCCESS;
41}
42
43wuff_sint32 wuff_set_output_format(struct wuff_handle * handle, wuff_uint16 format)
44{
45 wuff_uint16 bits;
46 wuff_uint16 stream_format;
47
48 if (handle == NULL)
49 return WUFF_INVALID_PARAM;
50 else if (format >= WUFF_FORMAT_MAX)
51 return WUFF_FORMAT_UNSUPPORTED;
52
53 stream_format = handle->stream.format;
54
55 switch (format)
56 {
57 case WUFF_FORMAT_PCM_U8:
58 bits = 8;
59 switch (stream_format)
60 {
61 case WUFF_FORMAT_PCM_U8:
62 handle->output.function = wuff_int8_to_int8;
63 break;
64 case WUFF_FORMAT_PCM_S16:
65 handle->output.function = wuff_int16_to_int8;
66 break;
67 case WUFF_FORMAT_PCM_S24:
68 handle->output.function = wuff_int24_to_int8;
69 break;
70 case WUFF_FORMAT_PCM_S32:
71 handle->output.function = wuff_int32_to_int8;
72 break;
73 case WUFF_FORMAT_IEEE_FLOAT_32:
74 handle->output.function = wuff_float32_to_int8;
75 break;
76 case WUFF_FORMAT_IEEE_FLOAT_64:
77 handle->output.function = wuff_float64_to_int8;
78 break;
79 }
80 break;
81 case WUFF_FORMAT_PCM_S16:
82 bits = 16;
83 switch (stream_format)
84 {
85 case WUFF_FORMAT_PCM_U8:
86 handle->output.function = wuff_int8_to_int16;
87 break;
88 case WUFF_FORMAT_PCM_S16:
89 handle->output.function = wuff_int16_to_int16;
90 break;
91 case WUFF_FORMAT_PCM_S24:
92 handle->output.function = wuff_int24_to_int16;
93 break;
94 case WUFF_FORMAT_PCM_S32:
95 handle->output.function = wuff_int32_to_int16;
96 break;
97 case WUFF_FORMAT_IEEE_FLOAT_32:
98 handle->output.function = wuff_float32_to_int16;
99 break;
100 case WUFF_FORMAT_IEEE_FLOAT_64:
101 handle->output.function = wuff_float64_to_int16;
102 break;
103 }
104 break;
105 case WUFF_FORMAT_PCM_S24:
106 bits = 24;
107 switch (stream_format)
108 {
109 case WUFF_FORMAT_PCM_U8:
110 handle->output.function = wuff_int8_to_int24;
111 break;
112 case WUFF_FORMAT_PCM_S16:
113 handle->output.function = wuff_int16_to_int24;
114 break;
115 case WUFF_FORMAT_PCM_S24:
116 handle->output.function = wuff_int24_to_int24;
117 break;
118 case WUFF_FORMAT_PCM_S32:
119 handle->output.function = wuff_int32_to_int24;
120 break;
121 case WUFF_FORMAT_IEEE_FLOAT_32:
122 handle->output.function = wuff_float32_to_int24;
123 break;
124 case WUFF_FORMAT_IEEE_FLOAT_64:
125 handle->output.function = wuff_float64_to_int24;
126 break;
127 }
128 break;
129 case WUFF_FORMAT_PCM_S32:
130 bits = 32;
131 switch (stream_format)
132 {
133 case WUFF_FORMAT_PCM_U8:
134 handle->output.function = wuff_int8_to_int32;
135 break;
136 case WUFF_FORMAT_PCM_S16:
137 handle->output.function = wuff_int16_to_int32;
138 break;
139 case WUFF_FORMAT_PCM_S24:
140 handle->output.function = wuff_int24_to_int32;
141 break;
142 case WUFF_FORMAT_PCM_S32:
143 handle->output.function = wuff_int32_to_int32;
144 break;
145 case WUFF_FORMAT_IEEE_FLOAT_32:
146 handle->output.function = wuff_float32_to_int32;
147 break;
148 case WUFF_FORMAT_IEEE_FLOAT_64:
149 handle->output.function = wuff_float64_to_int32;
150 break;
151 }
152 break;
153 case WUFF_FORMAT_IEEE_FLOAT_32:
154 bits = 32;
155 switch (stream_format)
156 {
157 case WUFF_FORMAT_PCM_U8:
158 handle->output.function = wuff_int8_to_float32;
159 break;
160 case WUFF_FORMAT_PCM_S16:
161 handle->output.function = wuff_int16_to_float32;
162 break;
163 case WUFF_FORMAT_PCM_S24:
164 handle->output.function = wuff_int24_to_float32;
165 break;
166 case WUFF_FORMAT_PCM_S32:
167 handle->output.function = wuff_int32_to_float32;
168 break;
169 case WUFF_FORMAT_IEEE_FLOAT_32:
170 handle->output.function = wuff_float32_to_float32;
171 break;
172 case WUFF_FORMAT_IEEE_FLOAT_64:
173 handle->output.function = wuff_float64_to_float32;
174 break;
175 }
176 break;
177 case WUFF_FORMAT_IEEE_FLOAT_64:
178 bits = 64;
179 switch (stream_format)
180 {
181 case WUFF_FORMAT_PCM_U8:
182 handle->output.function = wuff_int8_to_float64;
183 break;
184 case WUFF_FORMAT_PCM_S16:
185 handle->output.function = wuff_int16_to_float64;
186 break;
187 case WUFF_FORMAT_PCM_S24:
188 handle->output.function = wuff_int24_to_float64;
189 break;
190 case WUFF_FORMAT_PCM_S32:
191 handle->output.function = wuff_int32_to_float64;
192 break;
193 case WUFF_FORMAT_IEEE_FLOAT_32:
194 handle->output.function = wuff_float32_to_float64;
195 break;
196 case WUFF_FORMAT_IEEE_FLOAT_64:
197 handle->output.function = wuff_float64_to_float64;
198 break;
199 }
200 break;
201 default:
202 return WUFF_FORMAT_UNSUPPORTED;
203 }
204
205 handle->output.format = format;
206 handle->output.bytes_per_sample = bits / 8;
207 handle->output.block_size = handle->stream.header.channels * (bits / 8);
208
209 return WUFF_SUCCESS;
210}
211
212wuff_sint32 wuff_check_bits(wuff_uint16 bits, wuff_uint16 * format)
213{
214 if (*format == WUFF_FORMAT_PCM)
215 {
216 switch (bits)
217 {
218 case 8:
219 *format = WUFF_FORMAT_PCM_U8;
220 break;
221 case 16:
222 *format = WUFF_FORMAT_PCM_S16;
223 break;
224 case 24:
225 *format = WUFF_FORMAT_PCM_S24;
226 break;
227 case 32:
228 *format = WUFF_FORMAT_PCM_S32;
229 break;
230 default:
231 return WUFF_FORMAT_UNSUPPORTED;
232 }
233 }
234 else if (*format == WUFF_FORMAT_IEEE_FLOAT)
235 {
236 switch (bits)
237 {
238 case 32:
239 *format = WUFF_FORMAT_IEEE_FLOAT_32;
240 break;
241 case 64:
242 *format = WUFF_FORMAT_IEEE_FLOAT_64;
243 break;
244 default:
245 return WUFF_FORMAT_UNSUPPORTED;
246 }
247 }
248 else
249 {
250 return WUFF_FORMAT_UNSUPPORTED;
251 }
252
253 return WUFF_SUCCESS;
254}
255
256size_t wuff_calculate_samples(size_t target_size, wuff_uint8 sample_size, wuff_uint8 * head, wuff_uint8 * tail)
257{
258 size_t samples = 0;
259
260 if (*head != 0)
261 {
262 if (target_size <= *head)
263 {
264 *head = (wuff_uint8)target_size;
265 *tail = 0;
266 return 1;
267 }
268 target_size -= *head;
269 ++samples;
270 }
271
272 samples = target_size / sample_size;
273 *tail = target_size % sample_size;
274 if (*tail != 0)
275 ++samples;
276 return samples;
277}
278
279wuff_sint32 wuff_init_stream(struct wuff_handle * handle)
280{
281 /* Allocate some space on the stack. */
282 /* No need to do dynamic allocation for simple header probing. */
283 wuff_uint8 buffer[WUFF_HEADER_FETCH_SIZE];
284 size_t buffer_size = WUFF_HEADER_FETCH_SIZE;
285 wuff_uint64 search_offset;
286 struct wuff_chunk_header chunk;
287 wuff_sint32 wuff_status;
288
289 wuff_status = handle->callback->read(handle->userdata, buffer, &buffer_size);
290 WUFF_STATUS_BAIL()
291 else if (buffer_size < WUFF_STREAM_MIN_SIZE)
292 return WUFF_STREAM_NOT_RIFF;
293
294 /* Check for RIFF signature. */
295 wuff_copy_chunk_header_data(&chunk, buffer);
296 if (chunk.id != WUFF_RIFF_CHUNK_ID)
297 return WUFF_STREAM_NOT_RIFF;
298 handle->stream.size = chunk.size;
299
300 /* Check for WAVE format. */
301 wuff_copy_chunk_header_data(&chunk, buffer + 8);
302 if (chunk.id != WUFF_WAVE_CHUNK_ID)
303 return WUFF_STREAM_NOT_WAVE;
304
305 /* Search fmt chunk. */
306 wuff_copy_chunk_header_data(&chunk, buffer + 12);
307 search_offset = 12;
308 if (chunk.id != WUFF_FORMAT_CHUNK_ID)
309 {
310 chunk.id = 0;
311 /* The fmt chunk must appear before the data chunk. */
312 wuff_status = wuff_search_chunk(handle, &chunk, &search_offset, WUFF_FORMAT_CHUNK_ID, WUFF_DATA_CHUNK_ID);
313 if (wuff_status == WUFF_STREAM_CHUNK_NOT_FOUND)
314 return WUFF_STREAM_FORMAT_CHUNK_MISSING;
315 else WUFF_STATUS_BAIL()
316
317 /* In case the fmt chunk is not the first chunk, align it on the stack buffer as if it were. */
318 buffer_size = WUFF_HEADER_FETCH_SIZE - 20;
319 wuff_status = handle->callback->read(handle->userdata, buffer + 20, &buffer_size);
320 WUFF_STATUS_BAIL()
321 /* EOF bail. */
322 else if (buffer_size < WUFF_HEADER_MIN_SIZE)
323 return WUFF_STREAM_INVALID;
324 }
325
326 /* Extract header information. */
327 handle->stream.header.size = chunk.size;
328 handle->stream.header.offset = search_offset + 8;
329 handle->stream.header.format = wuff_get_uint16(buffer + 20);
330 handle->stream.header.channels = wuff_get_uint16(buffer + 22);
331 handle->stream.header.sample_rate = wuff_get_uint32(buffer + 24);
332 handle->stream.header.bits_per_sample = wuff_get_uint16(buffer + 34);
333 handle->stream.header.bytes_per_sample = handle->stream.header.bits_per_sample / 8;
334 handle->stream.header.block_size = handle->stream.header.channels * handle->stream.header.bytes_per_sample;
335
336 /* Bail on invalid streams. */
337 if (handle->stream.header.channels == 0)
338 return WUFF_STREAM_ZERO_CHANNELS;
339 else if (handle->stream.header.sample_rate == 0)
340 return WUFF_STREAM_ZERO_SAMPLE_RATE;
341 else if (handle->stream.header.bits_per_sample == 0)
342 return WUFF_STREAM_ZERO_BITS_PER_SAMPLE;
343
344 /* Grab the format from the extended header. */
345 if (handle->stream.header.size > WUFF_HEADER_MIN_SIZE && wuff_get_uint16(buffer + 36) == 22)
346 {
347 if (handle->stream.header.format == WUFF_FORMAT_EXTENSIBLE)
348 handle->stream.header.format = wuff_get_uint16(buffer + 44);
349 }
350
351 /* The check if this format is actually supported. */
352 handle->stream.format = handle->stream.header.format;
353 wuff_status = wuff_check_bits(handle->stream.header.bits_per_sample, &handle->stream.format);
354 WUFF_STATUS_BAIL()
355
356 /* The search for the data chunk begins. */
357 wuff_copy_chunk_header_data(&chunk, buffer + 20 + handle->stream.header.size);
358 search_offset = handle->stream.header.offset + handle->stream.header.size;
359 wuff_status = wuff_search_chunk(handle, &chunk, &search_offset, WUFF_DATA_CHUNK_ID, 0);
360 if (wuff_status == WUFF_STREAM_CHUNK_NOT_FOUND)
361 return WUFF_STREAM_DATA_CHUNK_MISSING;
362 else WUFF_STATUS_BAIL()
363
364 handle->stream.data.size = chunk.size;
365 handle->stream.data.offset = search_offset + 8;
366 handle->stream.length = handle->stream.data.size / handle->stream.header.channels / handle->stream.header.bytes_per_sample;
367 handle->stream.position = 0;
368
369 return WUFF_SUCCESS;
370}
371
372wuff_sint32 wuff_search_chunk(struct wuff_handle * handle, struct wuff_chunk_header * chunk, wuff_uint64 * offset, wuff_uint32 id, wuff_uint32 stop_id)
373{
374 wuff_uint8 buffer[8];
375 wuff_uint64 search_offset;
376 size_t buffer_size;
377 wuff_sint32 wuff_status = 0;
378
379 if (chunk->id != 0 && chunk->id == id)
380 return WUFF_SUCCESS;
381
382 /* Copy the current file position. */
383 search_offset = *offset;
384
385 while (wuff_status >= 0)
386 {
387 search_offset += 8 + chunk->size;
388 /* FIXME: Non-compliant RIFFs may not pad to WORD alignment. What now? */
389 if (search_offset & 1)
390 search_offset++;
391
392 wuff_status = handle->callback->seek(handle->userdata, search_offset);
393 WUFF_STATUS_BAIL()
394 /*else if (wuff_status == WUFF_CALLBACK_EOF)
395 return WUFF_STREAM_CHUNK_NOT_FOUND;*/
396
397 buffer_size = 8;
398 wuff_status = handle->callback->read(handle->userdata, buffer, &buffer_size);
399 WUFF_STATUS_BAIL()
400
401 wuff_copy_chunk_header_data(chunk, buffer);
402 /* Bail if we're at the EOF or the stop id. */
403 if (buffer_size < 8 || (stop_id != 0 && chunk->id == stop_id))
404 return WUFF_STREAM_CHUNK_NOT_FOUND;
405 else if (chunk->id == id)
406 break;
407 }
408
409 /* Report chunk offset. */
410 *offset = search_offset;
411
412 return WUFF_SUCCESS;
413}
414
415wuff_sint32 wuff_buffer_alloc(struct wuff_handle * handle)
416{
417 wuff_sint32 wuff_status;
418
419 if (handle == NULL)
420 return WUFF_INVALID_PARAM;
421
422 /* Try to allocate a buffer for 0.25 seconds, but clamp at some minimum and maximum value. */
423 handle->buffer.size = handle->stream.header.sample_rate * handle->stream.header.block_size / 4;
424 if (handle->buffer.size < WUFF_BUFFER_MIN_SIZE)
425 handle->buffer.size = WUFF_BUFFER_MIN_SIZE;
426 else if (handle->buffer.size > WUFF_BUFFER_MAX_SIZE)
427 handle->buffer.size = WUFF_BUFFER_MAX_SIZE;
428
429 handle->buffer.data = wuff_alloc(handle->buffer.size);
430 if (handle->buffer.data == NULL)
431 return WUFF_MEMALLOC_ERROR;
432
433 /* Just in case, let's null the offsets. */
434 wuff_status = wuff_buffer_clear(handle);
435 WUFF_STATUS_BAIL()
436
437 return WUFF_SUCCESS;
438}
439
440wuff_sint32 wuff_buffer_clear(struct wuff_handle * handle)
441{
442 wuff_uint64 position;
443 wuff_sint32 wuff_status;
444
445 if (handle == NULL)
446 return WUFF_INVALID_PARAM;
447
448 wuff_status = handle->callback->tell(handle->userdata, &position);
449 WUFF_STATUS_BAIL()
450
451 if (position < handle->stream.data.offset || position > handle->stream.data.offset + handle->stream.data.size)
452 return WUFF_BUFFER_INVALID_STREAM_POSITION;
453
454 handle->buffer.bytes_left = handle->stream.data.size - (position - handle->stream.data.offset);
455 handle->buffer.offset = 0;
456 handle->buffer.end = 0;
457
458 return WUFF_SUCCESS;
459}
460
461wuff_sint32 wuff_buffer_fill(struct wuff_handle * handle)
462{
463 size_t bytes_in_buffer;
464 size_t bytes_to_read;
465 wuff_sint32 wuff_status;
466
467 if (handle == NULL)
468 return WUFF_INVALID_PARAM;
469
470 /* Check if there are bytes in the buffer and move them to the start of the buffer. */
471 /* Probably not the most efficient way. Think on it some more! */
472 bytes_in_buffer = handle->buffer.end - handle->buffer.offset;
473
474 if (bytes_in_buffer == handle->buffer.size)
475 return WUFF_SUCCESS;
476 else if (bytes_in_buffer > 0)
477 memmove(handle->buffer.data, handle->buffer.data + handle->buffer.offset, bytes_in_buffer);
478
479 bytes_to_read = handle->buffer.size - bytes_in_buffer;
480 if (bytes_to_read > handle->buffer.bytes_left)
481 bytes_to_read = (size_t)handle->buffer.bytes_left;
482
483 wuff_status = handle->callback->read(handle->userdata, handle->buffer.data + bytes_in_buffer, &bytes_to_read);
484 WUFF_STATUS_BAIL()
485
486 handle->buffer.offset = 0;
487 handle->buffer.end = bytes_in_buffer + bytes_to_read;
488 handle->buffer.bytes_left -= bytes_to_read;
489
490 return WUFF_SUCCESS;
491}
492
493wuff_sint32 wuff_buffer_release(struct wuff_handle * handle, size_t samples)
494{
495 size_t size;
496
497 if (handle == NULL)
498 return WUFF_INVALID_PARAM;
499
500 size = samples * handle->stream.header.bytes_per_sample;
501
502 /* Check for an attempt to release more samples than the buffer could hold. */
503 /* "This should never happen." Let's throw an error anyway in case.*/
504 if (size > handle->buffer.end - handle->buffer.offset)
505 return WUFF_BUFFER_INVALID_SIZE;
506
507 handle->buffer.offset += size;
508
509 return WUFF_SUCCESS;
510}
511
512wuff_sint32 wuff_buffer_request(struct wuff_handle * handle, wuff_uint8 ** buffer, size_t * samples)
513{
514 size_t request_samples = *samples;
515 size_t buffer_samples, size;
516 size_t bps = handle->stream.header.bytes_per_sample;
517 wuff_sint32 wuff_status;
518
519 if (handle == NULL || buffer == NULL || samples == NULL)
520 return WUFF_INVALID_PARAM;
521
522 /* Fill the buffer some more if the requested size is bigger than the current data in the buffer. */
523 size = request_samples * bps;
524 if (size > handle->buffer.end - handle->buffer.offset)
525 {
526 wuff_status = wuff_buffer_fill(handle);
527 WUFF_STATUS_BAIL()
528 }
529
530 buffer_samples = (handle->buffer.end - handle->buffer.offset) / bps;
531
532 /* Report sample count change. */
533 if (buffer_samples < request_samples)
534 *samples = buffer_samples;
535
536 /* Report sample buffer start. */
537 *buffer = handle->buffer.data + handle->buffer.offset;
538
539 return WUFF_SUCCESS;
540}
541