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