7d441964b2436ff2e972454171e0cb6b92290fa1
[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 /*
38 extern "C" { 
39 #include "license_key.h"
40 }
41 */
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <fcntl.h>
45 #include <math.h>
46 #include <string.h>
47
48 #if defined(WIN32)
49 #include <io.h>
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 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)
294 {
295         unsigned int chunklength, length = 0, offset = 16;
296         char data[4];
297         
298         if (CheckSample(sample))
299         {
300                 memcpy(&chunklength, ((char*)sample) + offset, 4);
301                 /* This was endian unsafe. See top of the file for the define. */
302                 if (SND_fEndian == SND_endianBig) SWITCH_INT(chunklength);
303
304                 offset = offset + chunklength + 4;
305                 memcpy(data, ((char*)sample) + offset, 4);
306
307                 /* This seems very unsafe, what if data is never found (f.i. corrupt file)... */
308                 // lets find "data"
309                 while (memcmp(data, "data", 4))
310                 {
311                         offset += 4;
312                         memcpy(data, ((char*)sample) + offset, 4);
313                 }
314                 offset += 4;
315                 memcpy(&length, ((char*)sample) + offset, 4);
316
317                 /* This was endian unsafe. See top of the file for the define. */
318                 if (SND_fEndian == SND_endianBig) SWITCH_INT(length);
319         }
320
321         return length;
322 }
323
324
325
326 /* gets the size of the entire header (file - sampledata) */
327 unsigned int SND_GetHeaderSize(void* sample)
328 {
329         unsigned int chunklength, headersize = 0, offset = 16;
330         char data[4];
331         
332         if (CheckSample(sample))
333         {
334                 memcpy(&chunklength, ((char*)sample) + offset, 4);
335                 /* This was endian unsafe. See top of the file for the define. */
336                 if (SND_fEndian == SND_endianBig) SWITCH_INT(chunklength);
337                 offset = offset + chunklength + 4;
338                 memcpy(data, ((char*)sample) + offset, 4);
339
340                 // lets find "data"
341                 while (memcmp(data, "data", 4))
342                 {
343                         offset += 4;
344                         memcpy(data, ((char*)sample) + offset, 4);
345                 }
346                 headersize = offset + 8;
347         }
348
349
350         return headersize;
351 }
352
353
354
355 unsigned int SND_GetExtraChunk(void* sample)
356 {
357         unsigned int extrachunk = 0, chunklength, offset = 16;
358         char data[4];
359
360         if (CheckSample(sample))
361         {
362                 memcpy(&chunklength, ((char*)sample) + offset, 4);
363                 offset = offset + chunklength + 4;
364                 memcpy(data, ((char*)sample) + offset, 4);
365
366                 // lets find "cue"
367                 while (memcmp(data, "cue", 3))
368                 {
369                         offset += 4;
370                         memcpy(data, ((char*)sample) + offset, 4);
371                 }
372         }
373
374         return extrachunk;
375 }
376
377
378
379 void SND_GetSampleInfo(signed char* sample, SND_WaveSlot* waveslot)
380 {       
381         WavFileHeader   fileheader;
382         WavFmtHeader    fmtheader;
383         WavFmtExHeader  fmtexheader;
384         WavSampleHeader sampleheader;
385         WavChunkHeader  chunkheader;
386         
387         if (CheckSample(sample))
388         {
389                 memcpy(&fileheader, sample, sizeof(WavFileHeader));
390                 fileheader.size = SND_GetHeaderSize(sample);
391                 sample += sizeof(WavFileHeader);
392                 fileheader.size = ((fileheader.size+1) & ~1) - 4;
393
394                 while ((fileheader.size > 0) && (memcpy(&chunkheader, sample, sizeof(WavChunkHeader))))
395                 {
396                         sample += sizeof(WavChunkHeader);
397                         if (!memcmp(chunkheader.id, "fmt ", 4))
398                         {
399                                 memcpy(&fmtheader, sample, sizeof(WavFmtHeader));
400                                 waveslot->SetSampleFormat(fmtheader.format);
401
402                                 if (fmtheader.format == 0x0001)
403                                 {
404                                         waveslot->SetNumberOfChannels(fmtheader.numberofchannels);
405                                         waveslot->SetBitRate(fmtheader.bitrate);
406                                         waveslot->SetSampleRate(fmtheader.samplerate);
407                                         sample += chunkheader.size;
408                                 } 
409                                 else
410                                 {
411                                         memcpy(&fmtexheader, sample, sizeof(WavFmtExHeader));
412                                         sample += chunkheader.size;
413                                 }
414                         }
415                         else if (!memcmp(chunkheader.id, "data", 4))
416                         {
417                                 if (fmtheader.format == 0x0001)
418                                 {
419                                         waveslot->SetNumberOfSamples(chunkheader.size);
420                                         sample += chunkheader.size;
421                                 }
422                                 else if (fmtheader.format == 0x0011)
423                                 {
424                                         //IMA ADPCM
425                                 }
426                                 else if (fmtheader.format == 0x0055)
427                                 {
428                                         //MP3 WAVE
429                                 }
430                         }
431                         else if (!memcmp(chunkheader.id, "smpl", 4))
432                         {
433                                 memcpy(&sampleheader, sample, sizeof(WavSampleHeader));
434                                 //loop = sampleheader.loops;
435                                 sample += chunkheader.size;
436                         }
437                         else
438                                 sample += chunkheader.size;
439
440                         sample += chunkheader.size & 1;
441                         fileheader.size -= (((chunkheader.size + 1) & ~1) + 8);
442                 }
443         }
444 }