Commit 9e46baec authored by Emmanuel Milou's avatar Emmanuel Milou
Browse files

Each pulse audio streams is an object now

parent eea6e029
......@@ -23,15 +23,15 @@ endif
SUBDIRS = codecs
libaudio_la_SOURCES = audiofile.cpp tonelist.cpp \
audiortp.cpp dtmf.cpp tone.cpp alsalayer.cpp pulselayer.cpp audiodevice.cpp dtmfgenerator.cpp \
tonegenerator.cpp codecDescriptor.cpp \
audiortp.cpp audiostream.cpp dtmf.cpp tone.cpp alsalayer.cpp pulselayer.cpp audiodevice.cpp dtmfgenerator.cpp \
tonegenerator.cpp codecDescriptor.cpp samplecache.cpp\
audioloop.cpp ringbuffer.cpp $(SPEEX_SOURCES_CPP)
AM_CXXFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs $(libccext2_CFLAGS) $(libdbuscpp_CFLAGS) $(libccrtp1_CFLAGS) $(USER_INCLUDES) \
-DCODECS_DIR=\""$(sflcodecdir)"\" $(SPEEX_FLAG) $(GSM_FLAG) $(ILBC_FLAG)
noinst_HEADERS = audioloop.h common.h ringbuffer.h audiofile.h \
noinst_HEADERS = audioloop.h audiostream.h common.h ringbuffer.h audiofile.h \
tonelist.h audiortp.h audiolayer.h alsalayer.h pulselayer.h audiodevice.h \
dtmfgenerator.h tonegenerator.h \
dtmfgenerator.h tonegenerator.h samplecache.h \
codecDescriptor.h dtmf.h tone.h
/*
* 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.
*/
#include <audiostream.h>
static pa_channel_map channel_map ;
AudioStream::AudioStream( pa_context* context, int type, std::string desc )
{
_streamType = type;
_streamDescription = desc;
sample_spec.format = PA_SAMPLE_S16LE;
sample_spec.rate = 44100;
sample_spec.channels = 1;
channel_map.channels = 1;
flag = PA_STREAM_AUTO_TIMING_UPDATE;
_audiostream = createStream( context );
}
AudioStream::~AudioStream()
{
}
void
AudioStream::stream_state_callback( pa_stream* s, void* user_data )
{
_debug("The state of the stream changed\n");
assert(s);
switch(pa_stream_get_state(s)){
case PA_STREAM_CREATING:
case PA_STREAM_TERMINATED:
_debug("Stream is creating...\n");
break;
case PA_STREAM_READY:
_debug("Stream successfully created\n");
break;
case PA_STREAM_FAILED:
default:
_debug("Stream error: %s\n" , pa_strerror(pa_context_errno(pa_stream_get_context(s))));
break;
}
}
pa_stream*
AudioStream::createStream( pa_context* c )
{
_debug("Creating %s stream...\n" , _streamDescription.c_str());
pa_stream* s;
assert(pa_sample_spec_valid(&sample_spec));
assert(pa_channel_map_valid(&channel_map));
if( !( s = pa_stream_new( c, _streamDescription.c_str() , &sample_spec, &channel_map ) ) )
_debug("%s: pa_stream_new() failed : %s\n" , _streamDescription.c_str(), pa_strerror( pa_context_errno( c)));
assert( s );
if( _streamType == PLAYBACK_STREAM ){
pa_stream_connect_playback( s , NULL , NULL , flag , NULL, NULL );
//pa_stream_set_write_callback( s , audioCallback, this);
}
else if( _streamType == CAPTURE_STREAM ){
pa_stream_connect_record( s , NULL , NULL , flag );
//pa_stream_set_read_callback( s , audioCallback, this);
}
else if( _streamType == UPLOAD_STREAM ){
//pa_stream_connect_upload( s , 1024 );
}
else{
_debug( "Stream type unknown \n");
}
pa_stream_set_state_callback( s , stream_state_callback, NULL);
return s;
}
/*
* 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_STREAM_H
#define _AUDIO_STREAM_H
#include <pulse/pulseaudio.h>
#include <string>
#include "../global.h"
#include "ringbuffer.h"
#include "audioloop.h"
enum STREAM_TYPE {
PLAYBACK_STREAM,
CAPTURE_STREAM,
UPLOAD_STREAM
};
class AudioStream {
public:
AudioStream(pa_context* context , int type, std::string desc);
~AudioStream();
int putMain( void* buffer , int toCopy );
int putUrgent( void* buffer , int toCopy );
pa_stream* pulseStream(){ return _audiostream; }
private:
pa_stream* createStream( pa_context* c );
static void stream_state_callback( pa_stream* s, void* user_data );
static void audioCallback ( pa_stream* s, size_t bytes, void* userdata );
void write( void );
int _streamType;
std::string _streamDescription;
pa_stream* _audiostream;
pa_stream_flags_t flag;
pa_sample_spec sample_spec ;
//pa_channel_map channel_map;
};
#endif // _AUDIO_STREAM_H
......@@ -77,3 +77,4 @@ DTMF::generateDTMF (SFLDataFormat* buffer, size_t n)
return false;
}
}
......@@ -19,23 +19,13 @@
#include "pulselayer.h"
static pa_context *context = NULL;
static pa_stream* playback = NULL;
static pa_stream* record = NULL;
static pa_mainloop_api *mainloop_api = NULL;
static pa_threaded_mainloop *m = NULL;
static pa_sample_spec sample_spec ;
int framesPerBuffer = 1024;
static pa_channel_map channel_map;
static std::string pcm_p, pcm_r;
int framesPerBuffer = 882;
PulseLayer::PulseLayer(ManagerImpl* manager)
PulseLayer::PulseLayer(ManagerImpl* manager)
: AudioLayer( manager , PULSEAUDIO )
, _mainSndRingBuffer( SIZEBUF )
, _urgentRingBuffer( SIZEBUF)
,_mainSndRingBuffer( SIZEBUF )
{
_debug("Pulse audio constructor: Create context\n");
}
......@@ -43,11 +33,10 @@ int framesPerBuffer = 882;
// Destructor
PulseLayer::~PulseLayer (void)
{
assert(mainloop_api);
//mainloop_api->quit( mainloop_api, 0 );
pa_stream_flush( playback , NULL, NULL);
pa_stream_disconnect( playback );
pa_context_disconnect(context);
delete playback;
delete record;
////delete cache;
pa_context_disconnect(context);
}
void
......@@ -75,33 +64,12 @@ PulseLayer::connectPulseServer( void )
_debug("Context creation done\n");
}
void
PulseLayer::stream_state_callback( pa_stream* s, void* user_data )
{
_debug("The state of the stream changed\n");
assert(s);
switch(pa_stream_get_state(s)){
case PA_STREAM_CREATING:
case PA_STREAM_TERMINATED:
_debug("Stream is creating...\n");
break;
case PA_STREAM_READY:
_debug("Stream successfully created\n");
break;
case PA_STREAM_FAILED:
default:
_debug("Stream error: %s\n" , pa_strerror(pa_context_errno(pa_stream_get_context(s))));
break;
}
}
void
PulseLayer::context_state_callback( pa_context* c, void* user_data )
{
_debug("The state of the context changed\n");
PulseLayer* pulse = (PulseLayer*)user_data;
//pa_stream_flags_t flag = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE ;
assert(c && m);
assert(c && pulse->m);
switch(pa_context_get_state(c)){
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
......@@ -110,7 +78,6 @@ PulseLayer::context_state_callback( pa_context* c, void* user_data )
break;
case PA_CONTEXT_READY:
pa_cvolume cv;
assert(c && !playback && !record);
pulse->createStreams( c );
_debug("Connection to PulseAudio server established\n");
break;
......@@ -119,7 +86,7 @@ PulseLayer::context_state_callback( pa_context* c, void* user_data )
break;
case PA_CONTEXT_FAILED:
default:
_debug(" Error : %s" , pa_strerror(pa_context_errno(context)));
_debug(" Error : %s" , pa_strerror(pa_context_errno(c)));
exit(1);
}
}
......@@ -127,47 +94,13 @@ PulseLayer::context_state_callback( pa_context* c, void* user_data )
void
PulseLayer::createStreams( pa_context* c )
{
_debug( " Create Audio Streams \n ");
//pa_stream_flags_t flag = PA_STREAM_AUTO_TIMING_UPDATE;
pa_stream_flags_t flag = PA_STREAM_FIX_RATE;
sample_spec.format = PA_SAMPLE_S16LE;
sample_spec.rate = 44100;
sample_spec.channels = 1;
channel_map.channels = 1;
if( !pa_sample_spec_valid( &sample_spec ) ){
_debug("Invalid sample specifications\n");
exit(0);
}
if(!(playback = pa_stream_new( c, "SFLphone out" , &sample_spec, &channel_map))){
_debug("Playback: pa_stream_new() failed : %s\n" , pa_strerror( pa_context_errno( c)));
exit(0);
}
if(!(record = pa_stream_new( c, "SFLphone Mic" , &sample_spec, &channel_map))){
_debug("Capture: pa_stream_new() failed : %s\n" , pa_strerror( pa_context_errno( c)));
exit(0);
}
assert(playback);
assert(record);
assert(m);
// Set up the parameters required to open a (Callback)Stream:
pa_stream_connect_playback( playback, NULL , NULL , flag , NULL, NULL );
pa_stream_set_state_callback(playback, stream_state_callback, NULL);
// Transferring Data - Asynchronous Mode
pa_stream_set_write_callback(playback, audioCallback, this);
playback = new AudioStream(c, PLAYBACK_STREAM, "SFLphone out");
pa_stream_set_write_callback( playback->pulseStream() , audioCallback, this);
record = new AudioStream(c, CAPTURE_STREAM, "SFLphone in");
pa_stream_set_read_callback( record->pulseStream() , audioCallback, this);
//cache = new AudioStream(c, UPLOAD_STREAM, "Cache samples");
pa_threaded_mainloop_signal(m , 0);
pa_stream_set_state_callback(record, stream_state_callback, NULL);
// Transferring Data - Asynchronous Mode
pa_stream_set_read_callback(record, audioCallback, this);
pa_stream_connect_record( record, NULL , NULL , flag );
}
bool
......@@ -223,7 +156,6 @@ PulseLayer::putMain(void* buffer, int toCopy)
_debug("Chopping sound, Ouch! RingBuffer full ?\n");
return _mainSndRingBuffer.Put(buffer, a, 100);
}
return 0;
}
......@@ -235,6 +167,13 @@ PulseLayer::flushMain()
int
PulseLayer::putUrgent(void* buffer, int toCopy)
{
int a = _urgentRingBuffer.AvailForPut();
if ( a >= toCopy ) {
return _urgentRingBuffer.Put(buffer, toCopy, 100 );
} else {
return _urgentRingBuffer.Put(buffer, a, 100 );
}
return 0;
}
int
......@@ -254,23 +193,22 @@ PulseLayer::flushMic()
{
}
bool
/* bool
PulseLayer::isStreamStopped (void)
{
}
*/
void
PulseLayer::startStream (void)
{
_debug("Start stream\n");
//pa_stream_cork( playback , 0, NULL, NULL);
}
void
PulseLayer::stopStream (void)
{
_debug("Stop stream\n");
pa_stream_drop( playback );
//pa_stream_drop( playback );
}
bool
......@@ -281,67 +219,47 @@ PulseLayer::isStreamActive (void)
void
PulseLayer::audioCallback ( pa_stream* s, size_t bytes, void* userdata )
{
pa_threaded_mainloop_signal( m , 0);
assert( s && bytes );
PulseLayer* pulse = (PulseLayer*) userdata;
assert( s && bytes );
pulse->write();
//if(pa_stream_get_state(s) == PA_STREAM_READY )
//{
//if( bytes > 0 ){
// pa_stream_write( s, user_data, bytes, pa_xfree, 0, PA_SEEK_RELATIVE );
//}
//}
// pa_stream_write
// // pa_stream_peek ( to read the next fragment from the buffer ) / pa_stream_drop( to remove the data from the buffer )
//
// int toPut;
// int urgentAvail; // number of data right and data left
// int micAvailPut;
//
// // 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)(framesPerBuffer * sizeof(SFLDataFormat))) ? urgentAvail : framesPerBuffer * sizeof(SFLDataFormat);
// _urgentRingBuffer.Get(out, toGet, spkrVolume);
// // Consume the regular one as well (same amount of bytes)
// _mainSndRingBuffer.Discard(toGet);
// } else {
// AudioLoop* tone = _manager->getTelephoneTone();
// if ( tone != 0) {
// tone->getNext(out, framesPerBuffer, spkrVolume);
// } else if ( (tone=_manager->getTelephoneFile()) != 0 ) {
// tone->getNext(out, framesPerBuffer, spkrVolume);
// } else {
// }
// }
//
// // Additionally handle the mic's audio stream
// micAvailPut = _micRingBuffer.AvailForPut();
// toPut = (micAvailPut <= (int)(framesPerBuffer * sizeof(SFLDataFormat))) ? micAvailPut : framesPerBuffer * 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);
}
void
PulseLayer::write( void )
{
int toGet;
int urgentAvail; // number of data right and data left
int normalAvail; // number of data right and data left
SFLDataFormat* out = (SFLDataFormat*)malloc(framesPerBuffer * sizeof(SFLDataFormat));
normalAvail = _mainSndRingBuffer.AvailForGet();
toGet = (normalAvail < (int)(framesPerBuffer * sizeof(SFLDataFormat))) ? normalAvail : framesPerBuffer * sizeof(SFLDataFormat);
if (toGet) {
_mainSndRingBuffer.Get(out, toGet, 100);
_debug("Write %i bytes\n" , toGet);
pa_stream_write( playback , out , toGet , pa_xfree, 0 , PA_SEEK_RELATIVE);
urgentAvail = _urgentRingBuffer.AvailForGet();
if (urgentAvail > 0) {
// Urgent data (dtmf, incoming call signal) come first.
_debug("Play urgent!\n");
toGet = (urgentAvail < (int)(framesPerBuffer * sizeof(SFLDataFormat))) ? urgentAvail : framesPerBuffer * sizeof(SFLDataFormat);
_urgentRingBuffer.Get(out, toGet, 100);
// Consume the regular one as well (same amount of bytes)
_mainSndRingBuffer.Discard(toGet);
} else {
bzero(out, framesPerBuffer * sizeof(SFLDataFormat));
}
}
else
{
AudioLoop* tone = _manager->getTelephoneTone();
if ( tone != 0) {
tone->getNext(out, framesPerBuffer, 100);
toGet = framesPerBuffer;
} else if ( (tone=_manager->getTelephoneFile()) != 0 ) {
tone->getNext(out, framesPerBuffer, 100);
toGet = framesPerBuffer;
} else {
normalAvail = _mainSndRingBuffer.AvailForGet();
toGet = (normalAvail < (int)(framesPerBuffer * sizeof(SFLDataFormat))) ? normalAvail : framesPerBuffer * sizeof(SFLDataFormat);
if (toGet) {
_mainSndRingBuffer.Get(out, toGet, 100);
_debug("Write %i bytes\n" , toGet);
_mainSndRingBuffer.Discard(toGet);
} else {
bzero(out, framesPerBuffer * sizeof(SFLDataFormat));
}
}
}
pa_stream_write( playback->pulseStream() , out , toGet , pa_xfree, 0 , PA_SEEK_RELATIVE);
}
......@@ -21,6 +21,7 @@
#define _PULSE_LAYER_H
#include "audiolayer.h"
#include "audiostream.h"
class RingBuffer;
class ManagerImpl;
......@@ -48,8 +49,6 @@ class PulseLayer : public AudioLayer {
void startStream(void);
void stopStream(void);
bool isStreamActive(void);
bool isStreamStopped(void);
/**
* Check if the capture is running
......@@ -57,6 +56,7 @@ class PulseLayer : public AudioLayer {
* false otherwise
*/
bool isCaptureActive( void ) { return true; }
bool isStreamActive (void);
void flushMain();
int putMain(void* buffer, int toCopy);
......@@ -76,7 +76,6 @@ class PulseLayer : public AudioLayer {
static void audioCallback ( pa_stream* s, size_t bytes, void* userdata );
static void stream_state_callback( pa_stream* s, void* user_data );
static void context_state_callback( pa_context* c, void* user_data );
/**
......@@ -117,6 +116,8 @@ class PulseLayer : public AudioLayer {
*/
std::string getAudioPlugin( void ) { return "default"; }
//pa_stream* getCacheStream( void ) { return caching; }
private:
/**
* Drop the pending frames and close the capture device
......@@ -132,10 +133,17 @@ class PulseLayer : public AudioLayer {
void connectPulseServer( void );
/** Ringbuffers for data */
RingBuffer _mainSndRingBuffer;
RingBuffer _urgentRingBuffer;
/** PulseAudio streams and context */
pa_context* context;
pa_threaded_mainloop* m;
//pa_stream* playback;
//pa_stream* record;
AudioStream* playback;
AudioStream* record;
AudioStream* cache;
};
......
/*
* 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.
*/
#include <samplecache.h>
SampleCache::SampleCache( pa_stream* s )
{
_stream = s ;
}
SampleCache::~SampleCache()
{
//delete _pulse;
}
bool
SampleCache::uploadSample( SFLDataFormat* buffer , size_t size )
{
//pa_stream_write( pulse->caching , buffer , size , pa_xfree, 0 , PA_SEEK_RELATIVE);
//pa_stream_finish_upload( pulse->caching );
}
/*
* 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 _SAMPLE_CACHE_H
#define _SAMPLE_CACHE_H
#include <pulse/pulseaudio.h>
#include <audiolayer.h>
class SampleCache {
public:
SampleCache( pa_stream* stream );
~SampleCache();
bool uploadSample( SFLDataFormat* buffer, size_t size );
bool removeSample( );
bool isSampleCached( );
private:
pa_stream* _stream;
};
#endif // _SAMPLE_CACHE_H
......@@ -124,7 +124,6 @@ typedef short int16;
#define ALSA 0
#define PULSEAUDIO 1
//#define AUDIODRIVER ALSA
#define CHECK_INTERFACE( layer , api ) (layer == api)
#define CHECK_INTERFACE( layer , api ) (layer != api)