Merge pull request #1568 from reicast/baka/audio_options

AUDIO: Allow backend specific settings
This commit is contained in:
skmp 2019-05-03 03:57:17 +03:00 committed by GitHub
commit 3f8328da25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 245 additions and 31 deletions

View File

@ -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<std::string, std::map<std::string, std::string>>
for (std::map<std::string, std::map<std::string, std::string>>::iterator it = settings.audio.options.begin(); it != settings.audio.options.end(); ++it)
{
std::pair<std::string, std::map<std::string, std::string>> p = (std::pair<std::string, std::map<std::string, std::string>>)*it;
std::string section = p.first;
std::map<std::string, std::string> options = p.second;
for (std::map<std::string, std::string>::iterator it2 = options.begin(); it2 != options.end(); ++it2)
{
std::pair<std::string, std::string> p2 = (std::pair<std::string, std::string>)*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)

View File

@ -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<std::string> alsa_get_devicelist()
{
std::vector<std::string> 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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -11,6 +11,29 @@ u32 asRingFreeCount();
bool asRingRead(u8* dst,u32 count=0);
void UpdateBuff(u8* pos);
typedef std::vector<std::string> (*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();

View File

@ -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<std::string, std::string>* 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<std::string> list_items = options->list_callback();
for (std::vector<std::string>::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();
}

View File

@ -344,6 +344,7 @@ int darw_printf(const wchar* Text,...);
//includes from c++rt
#include <vector>
#include <string>
#include <map>
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<<key, value>>
std::map<std::string, std::map<std::string, std::string>> options;
} audio;
#if USE_OMX
struct
{

View File

@ -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);