enable openal support for os x and linux-powerpc
[blender-staging.git] / intern / SoundSystem / intern / SND_Utils.cpp
1 /*
2  * SND_Utils.cpp
3  *
4  * Util functions for soundthingies
5  *
6  * $Id$
7  *
8  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version. The Blender
14  * Foundation also sells licenses for use in proprietary software under
15  * the Blender License.  See http://www.blender.org/BL/ for information
16  * about this.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  *
27  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
28  * All rights reserved.
29  *
30  * The Original Code is: all of this file.
31  *
32  * Contributor(s): none yet.
33  *
34  * ***** END GPL/BL DUAL LICENSE BLOCK *****
35  */
36
37 #include "SND_Utils.h"
38 #include "SoundDefines.h"
39 #include "SND_DependKludge.h"
40 /*
41 extern "C" { 
42 #include "license_key.h"
43 }
44 */
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <fcntl.h>
48 #include <math.h>
49 #include <string.h>
50
51 #ifdef HAVE_CONFIG_H
52 #include <config.h>
53 #endif
54
55 #if defined(WIN32)
56 #include <io.h>
57 #else
58 #include <unistd.h>
59 #endif
60
61 #define BUFFERSIZE 32
62
63
64 /*****************************************************************************
65  * Begin of temporary Endian stuff.
66  * I think there should be a central place to handle endian conversion but for
67  * the time being it suffices. Note that the defines come from the Blender
68  * source.
69  *****************************************************************************/
70 typedef enum
71 {
72         SND_endianBig = 0,
73         SND_endianLittle
74 } SND_TEndian;
75
76 #ifdef __BIG_ENDIAN__
77 const SND_TEndian SND_fEndian = SND_endianBig;
78 #else
79 const SND_TEndian SND_fEndian = SND_endianLittle;
80 #endif
81
82 /* This one swaps the bytes in a short */
83 #define SWITCH_SHORT(a) { \
84     char s_i, *p_i; \
85     p_i= (char *)&(a); \
86     s_i=p_i[0]; \
87     p_i[0] = p_i[1]; \
88     p_i[1] = s_i; }
89
90 /* This one rotates the bytes in an int */
91 #define SWITCH_INT(a) { \
92     char s_i, *p_i; \
93     p_i= (char *)&(a); \
94     s_i=p_i[0]; p_i[0]=p_i[3]; p_i[3]=s_i; \
95     s_i=p_i[1]; p_i[1]=p_i[2]; p_i[2]=s_i; }
96 /*****************************************************************************
97  * End of temporary Endian stuff.
98  *****************************************************************************/
99
100
101 /* loads a file */
102 void* SND_LoadSample(char *filename)
103 {
104         int file, filelen, buffersize = BUFFERSIZE;
105         void* data = NULL;
106
107 #if defined(WIN32)      
108         file = open(filename, O_BINARY|O_RDONLY);
109 #else
110         file = open(filename, 0|O_RDONLY);
111 #endif
112
113         if (file == -1)
114         {
115                 //printf("can't open file.\n");
116                 //printf("press q for quit.\n");
117         }
118         else
119         {
120                 filelen = lseek(file, 0, SEEK_END);
121                 lseek(file, 0, SEEK_SET);
122                 
123                 if (filelen != 0)
124                 {
125                         data = malloc(buffersize);
126
127                         if (read(file, data, buffersize) != buffersize)
128                         {
129                                 free(data);
130                                 data = NULL;
131                         }
132                 }
133                 close(file);
134                 
135         }
136         return (data);
137 }
138
139
140
141 bool SND_IsSampleValid(const STR_String& name, void* memlocation)
142 {
143         bool result = false;
144         bool loadedsample = false;
145         char buffer[BUFFERSIZE];
146         
147         if (!memlocation)
148         {
149                 STR_String samplename = name;
150                 memlocation = SND_LoadSample(samplename.Ptr());
151                 
152                 if (memlocation)
153                         loadedsample = true;
154         }
155         
156         if (memlocation)
157         {
158                 memcpy(&buffer, memlocation, BUFFERSIZE);
159                 
160                 if(!(memcmp(buffer, "RIFF", 4) && memcmp(&(buffer[8]), "WAVEfmt ", 8)))
161                 {
162                         /* This was endian unsafe. See top of the file for the define. */
163                         short shortbuf = *((short *) &buffer[20]);
164                         if (SND_fEndian == SND_endianBig) SWITCH_SHORT(shortbuf);
165
166                         if (shortbuf == SND_WAVE_FORMAT_PCM)
167                                 result = true;
168                         
169                         /* only fmod supports compressed wav */
170 #ifdef USE_FMOD
171                         switch (shortbuf)
172                         {
173                                 case SND_WAVE_FORMAT_ADPCM:
174                                 case SND_WAVE_FORMAT_ALAW:
175                                 case SND_WAVE_FORMAT_MULAW:
176                                 case SND_WAVE_FORMAT_DIALOGIC_OKI_ADPCM:
177                                 case SND_WAVE_FORMAT_CONTROL_RES_VQLPC:
178                                 case SND_WAVE_FORMAT_GSM_610:
179                                 case SND_WAVE_FORMAT_MPEG3:
180                                         result = true;
181                                         break;
182                                 default:
183                                         {
184                                                 break;
185                                         }
186                         }
187 #endif
188                 }
189 #ifdef USE_FMOD
190                 /* only valid publishers may use ogg vorbis */
191                 else if (!memcmp(buffer, "OggS", 4))
192                 {
193                         result = true;
194                 }
195                 /* only valid publishers may use mp3 */
196                 else if (((!memcmp(buffer, "ID3", 3)) || (!memcmp(buffer, "ÿû", 2))))
197                 {
198                         result = true;
199                 }
200 #endif
201         }
202         if (loadedsample)
203         {
204                 free(memlocation);
205                 memlocation = NULL;
206         }
207
208         return result;
209 }
210
211
212
213 /* checks if the passed pointer is a valid sample */
214 bool CheckSample(void* sample)
215 {
216         bool valid = false;
217         char buffer[32];
218     
219         memcpy(buffer, sample, 16);
220
221         if(!(memcmp(buffer, "RIFF", 4) && memcmp(&(buffer[8]), "WAVEfmt ", 8)))
222         {
223                 valid = true;
224         }
225         
226         return valid;
227 }
228
229
230
231 /* gets the type of the sample (0 == unknown, 1 == PCM etc */
232 unsigned int SND_GetSampleFormat(void* sample)
233 {
234         short sampletype = 0;
235
236         if (CheckSample(sample))
237         {
238                 memcpy(&sampletype, ((char*)sample) + 20, 2);
239         }
240         /* This was endian unsafe. See top of the file for the define. */
241         if (SND_fEndian == SND_endianBig) SWITCH_SHORT(sampletype);
242
243         return (unsigned int)sampletype;
244 }
245
246
247
248 /* gets the number of channels in a sample */
249 unsigned int SND_GetNumberOfChannels(void* sample)
250 {
251         short numberofchannels = 0;
252
253         if (CheckSample(sample))
254         {
255                 memcpy(&numberofchannels, ((char*)sample) + 22, 2);
256         }
257         /* This was endian unsafe. See top of the file for the define. */
258         if (SND_fEndian == SND_endianBig) SWITCH_SHORT(numberofchannels);
259
260         return (unsigned int)numberofchannels;
261 }
262
263
264
265 /* gets the samplerate of a sample */
266 unsigned int SND_GetSampleRate(void* sample)
267 {
268         unsigned int samplerate = 0;
269         
270         if (CheckSample(sample))
271         {
272                 memcpy(&samplerate, ((char*)sample) + 24, 4);
273         }
274         /* This was endian unsafe. See top of the file for the define. */
275         if (SND_fEndian == SND_endianBig) SWITCH_INT(samplerate);
276
277         return samplerate;
278 }
279
280
281
282 /* gets the bitrate of a sample */
283 unsigned int SND_GetBitRate(void* sample)
284 {
285         short bitrate = 0;
286
287         if (CheckSample(sample))
288         {
289                 memcpy(&bitrate, ((char*)sample) + 34, 2);
290         }
291         /* This was endian unsafe. See top of the file for the define. */
292         if (SND_fEndian == SND_endianBig) SWITCH_SHORT(bitrate);
293
294         return (unsigned int)bitrate;
295 }
296
297
298
299 /* gets the length of the actual sample data (without the header) */
300 unsigned int SND_GetNumberOfSamples(void* sample)
301 {
302         unsigned int chunklength, length = 0, offset = 16;
303         char data[4];
304         
305         if (CheckSample(sample))
306         {
307                 memcpy(&chunklength, ((char*)sample) + offset, 4);
308                 /* This was endian unsafe. See top of the file for the define. */
309                 if (SND_fEndian == SND_endianBig) SWITCH_INT(chunklength);
310
311                 offset = offset + chunklength + 4;
312                 memcpy(data, ((char*)sample) + offset, 4);
313
314                 /* This seems very unsafe, what if data is never found (f.i. corrupt file)... */
315                 // lets find "data"
316                 while (memcmp(data, "data", 4))
317                 {
318                         offset += 4;
319                         memcpy(data, ((char*)sample) + offset, 4);
320                 }
321                 offset += 4;
322                 memcpy(&length, ((char*)sample) + offset, 4);
323
324                 /* This was endian unsafe. See top of the file for the define. */
325                 if (SND_fEndian == SND_endianBig) SWITCH_INT(length);
326         }
327
328         return length;
329 }
330
331
332
333 /* gets the size of the entire header (file - sampledata) */
334 unsigned int SND_GetHeaderSize(void* sample)
335 {
336         unsigned int chunklength, headersize = 0, offset = 16;
337         char data[4];
338         
339         if (CheckSample(sample))
340         {
341                 memcpy(&chunklength, ((char*)sample) + offset, 4);
342                 /* This was endian unsafe. See top of the file for the define. */
343                 if (SND_fEndian == SND_endianBig) SWITCH_INT(chunklength);
344                 offset = offset + chunklength + 4;
345                 memcpy(data, ((char*)sample) + offset, 4);
346
347                 // lets find "data"
348                 while (memcmp(data, "data", 4))
349                 {
350                         offset += 4;
351                         memcpy(data, ((char*)sample) + offset, 4);
352                 }
353                 headersize = offset + 8;
354         }
355
356
357         return headersize;
358 }
359
360
361
362 unsigned int SND_GetExtraChunk(void* sample)
363 {
364         unsigned int extrachunk = 0, chunklength, offset = 16;
365         char data[4];
366
367         if (CheckSample(sample))
368         {
369                 memcpy(&chunklength, ((char*)sample) + offset, 4);
370                 offset = offset + chunklength + 4;
371                 memcpy(data, ((char*)sample) + offset, 4);
372
373                 // lets find "cue"
374                 while (memcmp(data, "cue", 3))
375                 {
376                         offset += 4;
377                         memcpy(data, ((char*)sample) + offset, 4);
378                 }
379         }
380
381         return extrachunk;
382 }
383
384
385
386 void SND_GetSampleInfo(signed char* sample, SND_WaveSlot* waveslot)
387 {       
388         WavFileHeader   fileheader;
389         WavFmtHeader    fmtheader;
390         WavFmtExHeader  fmtexheader;
391         WavSampleHeader sampleheader;
392         WavChunkHeader  chunkheader;
393         
394         if (CheckSample(sample))
395         {
396                 memcpy(&fileheader, sample, sizeof(WavFileHeader));
397                 fileheader.size = SND_GetHeaderSize(sample);
398                 sample += sizeof(WavFileHeader);
399                 fileheader.size = ((fileheader.size+1) & ~1) - 4;
400
401                 while ((fileheader.size > 0) && (memcpy(&chunkheader, sample, sizeof(WavChunkHeader))))
402                 {
403                         sample += sizeof(WavChunkHeader);
404                         if (!memcmp(chunkheader.id, "fmt ", 4))
405                         {
406                                 memcpy(&fmtheader, sample, sizeof(WavFmtHeader));
407                                 waveslot->SetSampleFormat(fmtheader.format);
408
409                                 if (fmtheader.format == 0x0001)
410                                 {
411                                         waveslot->SetNumberOfChannels(fmtheader.numberofchannels);
412                                         waveslot->SetBitRate(fmtheader.bitrate);
413                                         waveslot->SetSampleRate(fmtheader.samplerate);
414                                         sample += chunkheader.size;
415                                 } 
416                                 else
417                                 {
418                                         memcpy(&fmtexheader, sample, sizeof(WavFmtExHeader));
419                                         sample += chunkheader.size;
420                                 }
421                         }
422                         else if (!memcmp(chunkheader.id, "data", 4))
423                         {
424                                 if (fmtheader.format == 0x0001)
425                                 {
426                                         waveslot->SetNumberOfSamples(chunkheader.size);
427                                         sample += chunkheader.size;
428                                 }
429                                 else if (fmtheader.format == 0x0011)
430                                 {
431                                         //IMA ADPCM
432                                 }
433                                 else if (fmtheader.format == 0x0055)
434                                 {
435                                         //MP3 WAVE
436                                 }
437                         }
438                         else if (!memcmp(chunkheader.id, "smpl", 4))
439                         {
440                                 memcpy(&sampleheader, sample, sizeof(WavSampleHeader));
441                                 //loop = sampleheader.loops;
442                                 sample += chunkheader.size;
443                         }
444                         else
445                                 sample += chunkheader.size;
446
447                         sample += chunkheader.size & 1;
448                         fileheader.size -= (((chunkheader.size + 1) & ~1) + 8);
449                 }
450         }
451 }