libretro / melonDS

DS emulator, sorta
GNU General Public License v3.0
38 stars 40 forks source link

[Feature request] Allow to use custom .wav audio file as MIC input #174

Open tairosonloa opened 1 year ago

tairosonloa commented 1 year ago

Standalone MelonDS allows using a custom .WAV file as microphone input, to be used instead of the default "white noise" when pressing the "make mic noise" button. See "Microphone input" in https://melonds.kuribo64.net/faq.php

Some games, like The Legend of Zelda Spirit Tracks, has very little sensibility to the default mic white noise, and a custom .wav file is needed instead to make the game properly work.

See: https://melonds.kuribo64.net/board/thread.php?pid=1403 Also see: https://www.reddit.com/r/RetroArch/comments/w68k9a/melonds_how_to_adjustfix_microphone_noise/

Would it be possible to add this feature to the RetroArch core too, please? :) (so I can play it on my steam deck with emudeck xD)

Thanks!

tairosonloa commented 1 year ago

I don't mind working on this, as this should be in the codebase already (from the fork). However, I would be grateful if someone could point me to some docs or how to / where to look to integrate it with the Retro Arch core menu or options.

up2early commented 1 year ago

Hey thanks for looking into this! I also want to play spirit tracks with emudeck 😃 Not sure how much progress you've made but I did some digging and found the docs related to retroarch core options.

retro_core_options_v2

/* const struct retro_core_options_v2 * -- * Allows an implementation to signal the environment * which variables it might want to check for later using * GET_VARIABLE. * This allows the frontend to present these variables to * a user dynamically. * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION * returns an API version of >= 2. * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES. * This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS. * This should be called the first time as early as * possible (ideally in retro_set_environment). * Afterwards it may be called again for the core to communicate * updated options to the frontend, but the number of core * options must not change from the number in the initial call. * If RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION returns an API * version of >= 2, this callback is guaranteed to succeed * (i.e. callback return value does not indicate success) * If callback returns true, frontend has core option category * support. * If callback returns false, frontend does not have core option * category support. * * 'data' points to a retro_core_options_v2 struct, containing * of two pointers: * - retro_core_options_v2::categories is an array of * retro_core_option_v2_category structs terminated by a * { NULL, NULL, NULL } element. If retro_core_options_v2::categories * is NULL, all core options will have no category and will be shown * at the top level of the frontend core option interface. If frontend * does not have core option category support, categories array will * be ignored. * - retro_core_options_v2::definitions is an array of * retro_core_option_v2_definition structs terminated by a * { NULL, NULL, NULL, NULL, NULL, NULL, {{0}}, NULL } * element. * * >> retro_core_option_v2_category notes: * * - retro_core_option_v2_category::key should contain string * that uniquely identifies the core option category. Valid * key characters are [a-z, A-Z, 0-9, _, -] * Namespace collisions with other implementations' category * keys are permitted. * - retro_core_option_v2_category::desc should contain a human * readable description of the category key. * - retro_core_option_v2_category::info should contain any * additional human readable information text that a typical * user may need to understand the nature of the core option * category. * * Example entry: * { * "advanced_settings", * "Advanced", * "Options affecting low-level emulation performance and accuracy." * } * * >> retro_core_option_v2_definition notes: * * - retro_core_option_v2_definition::key should be namespaced to not * collide with other implementations' keys. e.g. A core called * 'foo' should use keys named as 'foo_option'. Valid key characters * are [a-z, A-Z, 0-9, _, -]. * - retro_core_option_v2_definition::desc should contain a human readable * description of the key. Will be used when the frontend does not * have core option category support. Examples: "Aspect Ratio" or * "Video > Aspect Ratio". * - retro_core_option_v2_definition::desc_categorized should contain a * human readable description of the key, which will be used when * frontend has core option category support. Example: "Aspect Ratio", * where associated retro_core_option_v2_category::desc is "Video". * If empty or NULL, the string specified by * retro_core_option_v2_definition::desc will be used instead. * retro_core_option_v2_definition::desc_categorized will be ignored * if retro_core_option_v2_definition::category_key is empty or NULL. * - retro_core_option_v2_definition::info should contain any additional * human readable information text that a typical user may need to * understand the functionality of the option. * - retro_core_option_v2_definition::info_categorized should contain * any additional human readable information text that a typical user * may need to understand the functionality of the option, and will be * used when frontend has core option category support. This is provided * to accommodate the case where info text references an option by * name/desc, and the desc/desc_categorized text for that option differ. * If empty or NULL, the string specified by * retro_core_option_v2_definition::info will be used instead. * retro_core_option_v2_definition::info_categorized will be ignored * if retro_core_option_v2_definition::category_key is empty or NULL. * - retro_core_option_v2_definition::category_key should contain a * category identifier (e.g. "video" or "audio") that will be * assigned to the core option if frontend has core option category * support. A categorized option will be shown in a subsection/ * submenu of the frontend core option interface. If key is empty * or NULL, or if key does not match one of the * retro_core_option_v2_category::key values in the associated * retro_core_option_v2_category array, option will have no category * and will be shown at the top level of the frontend core option * interface. * - retro_core_option_v2_definition::values is an array of * retro_core_option_value structs terminated by a { NULL, NULL } * element. * --> retro_core_option_v2_definition::values[index].value is an * expected option value. * --> retro_core_option_v2_definition::values[index].label is a * human readable label used when displaying the value on screen. * If NULL, the value itself is used. * - retro_core_option_v2_definition::default_value is the default * core option setting. It must match one of the expected option * values in the retro_core_option_v2_definition::values array. If * it does not, or the default value is NULL, the first entry in the * retro_core_option_v2_definition::values array is treated as the * default. * * The number of possible option values should be very limited, * and must be less than RETRO_NUM_CORE_OPTION_VALUES_MAX. * i.e. it should be feasible to cycle through options * without a keyboard. * * Example entries: * * - Uncategorized: * * { * "foo_option", * "Speed hack coprocessor X", * NULL, * "Provides increased performance at the expense of reduced accuracy.", * NULL, * NULL, * { * { "false", NULL }, * { "true", NULL }, * { "unstable", "Turbo (Unstable)" }, * { NULL, NULL }, * }, * "false" * } * * - Categorized: * * { * "foo_option", * "Advanced > Speed hack coprocessor X", * "Speed hack coprocessor X", * "Setting 'Advanced > Speed hack coprocessor X' to 'true' or 'Turbo' provides increased performance at the expense of reduced accuracy", * "Setting 'Speed hack coprocessor X' to 'true' or 'Turbo' provides increased performance at the expense of reduced accuracy", * "advanced_settings", * { * { "false", NULL }, * { "true", NULL }, * { "unstable", "Turbo (Unstable)" }, * { NULL, NULL }, * }, * "false" * } * * Only strings are operated on. The possible values will * generally be displayed and stored as-is by the frontend. */

