ALSA audio backend fixes

This commit is contained in:
Nekotekina 2017-11-07 19:45:10 +03:00
parent dbc9bdfe02
commit f2980d57a1

View file

@ -1,5 +1,4 @@
#include "stdafx.h" #include "stdafx.h"
#include "Utilities/GSL.h"
#include "Emu/System.h" #include "Emu/System.h"
#include "ALSAThread.h" #include "ALSAThread.h"
@ -9,10 +8,12 @@
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
static thread_local snd_pcm_t* s_tls_handle{nullptr}; static thread_local snd_pcm_t* s_tls_handle{nullptr};
static thread_local snd_pcm_hw_params_t* s_tls_hw_params{nullptr};
static thread_local snd_pcm_sw_params_t* s_tls_sw_params{nullptr};
static void error(int err, const char* reason) static void error(int err, const char* reason)
{ {
fprintf(stderr, "ALSA: %s failed: %s\n", reason, snd_strerror(err)); LOG_ERROR(GENERAL, "ALSA: %s failed: %s\n", reason, snd_strerror(err));
} }
static bool check(int err, const char* reason) static bool check(int err, const char* reason)
@ -28,55 +29,86 @@ static bool check(int err, const char* reason)
ALSAThread::ALSAThread() ALSAThread::ALSAThread()
{ {
snd_pcm_hw_params_t* hw_params{nullptr};
auto at_ret = gsl::finally([&]()
{
if (hw_params)
{
snd_pcm_hw_params_free(hw_params);
}
});
if (!check(snd_pcm_open(&s_tls_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK), "snd_pcm_open")) if (!check(snd_pcm_open(&s_tls_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK), "snd_pcm_open"))
return; return;
if (!check(snd_pcm_hw_params_malloc(&hw_params), "snd_pcm_hw_params_malloc")) if (!check(snd_pcm_hw_params_malloc(&s_tls_hw_params), "snd_pcm_hw_params_malloc"))
return; return;
if (!check(snd_pcm_hw_params_any(s_tls_handle, hw_params), "snd_pcm_hw_params_any")) if (!check(snd_pcm_hw_params_any(s_tls_handle, s_tls_hw_params), "snd_pcm_hw_params_any"))
return; return;
if (!check(snd_pcm_hw_params_set_access(s_tls_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED), "snd_pcm_hw_params_set_access")) if (!check(snd_pcm_hw_params_set_access(s_tls_handle, s_tls_hw_params, SND_PCM_ACCESS_RW_INTERLEAVED), "snd_pcm_hw_params_set_access"))
return; return;
if (!check(snd_pcm_hw_params_set_format(s_tls_handle, hw_params, g_cfg.audio.convert_to_u16 ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_FLOAT_LE), "snd_pcm_hw_params_set_format")) if (!check(snd_pcm_hw_params_set_format(s_tls_handle, s_tls_hw_params, g_cfg.audio.convert_to_u16 ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_FLOAT_LE), "snd_pcm_hw_params_set_format"))
return; return;
if (!check(snd_pcm_hw_params_set_rate(s_tls_handle, hw_params, 48000, 0), "snd_pcm_hw_params_set_rate_near")) uint rate = 48000;
if (!check(snd_pcm_hw_params_set_rate_near(s_tls_handle, s_tls_hw_params, &rate, nullptr), "snd_pcm_hw_params_set_rate_near"))
return; return;
if (!check(snd_pcm_hw_params_set_channels(s_tls_handle, hw_params, g_cfg.audio.downmix_to_2ch ? 2 : 8), "snd_pcm_hw_params_set_channels")) if (!check(snd_pcm_hw_params_set_channels(s_tls_handle, s_tls_hw_params, g_cfg.audio.downmix_to_2ch ? 2 : 8), "snd_pcm_hw_params_set_channels"))
return; return;
if (!check(snd_pcm_hw_params_set_buffer_size(s_tls_handle, hw_params, g_cfg.audio.frames * 256), "snd_pcm_hw_params_set_buffer_size")) //uint period = 5333;
return; //if (!check(snd_pcm_hw_params_set_period_time_near(s_tls_handle, s_tls_hw_params, &period, nullptr), "snd_pcm_hw_params_set_period_time_near"))
if (!check(snd_pcm_hw_params_set_period_size(s_tls_handle, hw_params, 256, 0), "snd_pcm_hw_params_set_period_size"))
return;
//if (!check(snd_pcm_hw_params_set_periods(s_tls_handle, hw_params, 2, 0), "snd_pcm_hw_params_set_periods"))
// return; // return;
if (!check(snd_pcm_hw_params(s_tls_handle, hw_params), "snd_pcm_hw_params")) //if (!check(snd_pcm_hw_params_set_periods(s_tls_handle, s_tls_hw_params, 4, 0), "snd_pcm_hw_params_set_periods"))
// return;
snd_pcm_uframes_t bufsize_frames = g_cfg.audio.frames * 256;
snd_pcm_uframes_t period_frames = 256;
if (!check(snd_pcm_hw_params_set_buffer_size_near(s_tls_handle, s_tls_hw_params, &bufsize_frames), "snd_pcm_hw_params_set_buffer_size_near"))
return;
if (!check(snd_pcm_hw_params_set_period_size_near(s_tls_handle, s_tls_hw_params, &period_frames, 0), "snd_pcm_hw_params_set_period_size"))
return;
if (!check(snd_pcm_hw_params(s_tls_handle, s_tls_hw_params), "snd_pcm_hw_params"))
return;
if (!check(snd_pcm_hw_params_get_buffer_size(s_tls_hw_params, &bufsize_frames), "snd_pcm_hw_params_get_buffer_size"))
return;
if (!check(snd_pcm_hw_params_get_period_size(s_tls_hw_params, &period_frames, nullptr), "snd_pcm_hw_params_get_period_size"))
return;
if (!check(snd_pcm_sw_params_malloc(&s_tls_sw_params), "snd_pcm_sw_params_malloc"))
return;
if (!check(snd_pcm_sw_params_current(s_tls_handle, s_tls_sw_params), "snd_pcm_sw_params_current"))
return;
if (!check(snd_pcm_sw_params_set_start_threshold(s_tls_handle, s_tls_sw_params, period_frames), "snd_pcm_sw_params_set_start_threshold"))
return;
if (!check(snd_pcm_sw_params_set_stop_threshold(s_tls_handle, s_tls_sw_params, bufsize_frames), "snd_pcm_sw_params_set_stop_threshold"))
return;
if (!check(snd_pcm_sw_params(s_tls_handle, s_tls_sw_params), "snd_pcm_sw_params"))
return; return;
if (!check(snd_pcm_prepare(s_tls_handle), "snd_pcm_prepare")) if (!check(snd_pcm_prepare(s_tls_handle), "snd_pcm_prepare"))
return; return;
LOG_NOTICE(GENERAL, "ALSA: bufsize_frames=%u, period_frames=%u", bufsize_frames, period_frames);
} }
ALSAThread::~ALSAThread() ALSAThread::~ALSAThread()
{ {
if (s_tls_sw_params)
{
snd_pcm_sw_params_free(s_tls_sw_params);
}
if (s_tls_hw_params)
{
snd_pcm_hw_params_free(s_tls_hw_params);
}
if (s_tls_handle) if (s_tls_handle)
{ {
snd_pcm_close(s_tls_handle); snd_pcm_close(s_tls_handle);
@ -105,29 +137,30 @@ void ALSAThread::AddData(const void* src, int size)
size /= g_cfg.audio.convert_to_u16 ? 2 : 4; size /= g_cfg.audio.convert_to_u16 ? 2 : 4;
size /= g_cfg.audio.downmix_to_2ch ? 2 : 8; size /= g_cfg.audio.downmix_to_2ch ? 2 : 8;
int res; int res = snd_pcm_writei(s_tls_handle, src, size);
while (true)
{
res = snd_pcm_writei(s_tls_handle, src, size);
if (res == -EAGAIN) if (res == -EAGAIN)
{ {
continue; LOG_WARNING(GENERAL, "ALSA: EAGAIN");
return;
} }
else if (res == -EPIPE)
if (res < 0)
{ {
snd_pcm_prepare(s_tls_handle); res = snd_pcm_recover(s_tls_handle, res, 0);
}
else if (res < 0)
{ {
break; LOG_WARNING(GENERAL, "ALSA: failed to recover (%d)", res);
return;
} }
res = snd_pcm_writei(s_tls_handle, src, size);
} }
if (res != size) if (res != size)
{ {
error(res, "snd_pcm_writei"); LOG_WARNING(GENERAL, "ALSA: error (%d)", res);
} }
} }