Tizen 2.1 base
[external/freealut.git] / src / alutLoader.c
1 #include "alutInternal.h"
2 #include <ctype.h>
3
4 /****************************************************************************/
5
6 typedef enum
7 {
8   LittleEndian,
9   BigEndian,
10   UnknwonEndian                 /* has anybody still a PDP11? :-) */
11 } Endianess;
12
13 /* test from Harbison & Steele, "C - A Reference Manual", section 6.1.2 */
14 static Endianess
15 endianess (void)
16 {
17   union
18   {
19     long l;
20     char c[sizeof (long)];
21   } u;
22
23   u.l = 1;
24   return (u.c[0] == 1) ? LittleEndian :
25     ((u.c[sizeof (long) - 1] == 1) ? BigEndian : UnknwonEndian);
26 }
27
28 /****************************************************************************/
29
30 static int
31 safeToLower (int c)
32 {
33   return isupper (c) ? tolower (c) : c;
34 }
35
36 static int
37 hasSuffixIgnoringCase (const char *string, const char *suffix)
38 {
39   const char *stringPointer = string;
40   const char *suffixPointer = suffix;
41
42   if (suffix[0] == '\0')
43     {
44       return 1;
45     }
46
47   while (*stringPointer != '\0')
48     {
49       stringPointer++;
50     }
51
52   while (*suffixPointer != '\0')
53     {
54       suffixPointer++;
55     }
56
57   if (stringPointer - string < suffixPointer - suffix)
58     {
59       return 0;
60     }
61
62   while (safeToLower (*--suffixPointer) == safeToLower (*--stringPointer))
63     {
64       if (suffixPointer == suffix)
65         {
66           return 1;
67         }
68     }
69
70   return 0;
71 }
72
73 static BufferData *
74 loadWavFile (InputStream *stream)
75 {
76   ALboolean found_header = AL_FALSE;
77   UInt32LittleEndian chunkLength;
78   Int32BigEndian magic;
79   UInt16LittleEndian audioFormat;
80   UInt16LittleEndian numChannels;
81   UInt32LittleEndian sampleFrequency;
82   UInt32LittleEndian byteRate;
83   UInt16LittleEndian blockAlign;
84   UInt16LittleEndian bitsPerSample;
85   Codec *codec = _alutCodecLinear;
86
87   if (!_alutInputStreamReadUInt32LE (stream, &chunkLength) ||
88       !_alutInputStreamReadInt32BE (stream, &magic))
89     {
90       return NULL;
91     }
92
93   if (magic != 0x57415645)      /* "WAVE" */
94     {
95       _alutSetError (ALUT_ERROR_UNSUPPORTED_FILE_SUBTYPE);
96       return NULL;
97     }
98
99   while (1)
100     {
101       if (!_alutInputStreamReadInt32BE (stream, &magic) ||
102           !_alutInputStreamReadUInt32LE (stream, &chunkLength))
103         {
104           return NULL;
105         }
106
107       if (magic == 0x666d7420)  /* "fmt " */
108         {
109           found_header = AL_TRUE;
110
111           if (chunkLength < 16)
112             {
113               _alutSetError (ALUT_ERROR_CORRUPT_OR_TRUNCATED_DATA);
114               return NULL;
115             }
116
117           if (!_alutInputStreamReadUInt16LE (stream, &audioFormat) ||
118               !_alutInputStreamReadUInt16LE (stream, &numChannels) ||
119               !_alutInputStreamReadUInt32LE (stream, &sampleFrequency) ||
120               !_alutInputStreamReadUInt32LE (stream, &byteRate) ||
121               !_alutInputStreamReadUInt16LE (stream, &blockAlign) ||
122               !_alutInputStreamReadUInt16LE (stream, &bitsPerSample))
123             {
124               return NULL;
125             }
126
127           if (!_alutInputStreamSkip (stream, chunkLength - 16))
128             {
129               return NULL;
130             }
131
132           switch (audioFormat)
133             {
134             case 1:            /* PCM */
135               codec = (bitsPerSample == 8
136                        || endianess () ==
137                        LittleEndian) ? _alutCodecLinear : _alutCodecPCM16;
138               break;
139             case 7:            /* uLaw */
140               bitsPerSample *= 2;       /* ToDo: ??? */
141               codec = _alutCodecULaw;
142               break;
143             default:
144               _alutSetError (ALUT_ERROR_UNSUPPORTED_FILE_SUBTYPE);
145               return NULL;
146             }
147         }
148       else if (magic == 0x64617461)     /* "data" */
149         {
150           ALvoid *data;
151           if (!found_header)
152             {
153               /* ToDo: A bit wrong to check here, fmt chunk could come later... */
154               _alutSetError (ALUT_ERROR_CORRUPT_OR_TRUNCATED_DATA);
155               return NULL;
156             }
157           data = _alutInputStreamRead (stream, chunkLength);
158           if (data == NULL)
159             {
160               return NULL;
161             }
162           return codec (data, chunkLength, numChannels, bitsPerSample,
163                         (ALfloat) sampleFrequency);
164         }
165       else
166         {
167           if (!_alutInputStreamSkip (stream, chunkLength))
168             {
169               return NULL;
170             }
171         }
172
173       if ((chunkLength & 1) && !_alutInputStreamEOF (stream)
174           && !_alutInputStreamSkip (stream, 1))
175         {
176           return NULL;
177         }
178     }
179 }
180
181 static BufferData *
182 loadAUFile (InputStream *stream)
183 {
184   Int32BigEndian dataOffset;    /* byte offset to data part, minimum 24 */
185   Int32BigEndian len;           /* number of bytes in the data part, -1 = not known */
186   Int32BigEndian encoding;      /* encoding of the data part, see AUEncoding */
187   Int32BigEndian sampleFrequency;       /* number of samples per second */
188   Int32BigEndian numChannels;   /* number of interleaved channels */
189   size_t length;
190   Codec *codec;
191   char *data;
192   ALint bitsPerSample;
193
194   if (!_alutInputStreamReadInt32BE (stream, &dataOffset) ||
195       !_alutInputStreamReadInt32BE (stream, &len) ||
196       !_alutInputStreamReadInt32BE (stream, &encoding) ||
197       !_alutInputStreamReadInt32BE (stream, &sampleFrequency) ||
198       !_alutInputStreamReadInt32BE (stream, &numChannels))
199     {
200       return AL_FALSE;
201     }
202
203   length = (len == -1) ?
204     (_alutInputStreamGetRemainingLength (stream) - AU_HEADER_SIZE -
205      dataOffset) : (size_t) len;
206
207   if (!
208       (dataOffset >= AU_HEADER_SIZE && length > 0 && sampleFrequency >= 1
209        && numChannels >= 1))
210     {
211       _alutSetError (ALUT_ERROR_CORRUPT_OR_TRUNCATED_DATA);
212       return AL_FALSE;
213     }
214
215   if (!_alutInputStreamSkip (stream, dataOffset - AU_HEADER_SIZE))
216     {
217       return AL_FALSE;
218     }
219
220   switch (encoding)
221     {
222     case AU_ULAW_8:
223       bitsPerSample = 16;
224       codec = _alutCodecULaw;
225       break;
226     case AU_PCM_8:
227       bitsPerSample = 8;
228       codec = _alutCodecPCM8s;
229       break;
230     case AU_PCM_16:
231       bitsPerSample = 16;
232       codec =
233         (endianess () == BigEndian) ? _alutCodecLinear : _alutCodecPCM16;
234       break;
235     case AU_ALAW_8:
236       bitsPerSample = 16;
237       codec = _alutCodecALaw;
238       break;
239     default:
240       _alutSetError (ALUT_ERROR_UNSUPPORTED_FILE_SUBTYPE);
241       return AL_FALSE;
242     }
243
244   data = _alutInputStreamRead (stream, length);
245   if (data == NULL)
246     {
247       return NULL;
248     }
249   return codec (data, length, numChannels, bitsPerSample,
250                 (ALfloat) sampleFrequency);
251 }
252
253 static BufferData *
254 loadRawFile (InputStream *stream)
255 {
256   size_t length = _alutInputStreamGetRemainingLength (stream);
257   ALvoid *data = _alutInputStreamRead (stream, length);
258   if (data == NULL)
259     {
260       return NULL;
261     }
262   /* Guesses */
263   return _alutCodecLinear (data, length, 1, 8, 8000);
264 }
265
266 static BufferData *
267 loadFile (InputStream *stream)
268 {
269   const char *fileName;
270   Int32BigEndian magic;
271
272   /* Raw files have no magic number - so use the fileName extension */
273
274   fileName = _alutInputStreamGetFileName (stream);
275   if (fileName != NULL && hasSuffixIgnoringCase (fileName, ".raw"))
276     {
277       return loadRawFile (stream);
278     }
279
280   /* For other file formats, read the quasi-standard four byte magic number */
281   if (!_alutInputStreamReadInt32BE (stream, &magic))
282     {
283       return AL_FALSE;
284     }
285
286   /* Magic number 'RIFF' == Microsoft '.wav' format */
287   if (magic == 0x52494646)
288     {
289       return loadWavFile (stream);
290     }
291
292   /* Magic number '.snd' == Sun & Next's '.au' format */
293   if (magic == 0x2E736E64)
294     {
295       return loadAUFile (stream);
296     }
297
298   _alutSetError (ALUT_ERROR_UNSUPPORTED_FILE_TYPE);
299   return AL_FALSE;
300 }
301
302 ALuint
303 _alutCreateBufferFromInputStream (InputStream *stream)
304 {
305   BufferData *bufferData;
306   ALuint buffer;
307
308   if (stream == NULL)
309     {
310       return AL_NONE;
311     }
312
313   bufferData = loadFile (stream);
314   _alutInputStreamDestroy (stream);
315   if (bufferData == NULL)
316     {
317       return AL_NONE;
318     }
319
320   buffer = _alutPassBufferData (bufferData);
321   _alutBufferDataDestroy (bufferData);
322
323   return buffer;
324 }
325
326 ALuint
327 alutCreateBufferFromFile (const char *fileName)
328 {
329   InputStream *stream;
330   if (!_alutSanityCheck ())
331     {
332       return AL_NONE;
333     }
334   stream = _alutInputStreamConstructFromFile (fileName);
335   return _alutCreateBufferFromInputStream (stream);
336 }
337
338 ALuint
339 alutCreateBufferFromFileImage (const ALvoid *data, ALsizei length)
340 {
341   InputStream *stream;
342   if (!_alutSanityCheck ())
343     {
344       return AL_NONE;
345     }
346   stream = _alutInputStreamConstructFromMemory (data, length);
347   return _alutCreateBufferFromInputStream (stream);
348 }
349
350 void *
351 _alutLoadMemoryFromInputStream (InputStream *stream, ALenum *format,
352                                 ALsizei *size, ALfloat *frequency)
353 {
354   BufferData *bufferData;
355   ALenum fmt;
356   void *data;
357
358   if (stream == NULL)
359     {
360       return NULL;
361     }
362
363   bufferData = loadFile (stream);
364   if (bufferData == NULL)
365     {
366       _alutInputStreamDestroy (stream);
367       return NULL;
368     }
369   _alutInputStreamDestroy (stream);
370
371   if (!_alutGetFormat (bufferData, &fmt))
372     {
373       _alutBufferDataDestroy (bufferData);
374       return NULL;
375     }
376
377   if (size != NULL)
378     {
379       *size = (ALsizei) _alutBufferDataGetLength (bufferData);
380     }
381
382   if (format != NULL)
383     {
384       *format = fmt;
385     }
386
387   if (frequency != NULL)
388     {
389       *frequency = _alutBufferDataGetSampleFrequency (bufferData);
390     }
391
392   data = _alutBufferDataGetData (bufferData);
393   _alutBufferDataDetachData (bufferData);
394   _alutBufferDataDestroy (bufferData);
395   return data;
396 }
397
398 ALvoid *
399 alutLoadMemoryFromFile (const char *fileName, ALenum *format,
400                         ALsizei *size, ALfloat *frequency)
401 {
402   InputStream *stream;
403   if (!_alutSanityCheck ())
404     {
405       return NULL;
406     }
407   stream = _alutInputStreamConstructFromFile (fileName);
408   return _alutLoadMemoryFromInputStream (stream, format, size, frequency);
409 }
410
411 ALvoid *
412 alutLoadMemoryFromFileImage (const ALvoid *data, ALsizei length,
413                              ALenum *format, ALsizei *size,
414                              ALfloat *frequency)
415 {
416   InputStream *stream;
417   if (!_alutSanityCheck ())
418     {
419       return NULL;
420     }
421   stream = _alutInputStreamConstructFromMemory (data, length);
422   return _alutLoadMemoryFromInputStream (stream, format, size, frequency);
423 }
424
425 /*
426   Yukky backwards compatibility crap.
427 */
428
429 void
430 alutLoadWAVFile (ALbyte *fileName, ALenum *format, void **data, ALsizei *size,
431                  ALsizei *frequency
432 #if !defined(__APPLE__)
433                  , ALboolean *loop
434 #endif
435   )
436 {
437   InputStream *stream;
438   ALfloat freq;
439
440   /* Don't do an _alutSanityCheck () because it's not required in ALUT 0.x.x */
441
442   stream = _alutInputStreamConstructFromFile (fileName);
443   *data = _alutLoadMemoryFromInputStream (stream, format, size, &freq);
444   if (*data == NULL)
445     {
446       return;
447     }
448
449   if (frequency)
450     {
451       *frequency = (ALsizei) freq;
452     }
453
454 #if !defined(__APPLE__)
455   if (loop)
456     {
457       *loop = AL_FALSE;
458     }
459 #endif
460 }
461
462 void
463 alutLoadWAVMemory (ALbyte *buffer, ALenum *format, void **data, ALsizei *size,
464                    ALsizei *frequency
465 #if !defined(__APPLE__)
466                    , ALboolean *loop
467 #endif
468   )
469 {
470   InputStream *stream;
471   ALfloat freq;
472
473   /* Don't do an _alutSanityCheck () because it's not required in ALUT 0.x.x */
474
475   /* ToDo: Can we do something less insane than passing 0x7FFFFFFF? */
476   stream = _alutInputStreamConstructFromMemory (buffer, 0x7FFFFFFF);
477   _alutLoadMemoryFromInputStream (stream, format, size, &freq);
478   if (*data == NULL)
479     {
480       return;
481     }
482
483   if (frequency)
484     {
485       *frequency = (ALsizei) freq;
486     }
487
488 #if !defined(__APPLE__)
489   if (loop)
490     {
491       *loop = AL_FALSE;
492     }
493 #endif
494 }
495
496 void
497 alutUnloadWAV (ALenum UNUSED (format), ALvoid *data, ALsizei UNUSED (size),
498                ALsizei UNUSED (frequency))
499 {
500   /* Don't do an _alutSanityCheck () because it's not required in ALUT 0.x.x */
501
502   free (data);
503 }
504
505 const char *
506 alutGetMIMETypes (ALenum loader)
507 {
508   if (!_alutSanityCheck ())
509     {
510       return NULL;
511     }
512
513   /* We do not distinguish the loaders yet... */
514   switch (loader)
515     {
516     case ALUT_LOADER_BUFFER:
517       return "audio/basic,audio/x-raw,audio/x-wav";
518
519     case ALUT_LOADER_MEMORY:
520       return "audio/basic,audio/x-raw,audio/x-wav";
521
522     default:
523       _alutSetError (ALUT_ERROR_INVALID_ENUM);
524       return NULL;
525     }
526 }