Commit f8cdc96f authored by yanmorin's avatar yanmorin
Browse files

Audio refactoring to use float or integer with portaudio.

Samplerate is now a dependancy.
parent 10461835
2006-08-30 Yan Morin
* Set libsamplerate as a dependency
* Refactoring samplerate conversion, -DDATAFORMAT_IS_FLOAT allow the user to use float instead of int
2006-08-02 Yan Morin 2006-08-02 Yan Morin
* Add IAX quelch/unquelch * Add IAX quelch/unquelch
......
...@@ -80,11 +80,14 @@ Building the dependencies ...@@ -80,11 +80,14 @@ Building the dependencies
------------------------- -------------------------
If you do not use either the development packages of your distribution or the source packages made by the upstream authors of dependencies, you may want to try our custom dependencies building script in tools/ directory: If you do not use either the development packages of your distribution or the source packages made by the upstream authors of dependencies, you may want to try our custom dependencies building script in tools/ directory:
Note that commoncpp, ccrtp, libosip and libexosip, samplerate are in debian and fedora.
1. cd tools/ 1. cd tools/
2. edit config.sh to change the default prefix (/usr/local) 2. ./portaudio.sh <-- compile portaudio
3. ./download.sh 3. if you want to install other software, check inside the file config.sh
4. ./install.sh edit config.sh to change the default prefix (/usr/local)
5. ./portaudio.sh <-- compile portaudio ./download.sh
./install.sh
You can also compile each dependency, one by one: You can also compile each dependency, one by one:
...@@ -108,7 +111,7 @@ You can also compile each dependency, one by one: ...@@ -108,7 +111,7 @@ You can also compile each dependency, one by one:
make make
make install make install
Note: if you install portaudio in /usr/local, don't forget to set pkg-config path with: Note: if you install any package in /usr/local, don't forget to set pkg-config path with:
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig/ export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig/
...@@ -175,8 +178,16 @@ How to enable IAX support? ...@@ -175,8 +178,16 @@ How to enable IAX support?
-------------------------- --------------------------
Go inside libs directory and execute ./libiax2.sh script. Go inside libs directory and execute ./libiax2.sh script.
Run ./configure with --enable-iax2 option. Then, run ./configure with --enable-iax2 option.
Debugging SFLPhone
------------------
You can use the --with-debug option with configure
./configure --with-debug
make
cd src
PATH=. ./gui/qt/sflphone-qt
Run-time troubleshooting Run-time troubleshooting
...@@ -219,7 +230,7 @@ Short description of content of source tree ...@@ -219,7 +230,7 @@ Short description of content of source tree
audiodriver, rtp layer, audio codec ulaw, alaw and gsm. audiodriver, rtp layer, audio codec ulaw, alaw and gsm.
- src/audio/gsm/ contains the implementation of gsm audiocodec library. - src/audio/gsm/ contains the implementation of gsm audiocodec library.
- src/audio/pacpp/ implements PortAudioCpp, a native C++ binding of - src/audio/pacpp/ implements PortAudioCpp, a native C++ binding of
PortAudio V19. PortAudio V19. (remove in sflphone 0.7)
- src/gui/ is the old directory that contains all about different user - src/gui/ is the old directory that contains all about different user
interface. interface.
- src/gui/server is the directory that talk (tcp socket on port 3999) to - src/gui/server is the directory that talk (tcp socket on port 3999) to
......
...@@ -7,6 +7,7 @@ Management of account (add, remove, ...) ...@@ -7,6 +7,7 @@ Management of account (add, remove, ...)
Management of exceptions Management of exceptions
Remove all warnings in compilation Remove all warnings in compilation
Better handling for an reINVITE request. (done?) Better handling for an reINVITE request. (done?)
Mono channel, float for portaudio
For project dependencies: For project dependencies:
------------------------ ------------------------
......
...@@ -129,16 +129,22 @@ dnl Check for exosip2 ...@@ -129,16 +129,22 @@ dnl Check for exosip2
LP_CHECK_EXOSIP2 LP_CHECK_EXOSIP2
SFLPHONE_LIBS="$SFLPHONE_LIBS $EXOSIP_LIBS" SFLPHONE_LIBS="$SFLPHONE_LIBS $EXOSIP_LIBS"
dnl Check for samplerate dnl Check for samplerate
AC_CHECK_HEADER([samplerate.h], [ dnl AC_CHECK_HEADER([samplerate.h], [
AC_CHECK_LIB(samplerate, src_simple, [with_samplerate=yes], [with_samplerate=no]) dnl AC_CHECK_LIB(samplerate, src_simple, [with_samplerate=yes], [with_samplerate=no])
], [ with_samplerate=no ] dnl ], [ with_samplerate=no ]
) dnl )
AM_CONDITIONAL(USE_SAMPLERATE, test x$with_samplerate = xyes) dnl AM_CONDITIONAL(USE_SAMPLERATE, test x$with_samplerate = xyes)
dnl Check for GNU ccRTP dnl Check for GNU ccRTP
PKG_PROG_PKG_CONFIG PKG_PROG_PKG_CONFIG
LIBSAMPLERATE_MIN_VERSION=0.1.2
PKG_CHECK_MODULES(samplerate, samplerate >= ${LIBSAMPLERATE_MIN_VERSION})
SFLPHONE_CFLAGS="$SFLPHONE_CFLAGS $samplerate_CFLAGS"
SFLPHONE_LIBS="$SFLPHONE_LIBS $samplerate_LIBS"
LIBCCGNU2_MIN_VERSION=1.3.1 LIBCCGNU2_MIN_VERSION=1.3.1
PKG_CHECK_MODULES(libccgnu2, libccgnu2 >= ${LIBCCGNU2_MIN_VERSION}) PKG_CHECK_MODULES(libccgnu2, libccgnu2 >= ${LIBCCGNU2_MIN_VERSION})
SFLPHONE_CFLAGS="$SFLPHONE_CFLAGS $libccgnu2_CFLAGS" SFLPHONE_CFLAGS="$SFLPHONE_CFLAGS $libccgnu2_CFLAGS"
......
...@@ -25,14 +25,6 @@ IAXSOURCES = ...@@ -25,14 +25,6 @@ IAXSOURCES =
IAXHEADERS = IAXHEADERS =
endif endif
if USE_SAMPLERATE
SAMPLERATE_FLAG=-DUSE_SAMPLERATE
SAMPLERATE_LIB =-lsamplerate
else
SAMPLERATE_FLAG=
SAMPLERATE_LIB=
endif
SUBDIRS = audio config gui $(ZEROCONFDIR) SUBDIRS = audio config gui $(ZEROCONFDIR)
sflphoned_SOURCES = eventthread.cpp main.cpp voIPLink.cpp \ sflphoned_SOURCES = eventthread.cpp main.cpp voIPLink.cpp \
......
...@@ -12,14 +12,6 @@ SPEEX_FLAG= ...@@ -12,14 +12,6 @@ SPEEX_FLAG=
SPEEX_LIB= SPEEX_LIB=
endif endif
if USE_SAMPLERATE
SAMPLERATE_FLAG=-DUSE_SAMPLERATE
SAMPLERATE_LIB =-lsamplerate
else
SAMPLERATE_FLAG=
SAMPLERATE_LIB=
endif
libaudio_la_SOURCES = alaw.cpp audiofile.cpp g711.cpp tonelist.cpp \ libaudio_la_SOURCES = alaw.cpp audiofile.cpp g711.cpp tonelist.cpp \
audiortp.cpp dtmf.cpp tone.cpp audiocodec.cpp audiolayer.cpp audiodevice.cpp dtmfgenerator.cpp gsmcodec.cpp \ audiortp.cpp dtmf.cpp tone.cpp audiocodec.cpp audiolayer.cpp audiodevice.cpp dtmfgenerator.cpp gsmcodec.cpp \
tonegenerator.cpp ulaw.cpp codecDescriptor.cpp \ tonegenerator.cpp ulaw.cpp codecDescriptor.cpp \
......
...@@ -33,6 +33,9 @@ public: ...@@ -33,6 +33,9 @@ public:
AudioCodec(int payload, const std::string &codecName); AudioCodec(int payload, const std::string &codecName);
virtual ~AudioCodec(void); virtual ~AudioCodec(void);
/**
* @return the number of bytes decoded
*/
virtual int codecDecode(short *, unsigned char *, unsigned int) = 0; virtual int codecDecode(short *, unsigned char *, unsigned int) = 0;
virtual int codecEncode(unsigned char *, short *, unsigned int) = 0; virtual int codecEncode(unsigned char *, short *, unsigned int) = 0;
......
...@@ -23,6 +23,9 @@ ...@@ -23,6 +23,9 @@
#include "audiofile.h" #include "audiofile.h"
#include "codecDescriptor.h" #include "codecDescriptor.h"
#include <fstream> #include <fstream>
#include <math.h>
#include <samplerate.h>
AudioFile::AudioFile() AudioFile::AudioFile()
: AudioLoop() : AudioLoop()
...@@ -38,12 +41,13 @@ AudioFile::~AudioFile() ...@@ -38,12 +41,13 @@ AudioFile::~AudioFile()
delete _ulaw; delete _ulaw;
} }
// load file in mono format
bool bool
AudioFile::loadFile(const std::string& filename, unsigned int nbChannel=2) AudioFile::loadFile(const std::string& filename, unsigned int sampleRate=8000)
{ {
_nbChannel = (nbChannel==2) ? 2 : 1; // if the filename was already load, with the same samplerate
// we do nothing
if (_filename == filename) { if (_filename == filename && _sampleRate == sampleRate) {
return true; return true;
} else { } else {
// reset to 0 // reset to 0
...@@ -54,6 +58,7 @@ AudioFile::loadFile(const std::string& filename, unsigned int nbChannel=2) ...@@ -54,6 +58,7 @@ AudioFile::loadFile(const std::string& filename, unsigned int nbChannel=2)
// no filename to load // no filename to load
if (filename.empty()) { if (filename.empty()) {
_debug("Unable to open audio file: filename is empty\n");
return false; return false;
} }
...@@ -61,6 +66,7 @@ AudioFile::loadFile(const std::string& filename, unsigned int nbChannel=2) ...@@ -61,6 +66,7 @@ AudioFile::loadFile(const std::string& filename, unsigned int nbChannel=2)
file.open(filename.c_str(), std::fstream::in); file.open(filename.c_str(), std::fstream::in);
if (!file.is_open()) { if (!file.is_open()) {
// unable to load the file // unable to load the file
_debug("Unable to open audio file %s\n", filename.c_str());
return false; return false;
} }
...@@ -71,6 +77,7 @@ AudioFile::loadFile(const std::string& filename, unsigned int nbChannel=2) ...@@ -71,6 +77,7 @@ AudioFile::loadFile(const std::string& filename, unsigned int nbChannel=2)
// allocate memory: // allocate memory:
char fileBuffer[length]; char fileBuffer[length];
// read data as a block: // read data as a block:
file.read (fileBuffer,length); file.read (fileBuffer,length);
file.close(); file.close();
...@@ -80,29 +87,61 @@ AudioFile::loadFile(const std::string& filename, unsigned int nbChannel=2) ...@@ -80,29 +87,61 @@ AudioFile::loadFile(const std::string& filename, unsigned int nbChannel=2)
// expandedsize should be exactly two time more, else failed // expandedsize should be exactly two time more, else failed
int16 monoBuffer[length]; int16 monoBuffer[length];
unsigned int expandedsize = _ulaw->codecDecode (monoBuffer, (unsigned char *) fileBuffer, length); unsigned int expandedsize = _ulaw->codecDecode (monoBuffer, (unsigned char *) fileBuffer, length);
if (expandedsize != length*2) {
if (expandedsize != length*sizeof(int16)) {
_debug("Audio file error on loading audio file!"); _debug("Audio file error on loading audio file!");
return false; return false;
} }
unsigned int nbSampling = expandedsize/sizeof(int16);
// we need to change the sample rating here:
// case 1: we don't have to resample : only do splitting and convert
if ( sampleRate == 8000 ) {
// just s
_size = nbSampling;
_buffer = new SFLDataFormat[_size];
// src to dest
for(unsigned int i=0; i<nbSampling; i++) {
_buffer[i] = SFLConvertInt16(monoBuffer[i]);
}
} else {
// case 2: we need to convert it and split it
// convert here
double factord = (double)sampleRate / 8000;
float* floatBufferIn = new float[nbSampling];
int sizeOut = (int)(ceil(factord*nbSampling));
src_short_to_float_array(monoBuffer, floatBufferIn, nbSampling);
SFLDataFormat* bufferTmp = new SFLDataFormat[sizeOut];
unsigned int int16size = expandedsize/sizeof(int16); SRC_DATA src_data;
src_data.data_in = floatBufferIn;
src_data.input_frames = nbSampling;
src_data.output_frames = sizeOut;
src_data.src_ratio = factord;
if (_nbChannel == 2) { #ifdef DATAFORMAT_IS_FLOAT
_size = int16size<<1; // multiply by two // case number 2.1: the output is float32 : convert directly in _bufferTmp
_buffer = new int16[_size]; src_data.data_out = bufferTmp;
src_simple (&src_data, SRC_SINC_BEST_QUALITY, 1);
#else
// case number 2.2: the output is int16 : convert and change to int16
float* floatBufferOut = new float[sizeOut];
src_data.data_out = floatBufferOut;
for(unsigned int i=0, k=0; i<int16size; i++) { src_simple (&src_data, SRC_SINC_BEST_QUALITY, 1);
_buffer[k] = _buffer[k+1] = monoBuffer[i]; src_float_to_short_array(floatBufferOut, bufferTmp, src_data.output_frames_gen);
k+=2;
} delete [] floatBufferOut;
} else { #endif
// copy the mono buffer to a mono buffer delete [] floatBufferIn;
_size = int16size; nbSampling = src_data.output_frames_gen;
_buffer = new int16[_size];
// src to dest // if we are in mono, we send the bufferTmp location and don't delete it
bcopy(monoBuffer, _buffer, expandedsize); // else we split the audio in 2 and put it into buffer
_size = nbSampling;
_buffer = bufferTmp; // just send the buffer pointer;
bufferTmp = 0;
} }
return true; return true;
} }
...@@ -35,7 +35,7 @@ public: ...@@ -35,7 +35,7 @@ public:
AudioFile(); AudioFile();
~AudioFile(); ~AudioFile();
bool loadFile(const std::string& filename, unsigned int nbChannel/*=2*/); bool loadFile(const std::string& filename, unsigned int sampleRate/*=8000*/);
void start() { _start = true; } void start() { _start = true; }
void stop() { _start = false; } void stop() { _start = false; }
bool isStarted() { return _start; } bool isStarted() { return _start; }
......
...@@ -25,29 +25,67 @@ ...@@ -25,29 +25,67 @@
#include "../global.h" #include "../global.h"
#include "../manager.h" #include "../manager.h"
AudioLayer::AudioLayer() //#define SFL_TEST
//#define SFL_TEST_SINE
#ifdef SFL_TEST_SINE
#include <cmath>
#endif
AudioLayer::AudioLayer(ManagerImpl* manager)
: _urgentRingBuffer(SIZEBUF) : _urgentRingBuffer(SIZEBUF)
, _mainSndRingBuffer(SIZEBUF) , _mainSndRingBuffer(SIZEBUF)
, _micRingBuffer(SIZEBUF) , _micRingBuffer(SIZEBUF)
, _stream(NULL) , _stream(NULL)
, _errorMessage("") , _errorMessage("")
, _manager(manager)
{ {
_sampleRate = 8000; _sampleRate = 8000;
_inChannel = 1; _inChannel = 1; // don't put in stereo
_outChannel = 1; _outChannel = 1; // don't put in stereo
portaudio::System::initialize();
try {
portaudio::AutoSystem autoSys;
portaudio::System::initialize();
}
catch (const portaudio::PaException &e) {
setErrorMessage(e.paErrorText());
}
catch (const portaudio::PaCppException &e) {
setErrorMessage(e.what());
} // std::runtime_error &e (e.what())
catch (...) {
setErrorMessage("Unknown type error in portaudio initialization");
}
#ifdef SFL_TEST_SINE
leftPhase_ = 0;
tableSize_ = 200;
const double PI = 3.14159265;
table_ = new float[tableSize_];
for (int i = 0; i < tableSize_; ++i)
{
table_[i] = 0.125f * (float)sin(((double)i/(double)tableSize_)*PI*2.);
_debug("%9.8f\n", table_[i]);
}
#endif
} }
// Destructor // Destructor
AudioLayer::~AudioLayer (void) AudioLayer::~AudioLayer (void)
{ {
stopStream();
closeStream();
try { try {
portaudio::System::terminate(); portaudio::System::terminate();
} catch (const portaudio::PaException &e) { } catch (const portaudio::PaException &e) {
_debug("! AL: Catch an exception when portaudio tried to terminate\n"); _debug("! AL: Catch an exception when portaudio tried to terminate\n");
} }
closeStream(); #ifdef SFL_TEST_SINE
delete [] table_;
#endif
} }
void void
...@@ -72,40 +110,117 @@ AudioLayer::openDevice (int indexIn, int indexOut, int sampleRate) ...@@ -72,40 +110,117 @@ AudioLayer::openDevice (int indexIn, int indexOut, int sampleRate)
{ {
closeStream(); closeStream();
_sampleRate = sampleRate;
int portaudioFramePerBuffer = FRAME_PER_BUFFER; //=FRAME_PER_BUFFER; //= paFramesPerBufferUnspecified;
int nbDevice = getDeviceCount();
if (nbDevice == 0) {
_debug("Portaudio detect no sound card.");
return;
} else {
if (indexIn >= nbDevice) {
_debug(" Portaudio auto-select device #0 for input because device #%02d is not found\n", indexIn);
indexIn = 0;
}
if (indexOut >= nbDevice) {
_debug(" Portaudio auto-select device #0 for output because device #%02d is not found\n", indexOut);
indexOut = 0;
}
_debug(" Setting audiolayer: device in=%2d, out=%2d\n", indexIn, indexOut);
_debug(" : nb channel in=%2d, out=%2d\n", _inChannel, _outChannel);
_debug(" : sample rate=%5d, format=%s\n", _sampleRate, SFLPortaudioFormatString);
_debug(" : frame per buffer=%d\n", portaudioFramePerBuffer);
}
try { try {
// Set up the parameters required to open a (Callback)Stream: // Set up the parameters required to open a (Callback)Stream:
portaudio::DirectionSpecificStreamParameters portaudio::DirectionSpecificStreamParameters
outParams(portaudio::System::instance().deviceByIndex(indexOut), inParams(portaudio::System::instance().deviceByIndex(indexIn),
_outChannel, portaudio::INT16, true, _inChannel, SFLPortaudioFormat, true,
portaudio::System::instance().deviceByIndex(indexOut).defaultLowOutputLatency(),
NULL);
portaudio::DirectionSpecificStreamParameters
inParams(portaudio::System::instance().deviceByIndex(indexIn),
_inChannel, portaudio::INT16, true,
portaudio::System::instance().deviceByIndex(indexIn).defaultLowInputLatency(), portaudio::System::instance().deviceByIndex(indexIn).defaultLowInputLatency(),
NULL); NULL);
#ifdef USE_SAMPLERATE portaudio::DirectionSpecificStreamParameters outParams(
_sampleRate = sampleRate; portaudio::System::instance().deviceByIndex(indexOut),
#else _outChannel, SFLPortaudioFormat, true,
_sampleRate = 8000; portaudio::System::instance().deviceByIndex(indexOut).defaultLowOutputLatency(),
#endif NULL);
// we could put paFramesPerBufferUnspecified instead of FRAME_PER_BUFFER to be variable // like audacity
portaudio::StreamParameters const params(inParams, outParams, // DON'T USE paFramesPerBufferUnspecified, it's 32, instead of 160 for FRAME_PER_BUFFER
_sampleRate, FRAME_PER_BUFFER /*paFramesPerBufferUnspecified*/, paNoFlag /*paPrimeOutputBuffersUsingStreamCallback | paNeverDropInput*/); // DON'T USE paDitherOff or paClipOff,
// FRAME_PER_BUFFER | paFramesPerBufferUnspecified
// Create (and open) a new Stream, using the AudioLayer::audioCallback // paNoFlag | paClipOff || paDitherOff | paPrimeOutputBuffersUsingStreamCallback | paNeverDropInput
ost::MutexLock guard(_mutex);
_stream = new portaudio::MemFunCallbackStream<AudioLayer>(params, portaudio::StreamParameters const params(
*this, inParams,
&AudioLayer::audioCallback); outParams,
} catch(...) { _sampleRate, portaudioFramePerBuffer, paClipOff);
throw;
// Create (and open) a new Stream, using the AudioLayer::audioCallback
ost::MutexLock guard(_mutex);
#ifdef SFL_TEST
_stream = new portaudio::MemFunCallbackStream<AudioLayer>(params, *this, &AudioLayer::miniAudioCallback);
#else
_stream = new portaudio::MemFunCallbackStream<AudioLayer>(params,*this, &AudioLayer::audioCallback);
#endif
}
catch (const portaudio::PaException &e) {
setErrorMessage(e.paErrorText());
_debug("Portaudio openDevice error: %s\n", e.paErrorText());
} }
catch (const portaudio::PaCppException &e) {
setErrorMessage(e.what());
_debug("Portaudio openDevice error: %s\n", e.what());
} // std::runtime_error &e (e.what())
catch (...) {
setErrorMessage("Unknown type error in portaudio openDevice");
_debug("Portaudio openDevice: unknown error\n");
}
} }
int
AudioLayer::getDeviceCount()
{
return portaudio::System::instance().deviceCount();
}
AudioDevice*
AudioLayer::getAudioDeviceInfo(int index, int ioDeviceMask)
{
try {
portaudio::System& sys = portaudio::System::instance();
portaudio::Device& device = sys.deviceByIndex(index);
int deviceIsSupported = false;
if (ioDeviceMask == InputDevice && !device.isOutputOnlyDevice()) {
deviceIsSupported = true;
} else if (ioDeviceMask == OutputDevice && !device.isInputOnlyDevice()) {
deviceIsSupported = true;
} else if (device.isFullDuplexDevice()) {
deviceIsSupported = true;
}
if (deviceIsSupported) {
AudioDevice* audiodevice = new AudioDevice(index, device.hostApi().name(), device.name());
if (audiodevice) {
audiodevice->setRate(device.defaultSampleRate());
}
return audiodevice;
}
} catch (...) {
return 0;
}
return 0;
}
void void
AudioLayer::startStream(void) AudioLayer::startStream(void)
{ {
...@@ -115,7 +230,7 @@ AudioLayer::startStream(void) ...@@ -115,7 +230,7 @@ AudioLayer::startStream(void)
_debug("- AL Action: Starting sound stream\n"); _debug("- AL Action: Starting sound stream\n");
_stream->start(); _stream->start();
} else { } else {
_debug ("* AL Info: Stream doesn't exist or is already active\n"); _debug ("* AL Info: Stream doesn't exist or is already active\n");
} }
} catch (const portaudio::PaException &e) { } catch (const portaudio::PaException &e) {
_debugException("! AL: Portaudio error: error on starting audiolayer stream"); _debugException("! AL: Portaudio error: error on starting audiolayer stream");
...@@ -129,6 +244,7 @@ AudioLayer::startStream(void) ...@@ -129,6 +244,7 @@ AudioLayer::startStream(void)
void void
AudioLayer::stopStream(void) AudioLayer::stopStream(void)
{ {