music playback works on GNU/Linux
[dosdemo] / libs / mikmod / playercode / mwav.c
1 /*      MikMod sound library
2         (c) 2004, Raphael Assenat
3         (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS
4         for complete list.
5
6         This library is free software; you can redistribute it and/or modify
7         it under the terms of the GNU Library General Public License as
8         published by the Free Software Foundation; either version 2 of
9         the License, or (at your option) any later version.
10
11         This program is distributed in the hope that it will be useful,
12         but WITHOUT ANY WARRANTY; without even the implied warranty of
13         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14         GNU Library General Public License for more details.
15
16         You should have received a copy of the GNU Library General Public
17         License along with this library; if not, write to the Free Software
18         Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19         02111-1307, USA.
20 */
21
22 /*==============================================================================
23
24   WAV sample loader
25
26 ==============================================================================*/
27
28 /*
29    FIXME: Stereo .WAV files are not yet supported as samples.
30 */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39
40 #include <string.h>
41
42 #include "mikmod_internals.h"
43
44 #ifdef SUNOS
45 extern int fprintf(FILE *, const char *, ...);
46 #endif
47
48 static void extract_channel(const char *src, char *dst, int num_chan, int num_samples, int samp_size, int channel);
49
50
51 typedef struct WAV {
52         CHAR  rID[4];
53         ULONG rLen;
54         CHAR  wID[4];
55         CHAR  fID[4];
56         ULONG fLen;
57         UWORD wFormatTag;
58         UWORD nChannels;
59         ULONG nSamplesPerSec;
60         ULONG nAvgBytesPerSec;
61         UWORD nBlockAlign;
62         UWORD nFormatSpecific;
63 } WAV;
64
65 static BOOL isWaveFile(MREADER* reader)
66 {
67         WAV wh;
68
69         _mm_fseek(reader, SEEK_SET, 0);
70
71         /* read wav header */
72         _mm_read_string(wh.rID,4,reader);
73         wh.rLen = _mm_read_I_ULONG(reader);
74         _mm_read_string(wh.wID,4,reader);
75
76         /* check for correct header */
77         if(_mm_eof(reader)|| memcmp(wh.rID,"RIFF",4) || memcmp(wh.wID,"WAVE",4)) {
78                 return 0;
79         }
80         return 1;
81 }
82
83 static SAMPLE* Sample_LoadGeneric_internal_wav(MREADER* reader)
84 {
85         SAMPLE *si=NULL;
86         WAV wh;
87         BOOL have_fmt=0;
88
89         _mm_fseek(reader, SEEK_SET, 0);
90
91         /* read wav header */
92         _mm_read_string(wh.rID,4,reader);
93         wh.rLen = _mm_read_I_ULONG(reader);
94         _mm_read_string(wh.wID,4,reader);
95
96         /* check for correct header */
97         if(_mm_eof(reader)|| memcmp(wh.rID,"RIFF",4) || memcmp(wh.wID,"WAVE",4)) {
98                 _mm_errno = MMERR_UNKNOWN_WAVE_TYPE;
99                 return NULL;
100         }
101
102         /* scan all RIFF blocks until we find the sample data */
103         for(;;) {
104                 CHAR dID[4];
105                 ULONG len,start;
106
107                 _mm_read_string(dID,4,reader);
108                 len = _mm_read_I_ULONG(reader);
109                 /* truncated file ? */
110                 if (_mm_eof(reader)) {
111                         _mm_errno=MMERR_UNKNOWN_WAVE_TYPE;
112                         return NULL;
113                 }
114                 start = _mm_ftell(reader);
115
116                 /* sample format block
117                    should be present only once and before a data block */
118                 if(!memcmp(dID,"fmt ",4)) {
119                         wh.wFormatTag      = _mm_read_I_UWORD(reader);
120                         wh.nChannels       = _mm_read_I_UWORD(reader);
121                         wh.nSamplesPerSec  = _mm_read_I_ULONG(reader);
122                         wh.nAvgBytesPerSec = _mm_read_I_ULONG(reader);
123                         wh.nBlockAlign     = _mm_read_I_UWORD(reader);
124                         wh.nFormatSpecific = _mm_read_I_UWORD(reader);
125
126 #ifdef MIKMOD_DEBUG
127                         fprintf(stderr,"\rwavloader : wFormatTag=%04x blockalign=%04x nFormatSpc=%04x\n",
128                                 wh.wFormatTag,wh.nBlockAlign,wh.nFormatSpecific);
129 #endif
130
131                         if((have_fmt)||(wh.nChannels>1)) {
132                                 _mm_errno=MMERR_UNKNOWN_WAVE_TYPE;
133                                 return NULL;
134                         }
135                         have_fmt=1;
136                 } else
137                 /* sample data block
138                    should be present only once and after a format block */
139                   if(!memcmp(dID,"data",4)) {
140                         if(!have_fmt) {
141                                 _mm_errno=MMERR_UNKNOWN_WAVE_TYPE;
142                                 return NULL;
143                         }
144                         if(!(si=(SAMPLE*)MikMod_malloc(sizeof(SAMPLE)))) return NULL;
145                         si->speed  = wh.nSamplesPerSec/wh.nChannels;
146                         si->volume = 64;
147                         si->length = len;
148                         if(wh.nBlockAlign == 2) {
149                                 si->flags    = SF_16BITS | SF_SIGNED;
150                                 si->length >>= 1;
151                         }
152                         si->inflags = si->flags;
153                         SL_RegisterSample(si,MD_SNDFX,reader);
154                         SL_LoadSamples();
155
156                         /* skip any other remaining blocks - so in case of repeated sample
157                            fragments, we'll return the first anyway instead of an error */
158                         break;
159                 }
160                 /* onto next block */
161                 _mm_fseek(reader,start+len,SEEK_SET);
162                 if (_mm_eof(reader))
163                         break;
164         }
165
166         return si;
167 }
168
169 static SAMPLE* Sample_LoadRawGeneric_internal(MREADER* reader, ULONG rate, ULONG channel, ULONG flags)
170 {
171         SAMPLE *si;
172         long len;
173         int samp_size=1;
174
175         if(!(si=(SAMPLE*)MikMod_malloc(sizeof(SAMPLE)))) return NULL;
176
177         /* length */
178         _mm_fseek(reader, 0, SEEK_END);
179         len = _mm_ftell(reader);
180
181         si->panning = PAN_CENTER;
182         si->speed = rate/1;
183         si->volume = 64;
184         si->length = len;
185         si->loopstart=0;
186         si->loopend = len;
187         si->susbegin = 0;
188         si->susend = 0;
189         si->inflags = si->flags = flags;
190         if (si->flags & SF_16BITS) {
191                 si->length >>= 1;
192                 si->loopstart >>= 1;
193                 si->loopend >>= 1;
194                 samp_size = 2;
195         }
196
197         if (si->flags & SF_STEREO)
198         {
199                 char *data, *channel_data;
200                 int num_samp = si->length/samp_size/2;
201                 MREADER *chn_reader;
202
203                 data = (char*)MikMod_malloc(si->length);
204                 if (!data) { MikMod_free(si); return NULL; }
205
206                 channel_data = (char*)MikMod_malloc(si->length/2);
207                 if (!channel_data) { MikMod_free(data); MikMod_free(si); return NULL; }
208
209                 /* load the raw samples completely, and fully extract the
210                  * requested channel. Create a memory reader pointing to
211                  * the channel data. */
212                 _mm_fseek(reader, 0, SEEK_SET);
213                 reader->Read(reader, data, si->length);
214
215                 extract_channel(data, channel_data, 2, num_samp, samp_size, channel);
216                 chn_reader = _mm_new_mem_reader(channel_data, num_samp * samp_size);
217                 if (!chn_reader) {
218                         MikMod_free(channel_data);
219                         MikMod_free(data);
220                         MikMod_free(si);
221                         return NULL;
222                 }
223
224                 /* half of the samples were in the other channel */
225                 si->loopstart=0;
226                 si->length=num_samp;
227                 si->loopend=num_samp;
228
229                 SL_RegisterSample(si, MD_SNDFX, chn_reader);
230                 SL_LoadSamples();
231
232                 _mm_delete_mem_reader(chn_reader);
233                 MikMod_free(channel_data);
234                 MikMod_free(data);
235
236                 return si;
237         }
238
239         _mm_fseek(reader, 0, SEEK_SET);
240         SL_RegisterSample(si, MD_SNDFX, reader);
241         SL_LoadSamples();
242
243         return si;
244 }
245
246 static SAMPLE* Sample_LoadGeneric_internal(MREADER* reader, const char *options)
247 {
248         if (isWaveFile(reader)) {
249                 return Sample_LoadGeneric_internal_wav(reader);
250         }
251
252         return NULL;
253 }
254
255 MIKMODAPI SAMPLE* Sample_LoadRawGeneric(MREADER* reader, ULONG rate, ULONG channel, ULONG flags)
256 {
257         SAMPLE* result;
258
259         MUTEX_LOCK(vars);
260         result = Sample_LoadRawGeneric_internal(reader, rate, channel, flags);
261         MUTEX_UNLOCK(vars);
262
263         return result;
264 }
265
266 MIKMODAPI SAMPLE *Sample_LoadRawMem(const char *buf, int len, ULONG rate, ULONG channel, ULONG flags)
267 {
268         SAMPLE *result=NULL;
269         MREADER *reader;
270
271         if (!buf || len <= 0) return NULL;
272         if ((reader=_mm_new_mem_reader(buf, len)) != NULL) {
273                 result=Sample_LoadRawGeneric(reader, rate, channel, flags);
274                 _mm_delete_mem_reader(reader);
275         }
276         return result;
277 }
278
279 MIKMODAPI SAMPLE* Sample_LoadRawFP(FILE *fp, ULONG rate, ULONG channel, ULONG flags)
280 {
281         SAMPLE* result=NULL;
282         MREADER* reader;
283
284         if(fp && (reader=_mm_new_file_reader(fp)) != NULL) {
285                 result=Sample_LoadRawGeneric(reader, rate, channel, flags);
286                 _mm_delete_file_reader(reader);
287         }
288         return result;
289 }
290
291 MIKMODAPI SAMPLE* Sample_LoadRaw(const CHAR* filename, ULONG rate, ULONG channel, ULONG flags)
292 {
293         FILE *fp;
294         SAMPLE *si=NULL;
295
296         if(!(md_mode & DMODE_SOFT_SNDFX)) return NULL;
297         if((fp=_mm_fopen(filename,"rb")) != NULL) {
298                 si = Sample_LoadRawFP(fp, rate, channel, flags);
299                 _mm_fclose(fp);
300         }
301         return si;
302 }
303
304 MIKMODAPI SAMPLE* Sample_LoadGeneric(MREADER* reader)
305 {
306         SAMPLE* result;
307
308         MUTEX_LOCK(vars);
309         result=Sample_LoadGeneric_internal(reader, NULL);
310         MUTEX_UNLOCK(vars);
311
312         return result;
313 }
314
315 MIKMODAPI SAMPLE *Sample_LoadMem(const char *buf, int len)
316 {
317         SAMPLE* result=NULL;
318         MREADER* reader;
319
320         if (!buf || len <= 0) return NULL;
321         if ((reader=_mm_new_mem_reader(buf, len)) != NULL) {
322                 result=Sample_LoadGeneric(reader);
323                 _mm_delete_mem_reader(reader);
324         }
325         return result;
326 }
327
328 MIKMODAPI SAMPLE* Sample_LoadFP(FILE *fp)
329 {
330         SAMPLE* result=NULL;
331         MREADER* reader;
332
333         if(fp && (reader=_mm_new_file_reader(fp)) != NULL) {
334                 result=Sample_LoadGeneric(reader);
335                 _mm_delete_file_reader(reader);
336         }
337         return result;
338 }
339
340 MIKMODAPI SAMPLE* Sample_Load(const CHAR* filename)
341 {
342         FILE *fp;
343         SAMPLE *si=NULL;
344
345         if(!(md_mode & DMODE_SOFT_SNDFX)) return NULL;
346         if((fp=_mm_fopen(filename,"rb")) != NULL) {
347                 si = Sample_LoadFP(fp);
348                 _mm_fclose(fp);
349         }
350         return si;
351 }
352
353 MIKMODAPI void Sample_Free(SAMPLE* si)
354 {
355         if(si) {
356                 if (si->onfree) si->onfree(si->ctx);
357                 MD_SampleUnload(si->handle);
358                 MikMod_free(si);
359         }
360 }
361
362 void Sample_Free_internal(SAMPLE *si)
363 {
364         MUTEX_LOCK(vars);
365         Sample_Free(si);
366         MUTEX_UNLOCK(vars);
367 }
368
369 static void extract_channel(const char *src, char *dst, int num_chan, int num_samples, int samp_size, int channel)
370 {
371         int i;
372 #ifdef MIKMOD_DEBUG
373         fprintf(stderr,"Extract channel: %p %p, num_chan=%d, num_samples=%d, samp_size=%d, channel=%d\n",
374                 src,dst,num_chan,num_samples,samp_size,channel);
375 #endif
376         src += channel * samp_size;
377
378         while (num_samples--)
379         {
380                 for (i=0; i<samp_size; i++) {
381                         dst[i] = src[i];
382                 }
383                 src += samp_size * num_chan;
384                 dst += samp_size;
385         }
386 }
387
388 /* ex:set ts=8: */