I think that is the relevant portion and it looks like that struct gets defined here in the melonDS core!

tairosonloa commented 1 year ago

Hey thanks for looking into this! I also want to play spirit tracks with emudeck 😃 Not sure how much progress you've made

Hi @up2early! Sadly, I did not make any progress, and abandoned it after no getting response. Currently, my situation has changed, and I have not the time nor the energy to face this challenge, so it's all yours if you want to try. Good luck!

bslenul commented 1 year ago

Gave it a try few weeks ago and got it to work:

https://user-images.githubusercontent.com/33353403/203537480-d3732225-f4ad-4ccb-bbdc-bc60ee585515.mp4

I've made it so you have 3 choices as a core option:

Unfortunately there's a function I reused (https://github.com/libretro/melonDS/blob/master/src/frontend/qt_sdl/main.cpp#L184-L263) that requires SDL2 and apparently we want to avoid that... and I couldn't find a way around (my C/C++ are very limited) so I gave up :/

I really hope someone will be able to implement this properly at some point... :(

up2early commented 1 year ago

Hey @bslenul could you share the branch or compiled core?

bslenul commented 1 year ago

Sure! The changes: https://github.com/bslenul/melonDS/commit/270d0d4b737b009e5d789e387382795add5e2f5f

And the compiled core for Windows: melonds_win.zip and Linux: melonds_linux.zip

up2early commented 1 year ago

The blow noise option works in Spirit Tracks, thanks @bslenul!

If the blow noise works in most games, maybe loading a custom wav file is not needed and we could get the blow noise change released?

bslenul commented 1 year ago

Yeah, PR sent! With only Blow Noise/White Noise as a choice, hopefully someone will be able to include support for WAV files properly in the future :)

Mugglajo commented 11 months ago

Does anyone know if I can do this on mobile? I really enjoy driving the train in spirit tracks but I cant continue cause im at the first flute bit, and im not spending 200$+ for a copy of the game