Audio SDL plugin. Compile and run on the OpenPandora
[mupen64plus-pandora.git] / source / mupen64plus-audio-sdl / src / main.c
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2  *   Mupen64plus-sdl-audio - main.c                                        *
3  *   Mupen64Plus homepage: http://code.google.com/p/mupen64plus/           *
4  *   Copyright (C) 2007-2009 Richard Goedeken                              *
5  *   Copyright (C) 2007-2008 Ebenblues                                     *
6  *   Copyright (C) 2003 JttL                                               *
7  *   Copyright (C) 2002 Hacktarux                                          *
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  *   This program is distributed in the hope that it will be useful,       *
15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
17  *   GNU General Public License for more details.                          *
18  *                                                                         *
19  *   You should have received a copy of the GNU General Public License     *
20  *   along with this program; if not, write to the                         *
21  *   Free Software Foundation, Inc.,                                       *
22  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
23  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <SDL.h>
30 #include <SDL_audio.h>
31
32 #ifdef USE_SRC
33 #include <samplerate.h>
34 #endif
35 #ifdef USE_SPEEX
36 #include <speex/speex_resampler.h>
37 #endif
38
39 #define M64P_PLUGIN_PROTOTYPES 1
40 #include "m64p_types.h"
41 #include "m64p_plugin.h"
42 #include "m64p_common.h"
43 #include "m64p_config.h"
44
45 #include "main.h"
46 #include "volume.h"
47 #include "osal_dynamiclib.h"
48
49 /* Default start-time size of primary buffer (in equivalent output samples).
50    This is the buffer where audio is loaded after it's extracted from n64's memory.
51    This value must be larger than PRIMARY_BUFFER_TARGET */
52 #define PRIMARY_BUFFER_SIZE 16384
53
54 /* this is the buffer fullness level (in equivalent output samples) which is targeted
55    for the primary audio buffer (by inserting delays) each time data is received from
56    the running N64 program.  This value must be larger than the SECONDARY_BUFFER_SIZE.
57    Decreasing this value will reduce audio latency but requires a faster PC to avoid
58    choppiness. Increasing this will increase audio latency but reduce the chance of
59    drop-outs. The default value 10240 gives a 232ms maximum A/V delay at 44.1khz */
60 #define PRIMARY_BUFFER_TARGET 10240
61
62 /* Size of secondary buffer, in output samples. This is the requested size of SDL's
63    hardware buffer, and the size of the mix buffer for doing SDL volume control. The
64    SDL documentation states that this should be a power of two between 512 and 8192. */
65 #define SECONDARY_BUFFER_SIZE 8192
66 /*SEB 2048 before*/
67
68 /* This sets default frequency what is used if rom doesn't want to change it.
69    Probably only game that needs this is Zelda: Ocarina Of Time Master Quest 
70    *NOTICE* We should try to find out why Demos' frequencies are always wrong
71    They tend to rely on a default frequency, apparently, never the same one ;)*/
72 #define DEFAULT_FREQUENCY 33600
73
74 /* number of bytes per sample */
75 #define N64_SAMPLE_BYTES 4
76 #define SDL_SAMPLE_BYTES 4
77
78 /* volume mixer types */
79 #define VOLUME_TYPE_SDL     1
80 #define VOLUME_TYPE_OSS     2
81
82 /* local variables */
83 static void (*l_DebugCallback)(void *, int, const char *) = NULL;
84 static void *l_DebugCallContext = NULL;
85 static int l_PluginInit = 0;
86 static int l_PausedForSync = 1; /* Audio is started in paused state after SDL initialization */
87 static m64p_handle l_ConfigAudio;
88
89 enum resampler_type {
90         RESAMPLER_TRIVIAL,
91 #ifdef USE_SRC
92         RESAMPLER_SRC,
93 #endif
94 #ifdef USE_SPEEX
95         RESAMPLER_SPEEX,
96 #endif
97 };
98
99 /* Read header for type definition */
100 static AUDIO_INFO AudioInfo;
101 /* The hardware specifications we are using */
102 static SDL_AudioSpec *hardware_spec;
103 /* Pointer to the primary audio buffer */
104 static unsigned char *primaryBuffer = NULL;
105 static unsigned int primaryBufferBytes = 0;
106 /* Pointer to the mixing buffer for voume control*/
107 static unsigned char *mixBuffer = NULL;
108 /* Position in buffer array where next audio chunk should be placed */
109 static unsigned int buffer_pos = 0;
110 /* Audio frequency, this is usually obtained from the game, but for compatibility we set default value */
111 static int GameFreq = DEFAULT_FREQUENCY;
112 /* timestamp for the last time that our audio callback was called */
113 static unsigned int last_callback_ticks = 0;
114 /* SpeedFactor is used to increase/decrease game playback speed */
115 static unsigned int speed_factor = 100;
116 // If this is true then left and right channels are swapped */
117 static int SwapChannels = 0;
118 // Size of Primary audio buffer in equivalent output samples
119 static unsigned int PrimaryBufferSize = PRIMARY_BUFFER_SIZE;
120 // Fullness level target for Primary audio buffer, in equivalent output samples
121 static unsigned int PrimaryBufferTarget = PRIMARY_BUFFER_TARGET;
122 // Size of Secondary audio buffer in output samples
123 static unsigned int SecondaryBufferSize = SECONDARY_BUFFER_SIZE;
124 // Resample type
125 static enum resampler_type Resample = RESAMPLER_TRIVIAL;
126 // Resampler specific quality
127 static int ResampleQuality = 3;
128 // volume to scale the audio by, range of 0..100
129 // if muted, this holds the volume when not muted
130 static int VolPercent = 80;
131 // how much percent to increment/decrement volume by
132 static int VolDelta = 5;
133 // the actual volume passed into SDL, range of 0..SDL_MIX_MAXVOLUME
134 static int VolSDL = SDL_MIX_MAXVOLUME;
135 // Muted or not
136 static int VolIsMuted = 0;
137 //which type of volume control to use
138 static int VolumeControlType = VOLUME_TYPE_SDL;
139
140 static int OutputFreq;
141
142 // Prototype of local functions
143 static void my_audio_callback(void *userdata, unsigned char *stream, int len);
144 static void InitializeAudio(int freq);
145 static void ReadConfig(void);
146 static void InitializeSDL(void);
147
148 static int critical_failure = 0;
149
150 /* definitions of pointers to Core config functions */
151 ptr_ConfigOpenSection      ConfigOpenSection = NULL;
152 ptr_ConfigDeleteSection    ConfigDeleteSection = NULL;
153 ptr_ConfigSaveSection      ConfigSaveSection = NULL;
154 ptr_ConfigSetParameter     ConfigSetParameter = NULL;
155 ptr_ConfigGetParameter     ConfigGetParameter = NULL;
156 ptr_ConfigGetParameterHelp ConfigGetParameterHelp = NULL;
157 ptr_ConfigSetDefaultInt    ConfigSetDefaultInt = NULL;
158 ptr_ConfigSetDefaultFloat  ConfigSetDefaultFloat = NULL;
159 ptr_ConfigSetDefaultBool   ConfigSetDefaultBool = NULL;
160 ptr_ConfigSetDefaultString ConfigSetDefaultString = NULL;
161 ptr_ConfigGetParamInt      ConfigGetParamInt = NULL;
162 ptr_ConfigGetParamFloat    ConfigGetParamFloat = NULL;
163 ptr_ConfigGetParamBool     ConfigGetParamBool = NULL;
164 ptr_ConfigGetParamString   ConfigGetParamString = NULL;
165
166 /* Global functions */
167 static void DebugMessage(int level, const char *message, ...)
168 {
169   char msgbuf[1024];
170   va_list args;
171
172   if (l_DebugCallback == NULL)
173       return;
174
175   va_start(args, message);
176   vsprintf(msgbuf, message, args);
177
178   (*l_DebugCallback)(l_DebugCallContext, level, msgbuf);
179
180   va_end(args);
181 }
182
183 /* Mupen64Plus plugin functions */
184 EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context,
185                                    void (*DebugCallback)(void *, int, const char *))
186 {
187     ptr_CoreGetAPIVersions CoreAPIVersionFunc;
188     
189     int ConfigAPIVersion, DebugAPIVersion, VidextAPIVersion, bSaveConfig;
190     float fConfigParamsVersion = 0.0f;
191     
192     if (l_PluginInit)
193         return M64ERR_ALREADY_INIT;
194
195     /* first thing is to set the callback function for debug info */
196     l_DebugCallback = DebugCallback;
197     l_DebugCallContext = Context;
198
199     /* attach and call the CoreGetAPIVersions function, check Config API version for compatibility */
200     CoreAPIVersionFunc = (ptr_CoreGetAPIVersions) osal_dynlib_getproc(CoreLibHandle, "CoreGetAPIVersions");
201     if (CoreAPIVersionFunc == NULL)
202     {
203         DebugMessage(M64MSG_ERROR, "Core emulator broken; no CoreAPIVersionFunc() function found.");
204         return M64ERR_INCOMPATIBLE;
205     }
206     
207     (*CoreAPIVersionFunc)(&ConfigAPIVersion, &DebugAPIVersion, &VidextAPIVersion, NULL);
208     if ((ConfigAPIVersion & 0xffff0000) != (CONFIG_API_VERSION & 0xffff0000))
209     {
210         DebugMessage(M64MSG_ERROR, "Emulator core Config API (v%i.%i.%i) incompatible with plugin (v%i.%i.%i)",
211                 VERSION_PRINTF_SPLIT(ConfigAPIVersion), VERSION_PRINTF_SPLIT(CONFIG_API_VERSION));
212         return M64ERR_INCOMPATIBLE;
213     }
214
215     /* Get the core config function pointers from the library handle */
216     ConfigOpenSection = (ptr_ConfigOpenSection) osal_dynlib_getproc(CoreLibHandle, "ConfigOpenSection");
217     ConfigDeleteSection = (ptr_ConfigDeleteSection) osal_dynlib_getproc(CoreLibHandle, "ConfigDeleteSection");
218     ConfigSaveSection = (ptr_ConfigSaveSection) osal_dynlib_getproc(CoreLibHandle, "ConfigSaveSection");
219     ConfigSetParameter = (ptr_ConfigSetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigSetParameter");
220     ConfigGetParameter = (ptr_ConfigGetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParameter");
221     ConfigSetDefaultInt = (ptr_ConfigSetDefaultInt) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultInt");
222     ConfigSetDefaultFloat = (ptr_ConfigSetDefaultFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultFloat");
223     ConfigSetDefaultBool = (ptr_ConfigSetDefaultBool) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultBool");
224     ConfigSetDefaultString = (ptr_ConfigSetDefaultString) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultString");
225     ConfigGetParamInt = (ptr_ConfigGetParamInt) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamInt");
226     ConfigGetParamFloat = (ptr_ConfigGetParamFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamFloat");
227     ConfigGetParamBool = (ptr_ConfigGetParamBool) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamBool");
228     ConfigGetParamString = (ptr_ConfigGetParamString) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamString");
229
230     if (!ConfigOpenSection || !ConfigDeleteSection || !ConfigSetParameter || !ConfigGetParameter ||
231         !ConfigSetDefaultInt || !ConfigSetDefaultFloat || !ConfigSetDefaultBool || !ConfigSetDefaultString ||
232         !ConfigGetParamInt   || !ConfigGetParamFloat   || !ConfigGetParamBool   || !ConfigGetParamString)
233         return M64ERR_INCOMPATIBLE;
234
235     /* ConfigSaveSection was added in Config API v2.1.0 */
236     if (ConfigAPIVersion >= 0x020100 && !ConfigSaveSection)
237         return M64ERR_INCOMPATIBLE;
238
239     /* get a configuration section handle */
240     if (ConfigOpenSection("Audio-SDL", &l_ConfigAudio) != M64ERR_SUCCESS)
241     {
242         DebugMessage(M64MSG_ERROR, "Couldn't open config section 'Audio-SDL'");
243         return M64ERR_INPUT_NOT_FOUND;
244     }
245
246     /* check the section version number */
247     bSaveConfig = 0;
248     if (ConfigGetParameter(l_ConfigAudio, "Version", M64TYPE_FLOAT, &fConfigParamsVersion, sizeof(float)) != M64ERR_SUCCESS)
249     {
250         DebugMessage(M64MSG_WARNING, "No version number in 'Audio-SDL' config section. Setting defaults.");
251         ConfigDeleteSection("Audio-SDL");
252         ConfigOpenSection("Audio-SDL", &l_ConfigAudio);
253         bSaveConfig = 1;
254     }
255     else if (((int) fConfigParamsVersion) != ((int) CONFIG_PARAM_VERSION))
256     {
257         DebugMessage(M64MSG_WARNING, "Incompatible version %.2f in 'Audio-SDL' config section: current is %.2f. Setting defaults.", fConfigParamsVersion, (float) CONFIG_PARAM_VERSION);
258         ConfigDeleteSection("Audio-SDL");
259         ConfigOpenSection("Audio-SDL", &l_ConfigAudio);
260         bSaveConfig = 1;
261     }
262     else if ((CONFIG_PARAM_VERSION - fConfigParamsVersion) >= 0.0001f)
263     {
264         /* handle upgrades */
265         float fVersion = CONFIG_PARAM_VERSION;
266         ConfigSetParameter(l_ConfigAudio, "Version", M64TYPE_FLOAT, &fVersion);
267         DebugMessage(M64MSG_INFO, "Updating parameter set version in 'Audio-SDL' config section to %.2f", fVersion);
268         bSaveConfig = 1;
269     }
270
271     /* set the default values for this plugin */
272     ConfigSetDefaultFloat(l_ConfigAudio, "Version",             CONFIG_PARAM_VERSION,  "Mupen64Plus SDL Audio Plugin config parameter version number");
273     ConfigSetDefaultInt(l_ConfigAudio, "DEFAULT_FREQUENCY",     DEFAULT_FREQUENCY,     "Frequency which is used if rom doesn't want to change it");
274     ConfigSetDefaultBool(l_ConfigAudio, "SWAP_CHANNELS",        0,                     "Swaps left and right channels");
275     ConfigSetDefaultInt(l_ConfigAudio, "PRIMARY_BUFFER_SIZE",   PRIMARY_BUFFER_SIZE,   "Size of primary buffer in output samples. This is where audio is loaded after it's extracted from n64's memory.");
276     ConfigSetDefaultInt(l_ConfigAudio, "PRIMARY_BUFFER_TARGET", PRIMARY_BUFFER_TARGET, "Fullness level target for Primary audio buffer, in equivalent output samples");
277     ConfigSetDefaultInt(l_ConfigAudio, "SECONDARY_BUFFER_SIZE", SECONDARY_BUFFER_SIZE, "Size of secondary buffer in output samples. This is SDL's hardware buffer.");
278     ConfigSetDefaultString(l_ConfigAudio, "RESAMPLE",              "trivial",                     "Audio resampling algorithm. src-sinc-best-quality, src-sinc-medium-quality, src-sinc-fastest, src-zero-order-hold, src-linear, speex-fixed-{10-0}, trivial");
279     ConfigSetDefaultInt(l_ConfigAudio, "VOLUME_CONTROL_TYPE",   VOLUME_TYPE_SDL,       "Volume control type: 1 = SDL (only affects Mupen64Plus output)  2 = OSS mixer (adjusts master PC volume)");
280     ConfigSetDefaultInt(l_ConfigAudio, "VOLUME_ADJUST",         5,                     "Percentage change each time the volume is increased or decreased");
281     ConfigSetDefaultInt(l_ConfigAudio, "VOLUME_DEFAULT",        80,                    "Default volume when a game is started.  Only used if VOLUME_CONTROL_TYPE is 1");
282
283     if (bSaveConfig && ConfigAPIVersion >= 0x020100)
284         ConfigSaveSection("Audio-SDL");
285
286     l_PluginInit = 1;
287     return M64ERR_SUCCESS;
288 }
289
290 EXPORT m64p_error CALL PluginShutdown(void)
291 {
292     if (!l_PluginInit)
293         return M64ERR_NOT_INIT;
294
295     /* reset some local variables */
296     l_DebugCallback = NULL;
297     l_DebugCallContext = NULL;
298
299     /* make sure our buffer is freed */
300     if (mixBuffer != NULL)
301     {
302         free(mixBuffer);
303         mixBuffer = NULL;
304     }
305
306     l_PluginInit = 0;
307     return M64ERR_SUCCESS;
308 }
309
310 EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities)
311 {
312     /* set version info */
313     if (PluginType != NULL)
314         *PluginType = M64PLUGIN_AUDIO;
315
316     if (PluginVersion != NULL)
317         *PluginVersion = SDL_AUDIO_PLUGIN_VERSION;
318
319     if (APIVersion != NULL)
320         *APIVersion = AUDIO_PLUGIN_API_VERSION;
321     
322     if (PluginNamePtr != NULL)
323         *PluginNamePtr = "Mupen64Plus SDL Audio Plugin";
324
325     if (Capabilities != NULL)
326     {
327         *Capabilities = 0;
328     }
329                     
330     return M64ERR_SUCCESS;
331 }
332
333 /* ----------- Audio Functions ------------- */
334 EXPORT void CALL AiDacrateChanged( int SystemType )
335 {
336     int f = GameFreq;
337
338     if (!l_PluginInit)
339         return;
340
341     switch (SystemType)
342     {
343         case SYSTEM_NTSC:
344             f = 48681812 / (*AudioInfo.AI_DACRATE_REG + 1);
345             break;
346         case SYSTEM_PAL:
347             f = 49656530 / (*AudioInfo.AI_DACRATE_REG + 1);
348             break;
349         case SYSTEM_MPAL:
350             f = 48628316 / (*AudioInfo.AI_DACRATE_REG + 1);
351             break;
352     }
353     InitializeAudio(f);
354 }
355
356
357 EXPORT void CALL AiLenChanged( void )
358 {
359     unsigned int LenReg;
360     unsigned char *p;
361     unsigned int CurrLevel, CurrTime, ExpectedLevel, ExpectedTime;
362
363     if (critical_failure == 1)
364         return;
365     if (!l_PluginInit)
366         return;
367
368     LenReg = *AudioInfo.AI_LEN_REG;
369     p = AudioInfo.RDRAM + (*AudioInfo.AI_DRAM_ADDR_REG & 0xFFFFFF);
370
371     if (buffer_pos + LenReg < primaryBufferBytes)
372     {
373         unsigned int i;
374
375         SDL_LockAudio();
376         for ( i = 0 ; i < LenReg ; i += 4 )
377         {
378
379             if(SwapChannels == 0)
380             {
381                 // Left channel
382                 primaryBuffer[ buffer_pos + i ] = p[ i + 2 ];
383                 primaryBuffer[ buffer_pos + i + 1 ] = p[ i + 3 ];
384
385                 // Right channel
386                 primaryBuffer[ buffer_pos + i + 2 ] = p[ i ];
387                 primaryBuffer[ buffer_pos + i + 3 ] = p[ i + 1 ];
388             } else {
389                 // Left channel
390                 primaryBuffer[ buffer_pos + i ] = p[ i ];
391                 primaryBuffer[ buffer_pos + i + 1 ] = p[ i + 1 ];
392
393                 // Right channel
394                 primaryBuffer[ buffer_pos + i + 2 ] = p[ i + 2];
395                 primaryBuffer[ buffer_pos + i + 3 ] = p[ i + 3 ];
396             }
397         }
398         buffer_pos += i;
399         SDL_UnlockAudio();
400     }
401     else
402     {
403         DebugMessage(M64MSG_WARNING, "AiLenChanged(): Audio buffer overflow.");
404     }
405
406     /* Now we need to handle synchronization, by inserting time delay to keep the emulator running at the correct speed */
407     /* Start by calculating the current Primary buffer fullness in terms of output samples */
408     CurrLevel = (unsigned int) (((long long) (buffer_pos/N64_SAMPLE_BYTES) * OutputFreq * 100) / (GameFreq * speed_factor));
409     /* Next, extrapolate to the buffer level at the expected time of the next audio callback, assuming that the
410        buffer is filled at the same rate as the output frequency */
411     CurrTime = SDL_GetTicks();
412     ExpectedTime = last_callback_ticks + ((1000 * SecondaryBufferSize) / OutputFreq);
413     ExpectedLevel = CurrLevel;
414     if (CurrTime < ExpectedTime)
415         ExpectedLevel += (ExpectedTime - CurrTime) * OutputFreq / 1000;
416     /* If the expected value of the Primary Buffer Fullness at the time of the next audio callback is more than 10
417        milliseconds ahead of our target buffer fullness level, then insert a delay now */
418     DebugMessage(M64MSG_VERBOSE, "%03i New audio bytes: %i  Time to next callback: %i  Current/Expected buffer level: %i/%i",
419                  CurrTime % 1000, LenReg, (int) (ExpectedTime - CurrTime), CurrLevel, ExpectedLevel);
420     if (ExpectedLevel >= PrimaryBufferTarget + OutputFreq / 100)
421     {
422         unsigned int WaitTime = (ExpectedLevel - PrimaryBufferTarget) * 1000 / OutputFreq;
423         DebugMessage(M64MSG_VERBOSE, "    AiLenChanged(): Waiting %ims", WaitTime);
424         if (l_PausedForSync)
425             SDL_PauseAudio(0);
426         l_PausedForSync = 0;
427         SDL_Delay(WaitTime);
428     }
429     /* Or if the expected level of the primary buffer is less than the secondary buffer size
430        (ie, predicting an underflow), then pause the audio to let the emulator catch up to speed */
431     else if (ExpectedLevel < SecondaryBufferSize)
432     {
433         DebugMessage(M64MSG_VERBOSE, "    AiLenChanged(): Possible underflow at next audio callback; pausing playback");
434         if (!l_PausedForSync)
435             SDL_PauseAudio(1);
436         l_PausedForSync = 1;
437     }
438     /* otherwise the predicted buffer level is within our tolerance, so everything is okay */
439     else
440     {
441         if (l_PausedForSync)
442             SDL_PauseAudio(0);
443         l_PausedForSync = 0;
444     }
445 }
446
447 EXPORT int CALL InitiateAudio( AUDIO_INFO Audio_Info )
448 {
449     if (!l_PluginInit)
450         return 0;
451
452     AudioInfo = Audio_Info;
453     return 1;
454 }
455
456 static int underrun_count = 0;
457
458 #ifdef USE_SRC
459 static float *_src = NULL;
460 static unsigned int _src_len = 0;
461 static float *_dest = NULL;
462 static unsigned int _dest_len = 0;
463 static int error;
464 static SRC_STATE *src_state;
465 static SRC_DATA src_data;
466 #endif
467 #ifdef USE_SPEEX
468 SpeexResamplerState* spx_state = NULL;
469 static int error;
470 #endif
471
472 static int resample(unsigned char *input, int input_avail, int oldsamplerate, unsigned char *output, int output_needed, int newsamplerate)
473 {
474     int *psrc = (int*)input;
475     int *pdest = (int*)output;
476     int i = 0, j = 0;
477
478 #ifdef USE_SPEEX
479     spx_uint32_t in_len, out_len;
480     if(Resample == RESAMPLER_SPEEX)
481     {
482         if(spx_state == NULL)
483         {
484             spx_state = speex_resampler_init(2, oldsamplerate, newsamplerate, ResampleQuality,  &error);
485             if(spx_state == NULL)
486             {
487                 memset(output, 0, output_needed);
488                 return 0;
489             }
490         }
491         speex_resampler_set_rate(spx_state, oldsamplerate, newsamplerate);
492         in_len = input_avail / 4;
493         out_len = output_needed / 4;
494
495         if ((error = speex_resampler_process_interleaved_int(spx_state, (const spx_int16_t *)input, &in_len, (spx_int16_t *)output, &out_len)))
496         {
497             memset(output, 0, output_needed);
498             return input_avail;  // number of bytes consumed
499         }
500         return in_len * 4;
501     }
502 #endif
503 #ifdef USE_SRC
504     if(Resample == RESAMPLER_SRC)
505     {
506         // the high quality resampler needs more input than the samplerate ratio would indicate to work properly
507         if (input_avail > output_needed * 3 / 2)
508             input_avail = output_needed * 3 / 2; // just to avoid too much short-float-short conversion time
509         if (_src_len < input_avail*2 && input_avail > 0)
510         {
511             if(_src) free(_src);
512             _src_len = input_avail*2;
513             _src = malloc(_src_len);
514         }
515         if (_dest_len < output_needed*2 && output_needed > 0)
516         {
517             if(_dest) free(_dest);
518             _dest_len = output_needed*2;
519             _dest = malloc(_dest_len);
520         }
521         memset(_src,0,_src_len);
522         memset(_dest,0,_dest_len);
523         if(src_state == NULL)
524         {
525             src_state = src_new (ResampleQuality, 2, &error);
526             if(src_state == NULL)
527             {
528                 memset(output, 0, output_needed);
529                 return 0;
530             }
531         }
532         src_short_to_float_array ((short *) input, _src, input_avail/2);
533         src_data.end_of_input = 0;
534         src_data.data_in = _src;
535         src_data.input_frames = input_avail/4;
536         src_data.src_ratio = (float) newsamplerate / oldsamplerate;
537         src_data.data_out = _dest;
538         src_data.output_frames = output_needed/4;
539         if ((error = src_process (src_state, &src_data)))
540         {
541             memset(output, 0, output_needed);
542             return input_avail;  // number of bytes consumed
543         }
544         src_float_to_short_array (_dest, (short *) output, output_needed/2);
545         return src_data.input_frames_used * 4;
546     }
547 #endif
548     // RESAMPLE == TRIVIAL
549     if (newsamplerate >= oldsamplerate)
550     {
551         int sldf = oldsamplerate;
552         int const2 = 2*sldf;
553         int dldf = newsamplerate;
554         int const1 = const2 - 2*dldf;
555         int criteria = const2 - dldf;
556         for (i = 0; i < output_needed/4; i++)
557         {
558             pdest[i] = psrc[j];
559             if(criteria >= 0)
560             {
561                 ++j;
562                 criteria += const1;
563             }
564             else criteria += const2;
565         }
566         return j * 4; //number of bytes consumed
567     }
568     // newsamplerate < oldsamplerate, this only happens when speed_factor > 1
569     for (i = 0; i < output_needed/4; i++)
570     {
571         j = i * oldsamplerate / newsamplerate;
572         pdest[i] = psrc[j];
573     }
574     return j * 4; //number of bytes consumed
575 }
576
577 static void my_audio_callback(void *userdata, unsigned char *stream, int len)
578 {
579     int oldsamplerate, newsamplerate;
580
581     if (!l_PluginInit)
582         return;
583
584     /* mark the time, for synchronization on the input side */
585     last_callback_ticks = SDL_GetTicks();
586
587     newsamplerate = OutputFreq * 100 / speed_factor;
588     oldsamplerate = GameFreq;
589
590     if (buffer_pos > (unsigned int) (len * oldsamplerate) / newsamplerate)
591     {
592         int input_used;
593 #if defined(HAS_OSS_SUPPORT)
594         if (VolumeControlType == VOLUME_TYPE_OSS)
595         {
596             input_used = resample(primaryBuffer, buffer_pos, oldsamplerate, stream, len, newsamplerate);
597         }
598         else
599 #endif
600         {
601             input_used = resample(primaryBuffer, buffer_pos, oldsamplerate, mixBuffer, len, newsamplerate);
602             memset(stream, 0, len);
603             SDL_MixAudio(stream, mixBuffer, len, VolSDL);
604         }
605         memmove(primaryBuffer, &primaryBuffer[input_used], buffer_pos - input_used);
606         buffer_pos -= input_used;
607         DebugMessage(M64MSG_VERBOSE, "%03i my_audio_callback: used %i samples",
608                      last_callback_ticks % 1000, len / SDL_SAMPLE_BYTES);
609     }
610     else
611     {
612         unsigned int SamplesNeeded = (len * oldsamplerate) / (newsamplerate * SDL_SAMPLE_BYTES);
613         unsigned int SamplesPresent = buffer_pos / N64_SAMPLE_BYTES;
614         underrun_count++;
615         DebugMessage(M64MSG_VERBOSE, "%03i Buffer underflow (%i).  %i samples present, %i needed",
616                      last_callback_ticks % 1000, underrun_count, SamplesPresent, SamplesNeeded);
617         memset(stream , 0, len);
618     }
619 }
620 EXPORT int CALL RomOpen(void)
621 {
622     if (!l_PluginInit)
623         return 0;
624
625     ReadConfig();
626     InitializeAudio(GameFreq);
627     return 1;
628 }
629
630 static void InitializeSDL(void)
631 {
632     DebugMessage(M64MSG_INFO, "Initializing SDL audio subsystem...");
633
634     if(SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER) < 0)
635     {
636         DebugMessage(M64MSG_ERROR, "Failed to initialize SDL audio subsystem; forcing exit.\n");
637         critical_failure = 1;
638         return;
639     }
640     critical_failure = 0;
641
642 }
643
644 static void CreatePrimaryBuffer(void)
645 {
646     unsigned int newPrimaryBytes = (unsigned int) ((long long) PrimaryBufferSize * GameFreq * speed_factor /
647                                                    (OutputFreq * 100)) * N64_SAMPLE_BYTES;
648     if (primaryBuffer == NULL)
649     {
650         DebugMessage(M64MSG_VERBOSE, "Allocating memory for audio buffer: %i bytes.", newPrimaryBytes);
651         primaryBuffer = (unsigned char*) malloc(newPrimaryBytes);
652         memset(primaryBuffer, 0, newPrimaryBytes);
653         primaryBufferBytes = newPrimaryBytes;
654     }
655     else if (newPrimaryBytes > primaryBufferBytes) /* primary buffer only grows; there's no point in shrinking it */
656     {
657         unsigned char *newPrimaryBuffer = (unsigned char*) malloc(newPrimaryBytes);
658         unsigned char *oldPrimaryBuffer = primaryBuffer;
659         SDL_LockAudio();
660         memcpy(newPrimaryBuffer, oldPrimaryBuffer, primaryBufferBytes);
661         memset(newPrimaryBuffer + primaryBufferBytes, 0, newPrimaryBytes - primaryBufferBytes);
662         primaryBuffer = newPrimaryBuffer;
663         primaryBufferBytes = newPrimaryBytes;
664         SDL_UnlockAudio();
665         free(oldPrimaryBuffer);
666     }
667 }
668
669 static void InitializeAudio(int freq)
670 {
671     SDL_AudioSpec *desired, *obtained;
672     
673     if(SDL_WasInit(SDL_INIT_AUDIO|SDL_INIT_TIMER) == (SDL_INIT_AUDIO|SDL_INIT_TIMER) ) 
674     {
675         DebugMessage(M64MSG_VERBOSE, "InitializeAudio(): SDL Audio sub-system already initialized.");
676         SDL_PauseAudio(1);
677         SDL_CloseAudio();
678     }
679     else 
680     {
681         DebugMessage(M64MSG_VERBOSE, "InitializeAudio(): Initializing SDL Audio");
682         DebugMessage(M64MSG_VERBOSE, "Primary buffer: %i output samples.", PrimaryBufferSize);
683         DebugMessage(M64MSG_VERBOSE, "Primary target fullness: %i output samples.", PrimaryBufferTarget);
684         DebugMessage(M64MSG_VERBOSE, "Secondary buffer: %i output samples.", SecondaryBufferSize);
685         InitializeSDL();
686     }
687     if (critical_failure == 1)
688         return;
689     GameFreq = freq; // This is important for the sync
690     if(hardware_spec != NULL) free(hardware_spec);
691
692     // Allocate space for SDL_AudioSpec
693     desired = malloc(sizeof(SDL_AudioSpec));
694     obtained = malloc(sizeof(SDL_AudioSpec));
695
696     if(freq < 11025) OutputFreq = 11025;
697     else if(freq < 22050) OutputFreq = 22050;
698     else OutputFreq = 44100;
699     
700     desired->freq = OutputFreq;
701     
702     DebugMessage(M64MSG_VERBOSE, "Requesting frequency: %iHz.", desired->freq);
703     /* 16-bit signed audio */
704     desired->format=AUDIO_S16SYS;
705     DebugMessage(M64MSG_VERBOSE, "Requesting format: %i.", desired->format);
706     /* Stereo */
707     desired->channels=2;
708     /* reload these because they gets re-assigned from SDL data below, and InitializeAudio can be called more than once */
709     PrimaryBufferSize = ConfigGetParamInt(l_ConfigAudio, "PRIMARY_BUFFER_SIZE");
710     PrimaryBufferTarget = ConfigGetParamInt(l_ConfigAudio, "PRIMARY_BUFFER_TARGET");
711     SecondaryBufferSize = ConfigGetParamInt(l_ConfigAudio, "SECONDARY_BUFFER_SIZE");
712     desired->samples = SecondaryBufferSize;
713     /* Our callback function */
714     desired->callback = my_audio_callback;
715     desired->userdata = NULL;
716
717     /* Open the audio device */
718     l_PausedForSync = 1;
719     if (SDL_OpenAudio(desired, obtained) < 0)
720     {
721         DebugMessage(M64MSG_ERROR, "Couldn't open audio: %s", SDL_GetError());
722         critical_failure = 1;
723         return;
724     }
725     if (desired->format != obtained->format)
726     {
727         DebugMessage(M64MSG_WARNING, "Obtained audio format differs from requested.");
728     }
729     if (desired->freq != obtained->freq)
730     {
731         DebugMessage(M64MSG_WARNING, "Obtained frequency differs from requested.");
732     }
733
734     /* desired spec is no longer needed */
735     free(desired);
736     hardware_spec=obtained;
737
738     /* allocate memory for audio buffers */
739     OutputFreq = hardware_spec->freq;
740     SecondaryBufferSize = hardware_spec->samples;
741     if (PrimaryBufferTarget < SecondaryBufferSize)
742         PrimaryBufferTarget = SecondaryBufferSize;
743     if (PrimaryBufferSize < PrimaryBufferTarget)
744         PrimaryBufferSize = PrimaryBufferTarget;
745     if (PrimaryBufferSize < SecondaryBufferSize * 2)
746         PrimaryBufferSize = SecondaryBufferSize * 2;
747     CreatePrimaryBuffer();
748     if (mixBuffer != NULL)
749         free(mixBuffer);
750     mixBuffer = (unsigned char*) malloc(SecondaryBufferSize * SDL_SAMPLE_BYTES);
751
752     /* preset the last callback time */
753     if (last_callback_ticks == 0)
754         last_callback_ticks = SDL_GetTicks();
755
756     DebugMessage(M64MSG_VERBOSE, "Frequency: %i", hardware_spec->freq);
757     DebugMessage(M64MSG_VERBOSE, "Format: %i", hardware_spec->format);
758     DebugMessage(M64MSG_VERBOSE, "Channels: %i", hardware_spec->channels);
759     DebugMessage(M64MSG_VERBOSE, "Silence: %i", hardware_spec->silence);
760     DebugMessage(M64MSG_VERBOSE, "Samples: %i", hardware_spec->samples);
761     DebugMessage(M64MSG_VERBOSE, "Size: %i", hardware_spec->size);
762
763     /* set playback volume */
764 #if defined(HAS_OSS_SUPPORT)
765     if (VolumeControlType == VOLUME_TYPE_OSS)
766     {
767         VolPercent = volGet();
768     }
769     else
770 #endif
771     {
772         VolSDL = SDL_MIX_MAXVOLUME * VolPercent / 100;
773     }
774
775 }
776 EXPORT void CALL RomClosed( void )
777 {
778     if (!l_PluginInit)
779         return;
780    if (critical_failure == 1)
781        return;
782     DebugMessage(M64MSG_VERBOSE, "Cleaning up SDL sound plugin...");
783     
784     // Shut down SDL Audio output
785     SDL_PauseAudio(1);
786     SDL_CloseAudio();
787
788     // Delete the buffer, as we are done producing sound
789     if (primaryBuffer != NULL)
790     {
791         primaryBufferBytes = 0;
792         free(primaryBuffer);
793         primaryBuffer = NULL;
794     }
795     if (mixBuffer != NULL)
796     {
797         free(mixBuffer);
798         mixBuffer = NULL;
799     }
800
801     // Delete the hardware spec struct
802     if(hardware_spec != NULL) free(hardware_spec);
803     hardware_spec = NULL;
804
805     // Shutdown the respective subsystems
806     if(SDL_WasInit(SDL_INIT_AUDIO) != 0) SDL_QuitSubSystem(SDL_INIT_AUDIO);
807     if(SDL_WasInit(SDL_INIT_TIMER) != 0) SDL_QuitSubSystem(SDL_INIT_TIMER);
808 }
809
810 EXPORT void CALL ProcessAList(void)
811 {
812 }
813
814 EXPORT void CALL SetSpeedFactor(int percentage)
815 {
816     if (!l_PluginInit)
817         return;
818     if (percentage >= 10 && percentage <= 300)
819         speed_factor = percentage;
820     // we need a different size primary buffer to store the N64 samples when the speed changes
821     CreatePrimaryBuffer();
822 }
823
824 static void ReadConfig(void)
825 {
826     const char *resampler_id;
827
828     /* read the configuration values into our static variables */
829     GameFreq = ConfigGetParamInt(l_ConfigAudio, "DEFAULT_FREQUENCY");
830     SwapChannels = ConfigGetParamBool(l_ConfigAudio, "SWAP_CHANNELS");
831     PrimaryBufferSize = ConfigGetParamInt(l_ConfigAudio, "PRIMARY_BUFFER_SIZE");
832     PrimaryBufferTarget = ConfigGetParamInt(l_ConfigAudio, "PRIMARY_BUFFER_TARGET");
833     SecondaryBufferSize = ConfigGetParamInt(l_ConfigAudio, "SECONDARY_BUFFER_SIZE");
834     resampler_id = ConfigGetParamString(l_ConfigAudio, "RESAMPLE");
835     VolumeControlType = ConfigGetParamInt(l_ConfigAudio, "VOLUME_CONTROL_TYPE");
836     VolDelta = ConfigGetParamInt(l_ConfigAudio, "VOLUME_ADJUST");
837     VolPercent = ConfigGetParamInt(l_ConfigAudio, "VOLUME_DEFAULT");
838
839     if (!resampler_id) {
840         Resample = RESAMPLER_TRIVIAL;
841         DebugMessage(M64MSG_WARNING, "Could not find RESAMPLE configuration; use trivial resampler");
842         return;
843     }
844     if (strcmp(resampler_id, "trivial") == 0) {
845         Resample = RESAMPLER_TRIVIAL;
846         return;
847     }
848 #ifdef USE_SPEEX
849     if (strncmp(resampler_id, "speex-fixed-", strlen("speex-fixed-")) == 0) {
850         int i;
851         static const char *speex_quality[] = {
852             "speex-fixed-0",
853             "speex-fixed-1",
854             "speex-fixed-2",
855             "speex-fixed-3",
856             "speex-fixed-4",
857             "speex-fixed-5",
858             "speex-fixed-6",
859             "speex-fixed-7",
860             "speex-fixed-8",
861             "speex-fixed-9",
862             "speex-fixed-10",
863         };
864         Resample = RESAMPLER_SPEEX;
865         for (i = 0; i < sizeof(speex_quality) / sizeof(*speex_quality); i++) {
866             if (strcmp(speex_quality[i], resampler_id) == 0) {
867                 ResampleQuality = i;
868                 return;
869             }
870         }
871         DebugMessage(M64MSG_WARNING, "Unknown RESAMPLE configuration %s; use speex-fixed-4 resampler", resampler_id);
872         ResampleQuality = 4;
873         return;
874     }
875 #endif
876 #ifdef USE_SRC
877     if (strncmp(resampler_id, "src-", strlen("src-")) == 0) {
878         Resample = RESAMPLER_SRC;
879         if (strcmp(resampler_id, "src-sinc-best-quality") == 0) {
880             ResampleQuality = SRC_SINC_BEST_QUALITY;
881             return;
882         }
883         if (strcmp(resampler_id, "src-sinc-medium-quality") == 0) {
884             ResampleQuality = SRC_SINC_MEDIUM_QUALITY;
885             return;
886         }
887         if (strcmp(resampler_id, "src-sinc-fastest") == 0) {
888             ResampleQuality = SRC_SINC_FASTEST;
889             return;
890         }
891         if (strcmp(resampler_id, "src-zero-order-hold") == 0) {
892             ResampleQuality = SRC_ZERO_ORDER_HOLD;
893             return;
894         }
895         if (strcmp(resampler_id, "src-linear") == 0) {
896             ResampleQuality = SRC_LINEAR;
897             return;
898         }
899         DebugMessage(M64MSG_WARNING, "Unknown RESAMPLE configuration %s; use src-sinc-medium-quality resampler", resampler_id);
900         ResampleQuality = SRC_SINC_MEDIUM_QUALITY;
901         return;
902     }
903 #endif
904     DebugMessage(M64MSG_WARNING, "Unknown RESAMPLE configuration %s; use trivial resampler", resampler_id);
905     Resample = RESAMPLER_TRIVIAL;
906 }
907
908 // Returns the most recent ummuted volume level.
909 static int VolumeGetUnmutedLevel(void)
910 {
911 #if defined(HAS_OSS_SUPPORT)
912     // reload volume if we're using OSS
913     if (!VolIsMuted && VolumeControlType == VOLUME_TYPE_OSS)
914     {
915         return volGet();
916     }
917 #endif
918
919     return VolPercent;
920 }
921
922 // Sets the volume level based on the contents of VolPercent and VolIsMuted
923 static void VolumeCommit(void)
924 {
925     int levelToCommit = VolIsMuted ? 0 : VolPercent;
926
927 #if defined(HAS_OSS_SUPPORT)
928     if (VolumeControlType == VOLUME_TYPE_OSS)
929     {
930         //OSS mixer volume
931         volSet(levelToCommit);
932     }
933     else
934 #endif
935     {
936         VolSDL = SDL_MIX_MAXVOLUME * levelToCommit / 100;
937     }
938 }
939
940 EXPORT void CALL VolumeMute(void)
941 {
942     if (!l_PluginInit)
943         return;
944
945     // Store the volume level in order to restore it later
946     if (!VolIsMuted)
947         VolPercent = VolumeGetUnmutedLevel();
948
949     // Toogle mute
950     VolIsMuted = !VolIsMuted;
951     VolumeCommit();
952 }
953
954 EXPORT void CALL VolumeUp(void)
955 {
956     if (!l_PluginInit)
957         return;
958
959     VolumeSetLevel(VolumeGetUnmutedLevel() + VolDelta);
960 }
961
962 EXPORT void CALL VolumeDown(void)
963 {
964     if (!l_PluginInit)
965         return;
966
967     VolumeSetLevel(VolumeGetUnmutedLevel() - VolDelta);
968 }
969
970 EXPORT int CALL VolumeGetLevel(void)
971 {
972     return VolIsMuted ? 0 : VolumeGetUnmutedLevel();
973 }
974
975 EXPORT void CALL VolumeSetLevel(int level)
976 {
977     if (!l_PluginInit)
978         return;
979
980     //if muted, unmute first
981     VolIsMuted = 0;
982
983     // adjust volume 
984     VolPercent = level;
985     if (VolPercent < 0)
986         VolPercent = 0;
987     else if (VolPercent > 100)
988         VolPercent = 100;
989
990     VolumeCommit();
991 }
992
993 EXPORT const char * CALL VolumeGetString(void)
994 {
995     static char VolumeString[32];
996
997     if (VolIsMuted)
998     {
999         strcpy(VolumeString, "Mute");
1000     }
1001     else
1002     {
1003         sprintf(VolumeString, "%i%%", VolPercent);
1004     }
1005
1006     return VolumeString;
1007 }
1008