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