2 Copyright (C) 2003-2005 Mathieu Olivier
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.
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.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to:
18 Free Software Foundation, Inc.
19 59 Temple Place - Suite 330
20 Boston, MA 02111-1307, USA
27 #include "snd_modplug.h"
29 #ifdef SND_MODPLUG_STATIC
31 #include <libmodplug/modplug.h>
32 qboolean ModPlug_OpenLibrary (void)
34 return true; // statically linked
36 void ModPlug_CloseLibrary (void)
42 // BEGIN SECTION FROM modplug.h
45 * This source code is public domain.
47 * Authors: Kenton Varda <temporal@gauge3d.org> (C interface wrapper)
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. */
59 enum _ModPlug_ResamplingMode
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) */
67 typedef struct _ModPlug_Settings
69 int mFlags; /* One or more of the MODPLUG_ENABLE_* flags above, bitwise-OR'ed */
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 */
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.
89 typedef struct _ModPlugFile ModPlugFile;
91 // END SECTION FROM modplug.h
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;
103 static dllfunction_t modplugfuncs[] =
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},
114 // Handles for the modplug and modplugfile DLLs
115 static dllhandle_t modplug_dll = NULL;
118 =================================================================
122 =================================================================
129 Try to load the modplugFile DLL
132 qboolean ModPlug_OpenLibrary (void)
134 const char* dllnames_modplug [] =
141 #elif defined(MACOSX)
154 // COMMANDLINEOPTION: Sound: -nomodplug disables modplug sound support
155 if (COM_CheckParm("-nomodplug"))
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))
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");
177 Unload the modplugFile DLL
180 void ModPlug_CloseLibrary (void)
182 Sys_UnloadLibrary (&modplug_dll);
188 =================================================================
192 =================================================================
195 // Per-sfx data structure
201 unsigned int total_length;
204 } modplug_stream_persfx_t;
206 // Per-channel data structure
210 unsigned int sb_offset;
212 snd_buffer_t sb; // must be at the end due to its dynamically allocated size
213 } modplug_stream_perchannel_t;
221 static const snd_buffer_t* ModPlug_FetchSound (void *sfxfetcher, void **chfetcherpointer, unsigned int *start, unsigned int nbsampleframes)
223 modplug_stream_perchannel_t* per_ch = (modplug_stream_perchannel_t *)*chfetcherpointer;
224 modplug_stream_persfx_t* per_sfx = (modplug_stream_persfx_t *)sfxfetcher;
226 int newlength, done, ret;
227 unsigned int real_start;
230 // If there's no fetcher structure attached to the channel yet
233 size_t buff_len, memsize;
234 snd_format_t sb_format;
236 sb_format.speed = snd_renderbuffer->format.speed;
237 sb_format.width = per_sfx->format.width;
238 sb_format.channels = per_sfx->format.channels;
240 buff_len = STREAM_BUFFER_SIZE(&sb_format);
241 memsize = sizeof (*per_ch) - sizeof (per_ch->sb.samples) + buff_len;
242 per_ch = (modplug_stream_perchannel_t *)Mem_Alloc (snd_mempool, memsize);
244 // Open it with the modplugFile API
245 per_ch->mf = ModPlug_Load(per_sfx->file, per_sfx->filesize);
248 Con_Printf("error while reading ModPlug stream \"%s\"\n", per_sfx->name);
253 #ifndef SND_MODPLUG_STATIC
254 if(ModPlug_SetMasterVolume)
256 ModPlug_SetMasterVolume(per_ch->mf, 512); // max volume, DP scales down!
260 per_ch->sb_offset = 0;
261 per_ch->sb.format = sb_format;
262 per_ch->sb.nbframes = 0;
263 per_ch->sb.maxframes = buff_len / (per_ch->sb.format.channels * per_ch->sb.format.width);
265 *chfetcherpointer = per_ch;
271 factor = per_sfx->format.width * per_sfx->format.channels;
273 // If the stream buffer can't contain that much samples anyway
274 if (nbsampleframes > sb->maxframes)
276 Con_Printf ("ModPlug_FetchSound: stream buffer too small (%u sample frames required)\n", nbsampleframes);
280 // If the data we need has already been decompressed in the sfxbuffer, just return it
281 if (per_ch->sb_offset <= real_start && per_ch->sb_offset + sb->nbframes >= real_start + nbsampleframes)
283 *start = per_ch->sb_offset;
287 newlength = (int)(per_ch->sb_offset + sb->nbframes) - real_start;
289 // If we need to skip some data before decompressing the rest, or if the stream has looped
290 if (newlength < 0 || per_ch->sb_offset > real_start)
292 unsigned int time_start;
293 unsigned int modplug_start;
296 MODs loop on their own, so any position is valid!
297 if (real_start > (unsigned int)per_sfx->total_length)
299 Con_Printf ("ModPlug_FetchSound: asked for a start position after the end of the sfx! (%u > %u)\n",
300 real_start, per_sfx->total_length);
305 // We work with 200ms (1/5 sec) steps to avoid rounding errors
306 time_start = real_start * 5 / snd_renderbuffer->format.speed;
307 modplug_start = time_start * (1000 / 5);
309 Con_DPrintf("warning: mod file needed to seek (to %d)\n", modplug_start);
311 ModPlug_Seek(per_ch->mf, modplug_start);
314 real_start = (unsigned int) ((float)modplug_start / 1000 * snd_renderbuffer->format.speed);
315 if (*start - real_start + nbsampleframes > sb->maxframes)
317 Con_Printf ("ModPlug_FetchSound: stream buffer too small after seek (%u sample frames required)\n",
318 *start - real_start + nbsampleframes);
319 per_ch->sb_offset = real_start;
323 // Else, move forward the samples we need to keep in the sound buffer
326 memmove (sb->samples, sb->samples + (real_start - per_ch->sb_offset) * factor, newlength * factor);
327 sb->nbframes = newlength;
330 per_ch->sb_offset = real_start;
332 // We add more than one frame of sound to the buffer:
333 // 1- to ensure we won't lose many samples during the resampling process
334 // 2- to reduce calls to ModPlug_FetchSound to regulate workload
335 newlength = (int)(per_sfx->format.speed*STREAM_BUFFER_FILL);
336 if (newlength + sb->nbframes > sb->maxframes)
338 Con_Printf ("ModPlug_FetchSound: stream buffer overflow (%u sample frames / %u)\n",
339 sb->format.speed + sb->nbframes, sb->maxframes);
342 newlength *= factor; // convert from sample frames to bytes
343 if(newlength > (int)sizeof(resampling_buffer))
344 newlength = sizeof(resampling_buffer);
346 // Decompress in the resampling_buffer
348 while ((ret = ModPlug_Read (per_ch->mf, (char *)&resampling_buffer[done], (int)(newlength - done))) > 0)
352 // Argh. We didn't get as many samples as we wanted. Probably
353 // libmodplug forgot what mLoopCount==-1 means... basically, this means
354 // we can't loop like this. Try to let DP fix it later...
355 per_sfx->sfx->total_length = (real_start + ((size_t)done / (size_t)factor));
356 per_sfx->sfx->loopstart = 0;
358 if(newlength != done)
359 Con_DPrintf("ModPlug_Fetch: wanted: %d, got: %d\n", newlength, done);
362 Snd_AppendToSndBuffer (sb, resampling_buffer, (size_t)done / (size_t)factor, &per_sfx->format);
364 *start = per_ch->sb_offset;
374 static void ModPlug_FetchEnd (void *chfetcherdata)
376 modplug_stream_perchannel_t* per_ch = (modplug_stream_perchannel_t *)chfetcherdata;
380 // Free the modplug decoder
381 ModPlug_Unload (per_ch->mf);
393 static void ModPlug_FreeSfx (void *sfxfetcherdata)
395 modplug_stream_persfx_t* per_sfx = (modplug_stream_persfx_t *)sfxfetcherdata;
397 // Free the modplug file
398 Mem_Free(per_sfx->file);
400 // Free the stream structure
410 static const snd_format_t* ModPlug_GetFormat (sfx_t* sfx)
412 modplug_stream_persfx_t* per_sfx = (modplug_stream_persfx_t *)sfx->fetcher_data;
413 return &per_sfx->format;
416 static const snd_fetcher_t modplug_fetcher = { ModPlug_FetchSound, ModPlug_FetchEnd, ModPlug_FreeSfx, ModPlug_GetFormat };
421 ModPlug_LoadmodplugFile
423 Load an modplug file into memory
426 qboolean ModPlug_LoadModPlugFile (const char *filename, sfx_t *sfx)
429 fs_offset_t filesize;
431 modplug_stream_persfx_t* per_sfx;
438 if (sfx->fetcher != NULL)
442 data = FS_LoadFile (filename, snd_mempool, false, &filesize);
446 if (developer_loading.integer >= 2)
447 Con_Printf ("Loading ModPlug file \"%s\"\n", filename);
449 ModPlug_GetSettings(&s);
450 s.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION | MODPLUG_ENABLE_REVERB;
453 s.mFrequency = 44100;
454 s.mResamplingMode = MODPLUG_RESAMPLE_SPLINE;
456 ModPlug_SetSettings(&s);
458 // Open it with the modplugFile API
459 if (!(mf = ModPlug_Load (data, filesize)))
461 Con_Printf ("error while opening ModPlug file \"%s\"\n", filename);
466 #ifndef SND_MODPLUG_STATIC
467 if(ModPlug_SetMasterVolume)
469 ModPlug_SetMasterVolume(mf, 512); // max volume, DP scales down!
471 if (developer_loading.integer >= 2)
472 Con_Printf ("\"%s\" will be streamed\n", filename);
473 per_sfx = (modplug_stream_persfx_t *)Mem_Alloc (snd_mempool, sizeof (*per_sfx));
474 strlcpy(per_sfx->name, sfx->name, sizeof(per_sfx->name));
475 sfx->memsize += sizeof (*per_sfx);
476 per_sfx->file = data;
477 per_sfx->filesize = filesize;
478 sfx->memsize += filesize;
480 per_sfx->format.speed = 44100; // modplug always works at that rate
481 per_sfx->format.width = 2; // We always work with 16 bits samples
482 per_sfx->format.channels = 2; // stereo rulez ;) (MAYBE default to mono because Amiga MODs sound better then?)
485 sfx->fetcher_data = per_sfx;
486 sfx->fetcher = &modplug_fetcher;
487 sfx->flags |= SFXFLAG_STREAMED;
488 sfx->total_length = 2147384647; // they always loop
489 sfx->loopstart = sfx->total_length; // modplug does it