2015-02-09 21:44:32 +01:00
/** @file
2015-03-12 22:30:43 +01:00
* @ author Edouard DUPIN
2015-02-09 21:44:32 +01:00
* @ copyright 2011 , Edouard DUPIN , all right reserved
* @ license APACHE v2 .0 ( see license file )
* @ fork from RTAudio
2014-03-11 21:46:00 +01:00
*/
2015-04-10 22:06:17 +02:00
# if defined(ORCHESTRA_BUILD_ALSA)
2014-03-11 21:46:00 +01:00
# include <alsa/asoundlib.h>
# include <unistd.h>
2015-04-10 22:06:17 +02:00
# include <audio/orchestra/Interface.h>
# include <audio/orchestra/debug.h>
2015-02-26 22:25:15 +01:00
# include <etk/stdTools.h>
2015-03-20 21:07:58 +01:00
# include <etk/thread/tools.h>
2014-03-11 21:46:00 +01:00
# include <limits.h>
2015-04-10 22:06:17 +02:00
# include <audio/orchestra/api/Alsa.h>
2015-06-05 22:00:17 +02:00
extern " C " {
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sched.h>
# include <errno.h>
# include <getopt.h>
# include <sys/time.h>
# include <math.h>
# include <poll.h>
}
2015-01-27 23:06:19 +01:00
# undef __class__
# define __class__ "api::Alsa"
2015-05-11 21:33:58 +02:00
audio : : orchestra : : Api * audio : : orchestra : : api : : Alsa : : create ( ) {
2015-04-10 22:06:17 +02:00
return new audio : : orchestra : : api : : Alsa ( ) ;
2014-03-11 22:37:22 +01:00
}
2015-04-10 22:06:17 +02:00
namespace audio {
namespace orchestra {
namespace api {
class AlsaPrivate {
public :
2015-06-07 22:32:54 +02:00
snd_pcm_t * handle ;
2015-04-10 22:06:17 +02:00
bool xrun [ 2 ] ;
std11 : : condition_variable runnable_cv ;
bool runnable ;
std11 : : thread * thread ;
bool threadRunning ;
2015-06-05 22:00:17 +02:00
bool mmapInterface ; //!< enable or disable mmap mode...
2015-04-10 22:06:17 +02:00
enum timestampMode timeMode ; //!< the timestamp of the flow came from the harware.
2015-06-05 22:00:17 +02:00
std : : vector < snd_pcm_channel_area_t > areas ;
2015-04-10 22:06:17 +02:00
AlsaPrivate ( ) :
2015-06-07 22:32:54 +02:00
handle ( nullptr ) ,
2015-04-10 22:06:17 +02:00
runnable ( false ) ,
thread ( nullptr ) ,
threadRunning ( false ) ,
2015-06-05 22:00:17 +02:00
mmapInterface ( false ) ,
2015-04-10 22:06:17 +02:00
timeMode ( timestampMode_soft ) {
xrun [ 0 ] = false ;
xrun [ 1 ] = false ;
// TODO : Wait thread ...
}
} ;
}
}
}
2014-03-11 21:46:00 +01:00
2015-04-10 22:06:17 +02:00
audio : : orchestra : : api : : Alsa : : Alsa ( ) :
2015-06-05 22:00:17 +02:00
m_private ( std : : make_shared < audio : : orchestra : : api : : AlsaPrivate > ( ) ) {
2014-03-11 21:46:00 +01:00
// Nothing to do here.
}
2015-04-10 22:06:17 +02:00
audio : : orchestra : : api : : Alsa : : ~ Alsa ( ) {
if ( m_state ! = audio : : orchestra : : state_closed ) {
2014-03-11 21:46:00 +01:00
closeStream ( ) ;
}
}
2015-04-10 22:06:17 +02:00
uint32_t audio : : orchestra : : api : : Alsa : : getDeviceCount ( ) {
2014-03-11 21:46:00 +01:00
unsigned nDevices = 0 ;
2015-02-26 22:25:15 +01:00
int32_t result = - 1 ;
int32_t subdevice = - 1 ;
int32_t card = - 1 ;
2014-03-11 21:46:00 +01:00
char name [ 64 ] ;
snd_ctl_t * handle ;
// Count cards and devices
card = - 1 ;
snd_card_next ( & card ) ;
while ( card > = 0 ) {
sprintf ( name , " hw:%d " , card ) ;
result = snd_ctl_open ( & handle , name , 0 ) ;
if ( result < 0 ) {
2015-02-06 23:54:08 +01:00
ATA_ERROR ( " control open, card = " < < card < < " , " < < snd_strerror ( result ) < < " . " ) ;
2015-04-10 22:06:17 +02:00
// TODO : Return error audio::orchestra::error_warning;
2014-03-11 21:46:00 +01:00
goto nextcard ;
}
subdevice = - 1 ;
while ( 1 ) {
result = snd_ctl_pcm_next_device ( handle , & subdevice ) ;
if ( result < 0 ) {
2015-02-06 23:54:08 +01:00
ATA_ERROR ( " control next device, card = " < < card < < " , " < < snd_strerror ( result ) < < " . " ) ;
2015-04-10 22:06:17 +02:00
// TODO : Return error audio::orchestra::error_warning;
2014-03-11 21:46:00 +01:00
break ;
}
if ( subdevice < 0 ) {
break ;
}
2015-06-14 18:32:08 +02:00
// input/output
nDevices + = 2 ;
2014-03-11 21:46:00 +01:00
}
nextcard :
snd_ctl_close ( handle ) ;
snd_card_next ( & card ) ;
}
return nDevices ;
}
2015-06-14 18:32:08 +02:00
bool audio : : orchestra : : api : : Alsa : : getNamedDeviceInfoLocal ( const std : : string & _deviceName , audio : : orchestra : : DeviceInfo & _info , int32_t _cardId , int32_t _subdevice , int32_t _localDeviceId , bool _input ) {
2015-02-26 21:57:13 +01:00
int32_t result ;
2015-02-26 22:25:15 +01:00
snd_ctl_t * chandle ;
2015-02-26 21:57:13 +01:00
int32_t openMode = SND_PCM_ASYNC ;
snd_pcm_stream_t stream ;
snd_pcm_info_t * pcminfo ;
snd_pcm_info_alloca ( & pcminfo ) ;
snd_pcm_t * phandle ;
snd_pcm_hw_params_t * params ;
snd_pcm_hw_params_alloca ( & params ) ;
// First try for playback unless default _device (which has subdev -1)
2015-06-14 18:32:08 +02:00
_info . input = _input ;
if ( _input = = true ) {
stream = SND_PCM_STREAM_CAPTURE ;
} else {
stream = SND_PCM_STREAM_PLAYBACK ;
}
2015-02-26 21:57:13 +01:00
snd_pcm_info_set_stream ( pcminfo , stream ) ;
2015-02-26 22:25:15 +01:00
std : : vector < std : : string > listElement = etk : : split ( _deviceName , ' , ' ) ;
if ( listElement . size ( ) = = 0 ) {
ATA_ERROR ( " can not get control interface = ' " < < _deviceName < < " ' Can not plit at ',' ... " ) ;
return false ;
}
2015-03-12 22:30:43 +01:00
if ( listElement . size ( ) = = 1 ) {
// need to check if it is an input or output:
listElement = etk : : split ( _deviceName , ' _ ' ) ;
}
ATA_INFO ( " Open control : " < < listElement [ 0 ] ) ;
2015-02-26 22:25:15 +01:00
result = snd_ctl_open ( & chandle , listElement [ 0 ] . c_str ( ) , SND_CTL_NONBLOCK ) ;
if ( result < 0 ) {
ATA_ERROR ( " control open, card = " < < listElement [ 0 ] < < " , " < < snd_strerror ( result ) < < " . " ) ;
return false ;
}
if ( _subdevice ! = - 1 ) {
snd_pcm_info_set_device ( pcminfo , _subdevice ) ;
2015-02-26 21:57:13 +01:00
snd_pcm_info_set_subdevice ( pcminfo , 0 ) ;
result = snd_ctl_pcm_info ( chandle , pcminfo ) ;
if ( result < 0 ) {
2015-06-14 18:32:08 +02:00
// Device probably doesn't support IO.
return false ;
2015-02-26 21:57:13 +01:00
}
}
2015-06-14 18:32:08 +02:00
// ALSA doesn't provide default devices so we'll use the first available one.
if ( _localDeviceId = = 0 ) {
_info . isDefault = true ;
2015-02-26 21:57:13 +01:00
}
snd_pcm_info_set_stream ( pcminfo , stream ) ;
// Now try for capture unless default device (with subdev = -1)
2015-02-26 22:25:15 +01:00
if ( _subdevice ! = - 1 ) {
2015-02-26 21:57:13 +01:00
result = snd_ctl_pcm_info ( chandle , pcminfo ) ;
snd_ctl_close ( chandle ) ;
if ( result < 0 ) {
// Device probably doesn't support capture.
2015-06-14 18:32:08 +02:00
return false ;
2015-02-26 21:57:13 +01:00
}
}
2015-06-14 18:32:08 +02:00
// open device:
2015-02-26 21:57:13 +01:00
result = snd_pcm_open ( & phandle , _deviceName . c_str ( ) , stream , openMode | SND_PCM_NONBLOCK ) ;
if ( result < 0 ) {
2015-06-11 21:33:32 +02:00
ATA_ERROR ( " snd_pcm_open error for device ( " < < _deviceName < < " ), " < < snd_strerror ( result ) < < " . (seems to be already open) " ) ;
2015-06-14 18:32:08 +02:00
return true ;
2015-02-26 21:57:13 +01:00
}
// The device is open ... fill the parameter structure.
result = snd_pcm_hw_params_any ( phandle , params ) ;
if ( result < 0 ) {
snd_pcm_close ( phandle ) ;
ATA_ERROR ( " snd_pcm_hw_params error for device ( " < < _deviceName < < " ), " < < snd_strerror ( result ) < < " . " ) ;
2015-06-14 18:32:08 +02:00
return false ;
2015-02-26 21:57:13 +01:00
}
2015-06-14 18:32:08 +02:00
unsigned int value ;
2015-02-26 21:57:13 +01:00
result = snd_pcm_hw_params_get_channels_max ( params , & value ) ;
if ( result < 0 ) {
snd_pcm_close ( phandle ) ;
ATA_ERROR ( " error getting device ( " < < _deviceName < < " ) input channels, " < < snd_strerror ( result ) < < " . " ) ;
return false ;
}
2015-06-14 18:32:08 +02:00
ATA_ERROR ( " Input channel = " < < value ) ;
for ( int32_t iii = 0 ; iii < value ; + + iii ) {
_info . channels . push_back ( audio : : channel_unknow ) ;
2015-02-26 21:57:13 +01:00
}
// Test our discrete set of sample rate values.
_info . sampleRates . clear ( ) ;
2015-04-10 22:06:17 +02:00
for ( std : : vector < uint32_t > : : const_iterator it ( audio : : orchestra : : genericSampleRate ( ) . begin ( ) ) ;
it ! = audio : : orchestra : : genericSampleRate ( ) . end ( ) ;
2015-02-26 21:57:13 +01:00
+ + it ) {
if ( snd_pcm_hw_params_test_rate ( phandle , params , * it , 0 ) = = 0 ) {
_info . sampleRates . push_back ( * it ) ;
}
}
if ( _info . sampleRates . size ( ) = = 0 ) {
snd_pcm_close ( phandle ) ;
ATA_ERROR ( " no supported sample rates found for device ( " < < _deviceName < < " ). " ) ;
return false ;
}
// Probe the supported data formats ... we don't care about endian-ness just yet
snd_pcm_format_t format ;
_info . nativeFormats . clear ( ) ;
format = SND_PCM_FORMAT_S8 ;
if ( snd_pcm_hw_params_test_format ( phandle , params , format ) = = 0 ) {
_info . nativeFormats . push_back ( audio : : format_int8 ) ;
}
format = SND_PCM_FORMAT_S16 ;
if ( snd_pcm_hw_params_test_format ( phandle , params , format ) = = 0 ) {
_info . nativeFormats . push_back ( audio : : format_int16 ) ;
}
format = SND_PCM_FORMAT_S24 ;
if ( snd_pcm_hw_params_test_format ( phandle , params , format ) = = 0 ) {
_info . nativeFormats . push_back ( audio : : format_int24 ) ;
}
format = SND_PCM_FORMAT_S32 ;
if ( snd_pcm_hw_params_test_format ( phandle , params , format ) = = 0 ) {
_info . nativeFormats . push_back ( audio : : format_int32 ) ;
}
format = SND_PCM_FORMAT_FLOAT ;
if ( snd_pcm_hw_params_test_format ( phandle , params , format ) = = 0 ) {
_info . nativeFormats . push_back ( audio : : format_float ) ;
}
format = SND_PCM_FORMAT_FLOAT64 ;
if ( snd_pcm_hw_params_test_format ( phandle , params , format ) = = 0 ) {
_info . nativeFormats . push_back ( audio : : format_double ) ;
}
// Check that we have at least one supported format
if ( _info . nativeFormats . size ( ) = = 0 ) {
ATA_ERROR ( " pcm device ( " < < _deviceName < < " ) data format not supported by RtAudio. " ) ;
2015-04-10 22:06:17 +02:00
// TODO : Return audio::orchestra::error_warning;
2015-02-26 21:57:13 +01:00
return false ;
}
// Get the device name
2015-02-26 22:25:15 +01:00
if ( _cardId ! = - 1 ) {
char * cardname ;
char name [ 1024 ] ;
result = snd_card_get_name ( _cardId , & cardname ) ;
if ( result > = 0 ) {
sprintf ( name , " hw:%s,%d " , cardname , _subdevice ) ;
}
_info . name = name ;
} else {
_info . name = _deviceName ;
2015-02-26 21:57:13 +01:00
}
// That's all ... close the device and return
snd_pcm_close ( phandle ) ;
return true ;
}
2015-04-10 22:06:17 +02:00
audio : : orchestra : : DeviceInfo audio : : orchestra : : api : : Alsa : : getDeviceInfo ( uint32_t _device ) {
audio : : orchestra : : DeviceInfo info ;
2014-03-11 21:46:00 +01:00
unsigned nDevices = 0 ;
2015-02-26 22:25:15 +01:00
int32_t result = - 1 ;
int32_t subdevice = - 1 ;
int32_t card = - 1 ;
2015-06-14 18:32:08 +02:00
bool isInput = false ;
2014-03-11 21:46:00 +01:00
char name [ 64 ] ;
snd_ctl_t * chandle ;
// Count cards and devices
card = - 1 ;
snd_card_next ( & card ) ;
while ( card > = 0 ) {
sprintf ( name , " hw:%d " , card ) ;
result = snd_ctl_open ( & chandle , name , SND_CTL_NONBLOCK ) ;
if ( result < 0 ) {
2015-02-06 23:54:08 +01:00
ATA_WARNING ( " control open, card = " < < card < < " , " < < snd_strerror ( result ) < < " . " ) ;
2014-03-11 21:46:00 +01:00
goto nextcard ;
}
subdevice = - 1 ;
while ( 1 ) {
result = snd_ctl_pcm_next_device ( chandle , & subdevice ) ;
if ( result < 0 ) {
2015-02-06 23:54:08 +01:00
ATA_WARNING ( " control next device, card = " < < card < < " , " < < snd_strerror ( result ) < < " . " ) ;
2014-03-11 21:46:00 +01:00
break ;
}
if ( subdevice < 0 ) {
break ;
}
if ( nDevices = = _device ) {
2015-06-14 18:32:08 +02:00
// for input
sprintf ( name , " hw:%d,%d " , card , subdevice ) ;
isInput = true ;
goto foundDevice ;
}
if ( nDevices + 1 = = _device ) {
// for output
2014-03-11 21:46:00 +01:00
sprintf ( name , " hw:%d,%d " , card , subdevice ) ;
2015-06-14 18:32:08 +02:00
isInput = false ;
2014-03-11 21:46:00 +01:00
goto foundDevice ;
}
2015-06-14 18:32:08 +02:00
nDevices + = 2 ;
2014-03-11 21:46:00 +01:00
}
nextcard :
snd_ctl_close ( chandle ) ;
snd_card_next ( & card ) ;
}
if ( nDevices = = 0 ) {
2015-02-06 23:54:08 +01:00
ATA_ERROR ( " no devices found! " ) ;
2015-04-10 22:06:17 +02:00
// TODO : audio::orchestra::error_invalidUse;
2014-03-11 21:46:00 +01:00
return info ;
}
if ( _device > = nDevices ) {
2015-02-06 23:54:08 +01:00
ATA_ERROR ( " device ID is invalid! " ) ;
2015-04-10 22:06:17 +02:00
// TODO : audio::orchestra::error_invalidUse;
2014-03-11 21:46:00 +01:00
return info ;
}
foundDevice :
2015-02-26 22:25:15 +01:00
snd_ctl_close ( chandle ) ;
2014-03-11 21:46:00 +01:00
// If a stream is already open, we cannot probe the stream devices.
// Thus, use the saved results.
2015-04-10 22:06:17 +02:00
if ( m_state ! = audio : : orchestra : : state_closed
2015-02-09 21:44:32 +01:00
& & ( m_device [ 0 ] = = _device
| | m_device [ 1 ] = = _device ) ) {
2014-03-11 21:46:00 +01:00
if ( _device > = m_devices . size ( ) ) {
2015-02-06 23:54:08 +01:00
ATA_ERROR ( " device ID was not present before stream was opened. " ) ;
2015-04-10 22:06:17 +02:00
// TODO : return audio::orchestra::error_warning;
2014-03-11 21:46:00 +01:00
return info ;
}
2015-06-14 18:32:08 +02:00
return m_devices [ _device ] ;
2014-03-11 21:46:00 +01:00
}
2015-06-14 18:32:08 +02:00
bool ret = audio : : orchestra : : api : : Alsa : : getNamedDeviceInfoLocal ( name , info , card , subdevice , _device , isInput ) ;
2015-02-26 22:25:15 +01:00
if ( ret = = false ) {
// TODO : ...
2014-03-11 21:46:00 +01:00
return info ;
}
return info ;
}
2015-04-10 22:06:17 +02:00
void audio : : orchestra : : api : : Alsa : : saveDeviceInfo ( ) {
2014-03-11 21:46:00 +01:00
m_devices . clear ( ) ;
uint32_t nDevices = getDeviceCount ( ) ;
m_devices . resize ( nDevices ) ;
for ( uint32_t iii = 0 ; iii < nDevices ; + + iii ) {
m_devices [ iii ] = getDeviceInfo ( iii ) ;
}
}
2015-04-10 22:06:17 +02:00
bool audio : : orchestra : : api : : Alsa : : probeDeviceOpen ( uint32_t _device ,
2015-06-14 18:32:08 +02:00
audio : : orchestra : : mode _mode ,
uint32_t _channels ,
uint32_t _firstChannel ,
uint32_t _sampleRate ,
audio : : format _format ,
uint32_t * _bufferSize ,
const audio : : orchestra : : StreamOptions & _options ) {
2014-03-11 21:46:00 +01:00
// I'm not using the "plug" interface ... too much inconsistent behavior.
unsigned nDevices = 0 ;
int32_t result , subdevice , card ;
char name [ 64 ] ;
snd_ctl_t * chandle ;
2015-02-06 23:54:08 +01:00
// Count cards and devices
card = - 1 ;
// NOTE : Find the device name : [BEGIN]
snd_card_next ( & card ) ;
while ( card > = 0 ) {
sprintf ( name , " hw:%d " , card ) ;
result = snd_ctl_open ( & chandle , name , SND_CTL_NONBLOCK ) ;
if ( result < 0 ) {
ATA_ERROR ( " control open, card = " < < card < < " , " < < snd_strerror ( result ) < < " . " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
2015-02-06 23:54:08 +01:00
subdevice = - 1 ;
while ( 1 ) {
result = snd_ctl_pcm_next_device ( chandle , & subdevice ) ;
if ( result < 0 ) break ;
if ( subdevice < 0 ) break ;
2014-03-11 21:46:00 +01:00
if ( nDevices = = _device ) {
2015-02-06 23:54:08 +01:00
sprintf ( name , " hw:%d,%d " , card , subdevice ) ;
snd_ctl_close ( chandle ) ;
2014-03-11 21:46:00 +01:00
goto foundDevice ;
}
nDevices + + ;
}
2015-02-06 23:54:08 +01:00
snd_ctl_close ( chandle ) ;
snd_card_next ( & card ) ;
}
if ( nDevices = = 0 ) {
// This should not happen because a check is made before this function is called.
ATA_ERROR ( " no devices found! " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
2015-02-06 23:54:08 +01:00
if ( _device > = nDevices ) {
// This should not happen because a check is made before this function is called.
ATA_ERROR ( " device ID is invalid! " ) ;
return false ;
}
// NOTE : Find the device name : [ END ]
2014-03-11 21:46:00 +01:00
foundDevice :
2015-02-26 22:25:15 +01:00
return probeDeviceOpenName ( name , _mode , _channels , _firstChannel , _sampleRate , _format , _bufferSize , _options ) ;
}
2015-04-10 22:06:17 +02:00
bool audio : : orchestra : : api : : Alsa : : probeDeviceOpenName ( const std : : string & _deviceName ,
2015-06-05 22:00:17 +02:00
audio : : orchestra : : mode _mode ,
uint32_t _channels ,
uint32_t _firstChannel ,
uint32_t _sampleRate ,
audio : : format _format ,
uint32_t * _bufferSize ,
const audio : : orchestra : : StreamOptions & _options ) {
2015-03-23 23:14:45 +01:00
ATA_DEBUG ( " Probe ALSA device : " ) ;
ATA_DEBUG ( " _deviceName= " < < _deviceName ) ;
ATA_DEBUG ( " _mode= " < < _mode ) ;
ATA_DEBUG ( " _channels= " < < _channels ) ;
ATA_DEBUG ( " _firstChannel= " < < _firstChannel ) ;
ATA_DEBUG ( " _sampleRate= " < < _sampleRate ) ;
ATA_DEBUG ( " _format= " < < _format ) ;
2015-02-26 22:25:15 +01:00
// I'm not using the "plug" interface ... too much inconsistent behavior.
unsigned nDevices = 0 ;
int32_t result , subdevice , card ;
char name [ 64 ] ;
snd_ctl_t * chandle ;
2014-03-11 21:46:00 +01:00
// The getDeviceInfo() function will not work for a device that is
// already open. Thus, we'll probe the system before opening a
// stream and save the results for use by getDeviceInfo().
2015-06-07 22:32:54 +02:00
// TODO : ...
{
2014-03-11 21:46:00 +01:00
// only do once
this - > saveDeviceInfo ( ) ;
}
snd_pcm_stream_t stream ;
2015-04-10 22:06:17 +02:00
if ( _mode = = audio : : orchestra : : mode_output ) {
2014-03-11 21:46:00 +01:00
stream = SND_PCM_STREAM_PLAYBACK ;
} else {
stream = SND_PCM_STREAM_CAPTURE ;
}
2015-06-11 21:33:32 +02:00
//int32_t openMode = SND_PCM_NONBLOCK;
2014-03-11 21:46:00 +01:00
int32_t openMode = SND_PCM_ASYNC ;
2015-06-11 21:33:32 +02:00
//int32_t openMode = SND_PCM_ASYNC | SND_PCM_NONBLOCK;
result = snd_pcm_open ( & m_private - > handle , _deviceName . c_str ( ) , stream , openMode ) ;
2015-03-12 22:30:43 +01:00
ATA_DEBUG ( " Configure Mode : SND_PCM_ASYNC " ) ;
2014-03-11 21:46:00 +01:00
if ( result < 0 ) {
2015-04-10 22:06:17 +02:00
if ( _mode = = audio : : orchestra : : mode_output ) {
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " pcm device ( " < < _deviceName < < " ) won't open for output. " ) ;
2014-03-11 21:46:00 +01:00
} else {
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " pcm device ( " < < _deviceName < < " ) won't open for input. " ) ;
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
// Fill the parameter structure.
snd_pcm_hw_params_t * hw_params ;
snd_pcm_hw_params_alloca ( & hw_params ) ;
2015-06-11 21:33:32 +02:00
result = snd_pcm_hw_params_any ( m_private - > handle , hw_params ) ;
2014-03-11 21:46:00 +01:00
if ( result < 0 ) {
2015-06-11 21:33:32 +02:00
snd_pcm_close ( m_private - > handle ) ;
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " error getting pcm device ( " < < _deviceName < < " ) parameters, " < < snd_strerror ( result ) < < " . " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2015-06-07 22:32:54 +02:00
# if 1
ATA_DEBUG ( " configure Acces: SND_PCM_ACCESS_MMAP_INTERLEAVED " ) ;
2015-06-11 21:33:32 +02:00
result = snd_pcm_hw_params_set_access ( m_private - > handle , hw_params , SND_PCM_ACCESS_MMAP_INTERLEAVED ) ;
2015-06-05 22:00:17 +02:00
if ( result > = 0 ) {
2015-06-07 22:32:54 +02:00
m_deviceInterleaved [ modeToIdTable ( _mode ) ] = true ;
2015-06-05 22:00:17 +02:00
m_private - > mmapInterface = true ;
} else {
2015-06-07 22:32:54 +02:00
ATA_DEBUG ( " configure Acces: SND_PCM_ACCESS_MMAP_NONINTERLEAVED " ) ;
2015-06-11 21:33:32 +02:00
result = snd_pcm_hw_params_set_access ( m_private - > handle , hw_params , SND_PCM_ACCESS_MMAP_NONINTERLEAVED ) ;
2015-06-05 22:00:17 +02:00
if ( result > = 0 ) {
2015-06-07 22:32:54 +02:00
m_deviceInterleaved [ modeToIdTable ( _mode ) ] = false ;
m_private - > mmapInterface = true ;
2015-06-05 22:00:17 +02:00
} else {
2015-06-07 22:32:54 +02:00
ATA_DEBUG ( " configure Acces: SND_PCM_ACCESS_RW_INTERLEAVED " ) ;
2015-06-11 21:33:32 +02:00
result = snd_pcm_hw_params_set_access ( m_private - > handle , hw_params , SND_PCM_ACCESS_RW_INTERLEAVED ) ;
2015-06-05 22:00:17 +02:00
if ( result > = 0 ) {
2015-06-07 22:32:54 +02:00
m_deviceInterleaved [ modeToIdTable ( _mode ) ] = true ;
2015-06-05 22:00:17 +02:00
m_private - > mmapInterface = false ;
} else {
2015-06-07 22:32:54 +02:00
ATA_DEBUG ( " configure Acces: SND_PCM_ACCESS_RW_NONINTERLEAVED " ) ;
2015-06-11 21:33:32 +02:00
result = snd_pcm_hw_params_set_access ( m_private - > handle , hw_params , SND_PCM_ACCESS_RW_NONINTERLEAVED ) ;
2015-06-07 22:32:54 +02:00
if ( result > = 0 ) {
m_deviceInterleaved [ modeToIdTable ( _mode ) ] = false ;
m_private - > mmapInterface = false ;
} else {
ATA_ERROR ( " Can not open the interface ... " ) ;
return false ;
}
2015-06-05 22:00:17 +02:00
}
}
}
2015-06-07 22:32:54 +02:00
# else
ATA_DEBUG ( " configure Acces: SND_PCM_ACCESS_RW_INTERLEAVED " ) ;
2015-06-11 21:33:32 +02:00
result = snd_pcm_hw_params_set_access ( m_private - > handle , hw_params , SND_PCM_ACCESS_RW_INTERLEAVED ) ;
2015-06-07 22:32:54 +02:00
if ( result > = 0 ) {
m_deviceInterleaved [ modeToIdTable ( _mode ) ] = true ;
m_private - > mmapInterface = false ;
} else {
ATA_DEBUG ( " configure Acces: SND_PCM_ACCESS_RW_NONINTERLEAVED " ) ;
2015-06-11 21:33:32 +02:00
result = snd_pcm_hw_params_set_access ( m_private - > handle , hw_params , SND_PCM_ACCESS_RW_NONINTERLEAVED ) ;
2015-06-07 22:32:54 +02:00
if ( result > = 0 ) {
m_deviceInterleaved [ modeToIdTable ( _mode ) ] = false ;
m_private - > mmapInterface = false ;
} else {
ATA_ERROR ( " Can not open the interface ... " ) ;
return false ;
}
}
# endif
2014-03-11 21:46:00 +01:00
if ( result < 0 ) {
2015-06-11 21:33:32 +02:00
snd_pcm_close ( m_private - > handle ) ;
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " error setting pcm device ( " < < _deviceName < < " ) access, " < < snd_strerror ( result ) < < " . " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
// Determine how to set the device format.
2015-02-09 21:44:32 +01:00
m_userFormat = _format ;
2014-03-11 21:46:00 +01:00
snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN ;
2015-02-05 23:31:22 +01:00
if ( _format = = audio : : format_int8 ) {
2014-03-11 21:46:00 +01:00
deviceFormat = SND_PCM_FORMAT_S8 ;
2015-02-05 23:31:22 +01:00
} else if ( _format = = audio : : format_int16 ) {
2014-03-11 21:46:00 +01:00
deviceFormat = SND_PCM_FORMAT_S16 ;
2015-02-05 23:31:22 +01:00
} else if ( _format = = audio : : format_int24 ) {
2014-03-11 21:46:00 +01:00
deviceFormat = SND_PCM_FORMAT_S24 ;
2015-02-05 23:31:22 +01:00
} else if ( _format = = audio : : format_int32 ) {
2014-03-11 21:46:00 +01:00
deviceFormat = SND_PCM_FORMAT_S32 ;
2015-02-05 23:31:22 +01:00
} else if ( _format = = audio : : format_float ) {
2014-03-11 21:46:00 +01:00
deviceFormat = SND_PCM_FORMAT_FLOAT ;
2015-02-05 23:31:22 +01:00
} else if ( _format = = audio : : format_double ) {
2014-03-11 21:46:00 +01:00
deviceFormat = SND_PCM_FORMAT_FLOAT64 ;
}
2015-06-11 21:33:32 +02:00
if ( snd_pcm_hw_params_test_format ( m_private - > handle , hw_params , deviceFormat ) = = 0 ) {
2015-02-09 21:44:32 +01:00
m_deviceFormat [ modeToIdTable ( _mode ) ] = _format ;
2015-02-05 23:31:22 +01:00
} else {
// If we get here, no supported format was found.
2015-06-11 21:33:32 +02:00
snd_pcm_close ( m_private - > handle ) ;
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " pcm device " < < _deviceName < < " data format not supported: " < < _format ) ;
2015-02-05 23:31:22 +01:00
// TODO : display list of all supported format ..
return false ;
2014-03-11 21:46:00 +01:00
}
2015-03-12 22:30:43 +01:00
ATA_DEBUG ( " configure format: " < < _format ) ;
2015-06-11 21:33:32 +02:00
result = snd_pcm_hw_params_set_format ( m_private - > handle , hw_params , deviceFormat ) ;
2014-03-11 21:46:00 +01:00
if ( result < 0 ) {
2015-06-11 21:33:32 +02:00
snd_pcm_close ( m_private - > handle ) ;
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " error setting pcm device ( " < < _deviceName < < " ) data format, " < < snd_strerror ( result ) < < " . " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
// Determine whether byte-swaping is necessary.
2015-02-09 21:44:32 +01:00
m_doByteSwap [ modeToIdTable ( _mode ) ] = false ;
2014-03-11 21:46:00 +01:00
if ( deviceFormat ! = SND_PCM_FORMAT_S8 ) {
result = snd_pcm_format_cpu_endian ( deviceFormat ) ;
if ( result = = 0 ) {
2015-03-12 22:30:43 +01:00
ATA_DEBUG ( " configure swap Byte " ) ;
2015-02-09 21:44:32 +01:00
m_doByteSwap [ modeToIdTable ( _mode ) ] = true ;
2014-03-11 21:46:00 +01:00
} else if ( result < 0 ) {
2015-06-11 21:33:32 +02:00
snd_pcm_close ( m_private - > handle ) ;
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " error getting pcm device ( " < < _deviceName < < " ) endian-ness, " < < snd_strerror ( result ) < < " . " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
}
2015-03-12 22:30:43 +01:00
ATA_DEBUG ( " Set frequency " < < _sampleRate ) ;
2014-03-11 21:46:00 +01:00
// Set the sample rate.
2015-06-11 21:33:32 +02:00
result = snd_pcm_hw_params_set_rate_near ( m_private - > handle , hw_params , ( uint32_t * ) & _sampleRate , 0 ) ;
2014-03-11 21:46:00 +01:00
if ( result < 0 ) {
2015-06-11 21:33:32 +02:00
snd_pcm_close ( m_private - > handle ) ;
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " error setting sample rate on device ( " < < _deviceName < < " ), " < < snd_strerror ( result ) < < " . " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2015-06-11 21:33:32 +02:00
// Determine the number of channels for this device. We support a possible
2014-03-11 21:46:00 +01:00
// minimum device channel number > than the value requested by the user.
2015-02-09 21:44:32 +01:00
m_nUserChannels [ modeToIdTable ( _mode ) ] = _channels ;
2014-03-11 21:46:00 +01:00
uint32_t value ;
result = snd_pcm_hw_params_get_channels_max ( hw_params , & value ) ;
uint32_t deviceChannels = value ;
2015-03-23 23:14:45 +01:00
if ( result < 0 ) {
2015-06-11 21:33:32 +02:00
snd_pcm_close ( m_private - > handle ) ;
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " requested channel parameters not supported by device ( " < < _deviceName < < " ), " < < snd_strerror ( result ) < < " . " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2015-03-23 23:14:45 +01:00
if ( deviceChannels < _channels + _firstChannel ) {
2015-06-11 21:33:32 +02:00
snd_pcm_close ( m_private - > handle ) ;
2015-03-23 23:14:45 +01:00
ATA_ERROR ( " requested channel " < < _channels < < " have : " < < deviceChannels ) ;
return false ;
}
2014-03-11 21:46:00 +01:00
result = snd_pcm_hw_params_get_channels_min ( hw_params , & value ) ;
if ( result < 0 ) {
2015-06-11 21:33:32 +02:00
snd_pcm_close ( m_private - > handle ) ;
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " error getting minimum channels for device ( " < < _deviceName < < " ), " < < snd_strerror ( result ) < < " . " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
deviceChannels = value ;
2015-03-12 22:30:43 +01:00
ATA_DEBUG ( " Device Channel : " < < deviceChannels ) ;
2014-03-11 21:46:00 +01:00
if ( deviceChannels < _channels + _firstChannel ) {
deviceChannels = _channels + _firstChannel ;
}
2015-03-12 22:30:43 +01:00
ATA_DEBUG ( " snd_pcm_hw_params_set_channels: " < < deviceChannels ) ;
2015-02-09 21:44:32 +01:00
m_nDeviceChannels [ modeToIdTable ( _mode ) ] = deviceChannels ;
2014-03-11 21:46:00 +01:00
// Set the device channels.
2015-06-11 21:33:32 +02:00
result = snd_pcm_hw_params_set_channels ( m_private - > handle , hw_params , deviceChannels ) ;
2014-03-11 21:46:00 +01:00
if ( result < 0 ) {
2015-06-11 21:33:32 +02:00
snd_pcm_close ( m_private - > handle ) ;
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " error setting channels for device ( " < < _deviceName < < " ), " < < snd_strerror ( result ) < < " . " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2015-03-12 22:30:43 +01:00
ATA_DEBUG ( " configure channels : " < < deviceChannels ) ;
2014-03-11 21:46:00 +01:00
// Set the buffer (or period) size.
int32_t dir = 0 ;
snd_pcm_uframes_t periodSize = * _bufferSize ;
2015-06-11 21:33:32 +02:00
result = snd_pcm_hw_params_set_period_size_near ( m_private - > handle , hw_params , & periodSize , & dir ) ;
2014-03-11 21:46:00 +01:00
if ( result < 0 ) {
2015-06-11 21:33:32 +02:00
snd_pcm_close ( m_private - > handle ) ;
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " error setting period size for device ( " < < _deviceName < < " ), " < < snd_strerror ( result ) < < " . " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
* _bufferSize = periodSize ;
2015-03-12 22:30:43 +01:00
ATA_DEBUG ( " configure periode size : " < < periodSize ) ;
2014-03-11 21:46:00 +01:00
// Set the buffer number, which in ALSA is referred to as the "period".
uint32_t periods = 0 ;
2015-02-27 21:07:17 +01:00
if ( _options . flags . m_minimizeLatency = = true ) {
2015-02-06 23:54:08 +01:00
periods = 2 ;
}
/* TODO : Chouse the number of low level buffer ...
2015-02-27 21:07:17 +01:00
if ( _options . numberOfBuffers > 0 ) {
periods = _options . numberOfBuffers ;
2015-02-06 23:54:08 +01:00
}
*/
if ( periods < 2 ) {
periods = 4 ; // a fairly safe default value
}
2015-06-11 21:33:32 +02:00
result = snd_pcm_hw_params_set_periods_near ( m_private - > handle , hw_params , & periods , & dir ) ;
2014-03-11 21:46:00 +01:00
if ( result < 0 ) {
2015-06-11 21:33:32 +02:00
snd_pcm_close ( m_private - > handle ) ;
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " error setting periods for device ( " < < _deviceName < < " ), " < < snd_strerror ( result ) < < " . " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2015-03-12 22:30:43 +01:00
ATA_DEBUG ( " configure Buffer number: " < < periods ) ;
2015-03-03 21:28:07 +01:00
m_sampleRate = _sampleRate ;
2014-03-11 21:46:00 +01:00
// If attempting to setup a duplex stream, the bufferSize parameter
// MUST be the same in both directions!
2015-04-10 22:06:17 +02:00
if ( m_mode = = audio : : orchestra : : mode_output
& & _mode = = audio : : orchestra : : mode_input
2015-02-09 21:44:32 +01:00
& & * _bufferSize ! = m_bufferSize ) {
2015-06-11 21:33:32 +02:00
snd_pcm_close ( m_private - > handle ) ;
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " system error setting buffer size for duplex stream on device ( " < < _deviceName < < " ). " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2015-02-09 21:44:32 +01:00
m_bufferSize = * _bufferSize ;
2015-03-12 22:30:43 +01:00
ATA_INFO ( " configure buffer size = " < < m_bufferSize * periods < < " periode time in ms= " < < int64_t ( double ( m_bufferSize ) / double ( m_sampleRate ) * 1000000000.0 ) < < " ns " ) ;
2015-02-26 21:44:16 +01:00
// check if the hardware provide hardware clock :
if ( snd_pcm_hw_params_is_monotonic ( hw_params ) = = 0 ) {
2015-02-27 21:07:17 +01:00
ATA_INFO ( " ALSA Audio timestamp is NOT monotonic (Generate with the start timestamp) " ) ;
if ( _options . mode = = timestampMode_Hardware ) {
ATA_WARNING ( " Can not select Harware timeStamp ==> the IO is not monotonic ==> select " ) ;
m_private - > timeMode = timestampMode_trigered ;
} else {
m_private - > timeMode = _options . mode ;
}
2015-02-26 21:44:16 +01:00
} else {
2015-02-27 21:07:17 +01:00
ATA_DEBUG ( " ALSA Audio timestamp is monotonic (can came from harware) " ) ;
m_private - > timeMode = _options . mode ;
2015-02-26 21:44:16 +01:00
}
2014-03-11 21:46:00 +01:00
// Install the hardware configuration
2015-06-11 21:33:32 +02:00
result = snd_pcm_hw_params ( m_private - > handle , hw_params ) ;
2014-03-11 21:46:00 +01:00
if ( result < 0 ) {
2015-06-11 21:33:32 +02:00
snd_pcm_close ( m_private - > handle ) ;
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " error installing hardware configuration on device ( " < < _deviceName < < " ), " < < snd_strerror ( result ) < < " . " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2015-03-12 22:30:43 +01:00
snd_pcm_uframes_t val ;
2014-03-11 21:46:00 +01:00
// Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.
2015-02-16 23:01:26 +01:00
snd_pcm_sw_params_t * swParams = nullptr ;
snd_pcm_sw_params_alloca ( & swParams ) ;
2015-06-11 21:33:32 +02:00
snd_pcm_sw_params_current ( m_private - > handle , swParams ) ;
2015-03-12 22:30:43 +01:00
#if 0
ATA_DEBUG ( " configure start_threshold: " < < int64_t ( * _bufferSize ) ) ;
2015-06-11 21:33:32 +02:00
snd_pcm_sw_params_set_start_threshold ( m_private - > handle , swParams , * _bufferSize ) ;
2015-03-12 22:30:43 +01:00
# else
2015-06-07 22:32:54 +02:00
//ATA_DEBUG("configure start_threshold: " << int64_t(1));
2015-06-11 21:33:32 +02:00
//snd_pcm_sw_params_set_start_threshold(m_private->handle, swParams, 1);
2015-06-07 22:32:54 +02:00
ATA_DEBUG ( " configure start_threshold: " < < int64_t ( 0 ) ) ;
2015-06-11 21:33:32 +02:00
snd_pcm_sw_params_set_start_threshold ( m_private - > handle , swParams , 0 ) ;
2015-03-12 22:30:43 +01:00
# endif
2015-06-07 22:32:54 +02:00
# if 1
2015-03-12 22:30:43 +01:00
ATA_DEBUG ( " configure stop_threshold: " < < ULONG_MAX ) ;
2015-06-11 21:33:32 +02:00
snd_pcm_sw_params_set_stop_threshold ( m_private - > handle , swParams , ULONG_MAX ) ;
2015-03-12 22:30:43 +01:00
# else
ATA_DEBUG ( " configure stop_threshold: " < < m_bufferSize * periods ) ;
2015-06-11 21:33:32 +02:00
snd_pcm_sw_params_set_stop_threshold ( m_private - > handle , swParams , m_bufferSize * periods ) ;
2015-03-12 22:30:43 +01:00
# endif
//ATA_DEBUG("configure silence_threshold: " << 0);
2015-06-11 21:33:32 +02:00
//snd_pcm_sw_params_set_silence_threshold(m_private->handle, swParams, 0);
2014-03-11 21:46:00 +01:00
// The following two settings were suggested by Theo Veenker
2015-06-11 21:33:32 +02:00
# if 1
snd_pcm_sw_params_set_avail_min ( m_private - > handle , swParams , * _bufferSize * periods / 2 ) ;
2015-03-12 22:30:43 +01:00
snd_pcm_sw_params_get_avail_min ( swParams , & val ) ;
ATA_DEBUG ( " configure set availlable min: " < < * _bufferSize * periods / 2 < < " really set: " < < val ) ;
# endif
2015-06-11 21:33:32 +02:00
#if 0
int valInt ;
snd_pcm_sw_params_get_period_event ( swParams , & valInt ) ;
ATA_DEBUG ( " configure get period_event: " < < valInt ) ;
snd_pcm_sw_params_set_xfer_align ( m_private - > handle , swParams , 1 ) ;
2014-03-11 21:46:00 +01:00
// here are two options for a fix
2015-06-11 21:33:32 +02:00
//snd_pcm_sw_params_set_silence_size(m_private->handle, swParams, ULONG_MAX);
# endif
//snd_pcm_sw_params_set_tstamp_mode(m_private->handle, swParams, SND_PCM_TSTAMP_ENABLE);
2015-03-12 22:30:43 +01:00
ATA_DEBUG ( " configuration: " ) ;
//ATA_DEBUG(" start_mode: " << snd_pcm_start_mode_name(snd_pcm_sw_params_get_start_mode(swParams)));
//ATA_DEBUG(" xrun_mode: " << snd_pcm_xrun_mode_name(snd_pcm_sw_params_get_xrun_mode(swParams)));
snd_pcm_tstamp_t valTsMode ;
snd_pcm_sw_params_get_tstamp_mode ( swParams , & valTsMode ) ;
ATA_DEBUG ( " tstamp_mode: " < < snd_pcm_tstamp_mode_name ( valTsMode ) ) ;
//ATA_DEBUG(" period_step: " << swParams->period_step);
//ATA_DEBUG(" sleep_min: " << swParams->sleep_min);
snd_pcm_sw_params_get_avail_min ( swParams , & val ) ;
ATA_DEBUG ( " avail_min: " < < val ) ;
2015-06-11 21:33:32 +02:00
//snd_pcm_sw_params_get_xfer_align(swParams, &val);
//ATA_DEBUG(" xfer_align: " << val);
2015-03-12 22:30:43 +01:00
snd_pcm_sw_params_get_silence_threshold ( swParams , & val ) ;
ATA_DEBUG ( " silence_threshold: " < < val ) ;
snd_pcm_sw_params_get_silence_size ( swParams , & val ) ;
ATA_DEBUG ( " silence_size: " < < val ) ;
2015-02-16 23:01:26 +01:00
snd_pcm_sw_params_get_boundary ( swParams , & val ) ;
2015-03-12 22:30:43 +01:00
ATA_DEBUG ( " boundary: " < < val ) ;
2015-06-11 21:33:32 +02:00
result = snd_pcm_sw_params ( m_private - > handle , swParams ) ;
2014-03-11 21:46:00 +01:00
if ( result < 0 ) {
2015-06-11 21:33:32 +02:00
snd_pcm_close ( m_private - > handle ) ;
2015-02-26 22:25:15 +01:00
ATA_ERROR ( " error installing software configuration on device ( " < < _deviceName < < " ), " < < snd_strerror ( result ) < < " . " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2015-03-06 22:50:03 +01:00
{
snd_pcm_uframes_t _period_size = 0 ;
snd_pcm_uframes_t _buffer_size = 0 ;
snd_pcm_hw_params_get_period_size ( hw_params , & _period_size , & dir ) ;
snd_pcm_hw_params_get_buffer_size ( hw_params , & _buffer_size ) ;
2015-03-12 22:30:43 +01:00
ATA_DEBUG ( " ploooooo _period_size= " < < _period_size < < " _buffer_size= " < < _buffer_size ) ;
2015-03-06 22:50:03 +01:00
}
2014-03-11 21:46:00 +01:00
// Set flags for buffer conversion
2015-02-09 21:44:32 +01:00
m_doConvertBuffer [ modeToIdTable ( _mode ) ] = false ;
if ( m_userFormat ! = m_deviceFormat [ modeToIdTable ( _mode ) ] ) {
m_doConvertBuffer [ modeToIdTable ( _mode ) ] = true ;
2014-03-11 21:46:00 +01:00
}
2015-02-09 21:44:32 +01:00
if ( m_nUserChannels [ modeToIdTable ( _mode ) ] < m_nDeviceChannels [ modeToIdTable ( _mode ) ] ) {
m_doConvertBuffer [ modeToIdTable ( _mode ) ] = true ;
2014-03-11 21:46:00 +01:00
}
2015-02-09 21:44:32 +01:00
if ( m_deviceInterleaved [ modeToIdTable ( _mode ) ] = = false
& & m_nUserChannels [ modeToIdTable ( _mode ) ] > 1 ) {
m_doConvertBuffer [ modeToIdTable ( _mode ) ] = true ;
2014-03-11 21:46:00 +01:00
}
// Allocate necessary internal buffers.
uint64_t bufferBytes ;
2015-02-09 21:44:32 +01:00
bufferBytes = m_nUserChannels [ modeToIdTable ( _mode ) ] * * _bufferSize * audio : : getFormatBytes ( m_userFormat ) ;
m_userBuffer [ modeToIdTable ( _mode ) ] . resize ( bufferBytes , 0 ) ;
if ( m_userBuffer [ modeToIdTable ( _mode ) ] . size ( ) = = 0 ) {
2015-02-06 23:54:08 +01:00
ATA_ERROR ( " error allocating user buffer memory. " ) ;
2014-03-11 21:46:00 +01:00
goto error ;
}
2015-06-05 22:00:17 +02:00
// allocate areas interface:
m_private - > areas . resize ( m_nUserChannels [ modeToIdTable ( _mode ) ] ) ;
for ( size_t iii = 0 ; iii < m_private - > areas . size ( ) ; + + iii ) {
m_private - > areas [ iii ] . addr = & m_userBuffer [ modeToIdTable ( _mode ) ] [ 0 ] ;
m_private - > areas [ iii ] . first = iii * audio : : getFormatBytes ( m_userFormat ) ;
m_private - > areas [ iii ] . step = m_private - > areas . size ( ) * audio : : getFormatBytes ( m_userFormat ) ;
}
// Generate conbverters:
2015-02-09 21:44:32 +01:00
if ( m_doConvertBuffer [ modeToIdTable ( _mode ) ] ) {
bufferBytes = m_nDeviceChannels [ modeToIdTable ( _mode ) ] * audio : : getFormatBytes ( m_deviceFormat [ modeToIdTable ( _mode ) ] ) ;
2015-06-07 22:32:54 +02:00
bufferBytes * = * _bufferSize ;
if ( m_deviceBuffer ) {
free ( m_deviceBuffer ) ;
m_deviceBuffer = nullptr ;
}
m_deviceBuffer = ( char * ) calloc ( bufferBytes , 1 ) ;
if ( m_deviceBuffer = = nullptr ) {
ATA_ERROR ( " error allocating device buffer memory. " ) ;
goto error ;
2014-03-11 21:46:00 +01:00
}
}
2015-02-09 21:44:32 +01:00
m_nBuffers = periods ;
2015-03-03 21:28:07 +01:00
ATA_INFO ( " ALSA NB buffer = " < < m_nBuffers ) ;
2015-02-26 22:25:15 +01:00
// TODO : m_device[modeToIdTable(_mode)] = _device;
2015-04-10 22:06:17 +02:00
m_state = audio : : orchestra : : state_stopped ;
2014-03-11 21:46:00 +01:00
// Setup the buffer conversion information structure.
2015-02-09 21:44:32 +01:00
if ( m_doConvertBuffer [ modeToIdTable ( _mode ) ] ) {
2014-03-11 21:46:00 +01:00
setConvertInfo ( _mode , _firstChannel ) ;
}
2015-06-07 22:32:54 +02:00
m_mode = _mode ;
// Setup callback thread.
m_private - > threadRunning = true ;
ATA_INFO ( " create thread ... " ) ;
m_private - > thread = new std11 : : thread ( & audio : : orchestra : : api : : Alsa : : alsaCallbackEvent , this ) ;
if ( m_private - > thread = = nullptr ) {
m_private - > threadRunning = false ;
ATA_ERROR ( " creating callback thread! " ) ;
goto error ;
2014-03-11 21:46:00 +01:00
}
2015-06-07 22:32:54 +02:00
etk : : thread : : setPriority ( * m_private - > thread , - 6 ) ;
2014-03-13 21:16:30 +01:00
return true ;
2014-03-11 21:46:00 +01:00
error :
2015-06-07 22:32:54 +02:00
if ( m_private - > handle ) {
snd_pcm_close ( m_private - > handle ) ;
m_private - > handle = nullptr ;
2014-03-11 21:46:00 +01:00
}
for ( int32_t iii = 0 ; iii < 2 ; + + iii ) {
2015-02-09 21:44:32 +01:00
m_userBuffer [ iii ] . clear ( ) ;
2014-03-11 21:46:00 +01:00
}
2015-02-09 21:44:32 +01:00
if ( m_deviceBuffer ) {
free ( m_deviceBuffer ) ;
m_deviceBuffer = 0 ;
2014-03-11 21:46:00 +01:00
}
2015-04-10 22:06:17 +02:00
m_state = audio : : orchestra : : state_closed ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2015-04-10 22:06:17 +02:00
enum audio : : orchestra : : error audio : : orchestra : : api : : Alsa : : closeStream ( ) {
if ( m_state = = audio : : orchestra : : state_closed ) {
2015-02-06 23:54:08 +01:00
ATA_ERROR ( " no open stream to close! " ) ;
2015-04-10 22:06:17 +02:00
return audio : : orchestra : : error_warning ;
2014-03-11 21:46:00 +01:00
}
2015-02-17 21:08:15 +01:00
m_private - > threadRunning = false ;
2015-02-09 21:44:32 +01:00
m_mutex . lock ( ) ;
2015-04-10 22:06:17 +02:00
if ( m_state = = audio : : orchestra : : state_stopped ) {
2015-02-10 21:01:53 +01:00
m_private - > runnable = true ;
m_private - > runnable_cv . notify_one ( ) ;
2014-03-11 21:46:00 +01:00
}
2015-02-09 21:44:32 +01:00
m_mutex . unlock ( ) ;
2015-02-17 21:08:15 +01:00
if ( m_private - > thread ! = nullptr ) {
m_private - > thread - > join ( ) ;
2015-02-24 22:20:11 +01:00
m_private - > thread = nullptr ;
2014-03-11 21:46:00 +01:00
}
2015-04-10 22:06:17 +02:00
if ( m_state = = audio : : orchestra : : state_running ) {
m_state = audio : : orchestra : : state_stopped ;
2015-06-07 22:32:54 +02:00
snd_pcm_drop ( m_private - > handle ) ;
2014-03-11 21:46:00 +01:00
}
2015-02-10 21:01:53 +01:00
// close all stream :
2015-06-07 22:32:54 +02:00
if ( m_private - > handle ) {
snd_pcm_close ( m_private - > handle ) ;
m_private - > handle = nullptr ;
2014-03-11 21:46:00 +01:00
}
for ( int32_t iii = 0 ; iii < 2 ; + + iii ) {
2015-02-09 21:44:32 +01:00
m_userBuffer [ iii ] . clear ( ) ;
2014-03-11 21:46:00 +01:00
}
2015-02-09 21:44:32 +01:00
if ( m_deviceBuffer ) {
free ( m_deviceBuffer ) ;
m_deviceBuffer = 0 ;
2014-03-11 21:46:00 +01:00
}
2015-04-10 22:06:17 +02:00
m_mode = audio : : orchestra : : mode_unknow ;
m_state = audio : : orchestra : : state_closed ;
return audio : : orchestra : : error_none ;
2014-03-11 21:46:00 +01:00
}
2015-04-10 22:06:17 +02:00
enum audio : : orchestra : : error audio : : orchestra : : api : : Alsa : : startStream ( ) {
2015-02-10 22:38:30 +01:00
// TODO : Check return ...
2015-04-10 22:06:17 +02:00
//audio::orchestra::Api::startStream();
2014-03-11 21:46:00 +01:00
// This method calls snd_pcm_prepare if the device isn't already in that state.
2015-04-10 22:06:17 +02:00
if ( verifyStream ( ) ! = audio : : orchestra : : error_none ) {
return audio : : orchestra : : error_fail ;
2014-03-12 23:55:49 +01:00
}
2015-04-10 22:06:17 +02:00
if ( m_state = = audio : : orchestra : : state_running ) {
2015-02-06 23:54:08 +01:00
ATA_ERROR ( " the stream is already running! " ) ;
2015-04-10 22:06:17 +02:00
return audio : : orchestra : : error_warning ;
2014-03-11 21:46:00 +01:00
}
2015-02-24 22:20:11 +01:00
std11 : : unique_lock < std11 : : mutex > lck ( m_mutex ) ;
2014-03-11 21:46:00 +01:00
int32_t result = 0 ;
snd_pcm_state_t state ;
2015-06-07 22:32:54 +02:00
if ( m_private - > handle = = nullptr ) {
ATA_ERROR ( " send nullptr to alsa ... " ) ;
2014-03-11 21:46:00 +01:00
}
2015-06-07 22:32:54 +02:00
state = snd_pcm_state ( m_private - > handle ) ;
if ( state ! = SND_PCM_STATE_PREPARED ) {
ATA_ERROR ( " prepare stream " ) ;
result = snd_pcm_prepare ( m_private - > handle ) ;
if ( result < 0 ) {
ATA_ERROR ( " error preparing pcm device: ERR= " < < snd_strerror ( result ) < < " . " ) ;
goto unlock ;
2014-03-11 21:46:00 +01:00
}
}
2015-04-10 22:06:17 +02:00
m_state = audio : : orchestra : : state_running ;
2014-03-11 21:46:00 +01:00
unlock :
2015-02-10 21:01:53 +01:00
m_private - > runnable = true ;
m_private - > runnable_cv . notify_one ( ) ;
2014-03-11 21:46:00 +01:00
if ( result > = 0 ) {
2015-04-10 22:06:17 +02:00
return audio : : orchestra : : error_none ;
2014-03-11 21:46:00 +01:00
}
2015-04-10 22:06:17 +02:00
return audio : : orchestra : : error_systemError ;
2014-03-11 21:46:00 +01:00
}
2015-04-10 22:06:17 +02:00
enum audio : : orchestra : : error audio : : orchestra : : api : : Alsa : : stopStream ( ) {
if ( verifyStream ( ) ! = audio : : orchestra : : error_none ) {
return audio : : orchestra : : error_fail ;
2014-03-12 23:55:49 +01:00
}
2015-04-10 22:06:17 +02:00
if ( m_state = = audio : : orchestra : : state_stopped ) {
2015-02-06 23:54:08 +01:00
ATA_ERROR ( " the stream is already stopped! " ) ;
2015-04-10 22:06:17 +02:00
return audio : : orchestra : : error_warning ;
2014-03-11 21:46:00 +01:00
}
2015-04-10 22:06:17 +02:00
m_state = audio : : orchestra : : state_stopped ;
2015-02-24 22:20:11 +01:00
std11 : : unique_lock < std11 : : mutex > lck ( m_mutex ) ;
2014-03-11 21:46:00 +01:00
int32_t result = 0 ;
2015-06-07 22:32:54 +02:00
if ( m_mode = = audio : : orchestra : : mode_output ) {
result = snd_pcm_drain ( m_private - > handle ) ;
} else {
result = snd_pcm_drop ( m_private - > handle ) ;
2014-03-11 21:46:00 +01:00
}
2015-06-07 22:32:54 +02:00
if ( result < 0 ) {
ATA_ERROR ( " error draining output pcm device, " < < snd_strerror ( result ) < < " . " ) ;
goto unlock ;
2014-03-11 21:46:00 +01:00
}
unlock :
if ( result > = 0 ) {
2015-04-10 22:06:17 +02:00
return audio : : orchestra : : error_none ;
2014-03-11 21:46:00 +01:00
}
2015-04-10 22:06:17 +02:00
return audio : : orchestra : : error_systemError ;
2014-03-11 21:46:00 +01:00
}
2015-04-10 22:06:17 +02:00
enum audio : : orchestra : : error audio : : orchestra : : api : : Alsa : : abortStream ( ) {
if ( verifyStream ( ) ! = audio : : orchestra : : error_none ) {
return audio : : orchestra : : error_fail ;
2014-03-12 23:55:49 +01:00
}
2015-04-10 22:06:17 +02:00
if ( m_state = = audio : : orchestra : : state_stopped ) {
2015-02-06 23:54:08 +01:00
ATA_ERROR ( " the stream is already stopped! " ) ;
2015-04-10 22:06:17 +02:00
return audio : : orchestra : : error_warning ;
2014-03-11 21:46:00 +01:00
}
2015-04-10 22:06:17 +02:00
m_state = audio : : orchestra : : state_stopped ;
2015-02-24 22:20:11 +01:00
std11 : : unique_lock < std11 : : mutex > lck ( m_mutex ) ;
2014-03-11 21:46:00 +01:00
int32_t result = 0 ;
2015-06-07 22:32:54 +02:00
result = snd_pcm_drop ( m_private - > handle ) ;
if ( result < 0 ) {
ATA_ERROR ( " error aborting output pcm device, " < < snd_strerror ( result ) < < " . " ) ;
goto unlock ;
2014-03-11 21:46:00 +01:00
}
unlock :
if ( result > = 0 ) {
2015-04-10 22:06:17 +02:00
return audio : : orchestra : : error_none ;
2014-03-11 21:46:00 +01:00
}
2015-04-10 22:06:17 +02:00
return audio : : orchestra : : error_systemError ;
2014-03-11 21:46:00 +01:00
}
2015-02-09 21:44:32 +01:00
2015-04-10 22:06:17 +02:00
void audio : : orchestra : : api : : Alsa : : alsaCallbackEvent ( void * _userData ) {
audio : : orchestra : : api : : Alsa * myClass = reinterpret_cast < audio : : orchestra : : api : : Alsa * > ( _userData ) ;
2015-02-09 21:44:32 +01:00
myClass - > callbackEvent ( ) ;
}
2015-06-05 22:00:17 +02:00
/**
* @ briefTransfer method - write and wait for room in buffer using poll
*/
static int32_t wait_for_poll ( snd_pcm_t * _handle , struct pollfd * _ufds , unsigned int _count ) {
uint16_t revents ;
while ( true ) {
poll ( _ufds , _count , - 1 ) ;
snd_pcm_poll_descriptors_revents ( _handle , _ufds , _count , & revents ) ;
if ( revents & POLLERR ) {
return - EIO ;
}
if ( revents & POLLOUT ) {
return 0 ;
}
2015-06-11 21:33:32 +02:00
if ( revents & POLLIN ) {
return 0 ;
}
2015-06-05 22:00:17 +02:00
}
}
2015-04-10 22:06:17 +02:00
void audio : : orchestra : : api : : Alsa : : callbackEvent ( ) {
2015-06-11 21:33:32 +02:00
// Lock while the system is not started ...
if ( m_state = = audio : : orchestra : : state_stopped ) {
std11 : : unique_lock < std11 : : mutex > lck ( m_mutex ) ;
while ( ! m_private - > runnable ) {
m_private - > runnable_cv . wait ( lck ) ;
}
if ( m_state ! = audio : : orchestra : : state_running ) {
return ;
}
}
2015-03-20 21:07:58 +01:00
etk : : thread : : setName ( " Alsa IO- " + m_name ) ;
2015-06-05 22:00:17 +02:00
//Wait data with poll
2015-06-11 21:33:32 +02:00
std : : vector < struct pollfd > ufds ;
2015-06-05 22:00:17 +02:00
signed short * ptr ;
int32_t err , count , cptr , init ;
2015-06-07 22:32:54 +02:00
count = snd_pcm_poll_descriptors_count ( m_private - > handle ) ;
2015-06-05 22:00:17 +02:00
if ( count < = 0 ) {
ATA_CRITICAL ( " Invalid poll descriptors count " ) ;
}
2015-06-11 21:33:32 +02:00
ufds . resize ( count ) ;
if ( ufds . size ( ) = = 0 ) {
2015-06-05 22:00:17 +02:00
ATA_CRITICAL ( " No enough memory \n " ) ;
}
2015-06-11 21:33:32 +02:00
if ( ( err = snd_pcm_poll_descriptors ( m_private - > handle , & ( ufds [ 0 ] ) , count ) ) < 0 ) {
2015-06-05 22:00:17 +02:00
ATA_CRITICAL ( " Unable to obtain poll descriptors for playback: " < < snd_strerror ( err ) ) ;
}
2015-02-17 21:08:15 +01:00
while ( m_private - > threadRunning = = true ) {
2015-06-05 22:00:17 +02:00
// have data or need data ...
if ( m_private - > mmapInterface = = false ) {
if ( m_mode = = audio : : orchestra : : mode_input ) {
callbackEventOneCycleRead ( ) ;
} else {
callbackEventOneCycleWrite ( ) ;
}
} else {
if ( m_mode = = audio : : orchestra : : mode_input ) {
callbackEventOneCycleMMAPRead ( ) ;
} else {
callbackEventOneCycleMMAPWrite ( ) ;
}
}
2015-06-11 21:33:32 +02:00
ATA_VERBOSE ( " Poll [Start] " < < count ) ;
err = wait_for_poll ( m_private - > handle , & ( ufds [ 0 ] ) , count ) ;
ATA_VERBOSE ( " Poll [STOP] " < < err ) ;
if ( err < 0 ) {
ATA_ERROR ( " POLL timeout ... " ) ;
return ;
}
2015-02-09 21:44:32 +01:00
}
2015-06-11 21:33:32 +02:00
ATA_DEBUG ( " End of thread " ) ;
2015-02-09 21:44:32 +01:00
}
2015-04-13 21:49:48 +02:00
audio : : Time audio : : orchestra : : api : : Alsa : : getStreamTime ( ) {
2015-03-03 21:28:07 +01:00
//ATA_DEBUG("mode : " << m_private->timeMode);
2015-02-27 21:07:17 +01:00
if ( m_private - > timeMode = = timestampMode_Hardware ) {
2015-02-26 21:44:16 +01:00
snd_pcm_status_t * status = nullptr ;
snd_pcm_status_alloca ( & status ) ;
// get harware timestamp all the time:
2015-06-07 22:32:54 +02:00
snd_pcm_status ( m_private - > handle , status ) ;
2015-03-03 21:28:07 +01:00
# if 1
2015-02-26 21:44:16 +01:00
snd_timestamp_t timestamp ;
snd_pcm_status_get_tstamp ( status , & timestamp ) ;
2015-04-13 21:49:48 +02:00
m_startTime = audio : : Time ( timestamp . tv_sec , timestamp . tv_usec * 1000 ) ;
2015-02-26 21:44:16 +01:00
# else
2015-03-03 21:28:07 +01:00
# if 1
snd_htimestamp_t timestamp ;
snd_pcm_status_get_htstamp ( status , & timestamp ) ;
2015-04-13 21:49:48 +02:00
m_startTime = audio : : Time ( timestamp . tv_sec , timestamp . tv_nsec ) ;
2015-03-03 21:28:07 +01:00
# else
snd_htimestamp_t timestamp ;
snd_pcm_status_get_audio_htstamp ( status , & timestamp ) ;
2015-04-13 21:49:48 +02:00
m_startTime = audio : : Time ( timestamp . tv_sec , timestamp . tv_nsec ) ;
2015-03-03 21:28:07 +01:00
return m_startTime ;
# endif
2015-02-26 21:44:16 +01:00
# endif
ATA_VERBOSE ( " snd_pcm_status_get_htstamp : " < < m_startTime ) ;
snd_pcm_sframes_t delay = snd_pcm_status_get_delay ( status ) ;
2015-04-13 21:49:48 +02:00
audio : : Duration timeDelay = audio : : Duration ( 0 , delay * 1000000000LL / int64_t ( m_sampleRate ) ) ;
2015-02-26 21:44:16 +01:00
ATA_VERBOSE ( " delay : " < < timeDelay . count ( ) < < " ns " ) ;
//return m_startTime + m_duration;
2015-06-07 22:32:54 +02:00
if ( m_mode = = audio : : orchestra : : mode_output ) {
2015-02-26 21:44:16 +01:00
// output
m_startTime + = timeDelay ;
} else {
// input
m_startTime - = timeDelay ;
2015-02-19 22:00:21 +01:00
}
2015-02-26 21:44:16 +01:00
return m_startTime ;
2015-02-27 21:07:17 +01:00
} else if ( m_private - > timeMode = = timestampMode_trigered ) {
2015-04-13 21:49:48 +02:00
if ( m_startTime = = audio : : Time ( ) ) {
2015-02-27 21:07:17 +01:00
snd_pcm_status_t * status = nullptr ;
snd_pcm_status_alloca ( & status ) ;
// get harware timestamp all the time:
2015-06-07 22:32:54 +02:00
snd_pcm_status ( m_private - > handle , status ) ;
2015-02-27 21:07:17 +01:00
// get start time:
snd_timestamp_t timestamp ;
snd_pcm_status_get_trigger_tstamp ( status , & timestamp ) ;
2015-04-13 21:49:48 +02:00
m_startTime = audio : : Time ( timestamp . tv_sec , timestamp . tv_usec ) ;
2015-02-27 21:07:17 +01:00
ATA_VERBOSE ( " snd_pcm_status_get_trigger_tstamp : " < < m_startTime ) ;
}
return m_startTime + m_duration ;
2015-02-26 21:44:16 +01:00
} else {
2015-02-27 21:07:17 +01:00
// softaware mode ...
2015-04-13 21:49:48 +02:00
if ( m_startTime = = audio : : Time ( ) ) {
m_startTime = audio : : Time : : now ( ) ;
2015-03-03 21:28:07 +01:00
ATA_ERROR ( " START TIOMESTAMP : " < < m_startTime ) ;
2015-04-13 21:49:48 +02:00
audio : : Duration timeDelay = audio : : Duration ( 0 , m_bufferSize * 1000000000LL / int64_t ( m_sampleRate ) ) ;
2015-06-07 22:32:54 +02:00
if ( m_mode = = audio : : orchestra : : mode_output ) {
2015-02-26 21:44:16 +01:00
// output
m_startTime + = timeDelay ;
} else {
// input
m_startTime - = timeDelay ;
}
2015-04-13 21:49:48 +02:00
m_duration = audio : : Duration ( 0 ) ;
2015-02-19 22:49:33 +01:00
}
2015-02-19 22:00:21 +01:00
return m_startTime + m_duration ;
}
2015-02-26 21:44:16 +01:00
return m_startTime + m_duration ;
2015-02-19 22:00:21 +01:00
}
2015-02-16 23:01:26 +01:00
2015-06-05 22:00:17 +02:00
void audio : : orchestra : : api : : Alsa : : callbackEventOneCycleRead ( ) {
if ( m_state = = audio : : orchestra : : state_closed ) {
ATA_CRITICAL ( " the stream is closed ... this shouldn't happen! " ) ;
return ; // TODO : notify appl: audio::orchestra::error_warning;
}
int32_t doStopStream = 0 ;
audio : : Time streamTime ;
std : : vector < enum audio : : orchestra : : status > status ;
if ( m_private - > xrun [ 0 ] = = true ) {
status . push_back ( audio : : orchestra : : status_underflow ) ;
m_private - > xrun [ 0 ] = false ;
}
int32_t result ;
char * buffer ;
int32_t channels ;
snd_pcm_sframes_t frames ;
audio : : format format ;
if ( m_state = = audio : : orchestra : : state_stopped ) {
// !!! goto unlock;
}
std11 : : unique_lock < std11 : : mutex > lck ( m_mutex ) ;
// Setup parameters.
if ( m_doConvertBuffer [ 1 ] ) {
buffer = m_deviceBuffer ;
channels = m_nDeviceChannels [ 1 ] ;
format = m_deviceFormat [ 1 ] ;
} else {
buffer = & m_userBuffer [ 1 ] [ 0 ] ;
channels = m_nUserChannels [ 1 ] ;
format = m_userFormat ;
}
// Read samples from device in interleaved/non-interleaved format.
if ( m_deviceInterleaved [ 1 ] ) {
2015-06-07 22:32:54 +02:00
result = snd_pcm_readi ( m_private - > handle , buffer , m_bufferSize ) ;
2015-06-05 22:00:17 +02:00
} else {
void * bufs [ channels ] ;
size_t offset = m_bufferSize * audio : : getFormatBytes ( format ) ;
for ( int32_t i = 0 ; i < channels ; i + + )
bufs [ i ] = ( void * ) ( buffer + ( i * offset ) ) ;
2015-06-07 22:32:54 +02:00
result = snd_pcm_readn ( m_private - > handle , bufs , m_bufferSize ) ;
2015-06-05 22:00:17 +02:00
}
{
2015-06-07 22:32:54 +02:00
snd_pcm_state_t state = snd_pcm_state ( m_private - > handle ) ;
2015-06-05 22:00:17 +02:00
ATA_VERBOSE ( " plop : " < < state ) ;
if ( state = = SND_PCM_STATE_XRUN ) {
ATA_ERROR ( " Xrun... " ) ;
}
}
// get timestamp : (to init here ...
streamTime = getStreamTime ( ) ;
if ( result < ( int ) m_bufferSize ) {
// Either an error or overrun occured.
if ( result = = - EPIPE ) {
2015-06-07 22:32:54 +02:00
snd_pcm_state_t state = snd_pcm_state ( m_private - > handle ) ;
2015-06-05 22:00:17 +02:00
if ( state = = SND_PCM_STATE_XRUN ) {
m_private - > xrun [ 1 ] = true ;
2015-06-07 22:32:54 +02:00
result = snd_pcm_prepare ( m_private - > handle ) ;
2015-06-05 22:00:17 +02:00
if ( result < 0 ) {
ATA_ERROR ( " error preparing device after overrun, " < < snd_strerror ( result ) < < " . " ) ;
}
} else {
ATA_ERROR ( " error, current state is " < < snd_pcm_state_name ( state ) < < " , " < < snd_strerror ( result ) < < " . " ) ;
}
} else {
ATA_ERROR ( " audio read error, " < < snd_strerror ( result ) < < " . " ) ;
usleep ( 10000 ) ;
}
// TODO : Notify application ... audio::orchestra::error_warning;
goto noInput ;
}
// Do byte swapping if necessary.
if ( m_doByteSwap [ 1 ] ) {
byteSwapBuffer ( buffer , m_bufferSize * channels , format ) ;
}
// Do buffer conversion if necessary.
if ( m_doConvertBuffer [ 1 ] ) {
convertBuffer ( & m_userBuffer [ 1 ] [ 0 ] , m_deviceBuffer , m_convertInfo [ 1 ] ) ;
}
// Check stream latency
2015-06-07 22:32:54 +02:00
result = snd_pcm_delay ( m_private - > handle , & frames ) ;
2015-06-05 22:00:17 +02:00
if ( result = = 0 & & frames > 0 ) {
ATA_VERBOSE ( " Delay in the Input " < < frames < < " chunk " ) ;
m_latency [ 1 ] = frames ;
}
noInput :
streamTime = getStreamTime ( ) ;
{
audio : : Time startCall = audio : : Time : : now ( ) ;
doStopStream = m_callback ( & m_userBuffer [ 1 ] [ 0 ] ,
streamTime , // - audio::Duration(m_latency[1]*1000000000LL/int64_t(m_sampleRate)),
nullptr ,
audio : : Time ( ) ,
m_bufferSize ,
status ) ;
audio : : Time stopCall = audio : : Time : : now ( ) ;
audio : : Duration timeDelay ( 0 , m_bufferSize * 1000000000LL / int64_t ( m_sampleRate ) ) ;
audio : : Duration timeProcess = stopCall - startCall ;
if ( timeDelay < = timeProcess ) {
ATA_ERROR ( " SOFT XRUN ... : (bufferTime) " < < timeDelay . count ( ) < < " < " < < timeProcess . count ( ) < < " (process time) ns " ) ;
}
}
if ( doStopStream = = 2 ) {
abortStream ( ) ;
return ;
}
unlock :
audio : : orchestra : : Api : : tickStreamTime ( ) ;
if ( doStopStream = = 1 ) {
this - > stopStream ( ) ;
}
}
void audio : : orchestra : : api : : Alsa : : callbackEventOneCycleWrite ( ) {
if ( m_state = = audio : : orchestra : : state_closed ) {
ATA_CRITICAL ( " the stream is closed ... this shouldn't happen! " ) ;
return ; // TODO : notify appl: audio::orchestra::error_warning;
}
int32_t doStopStream = 0 ;
audio : : Time streamTime ;
std : : vector < enum audio : : orchestra : : status > status ;
if ( m_private - > xrun [ 1 ] = = true ) {
status . push_back ( audio : : orchestra : : status_overflow ) ;
m_private - > xrun [ 1 ] = false ;
}
int32_t result ;
char * buffer ;
int32_t channels ;
snd_pcm_sframes_t frames ;
audio : : format format ;
if ( m_state = = audio : : orchestra : : state_stopped ) {
// !!! goto unlock;
}
streamTime = getStreamTime ( ) ;
{
audio : : Time startCall = audio : : Time : : now ( ) ;
doStopStream = m_callback ( nullptr ,
audio : : Time ( ) ,
& m_userBuffer [ 0 ] [ 0 ] ,
streamTime , // + audio::Duration(m_latency[0]*1000000000LL/int64_t(m_sampleRate)),
m_bufferSize ,
status ) ;
audio : : Time stopCall = audio : : Time : : now ( ) ;
audio : : Duration timeDelay ( 0 , m_bufferSize * 1000000000LL / int64_t ( m_sampleRate ) ) ;
audio : : Duration timeProcess = stopCall - startCall ;
if ( timeDelay < = timeProcess ) {
ATA_ERROR ( " SOFT XRUN ... : (bufferTime) " < < timeDelay . count ( ) < < " < " < < timeProcess . count ( ) < < " (process time) ns " ) ;
}
}
if ( doStopStream = = 2 ) {
abortStream ( ) ;
return ;
}
std11 : : unique_lock < std11 : : mutex > lck ( m_mutex ) ;
// Setup parameters and do buffer conversion if necessary.
if ( m_doConvertBuffer [ 0 ] ) {
buffer = m_deviceBuffer ;
convertBuffer ( buffer , & m_userBuffer [ 0 ] [ 0 ] , m_convertInfo [ 0 ] ) ;
channels = m_nDeviceChannels [ 0 ] ;
format = m_deviceFormat [ 0 ] ;
} else {
buffer = & m_userBuffer [ 0 ] [ 0 ] ;
channels = m_nUserChannels [ 0 ] ;
format = m_userFormat ;
}
// Do byte swapping if necessary.
if ( m_doByteSwap [ 0 ] ) {
byteSwapBuffer ( buffer , m_bufferSize * channels , format ) ;
}
// Write samples to device in interleaved/non-interleaved format.
if ( m_deviceInterleaved [ 0 ] ) {
2015-06-07 22:32:54 +02:00
result = snd_pcm_writei ( m_private - > handle , buffer , m_bufferSize ) ;
2015-06-05 22:00:17 +02:00
} else {
void * bufs [ channels ] ;
size_t offset = m_bufferSize * audio : : getFormatBytes ( format ) ;
for ( int32_t i = 0 ; i < channels ; i + + ) {
bufs [ i ] = ( void * ) ( buffer + ( i * offset ) ) ;
}
2015-06-07 22:32:54 +02:00
result = snd_pcm_writen ( m_private - > handle , bufs , m_bufferSize ) ;
2015-06-05 22:00:17 +02:00
}
if ( result < ( int ) m_bufferSize ) {
// Either an error or underrun occured.
if ( result = = - EPIPE ) {
2015-06-07 22:32:54 +02:00
snd_pcm_state_t state = snd_pcm_state ( m_private - > handle ) ;
2015-06-05 22:00:17 +02:00
if ( state = = SND_PCM_STATE_XRUN ) {
m_private - > xrun [ 0 ] = true ;
2015-06-07 22:32:54 +02:00
result = snd_pcm_prepare ( m_private - > handle ) ;
2015-06-05 22:00:17 +02:00
if ( result < 0 ) {
ATA_ERROR ( " error preparing device after underrun, " < < snd_strerror ( result ) < < " . " ) ;
}
} else {
ATA_ERROR ( " error, current state is " < < snd_pcm_state_name ( state ) < < " , " < < snd_strerror ( result ) < < " . " ) ;
}
} else {
ATA_ERROR ( " audio write error, " < < snd_strerror ( result ) < < " . " ) ;
}
// TODO : Notuify application audio::orchestra::error_warning;
goto unlock ;
}
// Check stream latency
2015-06-07 22:32:54 +02:00
result = snd_pcm_delay ( m_private - > handle , & frames ) ;
2015-06-05 22:00:17 +02:00
if ( result = = 0 & & frames > 0 ) {
ATA_VERBOSE ( " Delay in the Output " < < frames < < " chunk " ) ;
m_latency [ 0 ] = frames ;
}
unlock :
audio : : orchestra : : Api : : tickStreamTime ( ) ;
if ( doStopStream = = 1 ) {
this - > stopStream ( ) ;
}
}
void audio : : orchestra : : api : : Alsa : : callbackEventOneCycleMMAPWrite ( ) {
if ( m_state = = audio : : orchestra : : state_closed ) {
ATA_CRITICAL ( " the stream is closed ... this shouldn't happen! " ) ;
return ; // TODO : notify appl: audio::orchestra::error_warning;
}
int32_t doStopStream = 0 ;
audio : : Time streamTime ;
std : : vector < enum audio : : orchestra : : status > status ;
if ( m_private - > xrun [ 1 ] = = true ) {
status . push_back ( audio : : orchestra : : status_overflow ) ;
m_private - > xrun [ 1 ] = false ;
}
int32_t result ;
char * buffer ;
int32_t channels ;
snd_pcm_sframes_t frames ;
audio : : format format ;
if ( m_state = = audio : : orchestra : : state_stopped ) {
// !!! goto unlock;
}
ATA_DEBUG ( " UPDATE " ) ;
2015-06-07 22:32:54 +02:00
int32_t avail = snd_pcm_avail_update ( m_private - > handle ) ;
2015-06-05 22:00:17 +02:00
if ( avail < 0 ) {
ATA_ERROR ( " Can not get buffer data ... " < < avail ) ;
return ;
}
streamTime = getStreamTime ( ) ;
{
audio : : Time startCall = audio : : Time : : now ( ) ;
doStopStream = m_callback ( nullptr ,
audio : : Time ( ) ,
& m_userBuffer [ 0 ] [ 0 ] ,
streamTime , // + audio::Duration(m_latency[0]*1000000000LL/int64_t(m_sampleRate)),
m_bufferSize ,
status ) ;
audio : : Time stopCall = audio : : Time : : now ( ) ;
audio : : Duration timeDelay ( 0 , m_bufferSize * 1000000000LL / int64_t ( m_sampleRate ) ) ;
audio : : Duration timeProcess = stopCall - startCall ;
if ( timeDelay < = timeProcess ) {
ATA_ERROR ( " SOFT XRUN ... : (bufferTime) " < < timeDelay . count ( ) < < " < " < < timeProcess . count ( ) < < " (process time) ns " ) ;
}
}
if ( doStopStream = = 2 ) {
abortStream ( ) ;
return ;
}
2015-06-07 22:32:54 +02:00
{
std11 : : unique_lock < std11 : : mutex > lck ( m_mutex ) ;
// Setup parameters and do buffer conversion if necessary.
if ( m_doConvertBuffer [ 0 ] ) {
buffer = m_deviceBuffer ;
convertBuffer ( buffer , & m_userBuffer [ 0 ] [ 0 ] , m_convertInfo [ 0 ] ) ;
channels = m_nDeviceChannels [ 0 ] ;
format = m_deviceFormat [ 0 ] ;
} else {
buffer = & m_userBuffer [ 0 ] [ 0 ] ;
channels = m_nUserChannels [ 0 ] ;
format = m_userFormat ;
2015-06-05 22:00:17 +02:00
}
2015-06-07 22:32:54 +02:00
// Do byte swapping if necessary.
if ( m_doByteSwap [ 0 ] ) {
byteSwapBuffer ( buffer , m_bufferSize * channels , format ) ;
2015-06-05 22:00:17 +02:00
}
2015-06-07 22:32:54 +02:00
# if 1
// Write samples to device in interleaved/non-interleaved format.
if ( m_deviceInterleaved [ 0 ] ) {
result = snd_pcm_mmap_writei ( m_private - > handle , buffer , m_bufferSize ) ;
} else {
void * bufs [ channels ] ;
size_t offset = m_bufferSize * audio : : getFormatBytes ( format ) ;
for ( int32_t i = 0 ; i < channels ; i + + ) {
bufs [ i ] = ( void * ) ( buffer + ( i * offset ) ) ;
}
result = snd_pcm_mmap_writen ( m_private - > handle , bufs , m_bufferSize ) ;
}
# else
// TODO: Understand why this does not work ...
// Write samples to device in interleaved/non-interleaved format.
if ( m_deviceInterleaved [ 0 ] ) {
const snd_pcm_channel_area_t * myAreas = nullptr ;
snd_pcm_uframes_t offset , frames ;
frames = m_bufferSize ;
ATA_DEBUG ( " START " ) ;
int err = snd_pcm_mmap_begin ( m_private - > handle , & myAreas , & offset , & frames ) ;
if ( err < 0 ) {
ATA_CRITICAL ( " SUPER_FAIL " ) ;
}
ATA_DEBUG ( " snd_pcm_mmap_begin " < < offset < < " frame= " < < frames < < " m_bufferSize= " < < m_bufferSize ) ;
ATA_DEBUG ( " copy " < < err < < " addr= " < < myAreas [ 0 ] . addr < < " first= " < < myAreas [ 0 ] . first < < " step= " < < myAreas [ 0 ] . step ) ;
//generate_sine(myAreas, offset, frames, &phase);
// Write samples to device in interleaved/non-interleaved format.
if ( m_deviceInterleaved [ 0 ] ) {
memcpy ( myAreas [ 0 ] . addr , buffer , m_bufferSize ) ;
result = m_bufferSize ;
} else {
void * bufs [ channels ] ;
size_t offset = m_bufferSize * audio : : getFormatBytes ( format ) ;
for ( int32_t i = 0 ; i < channels ; i + + ) {
bufs [ i ] = ( void * ) ( buffer + ( i * offset ) ) ;
}
memcpy ( myAreas [ 0 ] . addr , bufs , m_bufferSize ) ;
result = m_bufferSize ;
}
ATA_DEBUG ( " commit " < < offset < < " frame= " < < frames ) ;
int commitres = snd_pcm_mmap_commit ( m_private - > handle , offset , frames ) ;
if ( commitres < 0
| | ( snd_pcm_uframes_t ) commitres ! = frames ) {
ATA_CRITICAL ( " MMAP commit error: " < < snd_strerror ( err ) ) ;
}
} else {
void * bufs [ channels ] ;
size_t offset = m_bufferSize * audio : : getFormatBytes ( format ) ;
for ( int32_t i = 0 ; i < channels ; i + + ) {
bufs [ i ] = ( void * ) ( buffer + ( i * offset ) ) ;
}
result = snd_pcm_writen ( m_private - > handle , bufs , m_bufferSize ) ;
}
# endif
// Check stream latency
result = snd_pcm_delay ( m_private - > handle , & frames ) ;
if ( result = = 0 & & frames > 0 ) {
ATA_VERBOSE ( " Delay in the Output " < < frames < < " chunk " ) ;
m_latency [ 0 ] = frames ;
2015-06-05 22:00:17 +02:00
}
}
unlock :
audio : : orchestra : : Api : : tickStreamTime ( ) ;
if ( doStopStream = = 1 ) {
this - > stopStream ( ) ;
}
}
void audio : : orchestra : : api : : Alsa : : callbackEventOneCycleMMAPRead ( ) {
2015-04-10 22:06:17 +02:00
if ( m_state = = audio : : orchestra : : state_closed ) {
2015-02-06 23:54:08 +01:00
ATA_CRITICAL ( " the stream is closed ... this shouldn't happen! " ) ;
2015-04-10 22:06:17 +02:00
return ; // TODO : notify appl: audio::orchestra::error_warning;
2014-03-11 21:46:00 +01:00
}
int32_t doStopStream = 0 ;
2015-04-13 21:49:48 +02:00
audio : : Time streamTime ;
2015-04-10 22:06:17 +02:00
std : : vector < enum audio : : orchestra : : status > status ;
2015-06-07 22:32:54 +02:00
if ( m_private - > xrun [ 0 ] = = true ) {
2015-04-10 22:06:17 +02:00
status . push_back ( audio : : orchestra : : status_underflow ) ;
2015-02-10 21:01:53 +01:00
m_private - > xrun [ 0 ] = false ;
2014-03-11 21:46:00 +01:00
}
int32_t result ;
char * buffer ;
int32_t channels ;
snd_pcm_sframes_t frames ;
2015-02-05 23:31:22 +01:00
audio : : format format ;
2015-02-19 22:49:33 +01:00
2015-04-10 22:06:17 +02:00
if ( m_state = = audio : : orchestra : : state_stopped ) {
2015-02-19 22:49:33 +01:00
goto unlock ;
}
2015-06-07 22:32:54 +02:00
{
2015-02-24 22:20:11 +01:00
std11 : : unique_lock < std11 : : mutex > lck ( m_mutex ) ;
2014-03-11 21:46:00 +01:00
// Setup parameters.
2015-02-09 21:44:32 +01:00
if ( m_doConvertBuffer [ 1 ] ) {
buffer = m_deviceBuffer ;
channels = m_nDeviceChannels [ 1 ] ;
format = m_deviceFormat [ 1 ] ;
2014-03-11 21:46:00 +01:00
} else {
2015-02-09 21:44:32 +01:00
buffer = & m_userBuffer [ 1 ] [ 0 ] ;
channels = m_nUserChannels [ 1 ] ;
format = m_userFormat ;
2014-03-11 21:46:00 +01:00
}
// Read samples from device in interleaved/non-interleaved format.
2015-02-09 21:44:32 +01:00
if ( m_deviceInterleaved [ 1 ] ) {
2015-06-07 22:32:54 +02:00
result = snd_pcm_mmap_readi ( m_private - > handle , buffer , m_bufferSize ) ;
2014-03-11 21:46:00 +01:00
} else {
void * bufs [ channels ] ;
2015-02-09 21:44:32 +01:00
size_t offset = m_bufferSize * audio : : getFormatBytes ( format ) ;
2014-03-11 21:46:00 +01:00
for ( int32_t i = 0 ; i < channels ; i + + )
bufs [ i ] = ( void * ) ( buffer + ( i * offset ) ) ;
2015-06-07 22:32:54 +02:00
result = snd_pcm_mmap_readn ( m_private - > handle , bufs , m_bufferSize ) ;
2014-03-11 21:46:00 +01:00
}
2015-03-12 22:30:43 +01:00
{
2015-06-07 22:32:54 +02:00
snd_pcm_state_t state = snd_pcm_state ( m_private - > handle ) ;
2015-06-05 22:00:17 +02:00
ATA_VERBOSE ( " plop: " < < state ) ;
2015-03-12 22:30:43 +01:00
if ( state = = SND_PCM_STATE_XRUN ) {
ATA_ERROR ( " Xrun... " ) ;
}
}
2015-02-19 22:49:33 +01:00
// get timestamp : (to init here ...
streamTime = getStreamTime ( ) ;
2015-02-09 21:44:32 +01:00
if ( result < ( int ) m_bufferSize ) {
2014-03-11 21:46:00 +01:00
// Either an error or overrun occured.
if ( result = = - EPIPE ) {
2015-06-07 22:32:54 +02:00
snd_pcm_state_t state = snd_pcm_state ( m_private - > handle ) ;
2014-03-11 21:46:00 +01:00
if ( state = = SND_PCM_STATE_XRUN ) {
2015-02-10 21:01:53 +01:00
m_private - > xrun [ 1 ] = true ;
2015-06-07 22:32:54 +02:00
result = snd_pcm_prepare ( m_private - > handle ) ;
2014-03-11 21:46:00 +01:00
if ( result < 0 ) {
2015-02-06 23:54:08 +01:00
ATA_ERROR ( " error preparing device after overrun, " < < snd_strerror ( result ) < < " . " ) ;
2014-03-11 21:46:00 +01:00
}
} else {
2015-02-06 23:54:08 +01:00
ATA_ERROR ( " error, current state is " < < snd_pcm_state_name ( state ) < < " , " < < snd_strerror ( result ) < < " . " ) ;
2014-03-11 21:46:00 +01:00
}
} else {
2015-02-06 23:54:08 +01:00
ATA_ERROR ( " audio read error, " < < snd_strerror ( result ) < < " . " ) ;
2015-03-04 23:52:53 +01:00
usleep ( 10000 ) ;
2014-03-11 21:46:00 +01:00
}
2015-04-10 22:06:17 +02:00
// TODO : Notify application ... audio::orchestra::error_warning;
2015-02-19 22:49:33 +01:00
goto noInput ;
2014-03-11 21:46:00 +01:00
}
// Do byte swapping if necessary.
2015-02-09 21:44:32 +01:00
if ( m_doByteSwap [ 1 ] ) {
byteSwapBuffer ( buffer , m_bufferSize * channels , format ) ;
2014-03-11 21:46:00 +01:00
}
// Do buffer conversion if necessary.
2015-02-09 21:44:32 +01:00
if ( m_doConvertBuffer [ 1 ] ) {
convertBuffer ( & m_userBuffer [ 1 ] [ 0 ] , m_deviceBuffer , m_convertInfo [ 1 ] ) ;
2014-03-11 21:46:00 +01:00
}
// Check stream latency
2015-06-07 22:32:54 +02:00
result = snd_pcm_delay ( m_private - > handle , & frames ) ;
2014-03-12 23:55:49 +01:00
if ( result = = 0 & & frames > 0 ) {
2015-03-04 21:31:10 +01:00
ATA_VERBOSE ( " Delay in the Input " < < frames < < " chunk " ) ;
2015-02-09 21:44:32 +01:00
m_latency [ 1 ] = frames ;
2014-03-12 23:55:49 +01:00
}
2014-03-11 21:46:00 +01:00
}
2015-02-19 22:49:33 +01:00
noInput :
streamTime = getStreamTime ( ) ;
2015-03-06 22:50:03 +01:00
{
2015-04-13 21:49:48 +02:00
audio : : Time startCall = audio : : Time : : now ( ) ;
2015-03-06 22:50:03 +01:00
doStopStream = m_callback ( & m_userBuffer [ 1 ] [ 0 ] ,
2015-04-13 21:49:48 +02:00
streamTime , // - audio::Duration(m_latency[1]*1000000000LL/int64_t(m_sampleRate)),
2015-06-07 22:32:54 +02:00
nullptr ,
audio : : Time ( ) ,
2015-03-06 22:50:03 +01:00
m_bufferSize ,
status ) ;
2015-04-13 21:49:48 +02:00
audio : : Time stopCall = audio : : Time : : now ( ) ;
audio : : Duration timeDelay ( 0 , m_bufferSize * 1000000000LL / int64_t ( m_sampleRate ) ) ;
audio : : Duration timeProcess = stopCall - startCall ;
2015-03-06 22:50:03 +01:00
if ( timeDelay < = timeProcess ) {
ATA_ERROR ( " SOFT XRUN ... : (bufferTime) " < < timeDelay . count ( ) < < " < " < < timeProcess . count ( ) < < " (process time) ns " ) ;
}
}
2015-02-19 22:49:33 +01:00
if ( doStopStream = = 2 ) {
abortStream ( ) ;
return ;
}
2014-03-11 21:46:00 +01:00
unlock :
2015-04-10 22:06:17 +02:00
audio : : orchestra : : Api : : tickStreamTime ( ) ;
2014-03-11 21:46:00 +01:00
if ( doStopStream = = 1 ) {
this - > stopStream ( ) ;
}
}
2015-06-05 22:00:17 +02:00
2015-04-10 22:06:17 +02:00
bool audio : : orchestra : : api : : Alsa : : isMasterOf ( audio : : orchestra : : Api * _api ) {
audio : : orchestra : : api : : Alsa * slave = dynamic_cast < audio : : orchestra : : api : : Alsa * > ( _api ) ;
2015-02-27 21:07:17 +01:00
if ( slave = = nullptr ) {
ATA_ERROR ( " NULL ptr API (not ALSA ...) " ) ;
return false ;
}
2015-04-10 22:06:17 +02:00
if ( m_state = = audio : : orchestra : : state_running ) {
2015-02-27 21:07:17 +01:00
ATA_ERROR ( " The MASTER stream is already running! ==> can not synchronize ... " ) ;
return false ;
}
2015-04-10 22:06:17 +02:00
if ( slave - > m_state = = audio : : orchestra : : state_running ) {
2015-02-27 21:07:17 +01:00
ATA_ERROR ( " The SLAVE stream is already running! ==> can not synchronize ... " ) ;
return false ;
}
snd_pcm_t * master = nullptr ;
2015-06-07 22:32:54 +02:00
if ( m_private - > handle ! = nullptr ) {
master = m_private - > handle ;
2015-02-27 21:07:17 +01:00
}
if ( master = = nullptr ) {
ATA_ERROR ( " No ALSA handles ... " ) ;
return false ;
}
ATA_INFO ( " ==> plop " ) ;
2015-06-07 22:32:54 +02:00
if ( snd_pcm_link ( master , slave - > m_private - > handle ) ! = 0 ) {
ATA_ERROR ( " Can not syncronize handle output " ) ;
} else {
ATA_INFO ( " -------------------- LINK 0 -------------------- " ) ;
2015-02-27 21:07:17 +01:00
}
return true ;
}
2014-03-11 21:46:00 +01:00
# endif