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