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..47c4f348c 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); @@ -49,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; } @@ -62,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; } @@ -72,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; } @@ -80,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; } @@ -88,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; } @@ -97,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; } @@ -106,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; } } @@ -175,12 +193,83 @@ 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; + + // special value to automatically detect on initialization + result.push_back("auto"); + + 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_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); 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..408592b55 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 +{ + integer = 0 +, checkbox = 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..b6f0b30aa 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") @@ -1036,6 +1043,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 +1061,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,12 +1079,68 @@ static void gui_display_settings() ImGui::SameLine(); ShowHelpMarker("The audio backend to use"); - 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"); + 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 == 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)) + { + 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(); + } + } + else { + printf("Unknown option\n"); + } + + options++; + } + } + ImGui::PopStyleVar(); ImGui::EndTabItem(); } 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);