]> icculus.org git repositories - divverent/darkplaces.git/blob - snd_modplug.c
oops... forgot this file, sorry
[divverent/darkplaces.git] / snd_modplug.c
1 /*
2         Copyright (C) 2003-2005  Mathieu Olivier
3
4         This program is free software; you can redistribute it and/or
5         modify it under the terms of the GNU General Public License
6         as published by the Free Software Foundation; either version 2
7         of the License, or (at your option) any later version.
8
9         This program is distributed in the hope that it will be useful,
10         but WITHOUT ANY WARRANTY; without even the implied warranty of
11         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13         See the GNU General Public License for more details.
14
15         You should have received a copy of the GNU General Public License
16         along with this program; if not, write to:
17
18                 Free Software Foundation, Inc.
19                 59 Temple Place - Suite 330
20                 Boston, MA  02111-1307, USA
21
22 */
23
24
25 #include "quakedef.h"
26 #include "snd_main.h"
27 #include "snd_modplug.h"
28
29 // BEGIN SECTION FROM modplug.h
30
31         /*
32          * This source code is public domain.
33          *
34          * Authors: Kenton Varda <temporal@gauge3d.org> (C interface wrapper)
35          */
36
37         enum _ModPlug_Flags
38         {
39                         MODPLUG_ENABLE_OVERSAMPLING     = 1 << 0,  /* Enable oversampling (*highly* recommended) */
40                         MODPLUG_ENABLE_NOISE_REDUCTION  = 1 << 1,  /* Enable noise reduction */
41                         MODPLUG_ENABLE_REVERB           = 1 << 2,  /* Enable reverb */
42                         MODPLUG_ENABLE_MEGABASS         = 1 << 3,  /* Enable megabass */
43                         MODPLUG_ENABLE_SURROUND         = 1 << 4   /* Enable surround sound. */
44         };
45
46         enum _ModPlug_ResamplingMode
47         {
48                         MODPLUG_RESAMPLE_NEAREST = 0,  /* No interpolation (very fast, extremely bad sound quality) */
49                         MODPLUG_RESAMPLE_LINEAR  = 1,  /* Linear interpolation (fast, good quality) */
50                         MODPLUG_RESAMPLE_SPLINE  = 2,  /* Cubic spline interpolation (high quality) */
51                         MODPLUG_RESAMPLE_FIR     = 3   /* 8-tap fir filter (extremely high quality) */
52         };
53
54         typedef struct _ModPlug_Settings
55         {
56                         int mFlags;  /* One or more of the MODPLUG_ENABLE_* flags above, bitwise-OR'ed */
57                         
58                         /* Note that ModPlug always decodes sound at 44100kHz, 32 bit, stereo and then
59                          * down-mixes to the settings you choose. */
60                         int mChannels;       /* Number of channels - 1 for mono or 2 for stereo */
61                         int mBits;           /* Bits per sample - 8, 16, or 32 */
62                         int mFrequency;      /* Sampling rate - 11025, 22050, or 44100 */
63                         int mResamplingMode; /* One of MODPLUG_RESAMPLE_*, above */
64                         
65                         int mReverbDepth;    /* Reverb level 0(quiet)-100(loud)      */
66                         int mReverbDelay;    /* Reverb delay in ms, usually 40-200ms */
67                         int mBassAmount;     /* XBass level 0(quiet)-100(loud)       */
68                         int mBassRange;      /* XBass cutoff in Hz 10-100            */
69                         int mSurroundDepth;  /* Surround level 0(quiet)-100(heavy)   */
70                         int mSurroundDelay;  /* Surround delay in ms, usually 5-40ms */
71                         int mLoopCount;      /* Number of times to loop.  Zero prevents looping.
72                                                                         -1 loops forever. */
73         } ModPlug_Settings;
74
75         struct _ModPlugFile;
76         typedef struct _ModPlugFile ModPlugFile;
77
78 // END SECTION FROM modplug.h
79
80 static ModPlugFile* (*ModPlug_Load) (const void* data, int size);
81 static void (*ModPlug_Unload) (ModPlugFile* file);
82 static int (*ModPlug_Read) (ModPlugFile* file, void* buffer, int size);
83 static void (*ModPlug_Seek) (ModPlugFile* file, int millisecond);
84 static void (*ModPlug_GetSettings) (ModPlug_Settings* settings);
85 static void (*ModPlug_SetSettings) (const ModPlug_Settings* settings);
86
87 static dllfunction_t modplugfuncs[] =
88 {
89         {"ModPlug_Load",                        (void **) &ModPlug_Load},
90         {"ModPlug_Unload",                      (void **) &ModPlug_Unload},
91         {"ModPlug_Read",                        (void **) &ModPlug_Read},
92         {"ModPlug_Seek",                        (void **) &ModPlug_Seek},
93         {"ModPlug_GetSettings",         (void **) &ModPlug_GetSettings},
94         {"ModPlug_SetSettings",         (void **) &ModPlug_SetSettings},
95         {NULL, NULL}
96 };
97
98 // Handles for the modplug and modplugfile DLLs
99 static dllhandle_t modplug_dll = NULL;
100
101 /*
102 =================================================================
103
104   DLL load & unload
105
106 =================================================================
107 */
108
109 /*
110 ====================
111 ModPlug_OpenLibrary
112
113 Try to load the modplugFile DLL
114 ====================
115 */
116 qboolean ModPlug_OpenLibrary (void)
117 {
118         const char* dllnames_modplug [] =
119         {
120 #if defined(WIN64)
121                 "libmodplug64.dll",
122 #elif defined(WIN32)
123                 "libmodplug-0.dll",
124                 "modplug.dll",
125 #elif defined(MACOSX)
126                 "libmodplug.dylib",
127 #else
128                 "libmodplug.so.0",
129                 "libmodplug.so",
130 #endif
131                 NULL
132         };
133
134         // Already loaded?
135         if (modplug_dll)
136                 return true;
137
138 // COMMANDLINEOPTION: Sound: -nomodplug disables modplug sound support
139         if (COM_CheckParm("-nomodplug"))
140                 return false;
141
142         // Load the DLLs
143         // We need to load both by hand because some OSes seem to not load
144         // the modplug DLL automatically when loading the modplugFile DLL
145         if (! Sys_LoadLibrary (dllnames_modplug, &modplug_dll, modplugfuncs))
146         {
147                 Sys_UnloadLibrary (&modplug_dll);
148                 Con_Printf ("ModPlug support disabled\n");
149                 return false;
150         }
151
152         Con_Printf ("ModPlug support enabled\n");
153         return true;
154 }
155
156
157 /*
158 ====================
159 ModPlug_CloseLibrary
160
161 Unload the modplugFile DLL
162 ====================
163 */
164 void ModPlug_CloseLibrary (void)
165 {
166         Sys_UnloadLibrary (&modplug_dll);
167 }
168
169
170 /*
171 =================================================================
172
173         modplug decoding
174
175 =================================================================
176 */
177
178 #define STREAM_BUFFER_DURATION 1.5f     // 1.5 sec
179 #define STREAM_BUFFER_SIZE(format_ptr) ((int)(ceil (STREAM_BUFFER_DURATION * ((format_ptr)->speed * (format_ptr)->width * (format_ptr)->channels))))
180 // We work with 1 sec sequences, so this buffer must be able to contain
181 // 1 sec of sound of the highest quality (48 KHz, 16 bit samples, stereo)
182 static unsigned char resampling_buffer [48000 * 2 * 2];
183
184
185 // Per-sfx data structure
186 typedef struct
187 {
188         unsigned char   *file;
189         size_t                  filesize;
190         snd_format_t    format;
191         unsigned int    total_length;
192         char                    name[128];
193         sfx_t           *sfx;
194 } modplug_stream_persfx_t;
195
196 // Per-channel data structure
197 typedef struct
198 {
199         ModPlugFile     *mf;
200         unsigned int    sb_offset;
201         int                             bs;
202         snd_buffer_t    sb;             // must be at the end due to its dynamically allocated size
203 } modplug_stream_perchannel_t;
204
205
206 /*
207 ====================
208 ModPlug_FetchSound
209 ====================
210 */
211 static const snd_buffer_t* ModPlug_FetchSound (void *sfxfetcher, void **chfetcherpointer, unsigned int *start, unsigned int nbsampleframes)
212 {
213         modplug_stream_perchannel_t* per_ch = (modplug_stream_perchannel_t *)*chfetcherpointer;
214         modplug_stream_persfx_t* per_sfx = (modplug_stream_persfx_t *)sfxfetcher;
215         snd_buffer_t* sb;
216         int newlength, done, ret, bigendian;
217         unsigned int real_start;
218         unsigned int factor;
219
220         // If there's no fetcher structure attached to the channel yet
221         if (per_ch == NULL)
222         {
223                 size_t buff_len, memsize;
224                 snd_format_t sb_format;
225
226                 sb_format.speed = snd_renderbuffer->format.speed;
227                 sb_format.width = per_sfx->format.width;
228                 sb_format.channels = per_sfx->format.channels;
229
230                 buff_len = STREAM_BUFFER_SIZE(&sb_format);
231                 memsize = sizeof (*per_ch) - sizeof (per_ch->sb.samples) + buff_len;
232                 per_ch = (modplug_stream_perchannel_t *)Mem_Alloc (snd_mempool, memsize);
233
234                 // Open it with the modplugFile API
235                 per_ch->mf = ModPlug_Load(per_sfx->file, per_sfx->filesize);
236                 if (!per_ch->mf)
237                 {
238                         Con_Printf("error while reading ModPlug stream \"%s\"\n", per_sfx->name);
239                         Mem_Free (per_ch);
240                         return NULL;
241                 }
242                 per_ch->bs = 0;
243
244                 per_ch->sb_offset = 0;
245                 per_ch->sb.format = sb_format;
246                 per_ch->sb.nbframes = 0;
247                 per_ch->sb.maxframes = buff_len / (per_ch->sb.format.channels * per_ch->sb.format.width);
248
249                 *chfetcherpointer = per_ch;
250         }
251
252         real_start = *start;
253
254         sb = &per_ch->sb;
255         factor = per_sfx->format.width * per_sfx->format.channels;
256
257         // If the stream buffer can't contain that much samples anyway
258         if (nbsampleframes > sb->maxframes)
259         {
260                 Con_Printf ("ModPlug_FetchSound: stream buffer too small (%u sample frames required)\n", nbsampleframes);
261                 return NULL;
262         }
263
264         // If the data we need has already been decompressed in the sfxbuffer, just return it
265         if (per_ch->sb_offset <= real_start && per_ch->sb_offset + sb->nbframes >= real_start + nbsampleframes)
266         {
267                 *start = per_ch->sb_offset;
268                 return sb;
269         }
270
271         newlength = (int)(per_ch->sb_offset + sb->nbframes) - real_start;
272
273         // If we need to skip some data before decompressing the rest, or if the stream has looped
274         if (newlength < 0 || per_ch->sb_offset > real_start)
275         {
276                 unsigned int time_start;
277                 unsigned int modplug_start;
278
279                 /*
280                 MODs loop on their own, so any position is valid!
281                 if (real_start > (unsigned int)per_sfx->total_length)
282                 {
283                         Con_Printf ("ModPlug_FetchSound: asked for a start position after the end of the sfx! (%u > %u)\n",
284                                                 real_start, per_sfx->total_length);
285                         return NULL;
286                 }
287                 */
288
289                 // We work with 200ms (1/5 sec) steps to avoid rounding errors
290                 time_start = real_start * 5 / snd_renderbuffer->format.speed;
291                 modplug_start = time_start * (1000 / 5);
292
293                 Con_DPrintf("warning: mod file needed to seek (to %d)\n", modplug_start);
294
295                 ModPlug_Seek(per_ch->mf, modplug_start);
296                 sb->nbframes = 0;
297
298                 real_start = (float)modplug_start / 1000 * snd_renderbuffer->format.speed;
299                 if (*start - real_start + nbsampleframes > sb->maxframes)
300                 {
301                         Con_Printf ("ModPlug_FetchSound: stream buffer too small after seek (%u sample frames required)\n",
302                                                 *start - real_start + nbsampleframes);
303                         per_ch->sb_offset = real_start;
304                         return NULL;
305                 }
306         }
307         // Else, move forward the samples we need to keep in the sound buffer
308         else
309         {
310                 memmove (sb->samples, sb->samples + (real_start - per_ch->sb_offset) * factor, newlength * factor);
311                 sb->nbframes = newlength;
312         }
313
314         per_ch->sb_offset = real_start;
315
316         // We add exactly 1 sec of sound to the buffer:
317         // 1- to ensure we won't lose any sample during the resampling process
318         // 2- to force one call to ModPlug_FetchSound per second to regulate the workload
319         if (sb->format.speed + sb->nbframes > sb->maxframes)
320         {
321                 Con_Printf ("ModPlug_FetchSound: stream buffer overflow (%u sample frames / %u)\n",
322                                         sb->format.speed + sb->nbframes, sb->maxframes);
323                 return NULL;
324         }
325         newlength = per_sfx->format.speed * factor;  // -> 1 sec of sound before resampling
326         if(newlength > (int)sizeof(resampling_buffer))
327                 newlength = sizeof(resampling_buffer);
328
329         // Decompress in the resampling_buffer
330 #if BYTE_ORDER == BIG_ENDIAN
331         bigendian = 1;
332 #else
333         bigendian = 0;
334 #endif
335         done = 0;
336         while ((ret = ModPlug_Read (per_ch->mf, (char *)&resampling_buffer[done], (int)(newlength - done))) > 0)
337                 done += ret;
338         if(done < newlength)
339         {
340                 // Argh. We didn't get as many samples as we wanted. Probably
341                 // libmodplug forgot what mLoopCount==-1 means... basically, this means
342                 // we can't loop like this. Try to let DP fix it later...
343                 per_sfx->sfx->total_length = (real_start + ((size_t)done / (size_t)factor));
344                 per_sfx->sfx->loopstart = 0;
345
346                 if(newlength != done)
347                         Con_DPrintf("ModPlug_Fetch: wanted: %d, got: %d\n", newlength, done);
348         }
349
350         Snd_AppendToSndBuffer (sb, resampling_buffer, (size_t)done / (size_t)factor, &per_sfx->format);
351
352         *start = per_ch->sb_offset;
353         return sb;
354 }
355
356
357 /*
358 ====================
359 ModPlug_FetchEnd
360 ====================
361 */
362 static void ModPlug_FetchEnd (void *chfetcherdata)
363 {
364         modplug_stream_perchannel_t* per_ch = (modplug_stream_perchannel_t *)chfetcherdata;
365
366         if (per_ch != NULL)
367         {
368                 // Free the modplug decoder
369                 ModPlug_Unload (per_ch->mf);
370
371                 Mem_Free (per_ch);
372         }
373 }
374
375
376 /*
377 ====================
378 ModPlug_FreeSfx
379 ====================
380 */
381 static void ModPlug_FreeSfx (void *sfxfetcherdata)
382 {
383         modplug_stream_persfx_t* per_sfx = (modplug_stream_persfx_t *)sfxfetcherdata;
384
385         // Free the modplug file
386         Mem_Free(per_sfx->file);
387
388         // Free the stream structure
389         Mem_Free(per_sfx);
390 }
391
392
393 /*
394 ====================
395 ModPlug_GetFormat
396 ====================
397 */
398 static const snd_format_t* ModPlug_GetFormat (sfx_t* sfx)
399 {
400         modplug_stream_persfx_t* per_sfx = (modplug_stream_persfx_t *)sfx->fetcher_data;
401         return &per_sfx->format;
402 }
403
404 static const snd_fetcher_t modplug_fetcher = { ModPlug_FetchSound, ModPlug_FetchEnd, ModPlug_FreeSfx, ModPlug_GetFormat };
405
406
407 /*
408 ====================
409 ModPlug_LoadmodplugFile
410
411 Load an modplug file into memory
412 ====================
413 */
414 qboolean ModPlug_LoadModPlugFile (const char *filename, sfx_t *sfx)
415 {
416         unsigned char *data;
417         fs_offset_t filesize;
418         ModPlugFile *mf;
419         modplug_stream_persfx_t* per_sfx;
420         ModPlug_Settings s;
421
422         if (!modplug_dll)
423                 return false;
424
425         // Already loaded?
426         if (sfx->fetcher != NULL)
427                 return true;
428
429         // Load the file
430         data = FS_LoadFile (filename, snd_mempool, false, &filesize);
431         if (data == NULL)
432                 return false;
433
434         Con_DPrintf ("Loading ModPlug file \"%s\"\n", filename);
435
436         ModPlug_GetSettings(&s);
437         s.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION | MODPLUG_ENABLE_REVERB;
438         s.mChannels = 2;
439         s.mBits = 16;
440         s.mFrequency = 44100;
441         s.mResamplingMode = MODPLUG_RESAMPLE_SPLINE;
442         s.mLoopCount = -1;
443         ModPlug_SetSettings(&s);
444
445         // Open it with the modplugFile API
446         if (!(mf = ModPlug_Load (data, filesize)))
447         {
448                 Con_Printf ("error while opening ModPlug file \"%s\"\n", filename);
449                 Mem_Free(data);
450                 return false;
451         }
452
453         Con_DPrintf ("\"%s\" will be streamed\n", filename);
454         per_sfx = (modplug_stream_persfx_t *)Mem_Alloc (snd_mempool, sizeof (*per_sfx));
455         strlcpy(per_sfx->name, sfx->name, sizeof(per_sfx->name));
456         sfx->memsize += sizeof (*per_sfx);
457         per_sfx->file = data;
458         per_sfx->filesize = filesize;
459         sfx->memsize += filesize;
460
461         per_sfx->format.speed = 44100; // modplug always works at that rate
462         per_sfx->format.width = 2;  // We always work with 16 bits samples
463         per_sfx->format.channels = 2; // stereo rulez ;) (MAYBE default to mono because Amiga MODs sound better then?)
464         per_sfx->sfx = sfx;
465
466         sfx->fetcher_data = per_sfx;
467         sfx->fetcher = &modplug_fetcher;
468         sfx->flags |= SFXFLAG_STREAMED;
469         sfx->total_length = 2147384647; // they always loop
470         sfx->loopstart = sfx->total_length; // modplug does it
471
472         return true;
473 }