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