Commit 287935e2 authored by Emmanuel Milou's avatar Emmanuel Milou

Use a C++ thread to handle tones and DTMF in ALSA

parent cb5eaebb
......@@ -207,11 +207,10 @@ dbus_connect ()
}
nameOwnerProxy = dbus_g_proxy_new_for_name_owner( connection,
nameOwnerProxy = dbus_g_proxy_new_for_name( connection,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
&error);
DBUS_INTERFACE_DBUS);
if( nameOwnerProxy==NULL)
{
......@@ -227,11 +226,11 @@ dbus_connect ()
/* Create a proxy object for the "bus driver" (name "org.freedesktop.DBus") */
instanceProxy = dbus_g_proxy_new_for_name_owner (connection,
instanceProxy = dbus_g_proxy_new_for_name (connection,
"org.sflphone.SFLphone",
"/org/sflphone/SFLphone/Instance",
"org.sflphone.SFLphone.Instance",
&error);
"org.sflphone.SFLphone.Instance");
if (instanceProxy==NULL)
{
g_printerr ("Failed to get proxy to Instance\n");
......@@ -241,11 +240,11 @@ dbus_connect ()
g_print ("DBus connected to Instance\n");
callManagerProxy = dbus_g_proxy_new_for_name_owner (connection,
callManagerProxy = dbus_g_proxy_new_for_name (connection,
"org.sflphone.SFLphone",
"/org/sflphone/SFLphone/CallManager",
"org.sflphone.SFLphone.CallManager",
&error);
"org.sflphone.SFLphone.CallManager");
if (callManagerProxy==NULL)
{
g_printerr ("Failed to get proxy to CallManagers\n");
......@@ -288,11 +287,11 @@ dbus_connect ()
dbus_g_proxy_connect_signal (callManagerProxy,
"volumeChanged", G_CALLBACK(volume_changed_cb), NULL, NULL);
configurationManagerProxy = dbus_g_proxy_new_for_name_owner (connection,
configurationManagerProxy = dbus_g_proxy_new_for_name (connection,
"org.sflphone.SFLphone",
"/org/sflphone/SFLphone/ConfigurationManager",
"org.sflphone.SFLphone.ConfigurationManager",
&error);
"org.sflphone.SFLphone.ConfigurationManager");
if (!configurationManagerProxy)
{
g_printerr ("Failed to get proxy to ConfigurationManager\n");
......
......@@ -30,6 +30,7 @@ libaudio_la_SOURCES = \
dtmf.cpp \
tone.cpp \
alsalayer.cpp \
audiolayer.cpp \
pulselayer.cpp \
audiodevice.cpp \
dtmfgenerator.cpp \
......
......@@ -19,6 +19,8 @@
#include "alsalayer.h"
int framesPerBufferAlsa = 2048;
// Constructor
AlsaLayer::AlsaLayer( ManagerImpl* manager )
: AudioLayer( manager , ALSA )
......@@ -28,10 +30,11 @@
, _audioPlugin()
, _inChannel()
, _outChannel()
, _defaultVolume(100)
, IDSoundCards()
, IDSoundCards()
{
_debug(" Constructor of AlsaLayer called\n");
/* Instanciate the audio thread */
_audioThread = new AudioThread (this);
}
// Destructor
......@@ -44,21 +47,24 @@ AlsaLayer::~AlsaLayer (void)
AlsaLayer::closeLayer()
{
_debugAlsa("Close ALSA streams\n");
ost::MutexLock guard(_mutex);
if (_audioThread)
{
_audioThread->stop();
delete _audioThread; _audioThread=NULL;
}
closeCaptureStream();
closePlaybackStream();
deviceClosed = true;
}
bool
AlsaLayer::openDevice (int indexIn, int indexOut, int sampleRate, int frameSize, int stream , std::string plugin)
{
// We don't accept that the audio plugin is changed during a conversation
if( _talk ){
_debug("can't switch audio plugin when talking\n. Please hang up and try again...\n");
return false;
}
if(deviceClosed == false)
{
if( stream == SFL_PCM_CAPTURE )
......@@ -85,7 +91,7 @@ AlsaLayer::openDevice (int indexIn, int indexOut, int sampleRate, int frameSize,
_debugAlsa(" : nb channel in=%2d, out=%2d\n", _inChannel, _outChannel);
_debugAlsa(" : sample rate=%5d, format=%s\n", _sampleRate, SFLDataFormatString);
//ost::MutexLock lock( _mutex );
ost::MutexLock lock( _mutex );
/*void **hint;
......@@ -106,12 +112,12 @@ AlsaLayer::openDevice (int indexIn, int indexOut, int sampleRate, int frameSize,
void
AlsaLayer::startStream(void)
{
ost::MutexLock guard(_mutex);
int err;
if( _CaptureHandle && _PlaybackHandle )
{
_talk = true ;
_debugAlsa(" Start stream\n");
int err;
//ost::MutexLock lock( _mutex );
snd_pcm_prepare( _CaptureHandle );
snd_pcm_start( _CaptureHandle ) ;
......@@ -123,48 +129,22 @@ AlsaLayer::startStream(void)
void
AlsaLayer::stopStream(void)
{
ost::MutexLock guard(_mutex);
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();
flushUrgent();
flushMain();
}
}
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", frames);
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)
{
......@@ -172,36 +152,6 @@ AlsaLayer::isStreamActive (void)
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)
{
int nbBytes = 0;
if ( _PlaybackHandle ){
//fillHWBuffer();
int a = _urgentBuffer.AvailForPut();
if( a >= toCopy ){
nbBytes = _urgentBuffer.Put( buffer , toCopy , _defaultVolume );
} else {
nbBytes = _urgentBuffer.Put( buffer , a , _defaultVolume ) ;
}
}
return nbBytes;
}
int
AlsaLayer::canGetMic()
{
......@@ -225,7 +175,6 @@ AlsaLayer::getMic(void *buffer, int toCopy)
if( _CaptureHandle )
{
res = read( buffer, toCopy );
adjustVolume( buffer , toCopy , SFL_PCM_CAPTURE );
}
return res ;
}
......@@ -246,33 +195,6 @@ void AlsaLayer::restorePulseAppsVolume( void ){}
///////////////// ALSA PRIVATE FUNCTIONS ////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
void
AlsaLayer::playTones( void )
{
int frames;
int maxBytes;
//frames = _periodSize ;
frames = 940 ;
maxBytes = frames * sizeof(SFLDataFormat) ;
SFLDataFormat* out = (SFLDataFormat*)malloc(maxBytes * sizeof(SFLDataFormat));
if( _talk ) {}
else {
AudioLoop *tone = _manager -> getTelephoneTone();
if( tone != 0 ){
tone -> getNext( out , frames , _manager->getSpkrVolume() );
write( out , maxBytes );
}
else if( ( tone=_manager->getTelephoneFile() ) != 0 ){
tone ->getNext( out , frames , _manager->getSpkrVolume() );
write( out , maxBytes );
}
}
// free the temporary data buffer
free( out ); out = 0;
}
bool
AlsaLayer::isPlaybackActive(void) {
//ost::MutexLock guard( _mutex );
......@@ -437,8 +359,8 @@ AlsaLayer::open_device(std::string pcm_p, std::string pcm_c, int flag)
}
}
//TODO something, really!
_talk = false;
/* Start the secondary audio thread for callbacks */
_audioThread->start();
return true;
}
......@@ -481,6 +403,8 @@ AlsaLayer::write(void* buffer, int length)
int
AlsaLayer::read( void* buffer, int toCopy)
{
ost::MutexLock lock( _mutex );
if(deviceClosed || _CaptureHandle == NULL)
return 0;
......@@ -511,14 +435,6 @@ AlsaLayer::read( void* buffer, int toCopy)
return toCopy;
}
int
AlsaLayer::putInCache( char code UNUSED,
void *buffer UNUSED,
int toCopy UNUSED )
{
return 1;
}
void
......@@ -683,23 +599,62 @@ AlsaLayer::soundCardGetIndex( std::string description )
return 0;
}
void*
AlsaLayer::adjustVolume( void* buffer , int len, int stream )
void AlsaLayer::audioCallback (void)
{
int vol, i, size;
SFLDataFormat *src = NULL;
(stream == SFL_PCM_PLAYBACK)? vol = _manager->getSpkrVolume() : vol = _manager->getMicVolume();
src = (SFLDataFormat*) buffer;
if( vol != 100 )
{
size = len / sizeof(SFLDataFormat);
for( i = 0 ; i < size ; i++ ){
src[i] = src[i] * vol / 100 ;
int toGet, toPut, urgentAvail, normalAvail, micAvailPut, maxBytes;
unsigned short spkrVolume, micVolume;
AudioLoop *tone;
SFLDataFormat *out;
spkrVolume = _manager->getSpkrVolume();
micVolume = _manager->getMicVolume();
// AvailForGet tell the number of chars inside the buffer
// framePerBuffer are the number of data for one channel (left)
urgentAvail = _urgentRingBuffer.AvailForGet();
if (urgentAvail > 0) {
// Urgent data (dtmf, incoming call signal) come first.
toGet = (urgentAvail < (int)(framesPerBufferAlsa * sizeof(SFLDataFormat))) ? urgentAvail : framesPerBufferAlsa * sizeof(SFLDataFormat);
out = (SFLDataFormat*)malloc(toGet * sizeof(SFLDataFormat) );
_urgentRingBuffer.Get(out, toGet, spkrVolume);
/* Play the sound */
write( out , toGet );
free(out); out=0;
// Consume the regular one as well (same amount of bytes)
_voiceRingBuffer.Discard(toGet);
} else {
tone = _manager->getTelephoneTone();
toGet = 940 ;
maxBytes = toGet * sizeof(SFLDataFormat) ;
if ( tone != 0) {
out = (SFLDataFormat*)malloc(maxBytes * sizeof(SFLDataFormat));
tone->getNext(out, toGet, spkrVolume);
write (out , maxBytes);
} else if ( (tone=_manager->getTelephoneFile()) != 0 ) {
out = (SFLDataFormat*)malloc(maxBytes * sizeof(SFLDataFormat) );
tone->getNext(out, toGet, spkrVolume);
write (out , maxBytes);
} else {
// If nothing urgent, play the regular sound samples
normalAvail = _voiceRingBuffer.AvailForGet();
toGet = (normalAvail < (int)(framesPerBufferAlsa * sizeof(SFLDataFormat))) ? normalAvail : framesPerBufferAlsa * sizeof(SFLDataFormat);
out = (SFLDataFormat*)malloc(framesPerBufferAlsa * sizeof(SFLDataFormat));
if (toGet) {
_voiceRingBuffer.Get(out, toGet, spkrVolume);
write (out, toGet);
} else {
bzero(out, framesPerBufferAlsa * sizeof(SFLDataFormat));
}
}
free(out); out=0;
}
return src ;
// Additionally handle the mic's audio stream
micAvailPut = _micRingBuffer.AvailForPut();
toPut = (micAvailPut <= (int)(framesPerBufferAlsa * sizeof(SFLDataFormat))) ? micAvailPut : framesPerBufferAlsa * sizeof(SFLDataFormat);
//_debug("AL: Nb sample: %d char, [0]=%f [1]=%f [2]=%f\n", toPut, in[0], in[1], in[2]);
_micRingBuffer.Put(in, toPut, micVolume);
}
......@@ -21,6 +21,7 @@
#define _ALSA_LAYER_H
#include "audiolayer.h"
#include "eventthread.h"
#include <alsa/asoundlib.h>
class RingBuffer;
......@@ -106,25 +107,7 @@ class AlsaLayer : public AudioLayer {
*/
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
*/
......@@ -182,12 +165,6 @@ class AlsaLayer : public AudioLayer {
*/
std::string getAudioPlugin( void ) { return _audioPlugin; }
/**
* UNUSED in ALSA layer
*/
int putInCache( char code, void *buffer, int toCopy );
/**
* UNUSED in ALSA layer
*/
......@@ -204,12 +181,7 @@ class AlsaLayer : public AudioLayer {
void setPlaybackVolume( UNUSED int volume ){}
void setCaptureVolume( UNUSED int volume ){}
/**
* Callback used for asynchronous playback.
* Write tones buffer to the alsa internal ring buffer.
*/
void playTones( void );
void audioCallback (void);
private:
......@@ -231,18 +203,6 @@ class AlsaLayer : public AudioLayer {
*/
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);
/**
* Open the specified device.
* ALSA Library API
......@@ -306,14 +266,6 @@ class AlsaLayer : public AudioLayer {
*/
snd_pcm_uframes_t _periodSize;
/**
* 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
*/
......@@ -329,15 +281,13 @@ class AlsaLayer : public AudioLayer {
*/
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;
AudioThread *_audioThread;
};
#endif // _ALSA_LAYER_H_
/*
* Copyright (C) 2009 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.
*/
#include "audiolayer.h"
void AudioLayer::flushMain (void)
{
ost::MutexLock guard(_mutex);
_voiceRingBuffer.flush();
}
void AudioLayer::flushUrgent (void)
{
ost::MutexLock guard(_mutex);
_urgentRingBuffer.flush();
}
int AudioLayer::putUrgent(void* buffer, int toCopy)
{
int a;
ost::MutexLock guard(_mutex);
a = _urgentRingBuffer.AvailForPut();
if ( a >= toCopy ) {
return _urgentRingBuffer.Put(buffer, toCopy, _defaultVolume);
} else {
return _urgentRingBuffer.Put(buffer, a, _defaultVolume);
}
return 0;
}
int AudioLayer::putMain (void *buffer, int toCopy)
{
int a;
ost::MutexLock guard(_mutex);
a = _voiceRingBuffer.AvailForPut();
if ( a >= toCopy ) {
return _voiceRingBuffer.Put(buffer, toCopy, _defaultVolume);
} else {
_debug("Chopping sound, Ouch! RingBuffer full ?\n");
return _voiceRingBuffer.Put(buffer, a, _defaultVolume);
}
return 0;
}
......@@ -29,11 +29,9 @@
#include <cc++/thread.h> // for ost::Mutex
#include <pulse/pulseaudio.h>
#define FRAME_PER_BUFFER 160
/**
* @file audiolayer.h
* @brief Main sound class. Manages the data transfers between the application and the hardware.
......@@ -41,280 +39,292 @@
class AudioLayer {
private:
//copy constructor
AudioLayer(const AudioLayer& rh);
// assignment operator
AudioLayer& operator=(const AudioLayer& rh);
public:
/**
* Constructor
* @param manager An instance of managerimpl
*/
AudioLayer( ManagerImpl* manager , int type )
: _layerType( type )
, _manager(manager)
, _urgentBuffer( SIZEBUF )
, _talk ( false )
, deviceClosed ( true )
, _indexIn ( 0 )
, _indexOut ( 0 )
, _sampleRate ( 0 )
, _frameSize ( 0 )
, _inChannel( 1 )
, _outChannel ( 1 )
, _errorMessage ( 0 )
, _mutex ()
private:
//copy constructor
AudioLayer(const AudioLayer& rh);
// assignment operator
AudioLayer& operator=(const AudioLayer& rh);
public:
/**
* Constructor
* @param manager An instance of managerimpl
*/
AudioLayer( ManagerImpl* manager , int type )
: _layerType( type )
, _manager(manager)
, _voiceRingBuffer( SIZEBUF )
, _urgentRingBuffer( SIZEBUF)
, _micRingBuffer( SIZEBUF )
, _defaultVolume(100)
, _talk ( false )
, deviceClosed ( true )
, _indexIn ( 0 )
, _indexOut ( 0 )
, _sampleRate ( 0 )
, _frameSize ( 0 )
, _inChannel( 1 )
, _outChannel ( 1 )