Audio Notaz (from mupen64plus v1.5) plugin. Compile and run (very well) on the OpenPa...
[mupen64plus-pandora.git] / source / notaz_audio / main.c
CommitLineData
a84c12f4 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 */
54extern ptr_ConfigListSections ConfigListSections;
55extern ptr_ConfigOpenSection ConfigOpenSection;
56extern ptr_ConfigDeleteSection ConfigDeleteSection;
57extern ptr_ConfigSaveSection ConfigSaveSection;
58extern ptr_ConfigListParameters ConfigListParameters;
59extern ptr_ConfigSaveFile ConfigSaveFile;
60extern ptr_ConfigSetParameter ConfigSetParameter;
61extern ptr_ConfigGetParameter ConfigGetParameter;
62extern ptr_ConfigGetParameterHelp ConfigGetParameterHelp;
63extern ptr_ConfigSetDefaultInt ConfigSetDefaultInt;
64extern ptr_ConfigSetDefaultFloat ConfigSetDefaultFloat;
65extern ptr_ConfigSetDefaultBool ConfigSetDefaultBool;
66extern ptr_ConfigSetDefaultString ConfigSetDefaultString;
67extern ptr_ConfigGetParamInt ConfigGetParamInt;
68extern ptr_ConfigGetParamFloat ConfigGetParamFloat;
69extern ptr_ConfigGetParamBool ConfigGetParamBool;
70extern ptr_ConfigGetParamString ConfigGetParamString;
71
72/* definitions of pointers to Core config functions */
73ptr_ConfigOpenSection ConfigOpenSection = NULL;
74ptr_ConfigDeleteSection ConfigDeleteSection = NULL;
75ptr_ConfigSaveSection ConfigSaveSection = NULL;
76ptr_ConfigSetParameter ConfigSetParameter = NULL;
77ptr_ConfigGetParameter ConfigGetParameter = NULL;
78ptr_ConfigGetParameterHelp ConfigGetParameterHelp = NULL;
79ptr_ConfigSetDefaultInt ConfigSetDefaultInt = NULL;
80ptr_ConfigSetDefaultFloat ConfigSetDefaultFloat = NULL;
81ptr_ConfigSetDefaultBool ConfigSetDefaultBool = NULL;
82ptr_ConfigSetDefaultString ConfigSetDefaultString = NULL;
83ptr_ConfigGetParamInt ConfigGetParamInt = NULL;
84ptr_ConfigGetParamFloat ConfigGetParamFloat = NULL;
85ptr_ConfigGetParamBool ConfigGetParamBool = NULL;
86ptr_ConfigGetParamString ConfigGetParamString = NULL;
87
88/* local variables */
89static void (*l_DebugCallback)(void *, int, const char *) = NULL;
90static void *l_DebugCallContext = NULL;
91static int l_PluginInit = 0;
92static int l_PausedForSync = 1; /* Audio is started in paused state after SDL initialization */
93static 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 */
113static AUDIO_INFO audio_info;
114/* Audio frequency, this is usually obtained from the game,
115 * but for compatibility we set default value */
116static int input_freq = DEFAULT_FREQUENCY;
117
118/* config stuff */
119static int minimum_rate = 8000;
120static int pich_percent = 100;
121
122static unsigned int sound_out_buff[48000 * 4]; // ~4 sec, enough?
123static unsigned int sound_silence_buff[48000 / 10];
124
125static int sound_dev = -1;
126static int output_freq;
127static int resample_step;
128static int silence_bytes;
129static int fade_len;
130static int fade_step;
131
132/* rates Pandora supports natively, we'll need to do
133 * less resampling if we stick close to these */
134static const int supported_rates[] = {
135 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
136};
137
138static 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
202finish:
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
214static 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
238out:
239 return i * 4; // number of bytes to output
240}
241
242static 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
272EXPORT 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
290EXPORT 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/*
336EXPORT DWORD CALL AiReadLength(void)
337{
338 return 0;
339}
340EXPORT void CALL CloseDLL(void)
341{
342 if (sound_dev >= 0)
343 close(sound_dev);
344 sound_dev = -1;
345}
346*/
347static char config_file[512];
348
349static 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/*
380EXPORT void CALL SetConfigDir(char *configDir)
381{
382 snprintf(config_file, sizeof(config_file), "%snotaz_audio.conf", configDir);
383 read_config();
384}
385
386EXPORT void CALL DllTest(HWND hParent)
387{
388}
389
390EXPORT 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
399EXPORT void CALL DllAbout(HWND hParent)
400{
401}
402
403EXPORT 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*/
429EXPORT 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
438EXPORT 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
448EXPORT void CALL RomClosed(void)
449{
450}
451
452EXPORT void CALL ProcessAList(void)
453{
454}
455
456EXPORT void CALL SetSpeedFactor(int percentage)
457{
458}
459
460EXPORT void CALL VolumeUp(void)
461{
462}
463
464EXPORT void CALL VolumeDown(void)
465{
466}
467
468EXPORT void CALL VolumeMute(void)
469{
470}
471EXPORT int CALL VolumeGetLevel(void)
472{
473 return 255;
474}
475
476EXPORT void CALL VolumeSetLevel(int level)
477{
478}
479
480EXPORT const char * CALL VolumeGetString(void)
481{
482 return "100%%";
483}
484
485/* Global functions */
486static 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 */
503EXPORT 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
602EXPORT 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
621EXPORT 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}