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