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 */ |
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 | } |