Audio Notaz (from mupen64plus v1.5) plugin. Compile and run (very well) on the OpenPa...
[mupen64plus-pandora.git] / source / notaz_audio / main.c
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2  *   Mupen64plus - main.c                                                  *
3  *   Mupen64Plus homepage: http://code.google.com/p/mupen64plus/           *
4  *   notaz_audio: (c) notaz, 2010                                          *
5  *                                                                         *
6  *   This program is free software; you can redistribute it and/or modify  *
7  *   it under the terms of the GNU General Public License as published by  *
8  *   the Free Software Foundation; either version 2 of the License, or     *
9  *   (at your option) any later version.                                   *
10  *                                                                         *
11  *   This program is distributed in the hope that it will be useful,       *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14  *   GNU General Public License for more details.                          *
15  *                                                                         *
16  *   You should have received a copy of the GNU General Public License     *
17  *   along with this program; if not, write to the                         *
18  *   Free Software Foundation, Inc.,                                       *
19  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
20  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <sys/ioctl.h>
30 #include <linux/soundcard.h>
31 #include <stdarg.h>
32 /*
33 #include "../main/winlnxdefs.h"
34
35 #include "Audio_1.2.h"
36 */
37 #define M64P_PLUGIN_PROTOTYPES 1
38 #include "m64p_types.h"
39 #include "m64p_plugin.h"
40 #include "m64p_common.h"
41 #include "m64p_config.h"
42
43 #include "osal_dynamiclib.h"
44
45 /* version info */
46 #define NOTAZ_AUDIO_PLUGIN_VERSION 0x020000
47 #define AUDIO_PLUGIN_API_VERSION 0x020000
48 #define CONFIG_API_VERSION       0x020100
49 #define CONFIG_PARAM_VERSION     1.00
50
51 #define VERSION_PRINTF_SPLIT(x) (((x) >> 16) & 0xffff), (((x) >> 8) & 0xff), ((x) & 0xff)
52
53 /* declarations of pointers to Core config functions */
54 extern ptr_ConfigListSections     ConfigListSections;
55 extern ptr_ConfigOpenSection      ConfigOpenSection;
56 extern ptr_ConfigDeleteSection    ConfigDeleteSection;
57 extern ptr_ConfigSaveSection      ConfigSaveSection;
58 extern ptr_ConfigListParameters   ConfigListParameters;
59 extern ptr_ConfigSaveFile         ConfigSaveFile;
60 extern ptr_ConfigSetParameter     ConfigSetParameter;
61 extern ptr_ConfigGetParameter     ConfigGetParameter;
62 extern ptr_ConfigGetParameterHelp ConfigGetParameterHelp;
63 extern ptr_ConfigSetDefaultInt    ConfigSetDefaultInt;
64 extern ptr_ConfigSetDefaultFloat  ConfigSetDefaultFloat;
65 extern ptr_ConfigSetDefaultBool   ConfigSetDefaultBool;
66 extern ptr_ConfigSetDefaultString ConfigSetDefaultString;
67 extern ptr_ConfigGetParamInt      ConfigGetParamInt;
68 extern ptr_ConfigGetParamFloat    ConfigGetParamFloat;
69 extern ptr_ConfigGetParamBool     ConfigGetParamBool;
70 extern ptr_ConfigGetParamString   ConfigGetParamString;
71
72 /* definitions of pointers to Core config functions */
73 ptr_ConfigOpenSection      ConfigOpenSection = NULL;
74 ptr_ConfigDeleteSection    ConfigDeleteSection = NULL;
75 ptr_ConfigSaveSection      ConfigSaveSection = NULL;
76 ptr_ConfigSetParameter     ConfigSetParameter = NULL;
77 ptr_ConfigGetParameter     ConfigGetParameter = NULL;
78 ptr_ConfigGetParameterHelp ConfigGetParameterHelp = NULL;
79 ptr_ConfigSetDefaultInt    ConfigSetDefaultInt = NULL;
80 ptr_ConfigSetDefaultFloat  ConfigSetDefaultFloat = NULL;
81 ptr_ConfigSetDefaultBool   ConfigSetDefaultBool = NULL;
82 ptr_ConfigSetDefaultString ConfigSetDefaultString = NULL;
83 ptr_ConfigGetParamInt      ConfigGetParamInt = NULL;
84 ptr_ConfigGetParamFloat    ConfigGetParamFloat = NULL;
85 ptr_ConfigGetParamBool     ConfigGetParamBool = NULL;
86 ptr_ConfigGetParamString   ConfigGetParamString = NULL;
87
88 /* local variables */
89 static void (*l_DebugCallback)(void *, int, const char *) = NULL;
90 static void *l_DebugCallContext = NULL;
91 static int l_PluginInit = 0;
92 static int l_PausedForSync = 1; /* Audio is started in paused state after SDL initialization */
93 static m64p_handle l_ConfigAudio;
94
95 #undef PLUGIN_VERSION
96 #define PLUGIN_VERSION "r2"
97
98 #define PREFIX "[audio] "
99 #define log(f, ...) printf(PREFIX f, ##__VA_ARGS__)
100
101 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
102
103 /* comment from jttl_audio:
104  * This sets default frequency what is used if rom doesn't want to change it.
105  * Popably only game that needs this is Zelda: Ocarina Of Time Master Quest 
106  * *NOTICE* We should try to find out why Demos' frequencies are always wrong
107  * They tend to rely on a default frequency, apparently, never the same one ;)*/
108 #define DEFAULT_FREQUENCY 33600
109
110 #define OSS_FRAGMENT_COUNT 5
111
112 /* Read header for type definition */
113 static AUDIO_INFO audio_info;
114 /* Audio frequency, this is usually obtained from the game,
115  * but for compatibility we set default value */
116 static int input_freq = DEFAULT_FREQUENCY;
117
118 /* config stuff */
119 static int minimum_rate = 8000;
120 static int pich_percent = 100;
121
122 static unsigned int sound_out_buff[48000 * 4]; // ~4 sec, enough?
123 static unsigned int sound_silence_buff[48000 / 10];
124
125 static int sound_dev = -1;
126 static int output_freq;
127 static int resample_step;
128 static int silence_bytes;
129 static int fade_len;
130 static int fade_step;
131
132 /* rates Pandora supports natively, we'll need to do
133  * less resampling if we stick close to these */
134 static const int supported_rates[] = {
135         8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
136 };
137
138 static int init(int freq)
139 {
140         static int output_freq_old;
141         int stereo, bits, rate;
142         int bsize, frag;
143         int ret, i;
144
145         input_freq = freq;
146
147         minimum_rate = ConfigGetParamInt(l_ConfigAudio, "MINIMUM_RATE" );
148         output_freq = minimum_rate;
149         pich_percent = ConfigGetParamInt(l_ConfigAudio, "PITCH_PERCENT" );
150
151         // find lowest alowed rate that is higher than game's output
152         for (i = 0; i < ARRAY_SIZE(supported_rates); i++) {
153                 int rate = supported_rates[i];
154                 if (freq <= rate + 100 && rate >= minimum_rate) {
155                         output_freq = rate;
156                         break;
157                 }
158         }
159
160         if (sound_dev >= 0) {
161                 if (output_freq == output_freq_old)
162                         goto finish;
163                 close(sound_dev);
164         }
165
166         sound_dev = open("/dev/dsp", O_WRONLY);
167         if (sound_dev == -1) {
168                 perror(PREFIX "open(\"/dev/dsp\")");
169                 sound_dev = open("/dev/dsp1", O_WRONLY);
170                 if (sound_dev == -1) {
171                         perror(PREFIX "open(\"/dev/dsp1\")");
172                         return -1;
173                 }
174         }
175
176         bsize = output_freq / 20 * 4; // ~50ms
177         for (frag = 0; bsize; bsize >>= 1, frag++)
178                 ;
179
180         frag |= OSS_FRAGMENT_COUNT << 16;       // fragment count
181         ret = ioctl(sound_dev, SNDCTL_DSP_SETFRAGMENT, &frag);
182         if (ret < 0)
183                 perror(PREFIX "SNDCTL_DSP_SETFRAGMENT failed");
184
185         silence_bytes = output_freq / 30 * 4; // ~ 25ms
186         memset(sound_silence_buff, 0, sizeof(sound_silence_buff));
187
188         stereo = 1;
189         bits = 16;
190         rate = output_freq;
191         ret = ioctl(sound_dev, SNDCTL_DSP_STEREO, &stereo);
192         if (ret == 0)
193                 ret = ioctl(sound_dev, SNDCTL_DSP_SETFMT, &bits);
194         if (ret == 0)
195                 ret = ioctl(sound_dev, SNDCTL_DSP_SPEED, &rate);
196         if (ret < 0)
197                 perror(PREFIX "failed to set audio format");
198
199         if (rate != output_freq)
200                 log("warning: output rate %d differs from desired %d\n", rate, output_freq);
201
202 finish:
203         resample_step = freq * 1024 / output_freq;
204         if (pich_percent != 100)
205                 resample_step = resample_step * pich_percent / 100;
206         fade_len = output_freq / 1000;
207         fade_step = 64 * 1024 / fade_len;
208
209         log("(re)started, rates: %d -> %d\n", freq, output_freq);
210
211         return 0;
212 }
213
214 static unsigned int resample(unsigned int *input, int in_size, unsigned int *output, int out_size)
215 {
216         unsigned int i, j, t;
217         int step = resample_step;
218         int count = 0;
219
220         t = input[0];
221         t = (t << 16) | (t >> 16);
222
223         for (i = j = 0; i < out_size / 4;)
224         {
225                 output[i++] = t;
226
227                 count += step;
228                 while (count >= 1024) {
229                         count -= 1024;
230                         j++;
231                         if (j >= in_size / 4)
232                                 goto out;
233                         t = input[j];
234                         t = (t << 16) | (t >> 16);
235                 }
236         }
237
238 out:
239         return i * 4; // number of bytes to output
240 }
241
242 static void fade(unsigned int *buf, int buf_len, int up)
243 {
244         signed short *sb = (void *)buf;
245         int len = fade_len;
246         int step = fade_step;
247         int counter = 0, mult, mult_step;
248         int i;
249
250         if (up) {
251                 mult = 0;
252                 mult_step = 1;
253         } else {
254                 sb += buf_len / 2 - len * 2;
255                 if (sb < (signed short *)buf)
256                         sb = (void *)buf;
257                 mult = 63;
258                 mult_step = -1;
259         }
260
261         for (i = 0; i < len; i++) {
262                 counter += step;
263                 while (counter >= 1024) {
264                         counter -= 1024;
265                         mult += mult_step;
266                 }
267                 sb[i * 2] = sb[i * 2] / 64 * mult;
268                 sb[i * 2 + 1] = sb[i * 2] / 64 * mult;
269         }
270 }
271
272 EXPORT void CALL AiDacrateChanged( int SystemType )
273 {
274         int f = input_freq;
275         switch (SystemType)
276         {
277         case SYSTEM_NTSC:
278                 f = 48681812 / (*audio_info.AI_DACRATE_REG + 1);
279                 break;
280         case SYSTEM_PAL:
281                 f = 49656530 / (*audio_info.AI_DACRATE_REG + 1);
282                 break;
283         case SYSTEM_MPAL:
284                 f = 48628316 / (*audio_info.AI_DACRATE_REG + 1);
285                 break;
286         }
287         init(f);
288 }
289
290 EXPORT void CALL AiLenChanged(void)
291 {
292         static int had_uflow;
293         unsigned int ret, len, *in, *part2, part2_len;
294         audio_buf_info bi;
295
296         if (sound_dev < 0)
297                 return;
298
299         // XXX: what about alignment? len limit? rdram overflow?
300         in = (unsigned int *)(audio_info.RDRAM + (*audio_info.AI_DRAM_ADDR_REG & 0xFFFFFC));
301         len = *audio_info.AI_LEN_REG;
302
303         //log("AiLenChanged: %u\n", len);
304
305         ret = resample(in, len, sound_out_buff, sizeof(sound_out_buff));
306         if (ret >= sizeof(sound_out_buff))
307                 log("overflow, in_len=%d\n", len);
308
309         if (had_uflow)
310                 fade(sound_out_buff, ret / 2, 1);
311
312         write(sound_dev, sound_out_buff, ret / 2);
313         part2 = sound_out_buff + (ret / 4) / 2;
314         part2_len = ret - ret / 2;
315
316         // try to keep at most 2 free fragments to avoid
317         // hardware underflows that cause crackling on pandora
318         // XXX: for some reason GETOSPACE only works after write?
319         // XXX: .fragments sometimes overflows? ALSA OSS emu bugs?
320         ret = ioctl(sound_dev, SNDCTL_DSP_GETOSPACE, &bi);
321         if (ret == 0 && 2 < bi.fragments && bi.fragments <= OSS_FRAGMENT_COUNT) {
322                 fade(part2, part2_len, 0);
323                 write(sound_dev, part2, part2_len);
324                 write(sound_dev, sound_silence_buff, silence_bytes);
325                 if (bi.fragments == 4)
326                         write(sound_dev, sound_silence_buff, silence_bytes);
327                 had_uflow = 1;
328         }
329         else {
330                 write(sound_dev, part2, part2_len);
331                 had_uflow = 0;
332         }
333 }
334
335 /*
336 EXPORT DWORD CALL AiReadLength(void)
337 {
338         return 0;
339 }
340 EXPORT void CALL CloseDLL(void)
341 {
342         if (sound_dev >= 0)
343                 close(sound_dev);
344         sound_dev = -1;
345 }
346 */
347 static char config_file[512];
348
349 static void read_config(void)
350 {
351         char cfg[512], *p;
352         int val;
353         FILE *f;
354
355         f = fopen(config_file, "r");
356         if (f == NULL) {
357                 perror(PREFIX "can't open config");
358                 return;
359         }
360
361         while (1) {
362                 p = fgets(cfg, sizeof(cfg), f);
363                 if (p == NULL)
364                         break;
365                 if (p[0] == '#')
366                         continue;
367
368                 if (sscanf(p, "pich_percent = %d", &val) == 1 && 10 <= val && val <= 1000) {
369                         pich_percent = val;
370                         break;
371                 }
372                 if (sscanf(p, "minimum_rate = %d", &val) == 1) {
373                         minimum_rate = val;
374                         break;
375                 }
376         }
377         fclose(f);
378 }
379 /*
380 EXPORT void CALL SetConfigDir(char *configDir)
381 {
382         snprintf(config_file, sizeof(config_file), "%snotaz_audio.conf", configDir);
383         read_config();
384 }
385
386 EXPORT void CALL DllTest(HWND hParent)
387 {
388 }
389
390 EXPORT void CALL GetDllInfo(PLUGIN_INFO *PluginInfo)
391 {
392         PluginInfo->Version = 0x0101;
393         PluginInfo->Type    = PLUGIN_TYPE_AUDIO;
394         strcpy(PluginInfo->Name, "notaz's OSS audio " PLUGIN_VERSION);
395         PluginInfo->NormalMemory  = TRUE;
396         PluginInfo->MemoryBswaped = TRUE;
397 }
398
399 EXPORT void CALL DllAbout(HWND hParent)
400 {
401 }
402
403 EXPORT void CALL DllConfig(HWND hParent)
404 {
405         char cmd[512];
406         FILE *f;
407
408         f = fopen(config_file, "r");
409         if (f != NULL)
410                 fclose(f);
411         else {
412                 f = fopen(config_file, "w");
413                 if (f != NULL) {
414                         fprintf(f, "# minimum sample rate to use. Higher values sound better on Pandora's DAC.\n");
415                         fprintf(f, "minimum_rate = %d\n\n", minimum_rate);
416                         fprintf(f, "# sound playback speed compared to normal (10-200%%)\n");
417                         fprintf(f, "# this will affect gameplay speed and sound pitch\n");
418                         fprintf(f, "pich_percent = %d\n\n", pich_percent);
419                         fclose(f);
420                 }
421         }
422
423         snprintf(cmd, sizeof(cmd), "mousepad \"%s\"", config_file);
424         system(cmd);
425
426         read_config();
427 }
428 */
429 EXPORT int CALL InitiateAudio(AUDIO_INFO Audio_Info)
430 {
431     if (!l_PluginInit)
432         return 0;
433
434         audio_info = Audio_Info;
435         return 1;
436 }
437
438 EXPORT int CALL RomOpen(void)
439 {
440     if (!l_PluginInit)
441         return 0;
442
443         /* This function is for compatibility with Mupen64. */
444         init(input_freq);
445         return 1;
446 }
447
448 EXPORT void CALL RomClosed(void)
449 {
450 }
451
452 EXPORT void CALL ProcessAList(void)
453 {
454 }
455
456 EXPORT void CALL SetSpeedFactor(int percentage)
457 {
458 }
459
460 EXPORT void CALL VolumeUp(void)
461 {
462 }
463
464 EXPORT void CALL VolumeDown(void)
465 {
466 }
467
468 EXPORT void CALL VolumeMute(void)
469 {
470 }
471 EXPORT int CALL VolumeGetLevel(void)
472 {
473     return 255;
474 }
475
476 EXPORT void CALL VolumeSetLevel(int level)
477 {
478 }
479
480 EXPORT const char * CALL VolumeGetString(void)
481 {
482         return "100%%";
483 }
484
485 /* Global functions */
486 static void DebugMessage(int level, const char *message, ...)
487 {
488   char msgbuf[1024];
489   va_list args;
490
491   if (l_DebugCallback == NULL)
492       return;
493
494   va_start(args, message);
495   vsprintf(msgbuf, message, args);
496
497   (*l_DebugCallback)(l_DebugCallContext, level, msgbuf);
498
499   va_end(args);
500 }
501
502 /* Mupen64Plus plugin functions */
503 EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context,
504                                    void (*DebugCallback)(void *, int, const char *))
505 {
506     ptr_CoreGetAPIVersions CoreAPIVersionFunc;
507     
508     int ConfigAPIVersion, DebugAPIVersion, VidextAPIVersion, bSaveConfig;
509     float fConfigParamsVersion = 0.0f;
510     
511     if (l_PluginInit)
512         return M64ERR_ALREADY_INIT;
513
514     /* first thing is to set the callback function for debug info */
515     l_DebugCallback = DebugCallback;
516     l_DebugCallContext = Context;
517
518     /* attach and call the CoreGetAPIVersions function, check Config API version for compatibility */
519     CoreAPIVersionFunc = (ptr_CoreGetAPIVersions) osal_dynlib_getproc(CoreLibHandle, "CoreGetAPIVersions");
520     if (CoreAPIVersionFunc == NULL)
521     {
522         DebugMessage(M64MSG_ERROR, "Core emulator broken; no CoreAPIVersionFunc() function found.");
523         return M64ERR_INCOMPATIBLE;
524     }
525     
526     (*CoreAPIVersionFunc)(&ConfigAPIVersion, &DebugAPIVersion, &VidextAPIVersion, NULL);
527     if ((ConfigAPIVersion & 0xffff0000) != (CONFIG_API_VERSION & 0xffff0000))
528     {
529         DebugMessage(M64MSG_ERROR, "Emulator core Config API (v%i.%i.%i) incompatible with plugin (v%i.%i.%i)",
530                 VERSION_PRINTF_SPLIT(ConfigAPIVersion), VERSION_PRINTF_SPLIT(CONFIG_API_VERSION));
531         return M64ERR_INCOMPATIBLE;
532     }
533
534     /* Get the core config function pointers from the library handle */
535     ConfigOpenSection = (ptr_ConfigOpenSection) osal_dynlib_getproc(CoreLibHandle, "ConfigOpenSection");
536     ConfigDeleteSection = (ptr_ConfigDeleteSection) osal_dynlib_getproc(CoreLibHandle, "ConfigDeleteSection");
537     ConfigSaveSection = (ptr_ConfigSaveSection) osal_dynlib_getproc(CoreLibHandle, "ConfigSaveSection");
538     ConfigSetParameter = (ptr_ConfigSetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigSetParameter");
539     ConfigGetParameter = (ptr_ConfigGetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParameter");
540     ConfigSetDefaultInt = (ptr_ConfigSetDefaultInt) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultInt");
541     ConfigSetDefaultFloat = (ptr_ConfigSetDefaultFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultFloat");
542     ConfigSetDefaultBool = (ptr_ConfigSetDefaultBool) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultBool");
543     ConfigSetDefaultString = (ptr_ConfigSetDefaultString) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultString");
544     ConfigGetParamInt = (ptr_ConfigGetParamInt) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamInt");
545     ConfigGetParamFloat = (ptr_ConfigGetParamFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamFloat");
546     ConfigGetParamBool = (ptr_ConfigGetParamBool) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamBool");
547     ConfigGetParamString = (ptr_ConfigGetParamString) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamString");
548
549     if (!ConfigOpenSection || !ConfigDeleteSection || !ConfigSetParameter || !ConfigGetParameter ||
550         !ConfigSetDefaultInt || !ConfigSetDefaultFloat || !ConfigSetDefaultBool || !ConfigSetDefaultString ||
551         !ConfigGetParamInt   || !ConfigGetParamFloat   || !ConfigGetParamBool   || !ConfigGetParamString)
552         return M64ERR_INCOMPATIBLE;
553
554     /* ConfigSaveSection was added in Config API v2.1.0 */
555     if (ConfigAPIVersion >= 0x020100 && !ConfigSaveSection)
556         return M64ERR_INCOMPATIBLE;
557
558     /* get a configuration section handle */
559     if (ConfigOpenSection("Audio-Notaz", &l_ConfigAudio) != M64ERR_SUCCESS)
560     {
561         DebugMessage(M64MSG_ERROR, "Couldn't open config section 'Audio-Notaz'");
562         return M64ERR_INPUT_NOT_FOUND;
563     }
564
565     /* check the section version number */
566     bSaveConfig = 0;
567     if (ConfigGetParameter(l_ConfigAudio, "Version", M64TYPE_FLOAT, &fConfigParamsVersion, sizeof(float)) != M64ERR_SUCCESS)
568     {
569         DebugMessage(M64MSG_WARNING, "No version number in 'Audio-Notaz' config section. Setting defaults.");
570         ConfigDeleteSection("Audio-Notaz");
571         ConfigOpenSection("Audio-Notaz", &l_ConfigAudio);
572         bSaveConfig = 1;
573     }
574     else if (((int) fConfigParamsVersion) != ((int) CONFIG_PARAM_VERSION))
575     {
576         DebugMessage(M64MSG_WARNING, "Incompatible version %.2f in 'Audio-Notaz' config section: current is %.2f. Setting defaults.", fConfigParamsVersion, (float) CONFIG_PARAM_VERSION);
577         ConfigDeleteSection("Audio-Notaz");
578         ConfigOpenSection("Audio-Notaz", &l_ConfigAudio);
579         bSaveConfig = 1;
580     }
581     else if ((CONFIG_PARAM_VERSION - fConfigParamsVersion) >= 0.0001f)
582     {
583         /* handle upgrades */
584         float fVersion = CONFIG_PARAM_VERSION;
585         ConfigSetParameter(l_ConfigAudio, "Version", M64TYPE_FLOAT, &fVersion);
586         DebugMessage(M64MSG_INFO, "Updating parameter set version in 'Audio-Notaz' config section to %.2f", fVersion);
587         bSaveConfig = 1;
588     }
589
590     /* set the default values for this plugin */
591     ConfigSetDefaultFloat(l_ConfigAudio, "Version",             CONFIG_PARAM_VERSION,  "Mupen64Plus Notaz Audio Plugin config parameter version number");
592     ConfigSetDefaultInt(l_ConfigAudio, "MINIMUM_RATE",        44100,           "Minimum sample rate to use. Higher values sound better on Pandora's DAC.");
593     ConfigSetDefaultInt(l_ConfigAudio, "PITCH_PERCENT",   100,   "sound playback speed compared to normal (10-200%%). this will affect gameplay speed and sound pitch");
594
595     if (bSaveConfig && ConfigAPIVersion >= 0x020100)
596         ConfigSaveSection("Audio-SDL");
597
598     l_PluginInit = 1;
599     return M64ERR_SUCCESS;
600 }
601
602 EXPORT m64p_error CALL PluginShutdown(void)
603 {
604     if (!l_PluginInit)
605         return M64ERR_NOT_INIT;
606
607     /* reset some local variables */
608     l_DebugCallback = NULL;
609     l_DebugCallContext = NULL;
610
611         if (sound_dev >= 0)
612                 close(sound_dev);
613         sound_dev = -1;
614
615     l_PluginInit = 0;
616         log("Notaz-audio Plugin shutdown\n");
617     return M64ERR_SUCCESS;
618 }
619
620
621 EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities)
622 {
623     /* set version info */
624     if (PluginType != NULL)
625         *PluginType = M64PLUGIN_AUDIO;
626
627     if (PluginVersion != NULL)
628         *PluginVersion = NOTAZ_AUDIO_PLUGIN_VERSION;
629
630     if (APIVersion != NULL)
631         *APIVersion = AUDIO_PLUGIN_API_VERSION;
632     
633     if (PluginNamePtr != NULL)
634         *PluginNamePtr = "Mupen64Plus Notaz Audio Plugin";
635
636     if (Capabilities != NULL)
637     {
638         *Capabilities = 0;
639     }
640                     
641     return M64ERR_SUCCESS;
642 }