diff --git a/src/audio/Makefile.am b/src/audio/Makefile.am index 061c6c33db9f8991696968064b34193e30e588b4..22c0c99e45c3036718111ba701ec8294d6a50943 100644 --- a/src/audio/Makefile.am +++ b/src/audio/Makefile.am @@ -23,7 +23,7 @@ endif SUBDIRS = codecs libaudio_la_SOURCES = audiofile.cpp tonelist.cpp \ -audiortp.cpp dtmf.cpp tone.cpp audiolayer.cpp audiolayer-pulse.cpp audiodevice.cpp dtmfgenerator.cpp \ +audiortp.cpp dtmf.cpp tone.cpp alsalayer.cpp pulselayer.cpp audiodevice.cpp dtmfgenerator.cpp \ tonegenerator.cpp codecDescriptor.cpp \ audioloop.cpp ringbuffer.cpp $(SPEEX_SOURCES_CPP) @@ -31,7 +31,7 @@ AM_CXXFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs $(libccext2_CFLAGS) $ -DCODECS_DIR=\""$(sflcodecdir)"\" $(SPEEX_FLAG) $(GSM_FLAG) $(ILBC_FLAG) noinst_HEADERS = audioloop.h common.h ringbuffer.h audiofile.h \ - tonelist.h audiortp.h audiolayer.h audiolayer.h audiodevice.h \ + tonelist.h audiortp.h audiolayer.h alsalayer.h pulselayer.h audiodevice.h \ dtmfgenerator.h tonegenerator.h \ codecDescriptor.h dtmf.h tone.h diff --git a/src/audio/alsalayer.cpp b/src/audio/alsalayer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..44c11dcba46c0954841e1c33c5209aacc524ce5d --- /dev/null +++ b/src/audio/alsalayer.cpp @@ -0,0 +1,635 @@ +/* + * Copyright (C) 2008 Savoir-Faire Linux inc. + * Author: Yan Morin <yan.morin@savoirfairelinux.com> + * Author: Jerome Oufella <jerome.oufella@savoirfairelinux.com> + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "alsalayer.h" + + +// Constructor +AlsaLayer::AlsaLayer( ManagerImpl* manager ) + : AudioLayer( manager ) + , _PlaybackHandle(NULL) + , _CaptureHandle(NULL) +{ + _debug(" Constructor of AlsaLayer called\n"); + _defaultVolume = 100; +} + +// Destructor +AlsaLayer::~AlsaLayer (void) +{ + _debugAlsa("Close ALSA streams\n"); + closeCaptureStream(); + closePlaybackStream(); + deviceClosed = true; +} + + + bool +AlsaLayer::openDevice (int indexIn, int indexOut, int sampleRate, int frameSize, int stream , std::string plugin) +{ + + if(deviceClosed == false) + { + if( stream == SFL_PCM_CAPTURE ) + closeCaptureStream(); + else if( stream == SFL_PCM_PLAYBACK) + closePlaybackStream(); + else + { + closeCaptureStream(); + closePlaybackStream(); + } + } + + _indexIn = indexIn; + _indexOut = indexOut; + _sampleRate = sampleRate; + _frameSize = frameSize; + _audioPlugin = plugin; + + _debugAlsa(" Setting AlsaLayer: device in=%2d, out=%2d\n", _indexIn, _indexOut); + _debugAlsa(" : alsa plugin=%s\n", _audioPlugin.c_str()); + _debugAlsa(" : nb channel in=%2d, out=%2d\n", _inChannel, _outChannel); + _debugAlsa(" : sample rate=%5d, format=%s\n", _sampleRate, SFLDataFormatString); + + ost::MutexLock lock( _mutex ); + + std::string pcmp = buildDeviceTopo( plugin , indexOut , 0); + std::string pcmc = buildDeviceTopo( PCM_PLUGHW , indexIn , 0); + return open_device( pcmp , pcmc , stream); +} + + void +AlsaLayer::startStream(void) +{ + if( _CaptureHandle && _PlaybackHandle ) + { + _talk = true ; + _debugAlsa(" Start stream\n"); + int err; + //ost::MutexLock lock( _mutex ); + snd_pcm_prepare( _CaptureHandle ); + snd_pcm_start( _CaptureHandle ) ; + + snd_pcm_prepare( _PlaybackHandle ); + if( err = snd_pcm_start( _PlaybackHandle) < 0 ) _debugAlsa(" Cannot start (%s)\n", snd_strerror(err)); + } +} + + void +AlsaLayer::stopStream(void) +{ + if( _CaptureHandle && _PlaybackHandle ) + { + //ost::MutexLock lock( _mutex ); + _debugAlsa(" Stop Stream\n "); + _talk = false; + snd_pcm_drop( _CaptureHandle ); + snd_pcm_prepare( _CaptureHandle ); + snd_pcm_drop( _PlaybackHandle ); + snd_pcm_prepare( _PlaybackHandle ); + _urgentBuffer.flush(); + } +} + +void AlsaLayer::AlsaCallBack( snd_async_handler_t* pcm_callback ) +{ + ( ( AlsaLayer *)snd_async_handler_get_callback_private( pcm_callback )) -> playTones(); +} + + void +AlsaLayer::fillHWBuffer( void) +{ + unsigned char* data; + int pcmreturn, l1, l2; + short s1, s2; + int periodSize = 128 ; + int frames = periodSize >> 2 ; + _debug("frames = %d\n"); + + data = (unsigned char*)malloc(periodSize); + for(l1 = 0; l1 < 100; l1++) { + for(l2 = 0; l2 < frames; l2++) { + s1 = 0; + s2 = 0; + data[4*l2] = (unsigned char)s1; + data[4*l2+1] = s1 >> 8; + data[4*l2+2] = (unsigned char)s2; + data[4*l2+3] = s2 >> 8; + } + while ((pcmreturn = snd_pcm_writei(_PlaybackHandle, data, frames)) < 0) { + snd_pcm_prepare(_PlaybackHandle); + //_debugAlsa("< Buffer Underrun >\n"); + } + } +} + + bool +AlsaLayer::isStreamActive (void) +{ + ost::MutexLock lock( _mutex ); + return (isPlaybackActive() && isCaptureActive()); +} + + + int +AlsaLayer::playSamples(void* buffer, int toCopy, bool isTalking) +{ + //ost::MutexLock lock( _mutex ); + if( isTalking ) + _talk = true; + if ( _PlaybackHandle ){ + write( adjustVolume( buffer , toCopy , SFL_PCM_PLAYBACK ) , toCopy ); + } + return 0; +} + + int +AlsaLayer::putUrgent(void* buffer, int toCopy) +{ + if ( _PlaybackHandle ){ + fillHWBuffer(); + int a = _urgentBuffer.AvailForPut(); + if( a >= toCopy ){ + return _urgentBuffer.Put( buffer , toCopy , _defaultVolume ); + } else { + return _urgentBuffer.Put( buffer , a , _defaultVolume ) ; + } + } + return 0; +} + + int +AlsaLayer::canGetMic() +{ + int avail; + if ( _CaptureHandle ) { + avail = snd_pcm_avail_update( _CaptureHandle ); + //printf("%d\n", avail ); + if(avail > 0) + return avail; + else + return 0; + } + else + return 0; +} + + int +AlsaLayer::getMic(void *buffer, int toCopy) +{ + int res = 0 ; + if( _CaptureHandle ) + { + res = read( buffer, toCopy ); + adjustVolume( buffer , toCopy , SFL_PCM_CAPTURE ); + } + return res ; +} + + + bool +AlsaLayer::isStreamStopped (void) +{ + ost::MutexLock lock( _mutex ); + return !(isStreamActive()); +} + + +////////////////////////////////////////////////////////////////////////////////////////////// +///////////////// ALSA PRIVATE FUNCTIONS //////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// + + + + void +AlsaLayer::playTones( void ) +{ + int frames = _periodSize ; + int maxBytes = frames * sizeof(SFLDataFormat) ; + SFLDataFormat* out = (SFLDataFormat*)malloc(maxBytes * sizeof(SFLDataFormat)); + if( _talk ) {} + else { + AudioLoop *tone = _manager -> getTelephoneTone(); + int spkrVol = _manager -> getSpkrVolume(); + if( tone != 0 ){ + tone -> getNext( out , frames , spkrVol ); + write( out , maxBytes ); + } + else if( ( tone=_manager->getTelephoneFile() ) != 0 ){ + tone ->getNext( out , frames , spkrVol ); + write( out , maxBytes ); + } + } + // free the temporary data buffer + free( out ); out = 0; +} + +bool +AlsaLayer::isPlaybackActive(void) { + ost::MutexLock guard( _mutex ); + if( _PlaybackHandle ) + return (snd_pcm_state(_PlaybackHandle) == SND_PCM_STATE_RUNNING ? true : false); + else + return false; +} + +bool +AlsaLayer::isCaptureActive(void) { + ost::MutexLock guard( _mutex ); + if( _CaptureHandle ) + return (snd_pcm_state( _CaptureHandle) == SND_PCM_STATE_RUNNING ? true : false); + else + return false; +} + + + bool +AlsaLayer::open_device(std::string pcm_p, std::string pcm_c, int flag) +{ + std::stringstream errMsg; + int err; + snd_pcm_hw_params_t* hwParams = NULL; + snd_pcm_sw_params_t *swparams = NULL; + unsigned int rate_in = getSampleRate(); + unsigned int rate_out = getSampleRate(); + int dir = 0; + snd_pcm_uframes_t period_size = 2048; + snd_pcm_uframes_t buffer_size = period_size * 4 ; + snd_pcm_uframes_t threshold = 1024 ; + snd_pcm_uframes_t period_size_out = getFrameSize() * getSampleRate() / 1000 * 2;//1024 ; + snd_pcm_uframes_t buffer_size_out = period_size_out * 4 ; + + unsigned int buffer_time = 80000; //80ms + unsigned int period_time = buffer_time / 4 ; //20ms + + if(flag == SFL_PCM_BOTH || flag == SFL_PCM_CAPTURE) + { + _debugAlsa("Opening capture device %s\n", pcm_c.c_str()); + if(err = snd_pcm_open(&_CaptureHandle, pcm_c.c_str(), SND_PCM_STREAM_CAPTURE, 0) < 0){ + _debugAlsa("Error while opening capture device %s\n", pcm_c.c_str()); + setErrorMessage( ALSA_CAPTURE_DEVICE ); + return false; + } + + if( err = snd_pcm_hw_params_malloc( &hwParams ) < 0 ) { + _debugAlsa(" Cannot allocate hardware parameter structure (%s)\n", snd_strerror(err)); + return false; + } + if( err = snd_pcm_hw_params_any(_CaptureHandle, hwParams) < 0) _debugAlsa(" Cannot initialize hardware parameter structure (%s)\n", snd_strerror(err)); + if( err = snd_pcm_hw_params_set_access( _CaptureHandle, hwParams, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) _debugAlsa(" Cannot set access type (%s)\n", snd_strerror(err)); + if( err = snd_pcm_hw_params_set_format( _CaptureHandle, hwParams, SND_PCM_FORMAT_S16_LE) < 0) _debugAlsa(" Cannot set sample format (%s)\n", snd_strerror(err)); + if( err = snd_pcm_hw_params_set_rate_near( _CaptureHandle, hwParams, &rate_in, &dir) < 0) _debugAlsa(" Cannot set sample rate (%s)\n", snd_strerror(err)); + if( err = snd_pcm_hw_params_set_channels( _CaptureHandle, hwParams, 1) < 0) _debugAlsa(" Cannot set channel count (%s)\n", snd_strerror(err)); + if( err = snd_pcm_hw_params_set_period_time_near( _CaptureHandle, hwParams, &period_time , &dir) < 0) _debugAlsa(" Cannot set period time (%s)\n", snd_strerror(err)); + if( err = snd_pcm_hw_params_set_buffer_time_near( _CaptureHandle, hwParams, &buffer_time , &dir) < 0) _debugAlsa(" Cannot set buffer time (%s)\n", snd_strerror(err)); + if( err = snd_pcm_hw_params( _CaptureHandle, hwParams ) < 0) _debugAlsa(" Cannot set hw parameters (%s)\n", snd_strerror(err)); + snd_pcm_hw_params_free( hwParams ); + + snd_pcm_uframes_t val ; + snd_pcm_sw_params_malloc( &swparams ); + snd_pcm_sw_params_current( _CaptureHandle, swparams ); + + if( err = snd_pcm_sw_params_set_start_threshold( _CaptureHandle, swparams, period_size_out) < 0 ) _debugAlsa(" Cannot set start threshold (%s)\n", snd_strerror(err)); + snd_pcm_sw_params_get_start_threshold( swparams , &val); + _debug("Start threshold = %d\n" ,val); + if( err = snd_pcm_sw_params_set_avail_min( _CaptureHandle, swparams, period_size_out) < 0) _debugAlsa(" Cannot set min avail (%s)\n" , snd_strerror(err)); + snd_pcm_sw_params_get_avail_min( swparams , &val); + _debug("Min available = %d\n" ,val); + if( err = snd_pcm_sw_params( _CaptureHandle, swparams ) < 0 ) _debugAlsa(" Cannot set sw parameters (%s)\n", snd_strerror(err)); + snd_pcm_sw_params_free( swparams ); + deviceClosed = false; + } + + if(flag == SFL_PCM_BOTH || flag == SFL_PCM_PLAYBACK) + { + + _debugAlsa(" Opening playback device %s\n", pcm_p.c_str()); + if(err = snd_pcm_open(&_PlaybackHandle, pcm_p.c_str(), SND_PCM_STREAM_PLAYBACK, 0 ) < 0){ + _debugAlsa("Error while opening playback device %s\n", pcm_p.c_str()); + setErrorMessage( ALSA_PLAYBACK_DEVICE ); + return false; + } + if( err = snd_pcm_hw_params_malloc( &hwParams ) < 0 ) { + _debugAlsa(" Cannot allocate hardware parameter structure (%s)\n", snd_strerror(err)); + return false; + } + if( err = snd_pcm_hw_params_any( _PlaybackHandle,hwParams) < 0) _debugAlsa(" Cannot initialize hardware parameter structure (%s)\n", snd_strerror(err)); + if( err = snd_pcm_hw_params_set_access( _PlaybackHandle, hwParams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) _debugAlsa(" Cannot set access type (%s)\n", snd_strerror(err)); + if( err = snd_pcm_hw_params_set_format( _PlaybackHandle, hwParams, SND_PCM_FORMAT_S16_LE) < 0) _debugAlsa(" Cannot set sample format (%s)\n", snd_strerror(err)); + if( err = snd_pcm_hw_params_set_rate( _PlaybackHandle, hwParams, rate_out, dir) < 0) _debugAlsa(" Cannot set sample rate (%s)\n", snd_strerror(err)); + if( err = snd_pcm_hw_params_set_channels( _PlaybackHandle, hwParams, 1) < 0) _debugAlsa(" Cannot set channel count (%s)\n", snd_strerror(err)); + + if( err = snd_pcm_hw_params_set_buffer_size( _PlaybackHandle, hwParams, buffer_size ) < 0) _debugAlsa(" Cannot set buffer size (%s)\n", snd_strerror(err)); + if( err = snd_pcm_hw_params_set_period_size_near( _PlaybackHandle, hwParams, &period_size , &dir) < 0) _debugAlsa(" Cannot set period size (%s)\n", snd_strerror(err)); + + if( err = snd_pcm_hw_params( _PlaybackHandle, hwParams ) < 0) _debugAlsa(" Cannot set hw parameters (%s)\n", snd_strerror(err)); + + + snd_pcm_hw_params_free( hwParams ); + + snd_pcm_uframes_t val ; + snd_pcm_sw_params_malloc( &swparams ); + snd_pcm_sw_params_current( _PlaybackHandle, swparams ); + + if( err = snd_pcm_sw_params_set_start_threshold( _PlaybackHandle, swparams, period_size_out) < 0 ) _debugAlsa(" Cannot set start threshold (%s)\n", snd_strerror(err)); + snd_pcm_sw_params_get_start_threshold( swparams , &val); + _debug("Start threshold = %d\n" ,val); + //if( err = snd_pcm_sw_params_set_stop_threshold( _PlaybackHandle, swparams, buffer_size_out ) < 0 ) _debugAlsa(" Cannot set stop threshold (%s)\n", snd_strerror(err)); + //snd_pcm_sw_params_get_stop_threshold( swparams , &val); + //_debug("Stop threshold = %d\n" ,val); + if( err = snd_pcm_sw_params_set_avail_min( _PlaybackHandle, swparams, period_size_out) < 0) _debugAlsa(" Cannot set min avail (%s)\n" , snd_strerror(err)); + if( err = snd_pcm_sw_params_set_xfer_align( _PlaybackHandle, swparams, 1) < 0) _debugAlsa(" Cannot set xfer align (%s)\n" , snd_strerror(err)); + snd_pcm_sw_params_get_avail_min( swparams , &val); + _debug("Min available = %d\n" ,val); + //if( err = snd_pcm_sw_params_set_silence_threshold( _PlaybackHandle, swparams, period_size_out) < 0) _debugAlsa(" Cannot set silence threshold (%s)\n" , snd_strerror(err)); + //snd_pcm_sw_params_get_silence_threshold( swparams , &val); + // _debug("Silence threshold = %d\n" ,val); + if( err = snd_pcm_sw_params( _PlaybackHandle, swparams ) < 0 ) _debugAlsa(" Cannot set sw parameters (%s)\n", snd_strerror(err)); + snd_pcm_sw_params_free( swparams ); + + //if ( err = snd_async_add_pcm_handler( &_AsyncHandler, _PlaybackHandle , AlsaCallBack, this ) < 0) _debugAlsa(" Unable to install the async callback handler (%s)\n", snd_strerror(err)); + deviceClosed = false; + } + //fillHWBuffer(); + + _talk = false; + return true; +} + +//TODO first frame causes broken pipe (underrun) because not enough data are send --> make the handle wait to be ready + int +AlsaLayer::write(void* buffer, int length) +{ + snd_pcm_uframes_t frames = snd_pcm_bytes_to_frames( _PlaybackHandle, length); + //int err = snd_pcm_mmap_writei( _PlaybackHandle , buffer , frames ); + int err = snd_pcm_writei( _PlaybackHandle , buffer , frames ); + switch(err) { + case -EAGAIN: + _debugAlsa("EAGAIN (%s)\n", snd_strerror( err )); + //snd_pcm_resume( _PlaybackHandle ); + break; + case -EPIPE: + _debugAlsa(" UNDERRUN (%s)\n", snd_strerror(err)); + handle_xrun_playback(); + //snd_pcm_mmap_writei( _PlaybackHandle , buffer , frames ); + //snd_pcm_writei( _PlaybackHandle , buffer , frames ); + break; + case -ESTRPIPE: + _debugAlsa("ESTRPIPE (%s)\n", snd_strerror(err)); + //snd_pcm_resume( _PlaybackHandle ); + break; + case -EBADFD: + _debugAlsa("EBADFD (%s)\n", snd_strerror( err )); + break; + } + + if( err >=0 && err < frames ) + _debugAlsa("Short write : %d out of %d\n", err , frames); + + return ( err > 0 )? err : 0 ; +} + + int +AlsaLayer::read( void* buffer, int toCopy) +{ + if(deviceClosed || _CaptureHandle == NULL) + return 0; + int err; + if(snd_pcm_state( _CaptureHandle ) == SND_PCM_STATE_XRUN) + { + snd_pcm_prepare( _CaptureHandle ); + snd_pcm_start( _CaptureHandle ); + } + snd_pcm_uframes_t frames = snd_pcm_bytes_to_frames( _CaptureHandle, toCopy ); + if( err = snd_pcm_mmap_readi( _CaptureHandle, buffer, frames) < 0 ) { + switch(err){ + case EPERM: + _debugAlsa(" Capture EPERM (%s)\n", snd_strerror(err)); + snd_pcm_prepare( _CaptureHandle); + snd_pcm_start( _CaptureHandle ); + break; + case -ESTRPIPE: + _debugAlsa(" Capture ESTRPIPE (%s)\n", snd_strerror(err)); + snd_pcm_resume( _CaptureHandle); + break; + case -EAGAIN: + _debugAlsa(" Capture EAGAIN (%s)\n", snd_strerror(err)); + break; + case -EBADFD: + _debugAlsa(" Capture EBADFD (%s)\n", snd_strerror(err)); + break; + case -EPIPE: + _debugAlsa(" Capture EPIPE (%s)\n", snd_strerror(err)); + handle_xrun_capture(); + break; + } + return 0; + } + + return toCopy; + +} + + void +AlsaLayer::handle_xrun_capture( void ) +{ + snd_pcm_status_t* status; + snd_pcm_status_alloca( &status ); + + int res = snd_pcm_status( _CaptureHandle, status ); + if( res <= 0){ + if(snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN ){ + snd_pcm_drop( _CaptureHandle ); + snd_pcm_prepare( _CaptureHandle ); + snd_pcm_start( _CaptureHandle ); + } + } + else + _debugAlsa(" Get status failed\n"); +} + + void +AlsaLayer::handle_xrun_playback( void ) +{ + int state; + snd_pcm_status_t* status; + snd_pcm_status_alloca( &status ); + + if( state = snd_pcm_status( _PlaybackHandle, status ) < 0 ) _debugAlsa(" Error: Cannot get playback handle status (%s)\n" , snd_strerror( state ) ); + else + { + state = snd_pcm_status_get_state( status ); + if( state == SND_PCM_STATE_XRUN ) + { + //snd_pcm_drop( _PlaybackHandle ); + _debug("Underrun\n"); + snd_pcm_prepare( _PlaybackHandle ); + //snd_pcm_start( _PlaybackHandle ); + } + } +} + + std::string +AlsaLayer::buildDeviceTopo( std::string plugin, int card, int subdevice ) +{ + std::string pcm = plugin; + std::stringstream ss,ss1; + if( pcm == "default" || pcm == "pulse") + return pcm; + ss << card; + pcm.append(":"); + pcm.append(ss.str()); + if( subdevice != 0 ){ + pcm.append(","); + ss1 << subdevice; + pcm.append(ss1.str()); + } + return pcm; +} + + std::vector<std::string> +AlsaLayer::getSoundCardsInfo( int stream ) +{ + std::vector<std::string> cards_id; + HwIDPair p; + + snd_ctl_t* handle; + snd_ctl_card_info_t *info; + snd_pcm_info_t* pcminfo; + snd_ctl_card_info_alloca( &info ); + snd_pcm_info_alloca( &pcminfo ); + + int numCard = -1 ; + int err; + std::string description; + + if(snd_card_next( &numCard ) < 0 || numCard < 0) + return cards_id; + + while(numCard >= 0){ + std::stringstream ss; + ss << numCard; + std::string name= "hw:"; + name.append(ss.str()); + + if( snd_ctl_open( &handle, name.c_str(), 0) == 0 ){ + if( snd_ctl_card_info( handle, info) == 0){ + snd_pcm_info_set_device( pcminfo , 0); + snd_pcm_info_set_stream( pcminfo, ( stream == SFL_PCM_CAPTURE )? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK ); + if( snd_ctl_pcm_info ( handle ,pcminfo ) < 0) _debugAlsa(" Cannot get info\n"); + else{ + _debugAlsa("card %i : %s [%s]\n", + numCard, + snd_ctl_card_info_get_id(info), + snd_ctl_card_info_get_name( info )); + description = snd_ctl_card_info_get_name( info ); + description.append(" - "); + description.append(snd_pcm_info_get_name( pcminfo )); + cards_id.push_back( description ); + // The number of the sound card is associated with a string description + p = HwIDPair( numCard , description ); + IDSoundCards.push_back( p ); + } + } + snd_ctl_close( handle ); + } + if ( snd_card_next( &numCard ) < 0 ) { + break; + } + } + return cards_id; +} + + void +AlsaLayer::closeCaptureStream( void) +{ + if(_CaptureHandle){ + snd_pcm_drop( _CaptureHandle ); + snd_pcm_close( _CaptureHandle ); + _CaptureHandle = 0; + } +} + + void +AlsaLayer::closePlaybackStream( void) +{ + if(_PlaybackHandle){ + snd_pcm_drop( _PlaybackHandle ); + snd_pcm_close( _PlaybackHandle ); + _PlaybackHandle = 0; + } +} + + bool +AlsaLayer::soundCardIndexExist( int card , int stream ) +{ + snd_ctl_t* handle; + snd_pcm_info_t *pcminfo; + snd_pcm_info_alloca( &pcminfo ); + std::string name = "hw:"; + std::stringstream ss; + ss << card ; + name.append(ss.str()); + if(snd_ctl_open( &handle, name.c_str(), 0) == 0 ){ + snd_pcm_info_set_stream( pcminfo , ( stream == SFL_PCM_PLAYBACK )? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE ); + if( snd_ctl_pcm_info( handle , pcminfo ) < 0) return false; + else + return true; + } + else + return false; +} + + int +AlsaLayer::soundCardGetIndex( std::string description ) +{ + int i; + for( i = 0 ; i < IDSoundCards.size() ; i++ ) + { + HwIDPair p = IDSoundCards[i]; + if( p.second == description ) + return p.first ; + } + // else return the default one + return 0; +} + + void* +AlsaLayer::adjustVolume( void* buffer , int len, int stream ) +{ + int vol; + if( stream == SFL_PCM_PLAYBACK ) + vol = _manager->getSpkrVolume(); + else + vol = _manager->getMicVolume(); + + SFLDataFormat* src = (SFLDataFormat*) buffer; + if( vol != 100 ) + { + int size = len / sizeof(SFLDataFormat); + int i; + for( i = 0 ; i < size ; i++ ){ + src[i] = src[i] * vol / 100 ; + } + } + return src ; +} diff --git a/src/audio/alsalayer.h b/src/audio/alsalayer.h new file mode 100644 index 0000000000000000000000000000000000000000..05f5ebd79470ee1b542b8f1060aa6de886e683c2 --- /dev/null +++ b/src/audio/alsalayer.h @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2007 - 2008 Savoir-Faire Linux inc. + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _ALSA_LAYER_H +#define _ALSA_LAYER_H + +#include "audiolayer.h" + +class RingBuffer; +class ManagerImpl; + +/** Associate a sound card index to its string description */ +typedef std::pair<int , std::string> HwIDPair; + +/** + * @file AlsaLayer.h + * @brief Main sound class. Manages the data transfers between the application and the hardware. + */ + +class AlsaLayer : public AudioLayer { + public: + /** + * Constructor + * @param manager An instance of managerimpl + */ + AlsaLayer( ManagerImpl* manager ); + + /** + * Destructor + */ + ~AlsaLayer(void); + + /** + * Check if no devices are opened, otherwise close them. + * Then open the specified devices by calling the private functions open_device + * @param indexIn The number of the card choosen for capture + * @param indexOut The number of the card choosen for playback + * @param sampleRate The sample rate + * @param frameSize The frame size + * @param stream To indicate which kind of stream you want to open + * SFL_PCM_CAPTURE + * SFL_PCM_PLAYBACK + * SFL_PCM_BOTH + * @param plugin The alsa plugin ( dmix , default , front , surround , ...) + */ + bool openDevice(int indexIn, int indexOut, int sampleRate, int frameSize, int stream, std::string plugin); + + /** + * Start the capture stream and prepare the playback stream. + * The playback starts accordingly to its threshold + * ALSA Library API + */ + void startStream(void); + + /** + * Stop the playback and capture streams. + * Drops the pending frames and put the capture and playback handles to PREPARED state + * ALSA Library API + */ + void stopStream(void); + + /** + * Check if the playback is running + * @return true if the state of the playback handle equals SND_PCM_STATE_RUNNING + * false otherwise + */ + bool isPlaybackActive( void ); + + /** + * Check if the capture is running + * @return true if the state of the capture handle equals SND_PCM_STATE_RUNNING + * false otherwise + */ + bool isCaptureActive( void ); + + /** + * Check if both capture and playback are running + * @return true if capture and playback are running + * false otherwise + */ + bool isStreamActive(void); + + /** + * Check if both capture and playback are stopped + * @return true if capture and playback are stopped + * false otherwise + */ + bool isStreamStopped(void); + + /** + * Send samples to the audio device. + * @param buffer The buffer containing the data to be played ( voice and DTMF ) + * @param toCopy The number of samples, in bytes + * @param isTalking If whether or not the conversation is running + * @return int The number of bytes played + */ + int playSamples(void* buffer, int toCopy, bool isTalking); + + /** + * Send a chunk of data to the hardware buffer to start the playback + * Copy data in the urgent buffer. + * @param buffer The buffer containing the data to be played ( ringtones ) + * @param toCopy The size of the buffer + * @return int The number of bytes copied in the urgent buffer + */ + int putUrgent(void* buffer, int toCopy); + + /** + * Query the capture device for number of bytes available in the hardware ring buffer + * @return int The number of bytes available + */ + int canGetMic(); + + /** + * Get data from the capture device + * @param buffer The buffer for data + * @param toCopy The number of bytes to get + * @return int The number of bytes acquired ( 0 if an error occured) + */ + int getMic(void * buffer, int toCopy); + + /** + * Concatenate two strings. Used to build a valid pcm device name. + * @param plugin the alsa PCM name + * @param card the sound card number + * @param subdevice the subdevice number + * @return std::string the concatenated string + */ + std::string buildDeviceTopo( std::string plugin, int card, int subdevice ); + + /** + * Scan the sound card available on the system + * @param stream To indicate whether we are looking for capture devices or playback devices + * SFL_PCM_CAPTURE + * SFL_PCM_PLAYBACK + * SFL_PCM_BOTH + * @return std::vector<std::string> The vector containing the string description of the card + */ + std::vector<std::string> getSoundCardsInfo( int stream ); + + /** + * Check if the given index corresponds to an existing sound card and supports the specified streaming mode + * @param card An index + * @param stream The stream mode + * SFL_PCM_CAPTURE + * SFL_PCM_PLAYBACK + * SFL_PCM_BOTH + * @return bool True if it exists and supports the mode + * false otherwise + */ + bool soundCardIndexExist( int card , int stream ); + + /** + * An index is associated with its string description + * @param description The string description + * @return int Its index + */ + int soundCardGetIndex( std::string description ); + + /** + * Get the current audio plugin. + * @return std::string The name of the audio plugin + */ + std::string getAudioPlugin( void ) { return _audioPlugin; } + + private: + + /** + * Drop the pending frames and close the capture device + * ALSA Library API + */ + void closeCaptureStream( void ); + + /** + * Drop the pending frames and close the playback device + * ALSA Library API + */ + void closePlaybackStream( void ); + + /** + * Fill the alsa internal ring buffer with chunks of data + */ + void fillHWBuffer( void) ; + + /** + * Callback used for asynchronous playback. + * Called when a certain amount of data is written ot the device + * @param pcm_callback The callback pointer + */ + static void AlsaCallBack( snd_async_handler_t* pcm_callback); + + /** + * Callback used for asynchronous playback. + * Write tones buffer to the alsa internal ring buffer. + */ + void playTones( void ); + + /** + * Open the specified device. + * ALSA Library API + * @param pcm_p The string name for the playback device + * @param pcm_c The string name for the capture device + * @param flag To indicate which kind of stream you want to open + * SFL_PCM_CAPTURE + * SFL_PCM_PLAYBACK + * SFL_PCM_BOTH + * @return true if successful + * false otherwise + */ + bool open_device( std::string pcm_p, std::string pcm_c, int flag); + + /** + * Copy a data buffer in the internal ring buffer + * ALSA Library API + * @param buffer The data to be copied + * @param length The size of the buffer + * @return int The number of frames actually copied + */ + int write( void* buffer, int length); + + /** + * Read data from the internal ring buffer + * ALSA Library API + * @param buffer The buffer to stock the read data + * @param toCopy The number of bytes to get + * @return int The number of frames actually read + */ + int read( void* buffer, int toCopy); + + /** + * Recover from XRUN state for capture + * ALSA Library API + */ + void handle_xrun_capture( void ); + + /** + * Recover from XRUN state for playback + * ALSA Library API + */ + void handle_xrun_playback( void ); + + /** + * Handles to manipulate playback stream + * ALSA Library API + */ + snd_pcm_t* _PlaybackHandle; + + /** + * Handles to manipulate capture stream + * ALSA Library API + */ + snd_pcm_t* _CaptureHandle; + + /** + * Alsa parameter - Size of a period in the hardware ring buffer + */ + snd_pcm_uframes_t _periodSize; + + /** + * Handle on asynchronous event + */ + snd_async_handler_t *_AsyncHandler; + + /** + * Volume is controlled by the application. Data buffer are modified here to adjust to the right volume selected by the user on the main interface + * @param buffer The buffer to adjust + * @param len The number of bytes + * @param stream The stream mode ( PLAYBACK - CAPTURE ) + */ + void * adjustVolume( void * , int , int); + + /** + * name of the alsa audio plugin used + */ + std::string _audioPlugin; + + /** + * Input channel (mic) should be 1 mono + */ + unsigned int _inChannel; + + /** + * Output channel (stereo) should be 1 mono + */ + unsigned int _outChannel; + + /** + * Default volume for incoming RTP and Urgent sounds. + */ + unsigned short _defaultVolume; // 100 + + + /** Vector to manage all soundcard index - description association of the system */ + std::vector<HwIDPair> IDSoundCards; + +}; + +#endif // _ALSA_LAYER_H_ diff --git a/src/audio/audiolayer-pulse.h b/src/audio/audiolayer-pulse.h deleted file mode 100644 index e58f3d01dfbc679a02ada63a280933cf5e595ad5..0000000000000000000000000000000000000000 --- a/src/audio/audiolayer-pulse.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2008 Savoir-Faire Linux inc. - * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef _AUDIO_LAYER_PULSE_H -#define _AUDIO_LAYER_PULSE_H - -#include <cc++/thread.h> // for ost::Mutex - - -#include "../global.h" -#include "ringbuffer.h" -#include "audiodevice.h" - -#define FRAME_PER_BUFFER 160 - -class RingBuffer; -class ManagerImpl; - -class PulseLayer { - public: - PulseLayer(ManagerImpl* manager); - ~PulseLayer(void); - - /* - * @param indexIn - * @param indexOut - * @param sampleRate - * @param frameSize - */ - void openDevice(int, int, int, int); - void startStream(void); - void stopStream(void); - void sleep(int); - bool hasStream(void); - bool isStreamActive(void); - bool isStreamStopped(void); - - void flushMain(); - int putMain(void* buffer, int toCopy); - int putUrgent(void* buffer, int toCopy); - int canGetMic(); - int getMic(void *, int); - void flushMic(); - - int audioCallback (); - - - - void setErrorMessage(const std::string& error) { _errorMessage = error; } - std::string getErrorMessage() { return _errorMessage; } - - /** - * Get the sample rate of PulseLayer - * accessor only - */ - unsigned int getSampleRate() { return _sampleRate; } - unsigned int getFrameSize() { return _frameSize; } - - /** - * Toggle echo testing on/off - */ - void toggleEchoTesting(); - - private: - void closeStream (void); - RingBuffer _urgentRingBuffer; - RingBuffer _mainSndRingBuffer; - RingBuffer _micRingBuffer; - ManagerImpl* _manager; // augment coupling, reduce indirect access - // a PulseLayer can't live without manager - - pa_stream* playback; - pa_stream* record; - - /** - * Sample Rate SFLphone should send sound data to the sound card - * The value can be set in the user config file- now: 44100HZ - */ - unsigned int _sampleRate; - - /** - * Length of the sound frame we capture or read in ms - * The value can be set in the user config file - now: 20ms - */ - unsigned int _frameSize; - - /** - * Input channel (mic) should be 1 mono - */ - unsigned int _inChannel; // mic - - /** - * Output channel (stereo) should be 1 mono - */ - unsigned int _outChannel; // speaker - - /** - * Default volume for incoming RTP and Urgent sounds. - */ - //unsigned short _defaultVolume; // 100 - pa_volume_t _defaultVolume; - - /** - * Echo testing or not - */ - bool _echoTesting; - - std::string _errorMessage; - ost::Mutex _mutex; - - float *table_; - int tableSize_; - int leftPhase_; -}; - -#endif // _AUDIO_LAYER_PULSE_H_ - diff --git a/src/audio/audiolayer.h b/src/audio/audiolayer.h index f303167348215b7cf36ec4608351739e997891f1..3a0f549936a46dc67e468588ac9e35bcf6c43330 100644 --- a/src/audio/audiolayer.h +++ b/src/audio/audiolayer.h @@ -23,22 +23,22 @@ #define _AUDIO_LAYER_H #include "../global.h" +#include "../manager.h" #include "audiodevice.h" #include "ringbuffer.h" #include <cc++/thread.h> // for ost::Mutex #include <vector> + #include <alsa/asoundlib.h> +#include <pulse/pulseaudio.h> + #include <iostream> #include <istream> #include <sstream> -#define FRAME_PER_BUFFER 160 -class RingBuffer; -class ManagerImpl; +#define FRAME_PER_BUFFER 160 -/** Associate a sound card index to its string description */ -typedef std::pair<int , std::string> HwIDPair; /** * @file audiolayer.h @@ -51,12 +51,21 @@ class AudioLayer { * Constructor * @param manager An instance of managerimpl */ - AudioLayer(ManagerImpl* manager); + AudioLayer( ManagerImpl* manager ) + : _manager(manager) + , _urgentBuffer( SIZEBUF ) + { + _inChannel = 1; // don't put in stereo + _outChannel = 1; // don't put in stereo + + deviceClosed = true; + + } /** * Destructor */ - ~AudioLayer(void); + ~AudioLayer(void){} /** * Check if no devices are opened, otherwise close them. @@ -71,49 +80,35 @@ class AudioLayer { * SFL_PCM_BOTH * @param plugin The alsa plugin ( dmix , default , front , surround , ...) */ - bool openDevice(int indexIn, int indexOut, int sampleRate, int frameSize, int stream, std::string plugin); + virtual bool openDevice(int indexIn, int indexOut, int sampleRate, int frameSize, int stream, std::string plugin) = 0; /** * Start the capture stream and prepare the playback stream. * The playback starts accordingly to its threshold * ALSA Library API */ - void startStream(void); + virtual void startStream(void) = 0; /** * Stop the playback and capture streams. * Drops the pending frames and put the capture and playback handles to PREPARED state * ALSA Library API */ - void stopStream(void); + virtual void stopStream(void) = 0; - /** - * Check if the playback is running - * @return true if the state of the playback handle equals SND_PCM_STATE_RUNNING - * false otherwise - */ - bool isPlaybackActive( void ); - - /** - * Check if the capture is running - * @return true if the state of the capture handle equals SND_PCM_STATE_RUNNING - * false otherwise - */ - bool isCaptureActive( void ); - /** * Check if both capture and playback are running * @return true if capture and playback are running * false otherwise */ - bool isStreamActive(void); + virtual bool isStreamActive(void) = 0; /** - * Check if both capture and playback are stopped - * @return true if capture and playback are stopped + * Check if the capture is running + * @return true if the state of the capture handle equals SND_PCM_STATE_RUNNING * false otherwise */ - bool isStreamStopped(void); + virtual bool isCaptureActive( void ) = 0; /** * Send samples to the audio device. @@ -122,7 +117,7 @@ class AudioLayer { * @param isTalking If whether or not the conversation is running * @return int The number of bytes played */ - int playSamples(void* buffer, int toCopy, bool isTalking); + virtual int playSamples(void* buffer, int toCopy, bool isTalking) = 0; /** * Send a chunk of data to the hardware buffer to start the playback @@ -131,13 +126,13 @@ class AudioLayer { * @param toCopy The size of the buffer * @return int The number of bytes copied in the urgent buffer */ - int putUrgent(void* buffer, int toCopy); + virtual int putUrgent(void* buffer, int toCopy) = 0; /** * Query the capture device for number of bytes available in the hardware ring buffer * @return int The number of bytes available */ - int canGetMic(); + virtual int canGetMic() = 0; /** * Get data from the capture device @@ -145,17 +140,8 @@ class AudioLayer { * @param toCopy The number of bytes to get * @return int The number of bytes acquired ( 0 if an error occured) */ - int getMic(void * buffer, int toCopy); + virtual int getMic(void * buffer, int toCopy) = 0; - /** - * Concatenate two strings. Used to build a valid pcm device name. - * @param plugin the alsa PCM name - * @param card the sound card number - * @param subdevice the subdevice number - * @return std::string the concatenated string - */ - std::string buildDeviceTopo( std::string plugin, int card, int subdevice ); - /** * Scan the sound card available on the system * @param stream To indicate whether we are looking for capture devices or playback devices @@ -164,7 +150,7 @@ class AudioLayer { * SFL_PCM_BOTH * @return std::vector<std::string> The vector containing the string description of the card */ - std::vector<std::string> getSoundCardsInfo( int stream ); + virtual std::vector<std::string> getSoundCardsInfo( int stream ) = 0; /** * Check if the given index corresponds to an existing sound card and supports the specified streaming mode @@ -176,14 +162,20 @@ class AudioLayer { * @return bool True if it exists and supports the mode * false otherwise */ - bool soundCardIndexExist( int card , int stream ); + virtual bool soundCardIndexExist( int card , int stream ) = 0; /** * An index is associated with its string description * @param description The string description * @return int Its index */ - int soundCardGetIndex( std::string description ); + virtual int soundCardGetIndex( std::string description ) = 0; + + /** + * Get the current audio plugin. + * @return std::string The name of the audio plugin + */ + virtual std::string getAudioPlugin( void ) = 0; /** * Write accessor to the error state @@ -227,11 +219,6 @@ class AudioLayer { */ unsigned int getFrameSize() { return _frameSize; } - /** - * Get the current audio plugin. - * @return std::string The name of the audio plugin - */ - std::string getAudioPlugin( void ) { return _audioPlugin; } /** * Get the current state. Conversation or not * @return bool true if playSamples has been called @@ -239,124 +226,25 @@ class AudioLayer { */ bool getCurrentState( void ) { return _talk; } - /** - * Toggle echo testing on/off - */ - void toggleEchoTesting(); - - private: + protected: /** * Drop the pending frames and close the capture device - * ALSA Library API */ - void closeCaptureStream( void ); + virtual void closeCaptureStream( void ) = 0; /** * Drop the pending frames and close the playback device - * ALSA Library API - */ - void closePlaybackStream( void ); - - /** - * Fill the alsa internal ring buffer with chunks of data - */ - void fillHWBuffer( void) ; - - /** - * Callback used for asynchronous playback. - * Called when a certain amount of data is written ot the device - * @param pcm_callback The callback pointer - */ - static void AlsaCallBack( snd_async_handler_t* pcm_callback); - - /** - * Callback used for asynchronous playback. - * Write tones buffer to the alsa internal ring buffer. - */ - void playTones( void ); - - /** - * Open the specified device. - * ALSA Library API - * @param pcm_p The string name for the playback device - * @param pcm_c The string name for the capture device - * @param flag To indicate which kind of stream you want to open - * SFL_PCM_CAPTURE - * SFL_PCM_PLAYBACK - * SFL_PCM_BOTH - * @return true if successful - * false otherwise */ - bool open_device( std::string pcm_p, std::string pcm_c, int flag); + virtual void closePlaybackStream( void ) = 0; - /** - * Copy a data buffer in the internal ring buffer - * ALSA Library API - * @param buffer The data to be copied - * @param length The size of the buffer - * @return int The number of frames actually copied - */ - int write( void* buffer, int length); - - /** - * Read data from the internal ring buffer - * ALSA Library API - * @param buffer The buffer to stock the read data - * @param toCopy The number of bytes to get - * @return int The number of frames actually read - */ - int read( void* buffer, int toCopy); - - /** - * Recover from XRUN state for capture - * ALSA Library API - */ - void handle_xrun_capture( void ); - - /** - * Recover from XRUN state for playback - * ALSA Library API - */ - void handle_xrun_playback( void ); - /** Augment coupling, reduce indirect access */ ManagerImpl* _manager; - /** - * Handles to manipulate playback stream - * ALSA Library API - */ - snd_pcm_t* _PlaybackHandle; - - /** - * Handles to manipulate capture stream - * ALSA Library API - */ - snd_pcm_t* _CaptureHandle; - - /** - * Alsa parameter - Size of a period in the hardware ring buffer - */ - snd_pcm_uframes_t _periodSize; - - /** - * Handle on asynchronous event - */ - snd_async_handler_t *_AsyncHandler; - /** * Urgent ring buffer used for ringtones */ RingBuffer _urgentBuffer; - - /** - * Volume is controlled by the application. Data buffer are modified here to adjust to the right volume selected by the user on the main interface - * @param buffer The buffer to adjust - * @param len The number of bytes - * @param stream The stream mode ( PLAYBACK - CAPTURE ) - */ - void * adjustVolume( void * , int , int); /** * Determine if both endpoints hang up. @@ -394,11 +282,6 @@ class AudioLayer { */ unsigned int _frameSize; - /** - * name of the alsa audio plugin used - */ - std::string _audioPlugin; - /** * Input channel (mic) should be 1 mono */ @@ -409,20 +292,7 @@ class AudioLayer { */ unsigned int _outChannel; - /** - * Default volume for incoming RTP and Urgent sounds. - */ - unsigned short _defaultVolume; // 100 - - /** - * Echo testing or not - */ - bool _echoTesting; - - /** Vector to manage all soundcard index - description association of the system */ - std::vector<HwIDPair> IDSoundCards; - - /** Contains the current error code */ + /** Contains the current error code */ int _errorMessage; ost::Mutex _mutex; diff --git a/src/audio/audiolayer-pulse.cpp b/src/audio/pulselayer.cpp similarity index 76% rename from src/audio/audiolayer-pulse.cpp rename to src/audio/pulselayer.cpp index 88e0a8cb6245d2a756c05ce916503e5aca9b2aea..9755ced1e3b8e247b3030444250570962d44d4e2 100644 --- a/src/audio/audiolayer-pulse.cpp +++ b/src/audio/pulselayer.cpp @@ -1,7 +1,5 @@ /* - * Copyright (C) 2005 Savoir-Faire Linux inc. - * Author: Yan Morin <yan.morin@savoirfairelinux.com> - * Author: Jerome Oufella <jerome.oufella@savoirfairelinux.com> + * Copyright (C) 2008 Savoir-Faire Linux inc. * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify @@ -19,14 +17,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <stdio.h> -#include <stdlib.h> -#include <pulse/pulseaudio.h> - -#include "audiolayer-pulse.h" -#include "../global.h" -#include "../manager.h" +#include "pulselayer.h" static pa_context *context = NULL; static pa_mainloop_api *mainloop_api = NULL; @@ -37,21 +29,11 @@ static std::string stream_p = NULL; static std::string stream_r = NULL; - PulseLayer::PulseLayer(ManagerImpl* manager) - : _urgentRingBuffer(SIZEBUF) - , _mainSndRingBuffer(SIZEBUF) - , _micRingBuffer(SIZEBUF) - , _defaultVolume(PA_VOLUME_NORM) - , playback(NULL) - , record(NULL) - , _errorMessage("") - , _manager(manager) +PulseLayer::PulseLayer(ManagerImpl* manager) + : AudioLayer( manager ) { - _sampleRate = 8000; - - _inChannel = 1; // don't put in stereo - _outChannel = 1; // don't put in stereo - _echoTesting = false; + playback = NULL; + record = NULL; } // Destructor @@ -63,6 +45,12 @@ PulseLayer::~PulseLayer (void) // pa_stream_disconnect(); } +void +PulseLayer::stream_state_callback( pa_stream* s, void* user_data ) +{ + _debug("The state of the stream changed\n"); +} + void PulseLayer::openDevice (int indexIn, int indexOut, int sampleRate, int frameSize) @@ -74,7 +62,7 @@ PulseLayer::openDevice (int indexIn, int indexOut, int sampleRate, int frameSize sample_spec.format = PA_SAMPLE_S16LE; sample_spec.channels = 1; channel_map.channels = 1; - + pa_stream_flags_t flag = PA_STREAM_START_CORKED ; _debug(" Setting PulseLayer: device in=%2d, out=%2d\n", indexIn, indexOut); _debug(" : nb channel in=%2d, out=%2d\n", _inChannel, _outChannel); @@ -101,24 +89,19 @@ PulseLayer::openDevice (int indexIn, int indexOut, int sampleRate, int frameSize pa_stream_set_state_callback(playback, stream_state_callback, NULL); // Transferring Data - Asynchronous Mode pa_stream_set_write_callback(playback, audioCallback, NULL); - pa_stream_connect_playback( playback, NULL , NULL , 0 , NULL, NULL ); + pa_stream_connect_playback( playback, NULL , NULL , flag , NULL, NULL ); break; case PA_CONTEXT_TERMINATED: - quit(0); + _debug("Context terminated\n"); break; case PA_CONTEXT_FAILED: default: - _debug(" Error : %s" , pa_strerror(pa_context_errno(c))); - quit(1); + _debug(" Error : %s" , pa_strerror(pa_context_errno(context))); + exit(1); } } -PulseLayer::stream_state_callback( void ) -{ - _debug("The state of the stream changed\n"); -} - int PulseLayer::putMain(void* buffer, int toCopy) { @@ -154,12 +137,9 @@ PulseLayer::isStreamStopped (void) { } -void -PulseLayer::toggleEchoTesting() { -} - int -PulseLayer::audioCallback () + void +PulseLayer::audioCallback ( pa_stream* s, size_t bytes, void* user_data ) { _debug("Audio callback: New data may be written to the stream\n"); // pa_stream_write diff --git a/src/audio/pulselayer.h b/src/audio/pulselayer.h new file mode 100644 index 0000000000000000000000000000000000000000..9fc0d6a3d5cdb5756f2da029293d17142c224d31 --- /dev/null +++ b/src/audio/pulselayer.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2008 Savoir-Faire Linux inc. + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _PULSE_LAYER_H +#define _PULSE_LAYER_H + +#include "audiolayer.h" + +class RingBuffer; +class ManagerImpl; + +class PulseLayer : public AudioLayer { + public: + PulseLayer(ManagerImpl* manager); + ~PulseLayer(void); + + /* + * @param indexIn + * @param indexOut + * @param sampleRate + * @param frameSize + */ + void openDevice(int, int, int, int); + void startStream(void); + void stopStream(void); + bool isStreamActive(void); + bool isStreamStopped(void); + + /** + * Check if the capture is running + * @return true if the state of the capture handle equals SND_PCM_STATE_RUNNING + * false otherwise + */ + bool isCaptureActive( void ) { return true; } + + void flushMain(); + int putMain(void* buffer, int toCopy); + int putUrgent(void* buffer, int toCopy); + int canGetMic(); + int getMic(void *, int); + void flushMic(); + + static void audioCallback ( pa_stream* s, size_t bytes, void* user_data ); + + static void stream_state_callback( pa_stream* s, void* user_data ); + + /** + * Scan the sound card available on the system + * @param stream To indicate whether we are looking for capture devices or playback devices + * SFL_PCM_CAPTURE + * SFL_PCM_PLAYBACK + * SFL_PCM_BOTH + * @return std::vector<std::string> The vector containing the string description of the card + */ + std::vector<std::string> getSoundCardsInfo( int stream ) { + std::vector<std::string> tmp; + return tmp; + } + + /** + * Check if the given index corresponds to an existing sound card and supports the specified streaming mode + * @param card An index + * @param stream The stream mode + * SFL_PCM_CAPTURE + * SFL_PCM_PLAYBACK + * SFL_PCM_BOTH + * @return bool True if it exists and supports the mode + * false otherwise + */ + bool soundCardIndexExist( int card , int stream ) { return true; } + + /** + * An index is associated with its string description + * @param description The string description + * @return int Its index + */ + int soundCardGetIndex( std::string description ) { return 0;} + + /** + * Get the current audio plugin. + * @return std::string The name of the audio plugin + */ + std::string getAudioPlugin( void ) { return ""; } + + private: + void closeStream (void); + + pa_stream* playback; + pa_stream* record; + +}; + +#endif // _PULSE_LAYER_H_ + diff --git a/src/managerimpl.cpp b/src/managerimpl.cpp index 349413911f64ea168a9608cdff78607e850d14af..6b9f06e12fd79701817c3c86369568c0b9097cf1 100644 --- a/src/managerimpl.cpp +++ b/src/managerimpl.cpp @@ -38,6 +38,8 @@ #include "manager.h" #include "account.h" #include "audio/audiolayer.h" +#include "audio/alsalayer.h" +#include "audio/pulselayer.h" #include "audio/tonelist.h" #include "accountcreator.h" // create new account @@ -1431,7 +1433,8 @@ ManagerImpl::getCurrentAudioOutputPlugin( void ) ManagerImpl::initAudioDriver(void) { _debugInit("AudioLayer Creation"); - _audiodriver = new AudioLayer(this); + //_audiodriver = new AudioLayer(this); + _audiodriver = new AlsaLayer( this ); if (_audiodriver == 0) { _debug("Init audio driver error\n"); } else {