2013-04-21 06:17:03 +00:00
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
2009-09-09 21:26:33 +00:00
2011-01-31 08:19:27 +00:00
# include <functional>
2014-02-17 10:18:15 +00:00
# include "AudioCommon/AlsaSoundStream.h"
# include "Common/Common.h"
# include "Common/Thread.h"
2009-09-09 21:26:33 +00:00
2010-05-21 22:48:57 +00:00
# define FRAME_COUNT_MIN 256
# define BUFFER_SIZE_MAX 8192
# define BUFFER_SIZE_BYTES (BUFFER_SIZE_MAX*2*2)
2009-09-09 21:26:33 +00:00
2014-03-09 20:14:26 +00:00
AlsaSound : : AlsaSound ( CMixer * mixer ) : SoundStream ( mixer ) , thread_data ( 0 ) , handle ( nullptr ) , frames_to_deliver ( FRAME_COUNT_MIN )
2009-09-09 21:26:33 +00:00
{
mix_buffer = new u8 [ BUFFER_SIZE_BYTES ] ;
}
AlsaSound : : ~ AlsaSound ( )
{
delete [ ] mix_buffer ;
}
bool AlsaSound : : Start ( )
{
2014-04-01 16:09:22 +00:00
thread = std : : thread ( std : : mem_fn ( & AlsaSound : : SoundLoop ) , this ) ;
2009-09-09 21:26:33 +00:00
thread_data = 0 ;
return true ;
}
void AlsaSound : : Stop ( )
{
thread_data = 1 ;
2011-01-27 21:34:37 +00:00
thread . join ( ) ;
2009-09-09 21:26:33 +00:00
}
void AlsaSound : : Update ( )
{
// don't need to do anything here.
}
// Called on audio thread.
void AlsaSound : : SoundLoop ( )
{
2010-05-21 22:48:57 +00:00
if ( ! AlsaInit ( ) ) {
thread_data = 2 ;
return ;
}
2011-12-31 04:22:48 +00:00
Common : : SetCurrentThreadName ( " Audio thread - alsa " ) ;
2009-09-09 21:26:33 +00:00
while ( ! thread_data )
{
m_mixer - > Mix ( reinterpret_cast < short * > ( mix_buffer ) , frames_to_deliver ) ;
2009-12-13 11:51:29 +00:00
int rc = m_muted ? 1337 : snd_pcm_writei ( handle , mix_buffer , frames_to_deliver ) ;
2009-09-09 21:26:33 +00:00
if ( rc = = - EPIPE )
{
// Underrun
snd_pcm_prepare ( handle ) ;
}
2013-10-29 05:23:17 +00:00
else if ( rc < 0 )
2009-09-09 21:26:33 +00:00
{
ERROR_LOG ( AUDIO , " writei fail: %s " , snd_strerror ( rc ) ) ;
}
}
AlsaShutdown ( ) ;
thread_data = 2 ;
}
bool AlsaSound : : AlsaInit ( )
{
unsigned int sample_rate = m_mixer - > GetSampleRate ( ) ;
int err ;
int dir ;
snd_pcm_sw_params_t * swparams ;
snd_pcm_hw_params_t * hwparams ;
2010-05-21 22:48:57 +00:00
snd_pcm_uframes_t buffer_size , buffer_size_max ;
2010-05-07 23:14:40 +00:00
unsigned int periods ;
2013-03-20 01:51:12 +00:00
2009-09-09 21:26:33 +00:00
err = snd_pcm_open ( & handle , " default " , SND_PCM_STREAM_PLAYBACK , 0 ) ;
2013-10-29 05:23:17 +00:00
if ( err < 0 )
2009-09-09 21:26:33 +00:00
{
ERROR_LOG ( AUDIO , " Audio open error: %s \n " , snd_strerror ( err ) ) ;
return false ;
}
snd_pcm_hw_params_alloca ( & hwparams ) ;
2013-03-20 01:51:12 +00:00
2009-09-09 21:26:33 +00:00
err = snd_pcm_hw_params_any ( handle , hwparams ) ;
2013-10-29 05:23:17 +00:00
if ( err < 0 )
2009-09-09 21:26:33 +00:00
{
ERROR_LOG ( AUDIO , " Broken configuration for this PCM: %s \n " , snd_strerror ( err ) ) ;
return false ;
}
2013-10-29 05:23:17 +00:00
2009-09-09 21:26:33 +00:00
err = snd_pcm_hw_params_set_access ( handle , hwparams , SND_PCM_ACCESS_RW_INTERLEAVED ) ;
2013-10-29 05:23:17 +00:00
if ( err < 0 )
2009-09-09 21:26:33 +00:00
{
ERROR_LOG ( AUDIO , " Access type not available: %s \n " , snd_strerror ( err ) ) ;
return false ;
}
2013-03-20 01:51:12 +00:00
err = snd_pcm_hw_params_set_format ( handle , hwparams , SND_PCM_FORMAT_S16_LE ) ;
2013-10-29 05:23:17 +00:00
if ( err < 0 )
2009-09-09 21:26:33 +00:00
{
ERROR_LOG ( AUDIO , " Sample format not available: %s \n " , snd_strerror ( err ) ) ;
return false ;
}
2010-07-06 16:16:07 +00:00
2010-12-27 15:05:18 +00:00
dir = 0 ;
2009-10-15 02:04:27 +00:00
err = snd_pcm_hw_params_set_rate_near ( handle , hwparams , & sample_rate , & dir ) ;
2013-10-29 05:23:17 +00:00
if ( err < 0 )
2009-09-09 21:26:33 +00:00
{
ERROR_LOG ( AUDIO , " Rate not available: %s \n " , snd_strerror ( err ) ) ;
return false ;
}
2013-03-20 01:51:12 +00:00
err = snd_pcm_hw_params_set_channels ( handle , hwparams , 2 ) ;
2013-10-29 05:23:17 +00:00
if ( err < 0 )
2009-09-09 21:26:33 +00:00
{
ERROR_LOG ( AUDIO , " Channels count not available: %s \n " , snd_strerror ( err ) ) ;
return false ;
}
2013-03-20 01:51:12 +00:00
2010-05-21 22:48:57 +00:00
periods = BUFFER_SIZE_MAX / FRAME_COUNT_MIN ;
err = snd_pcm_hw_params_set_periods_max ( handle , hwparams , & periods , & dir ) ;
2013-10-29 05:23:17 +00:00
if ( err < 0 )
2010-05-07 23:14:40 +00:00
{
ERROR_LOG ( AUDIO , " Cannot set Minimum periods: %s \n " , snd_strerror ( err ) ) ;
return false ;
}
2010-05-21 22:48:57 +00:00
buffer_size_max = BUFFER_SIZE_MAX ;
err = snd_pcm_hw_params_set_buffer_size_max ( handle , hwparams , & buffer_size_max ) ;
2013-10-29 05:23:17 +00:00
if ( err < 0 )
2010-05-07 23:14:40 +00:00
{
ERROR_LOG ( AUDIO , " Cannot set minimum buffer size: %s \n " , snd_strerror ( err ) ) ;
return false ;
}
2010-05-21 22:48:57 +00:00
err = snd_pcm_hw_params ( handle , hwparams ) ;
2013-10-29 05:23:17 +00:00
if ( err < 0 )
2010-05-07 23:14:40 +00:00
{
2013-03-20 01:51:12 +00:00
ERROR_LOG ( AUDIO , " Unable to install hw params: %s \n " , snd_strerror ( err ) ) ;
2010-05-21 22:48:57 +00:00
return false ;
}
2013-03-20 01:51:12 +00:00
2010-05-21 22:48:57 +00:00
err = snd_pcm_hw_params_get_buffer_size ( hwparams , & buffer_size ) ;
2013-10-29 05:23:17 +00:00
if ( err < 0 )
2010-05-21 22:48:57 +00:00
{
ERROR_LOG ( AUDIO , " Cannot get buffer size: %s \n " , snd_strerror ( err ) ) ;
2010-05-07 23:14:40 +00:00
return false ;
}
2009-09-09 21:26:33 +00:00
2010-05-21 22:48:57 +00:00
err = snd_pcm_hw_params_get_periods_max ( hwparams , & periods , & dir ) ;
2013-10-29 05:23:17 +00:00
if ( err < 0 )
2009-09-09 21:26:33 +00:00
{
2010-05-21 22:48:57 +00:00
ERROR_LOG ( AUDIO , " Cannot get periods: %s \n " , snd_strerror ( err ) ) ;
2009-09-09 21:26:33 +00:00
return false ;
}
2010-05-21 22:48:57 +00:00
2013-10-29 05:23:17 +00:00
//periods is the number of fragments alsa can wait for during one
2010-05-21 22:48:57 +00:00
//buffer_size
frames_to_deliver = buffer_size / periods ;
//limit the minimum size. pulseaudio advertises a minimum of 32 samples.
if ( frames_to_deliver < FRAME_COUNT_MIN )
2013-03-20 01:51:12 +00:00
frames_to_deliver = FRAME_COUNT_MIN ;
2010-05-21 22:48:57 +00:00
//it is probably a bad idea to try to send more than one buffer of data
if ( ( unsigned int ) frames_to_deliver > buffer_size )
2013-03-20 01:51:12 +00:00
frames_to_deliver = buffer_size ;
2010-12-05 09:04:34 +00:00
NOTICE_LOG ( AUDIO , " ALSA gave us a %ld sample \" hardware \" buffer with %d periods. Will send %d samples per fragments. \n " , buffer_size , periods , frames_to_deliver ) ;
2010-05-21 22:48:57 +00:00
2009-09-09 21:26:33 +00:00
snd_pcm_sw_params_alloca ( & swparams ) ;
err = snd_pcm_sw_params_current ( handle , swparams ) ;
2013-10-29 05:23:17 +00:00
if ( err < 0 )
2009-09-09 21:26:33 +00:00
{
2013-03-20 01:51:12 +00:00
ERROR_LOG ( AUDIO , " cannot init sw params: %s \n " , snd_strerror ( err ) ) ;
2009-09-09 21:26:33 +00:00
return false ;
}
2013-03-20 01:51:12 +00:00
2009-09-09 21:26:33 +00:00
err = snd_pcm_sw_params_set_start_threshold ( handle , swparams , 0U ) ;
2013-10-29 05:23:17 +00:00
if ( err < 0 )
2009-09-09 21:26:33 +00:00
{
2013-03-20 01:51:12 +00:00
ERROR_LOG ( AUDIO , " cannot set start thresh: %s \n " , snd_strerror ( err ) ) ;
2009-09-09 21:26:33 +00:00
return false ;
}
2013-03-20 01:51:12 +00:00
2009-09-09 21:26:33 +00:00
err = snd_pcm_sw_params ( handle , swparams ) ;
2013-10-29 05:23:17 +00:00
if ( err < 0 )
2009-09-09 21:26:33 +00:00
{
2013-03-20 01:51:12 +00:00
ERROR_LOG ( AUDIO , " cannot set sw params: %s \n " , snd_strerror ( err ) ) ;
2009-09-09 21:26:33 +00:00
return false ;
}
err = snd_pcm_prepare ( handle ) ;
2013-10-29 05:23:17 +00:00
if ( err < 0 )
2009-09-09 21:26:33 +00:00
{
ERROR_LOG ( AUDIO , " Unable to prepare: %s \n " , snd_strerror ( err ) ) ;
return false ;
}
NOTICE_LOG ( AUDIO , " ALSA successfully initialized. \n " ) ;
return true ;
}
void AlsaSound : : AlsaShutdown ( )
{
2014-03-09 20:14:26 +00:00
if ( handle ! = nullptr )
2009-09-09 21:26:33 +00:00
{
snd_pcm_drop ( handle ) ;
snd_pcm_close ( handle ) ;
2014-03-09 20:14:26 +00:00
handle = nullptr ;
2009-09-09 21:26:33 +00:00
}
}