From 0a3c361da2adce112f43964bd86ae6126d81dd34 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Wed, 24 Apr 2019 21:41:38 +0200 Subject: [PATCH 1/6] AUDIO: Allow backend specific settings --- core/nullDC.cpp | 20 ++++++ core/oslib/audiobackend_alsa.cpp | 71 ++++++++++++++++++- core/oslib/audiobackend_omx.cpp | 3 +- core/oslib/audiobackend_oss.cpp | 3 +- core/oslib/audiobackend_pulseaudio.cpp | 3 +- core/oslib/audiostream.h | 24 +++++++ core/rend/gui.cpp | 53 +++++++++++++- core/types.h | 8 ++- .../reicast/src/main/jni/src/Android.cpp | 3 +- 9 files changed, 181 insertions(+), 7 deletions(-) diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 2f673fc5f..1deeebad7 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -721,6 +721,26 @@ void SaveSettings() cfgSaveBool("config", "aica.NoBatch", settings.aica.NoBatch); cfgSaveBool("config", "aica.NoSound", settings.aica.NoSound); cfgSaveStr("audio", "backend", settings.audio.backend.c_str()); + + // Write backend specific settings + // std::map> + for (std::map>::iterator it = settings.audio.options.begin(); it != settings.audio.options.end(); ++it) + { + + std::pair> p = (std::pair>)*it; + std::string section = p.first; + std::map options = p.second; + + for (std::map::iterator it2 = options.begin(); it2 != options.end(); ++it2) + { + std::pair p2 = (std::pair)*it2; + std::string key = p2.first; + std::string val = p2.second; + + cfgSaveStr(section.c_str(), key.c_str(), val.c_str()); + } + } + cfgSaveBool("config", "rend.WideScreen", settings.rend.WideScreen); cfgSaveBool("config", "rend.ShowFPS", settings.rend.ShowFPS); if (!rtt_to_buffer_game || !settings.rend.RenderToTextureBuffer) diff --git a/core/oslib/audiobackend_alsa.cpp b/core/oslib/audiobackend_alsa.cpp index 2ee230976..9effbcb6d 100644 --- a/core/oslib/audiobackend_alsa.cpp +++ b/core/oslib/audiobackend_alsa.cpp @@ -175,12 +175,81 @@ static void alsa_term() snd_pcm_close(handle); } +std::vector alsa_get_devicelist() +{ + std::vector result; + + char **hints; + int err = snd_device_name_hint(-1, "pcm", (void***)&hints); + + // Error initializing ALSA + if (err != 0) + return result; + + + char** n = hints; + while (*n != NULL) + { + // Get the type (NULL/Input/Output) + char *type = snd_device_name_get_hint(*n, "IOID"); + char *name = snd_device_name_get_hint(*n, "NAME"); + + if (name != NULL) + { + // We only want output or special devices (like "default" or "pulse") + // TODO Only those with type == NULL? + if (type == NULL || strcmp(type, "Output") == 0) + { + // TODO Check if device works (however we need to hash the resulting list then) + /*snd_pcm_t *handle; + int rc = snd_pcm_open(&handle, name, SND_PCM_STREAM_PLAYBACK, 0); + + if (rc == 0) + { + result.push_back(name); + snd_pcm_close(handle); + } + */ + + result.push_back(name); + } + + } + + if (type != NULL) + free(type); + + if (name != NULL) + free(name); + + n++; + } + + snd_device_name_free_hint((void**)hints); + + return result; +} + +static audio_option_t* alsa_audio_options(int* option_count) +{ + *option_count = 1; + static audio_option_t result[1]; + + result[0].cfg_name = "device"; + result[0].caption = "Device"; + result[0].type = list; + result[0].list_callback = alsa_get_devicelist; + + return result; +} + static audiobackend_t audiobackend_alsa = { "alsa", // Slug "Advanced Linux Sound Architecture", // Name &alsa_init, &alsa_push, - &alsa_term + &alsa_term, + &alsa_audio_options }; static bool alsa = RegisterAudioBackend(&audiobackend_alsa); diff --git a/core/oslib/audiobackend_omx.cpp b/core/oslib/audiobackend_omx.cpp index 71c426b68..c8c8a6536 100644 --- a/core/oslib/audiobackend_omx.cpp +++ b/core/oslib/audiobackend_omx.cpp @@ -313,7 +313,8 @@ audiobackend_t audiobackend_omx = { "OpenMAX IL", // Name &omx_init, &omx_push, - &omx_term + &omx_term, + NULL }; static bool omx = RegisterAudioBackend(&audiobackend_omx); diff --git a/core/oslib/audiobackend_oss.cpp b/core/oslib/audiobackend_oss.cpp index cc653466f..b3a48cbd6 100644 --- a/core/oslib/audiobackend_oss.cpp +++ b/core/oslib/audiobackend_oss.cpp @@ -48,7 +48,8 @@ audiobackend_t audiobackend_oss = { "Open Sound System", // Name &oss_init, &oss_push, - &oss_term + &oss_term, + NULL }; static bool oss = RegisterAudioBackend(&audiobackend_oss); diff --git a/core/oslib/audiobackend_pulseaudio.cpp b/core/oslib/audiobackend_pulseaudio.cpp index 28963b00a..b651b4003 100644 --- a/core/oslib/audiobackend_pulseaudio.cpp +++ b/core/oslib/audiobackend_pulseaudio.cpp @@ -43,7 +43,8 @@ audiobackend_t audiobackend_pulseaudio = { "PulseAudio", // Name &pulseaudio_init, &pulseaudio_push, - &pulseaudio_term + &pulseaudio_term, + NULL }; static bool pulse = RegisterAudioBackend(&audiobackend_pulseaudio); diff --git a/core/oslib/audiostream.h b/core/oslib/audiostream.h index 2726db0e1..37c1cbc43 100644 --- a/core/oslib/audiostream.h +++ b/core/oslib/audiostream.h @@ -11,6 +11,29 @@ u32 asRingFreeCount(); bool asRingRead(u8* dst,u32 count=0); void UpdateBuff(u8* pos); +typedef std::vector (*audio_option_callback_t)(); +enum audio_option_type +{ + text = 0 +, integer = 1 +, list = 2 +}; + +typedef struct { + std::string cfg_name; + std::string caption; + audio_option_type type; + + // type int_value (spin edit) + int min_value; + int max_value; + + // type list edit (string/char*) + audio_option_callback_t list_callback; +} audio_option_t; + +typedef audio_option_t* (*audio_options_func_t)(int* option_count); + typedef void (*audio_backend_init_func_t)(); typedef u32 (*audio_backend_push_func_t)(void*, u32, bool); typedef void (*audio_backend_term_func_t)(); @@ -20,6 +43,7 @@ typedef struct { audio_backend_init_func_t init; audio_backend_push_func_t push; audio_backend_term_func_t term; + audio_options_func_t get_options; } audiobackend_t; extern bool RegisterAudioBackend(audiobackend_t* backend); extern void InitAudio(); diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index a530f5b5c..008ed1041 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -1036,6 +1036,8 @@ static void gui_display_settings() } SortAudioBackends(); + + audiobackend_t* current_backend = backend; if (ImGui::BeginCombo("Audio Backend", backend_name.c_str(), ImGuiComboFlags_None)) { bool is_selected = (settings.audio.backend == "auto"); @@ -1052,9 +1054,12 @@ static void gui_display_settings() for (int i = 0; i < GetAudioBackendCount(); i++) { - audiobackend_t* backend = GetAudioBackend(i); + backend = GetAudioBackend(i); is_selected = (settings.audio.backend == backend->slug); + if (is_selected) + current_backend = backend; + if (ImGui::Selectable(backend->slug.c_str(), &is_selected)) settings.audio.backend = backend->slug; ImGui::SameLine(); ImGui::Text("-"); @@ -1067,6 +1072,52 @@ static void gui_display_settings() ImGui::SameLine(); ShowHelpMarker("The audio backend to use"); + if (current_backend != NULL && current_backend->get_options != NULL) + { + // get backend specific options + int option_count; + audio_option_t* options = current_backend->get_options(&option_count); + + // initialize options if not already done + std::map* cfg_entries = &settings.audio.options[current_backend->slug]; + bool populate_entries = (cfg_entries->size() == 0); + + for (int o = 0; o < option_count; o++) + { + std::string value; + if (populate_entries) + { + value = cfgLoadStr(current_backend->slug.c_str(), options->cfg_name.c_str(), ""); + (*cfg_entries)[options->cfg_name] = value; + } + value = (*cfg_entries)[options->cfg_name]; + + if (options->type == list) + { + if (ImGui::BeginCombo(options->caption.c_str(), value.c_str(), ImGuiComboFlags_None)) + { + bool is_selected = false; + std::vector list_items = options->list_callback(); + for (std::vector::iterator it = list_items.begin() ; it != list_items.end(); ++it) + { + std::string cur = (std::string)*it; + is_selected = (value == cur); + if (ImGui::Selectable(cur.c_str(), &is_selected)) + { + (*cfg_entries)[options->cfg_name] = cur; + } + + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + } + + options++; + } + } + ImGui::Checkbox("Enable DSP", &settings.aica.NoBatch); ImGui::SameLine(); ShowHelpMarker("Enable the Dreamcast Digital Sound Processor. Only recommended on fast and arm64 platforms"); diff --git a/core/types.h b/core/types.h index 771d758e6..8b1a039a8 100644 --- a/core/types.h +++ b/core/types.h @@ -344,6 +344,7 @@ int darw_printf(const wchar* Text,...); //includes from c++rt #include #include +#include using namespace std; //used for asm-olny functions @@ -387,7 +388,7 @@ using namespace std; #ifndef RELEASE #define EMUERROR(format, ...) printf("Error in %20s:%s:%d: " format "\n", \ __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) -//strlen(__FILE__) <= 20 ? __FILE__ : __FILE__ + strlen(__FILE__) - 20, +//strlen(__FILE__) <= 20 ? __FILE__ : __FILE__ + strlen(__FILE__) - 20, #else #define EMUERROR(format, ...) #endif @@ -681,7 +682,12 @@ struct settings_t struct{ std::string backend; + + // slug<> + std::map> options; } audio; + + #if USE_OMX struct { diff --git a/shell/android-studio/reicast/src/main/jni/src/Android.cpp b/shell/android-studio/reicast/src/main/jni/src/Android.cpp index 7b6ad5533..07521e6ee 100644 --- a/shell/android-studio/reicast/src/main/jni/src/Android.cpp +++ b/shell/android-studio/reicast/src/main/jni/src/Android.cpp @@ -547,7 +547,8 @@ audiobackend_t audiobackend_android = { "Android Audio", // Name &androidaudio_init, &androidaudio_push, - &androidaudio_term + &androidaudio_term, + NULL }; static bool android = RegisterAudioBackend(&audiobackend_android); From 684ba26ec0bd78fc98eea5b1d7bbbc3962048177 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Thu, 2 May 2019 18:41:45 +0200 Subject: [PATCH 2/6] AUDIO: Reorganize settings Move DSP and FPS above the slug selection so that all slug specific settings are after the slug selection. --- core/rend/gui.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 008ed1041..0404b503a 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -1026,6 +1026,13 @@ static void gui_display_settings() ImGui::SameLine(); ShowHelpMarker("Disable the emulator sound output"); + ImGui::Checkbox("Enable DSP", &settings.aica.NoBatch); + ImGui::SameLine(); + ShowHelpMarker("Enable the Dreamcast Digital Sound Processor. Only recommended on fast and arm64 platforms"); + ImGui::Checkbox("Limit FPS", &settings.aica.LimitFPS); + ImGui::SameLine(); + ShowHelpMarker("Use the sound output to limit the speed of the emulator. Recommended in most cases"); + audiobackend_t* backend = NULL;; std::string backend_name = settings.audio.backend; if (backend_name != "auto" && backend_name != "none") @@ -1118,12 +1125,6 @@ static void gui_display_settings() } } - ImGui::Checkbox("Enable DSP", &settings.aica.NoBatch); - ImGui::SameLine(); - ShowHelpMarker("Enable the Dreamcast Digital Sound Processor. Only recommended on fast and arm64 platforms"); - ImGui::Checkbox("Limit FPS", &settings.aica.LimitFPS); - ImGui::SameLine(); - ShowHelpMarker("Use the sound output to limit the speed of the emulator. Recommended in most cases"); ImGui::PopStyleVar(); ImGui::EndTabItem(); } From 5678556cb6ee2378a03f2ac8d318d116f10dc8a0 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Thu, 2 May 2019 18:48:09 +0200 Subject: [PATCH 3/6] ALSA: Rework initialization of pcm device As we can now configure the alsa device in the GUI, it's no longer necessary to write the first working device back to the configuration. Also there is now the "auto" device to automatically try to initialize the alsa device. --- core/oslib/audiobackend_alsa.cpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/core/oslib/audiobackend_alsa.cpp b/core/oslib/audiobackend_alsa.cpp index 9effbcb6d..52223d895 100644 --- a/core/oslib/audiobackend_alsa.cpp +++ b/core/oslib/audiobackend_alsa.cpp @@ -18,30 +18,43 @@ static void alsa_init() string device = cfgLoadStr("alsa", "device", ""); int rc = -1; - if (device == "") + if (device == "" || device == "auto") { printf("ALSA: trying to determine audio device\n"); - /* Open PCM device for playback. */ + + // trying default device device = "default"; rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); + // "default" didn't work, try first device if (rc < 0) { device = "plughw:0,0,0"; rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); + + if (rc < 0) + { + device = "plughw:0,0"; + rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); + } } + // first didn't work, try second if (rc < 0) { - device = "plughw:0,0"; + device = "plughw:1,0"; rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); } - if (rc >= 0) + // try pulse audio backend + if (rc < 0) { - // init successfull, write value back to config - cfgSaveStr("alsa", "device", device.c_str()); + device = "pulse"; + rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); } + + if (rc < 0) + printf("ALSA: unable to automatically determine audio device.\n"); } else { rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); @@ -186,6 +199,8 @@ std::vector alsa_get_devicelist() if (err != 0) return result; + // special value to automatically detect on initialization + result.push_back("auto"); char** n = hints; while (*n != NULL) From dc709c604f06bb18bc8f092353ec0b7d53d1a6ce Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Thu, 2 May 2019 18:49:44 +0200 Subject: [PATCH 4/6] ALSA: prefix output with "ALSA:" --- core/oslib/audiobackend_alsa.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/core/oslib/audiobackend_alsa.cpp b/core/oslib/audiobackend_alsa.cpp index 52223d895..47c4f348c 100644 --- a/core/oslib/audiobackend_alsa.cpp +++ b/core/oslib/audiobackend_alsa.cpp @@ -62,7 +62,7 @@ static void alsa_init() if (rc < 0) { - fprintf(stderr, "unable to open PCM device %s: %s\n", device.c_str(), snd_strerror(rc)); + fprintf(stderr, "ALSA: unable to open PCM device %s: %s\n", device.c_str(), snd_strerror(rc)); return; } @@ -75,7 +75,7 @@ static void alsa_init() rc=snd_pcm_hw_params_any(handle, params); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_any %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_any %s\n", snd_strerror(rc)); return; } @@ -85,7 +85,7 @@ static void alsa_init() rc=snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_access %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_access %s\n", snd_strerror(rc)); return; } @@ -93,7 +93,7 @@ static void alsa_init() rc=snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_format %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_format %s\n", snd_strerror(rc)); return; } @@ -101,7 +101,7 @@ static void alsa_init() rc=snd_pcm_hw_params_set_channels(handle, params, 2); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_channels %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_channels %s\n", snd_strerror(rc)); return; } @@ -110,7 +110,7 @@ static void alsa_init() rc=snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_rate_near %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_rate_near %s\n", snd_strerror(rc)); return; } @@ -119,26 +119,31 @@ static void alsa_init() rc=snd_pcm_hw_params_set_period_size_near(handle, params, &period_size, &dir); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_buffer_size_near %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_buffer_size_near %s\n", snd_strerror(rc)); return; } else + { printf("ALSA: period size set to %ld\n", period_size); + } + buffer_size = (44100 * 100 /* settings.omx.Audio_Latency */ / 1000 / period_size + 1) * period_size; rc=snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_buffer_size_near %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_buffer_size_near %s\n", snd_strerror(rc)); return; } else + { printf("ALSA: buffer size set to %ld\n", buffer_size); + } /* Write the parameters to the driver */ rc = snd_pcm_hw_params(handle, params); if (rc < 0) { - fprintf(stderr, "Unable to set hw parameters: %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Unable to set hw parameters: %s\n", snd_strerror(rc)); return; } } From 139ef22408679f255fdc107c313364c540936b3d Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Thu, 2 May 2019 19:02:34 +0200 Subject: [PATCH 5/6] AUDIO: compile fixes for coreaudio, dsound and libao --- core/oslib/audiobackend_coreaudio.cpp | 3 ++- core/oslib/audiobackend_directsound.cpp | 3 ++- core/oslib/audiobackend_libao.cpp | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/oslib/audiobackend_coreaudio.cpp b/core/oslib/audiobackend_coreaudio.cpp index dd04926ce..8e0e9b344 100644 --- a/core/oslib/audiobackend_coreaudio.cpp +++ b/core/oslib/audiobackend_coreaudio.cpp @@ -159,7 +159,8 @@ audiobackend_t audiobackend_coreaudio = { "Core Audio", // Name &coreaudio_init, &coreaudio_push, - &coreaudio_term + &coreaudio_term, + NULL }; static bool core = RegisterAudioBackend(&audiobackend_coreaudio); diff --git a/core/oslib/audiobackend_directsound.cpp b/core/oslib/audiobackend_directsound.cpp index d173e434b..f004bdfd2 100644 --- a/core/oslib/audiobackend_directsound.cpp +++ b/core/oslib/audiobackend_directsound.cpp @@ -185,7 +185,8 @@ audiobackend_t audiobackend_directsound = { "Microsoft DirectSound", // Name &directsound_init, &directsound_push, - &directsound_term + &directsound_term, + NULL }; static bool ds = RegisterAudioBackend(&audiobackend_directsound); diff --git a/core/oslib/audiobackend_libao.cpp b/core/oslib/audiobackend_libao.cpp index c235dca00..0a6a72e7d 100644 --- a/core/oslib/audiobackend_libao.cpp +++ b/core/oslib/audiobackend_libao.cpp @@ -43,7 +43,8 @@ audiobackend_t audiobackend_libao = { "libao", // Name &libao_init, &libao_push, - &libao_term + &libao_term, + NULL }; static bool ao = RegisterAudioBackend(&audiobackend_libao); From 99033e297c604934dece7265d0e952740d7453a8 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Thu, 2 May 2019 20:24:49 +0200 Subject: [PATCH 6/6] AUDIO: Implement integer and checkbox options I remove "text" as a possibility for the moment as we're currently not having **any** text option. --- core/oslib/audiostream.h | 4 ++-- core/rend/gui.cpp | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/core/oslib/audiostream.h b/core/oslib/audiostream.h index 37c1cbc43..408592b55 100644 --- a/core/oslib/audiostream.h +++ b/core/oslib/audiostream.h @@ -14,8 +14,8 @@ void UpdateBuff(u8* pos); typedef std::vector (*audio_option_callback_t)(); enum audio_option_type { - text = 0 -, integer = 1 + integer = 0 +, checkbox = 1 , list = 2 }; diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 0404b503a..b6f0b30aa 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -1099,7 +1099,20 @@ static void gui_display_settings() } value = (*cfg_entries)[options->cfg_name]; - if (options->type == list) + if (options->type == integer) + { + int val = stoi(value); + ImGui::SliderInt(options->caption.c_str(), &val, options->min_value, options->max_value); + (*cfg_entries)[options->cfg_name] = to_string(val); + } + else if (options->type == checkbox) + { + bool check = (value == "1"); + ImGui::Checkbox(options->caption.c_str(), &check); + std::string cur = check ? "1" : "0"; + (*cfg_entries)[options->cfg_name] = cur; + } + else if (options->type == list) { if (ImGui::BeginCombo(options->caption.c_str(), value.c_str(), ImGuiComboFlags_None)) { @@ -1120,6 +1133,9 @@ static void gui_display_settings() ImGui::EndCombo(); } } + else { + printf("Unknown option\n"); + } options++; }