From 4df0d6a2a47e449414ce2f9e73cdf6bac52be91a Mon Sep 17 00:00:00 2001 From: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> Date: Tue, 10 Jun 2008 15:39:28 -0400 Subject: [PATCH] Sample rate conversion stands in one place, tight to the config --- debian/changelog | 5 + src/audio/audiortp.cpp | 151 ++------- src/audio/audiortp.h | 52 +--- src/iaxvoiplink.cpp | 602 +++++++++++++++--------------------- src/iaxvoiplink.h | 38 +-- src/samplerateconverter.cpp | 81 +++-- src/samplerateconverter.h | 25 +- 7 files changed, 378 insertions(+), 576 deletions(-) diff --git a/debian/changelog b/debian/changelog index fb8e8a8865..1429b6ba5e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,8 @@ +sflphone (0.9.2) unstable; urgency=low + * Ticket #14: Sample rate conversion stands in one place now + + -- Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> Thu, 22 May 2008 11:14:25 -0500 + sflphone (0.9.1) unstable; urgency=low * Add a search tool in the history * Migrate some gtk_entry_new to sexy_icon_entry_new diff --git a/src/audio/audiortp.cpp b/src/audio/audiortp.cpp index d4fd9f04ed..81eaa33853 100644 --- a/src/audio/audiortp.cpp +++ b/src/audio/audiortp.cpp @@ -39,7 +39,6 @@ #include "ringbuffer.h" #include "../user_cfg.h" #include "../sipcall.h" -#include <samplerate.h> //////////////////////////////////////////////////////////////////////////////// // AudioRtp @@ -103,9 +102,6 @@ AudioRtpRTX::AudioRtpRTX (SIPCall *sipcall, bool sym) _ca = sipcall; _sym = sym; // AudioRtpRTX should be close if we change sample rate - //int IDcodec= _ca->getAudioCodec(); - //_codecSampleRate = _ca->getAudioCodec()->getClockRate(); - //_codecDesc = Manager::instance().getCodecDescriptorMap(); // TODO: Change bind address according to user settings. // TODO: this should be the local ip not the external (router) IP std::string localipConfig = _ca->getLocalIp(); // _ca->getLocalIp(); @@ -119,13 +115,6 @@ AudioRtpRTX::AudioRtpRTX (SIPCall *sipcall, bool sym) _sessionRecv = NULL; _sessionSend = NULL; } - - // libsamplerate-related - // Set the converter type for the upsampling and the downsampling - // interpolator SRC_SINC_BEST_QUALITY - _src_state_mic = src_new(SRC_SINC_BEST_QUALITY, 1, &_src_err); - _src_state_spkr = src_new(SRC_SINC_BEST_QUALITY, 1, &_src_err); - } AudioRtpRTX::~AudioRtpRTX () { @@ -146,41 +135,34 @@ AudioRtpRTX::~AudioRtpRTX () { delete _session; _session = NULL; } - delete [] _intBufferDown; _intBufferDown = NULL; - delete [] _floatBufferUp; _floatBufferUp = NULL; - delete [] _floatBufferDown; _floatBufferDown = NULL; - delete [] _dataAudioLayer; _dataAudioLayer = NULL; + delete [] micData; micData = NULL; + delete [] micDataConverted; micDataConverted = NULL; + delete [] micDataEncoded; micDataEncoded = NULL; - delete [] _sendDataEncoded; _sendDataEncoded = NULL; - delete [] _receiveDataDecoded; _receiveDataDecoded = NULL; + delete [] spkrDataDecoded; spkrDataDecoded = NULL; + delete [] spkrDataConverted; spkrDataConverted = NULL; delete time; time = NULL; - - // libsamplerate-related - _src_state_mic = src_delete(_src_state_mic); - _src_state_spkr = src_delete(_src_state_spkr); } void AudioRtpRTX::initBuffers() { - converter = new SamplerateConverter(); + converter = new SamplerateConverter( _layerSampleRate , _layerFrameSize ); int nbSamplesMax = (int) (_layerSampleRate * _layerFrameSize /1000); - _dataAudioLayer = new SFLDataFormat[nbSamplesMax]; - _receiveDataDecoded = new int16[nbSamplesMax]; - _floatBufferDown = new float32[nbSamplesMax]; - _floatBufferUp = new float32[nbSamplesMax]; - _sendDataEncoded = new unsigned char[nbSamplesMax]; - _intBufferDown = new int16[nbSamplesMax]; + + micData = new SFLDataFormat[nbSamplesMax]; + micDataConverted = new SFLDataFormat[nbSamplesMax]; + micDataEncoded = new unsigned char[nbSamplesMax]; + + spkrDataConverted = new SFLDataFormat[nbSamplesMax]; + spkrDataDecoded = new SFLDataFormat[nbSamplesMax]; } void AudioRtpRTX::initAudioRtpSession (void) { - - - try { if (_ca == 0) { return; } _audiocodec = Manager::instance().getCodecDescriptorMap().getCodec( _ca->getAudioCodec() ); @@ -257,7 +239,6 @@ AudioRtpRTX::sendSessionFromMic(int timestamp) // 3. encode it // 4. send it try { - int16* toSIP = NULL; timestamp += time->getSecond(); if (_ca==0) { _debug(" !ARTP: No call associated (mic)\n"); return; } // no call, so we do nothing @@ -270,50 +251,38 @@ AudioRtpRTX::sendSessionFromMic(int timestamp) int maxBytesToGet = _layerSampleRate * _layerFrameSize * sizeof(SFLDataFormat) / 1000; // available bytes inside ringbuffer int availBytesFromMic = audiolayer->canGetMic(); - //printf("%i \n", availBytesFromMic); // take the lowest int bytesAvail = (availBytesFromMic < maxBytesToGet) ? availBytesFromMic : maxBytesToGet; - //printf("%i\n", bytesAvail); // Get bytes from micRingBuffer to data_from_mic - int nbSample = audiolayer->getMic(_dataAudioLayer, bytesAvail) / sizeof(SFLDataFormat); + //_debug("get data from mic\n"); + int nbSample = audiolayer->getMic( micData , bytesAvail ) / sizeof(SFLDataFormat); int nb_sample_up = nbSample; int nbSamplesMax = _layerFrameSize * _audiocodec->getClockRate() / 1000; + //_debug("resample data\n"); nbSample = reSampleData(_audiocodec->getClockRate(), nb_sample_up, DOWN_SAMPLING); - toSIP = _intBufferDown; - if ( nbSample < nbSamplesMax - 10 ) { // if only 10 is missing, it's ok // fill end with 0... - //_debug("begin: %p, nbSample: %d\n", toSIP, nbSample); - memset(toSIP + nbSample, 0, (nbSamplesMax-nbSample)*sizeof(int16)); + memset( micDataConverted + nbSample, 0, (nbSamplesMax-nbSample)*sizeof(int16)); nbSample = nbSamplesMax; } - // debug - dump sound in a file - //_debug("AR: Nb sample: %d int, [0]=%d [1]=%d [2]=%d\n", nbSample, toSIP[0], toSIP[1], toSIP[2]); - // for the mono: range = 0 to RTP_FRAME2SEND * sizeof(int16) - // codecEncode(char *dest, int16* src, size in bytes of the src) - int compSize = _audiocodec->codecEncode(_sendDataEncoded, toSIP, nbSample*sizeof(int16)); - //printf("jusqu'ici tout vas bien\n"); - + int compSize = _audiocodec->codecEncode( micDataEncoded , micDataConverted , nbSample*sizeof(int16)); // encode divise by two // Send encoded audio sample over the network if (compSize > nbSamplesMax) { _debug("! ARTP: %d should be %d\n", compSize, nbSamplesMax);} if (!_sym) { - _sessionSend->putData(timestamp, _sendDataEncoded, compSize); + _sessionSend->putData(timestamp, micDataEncoded, compSize); } else { - _session->putData(timestamp, _sendDataEncoded, compSize); + _session->putData(timestamp, micDataEncoded, compSize); } - toSIP = NULL; } catch(...) { _debugException("! ARTP: sending failed"); throw; } } - - void AudioRtpRTX::receiveSessionForSpkr (int& countTime) { @@ -338,11 +307,9 @@ AudioRtpRTX::receiveSessionForSpkr (int& countTime) } int payload = adu->getType(); // codec type - unsigned char* data = (unsigned char*)adu->getData(); // data in char + unsigned char* spkrData = (unsigned char*)adu->getData(); // data in char unsigned int size = adu->getSize(); // size in char - - //_fstream.write((char*) data, size); // Decode data with relevant codec int max = (int)(_codecSampleRate * _layerFrameSize / 1000); @@ -352,13 +319,9 @@ AudioRtpRTX::receiveSessionForSpkr (int& countTime) size=max; } - //printf("size = %i\n", size); - if (_audiocodec != NULL) { - int expandedSize = _audiocodec->codecDecode(_receiveDataDecoded, data, size); - // printf("%i\n", expandedSize); - //_fstream.write((char*) _receiveDataDecoded, ); + int expandedSize = _audiocodec->codecDecode( spkrDataDecoded , spkrData , size ); //buffer _receiveDataDecoded ----> short int or int16, coded on 2 bytes int nbInt16 = expandedSize / sizeof(int16); //nbInt16 represents the number of samples we just decoded @@ -366,21 +329,16 @@ AudioRtpRTX::receiveSessionForSpkr (int& countTime) _debug("We have decoded an RTP packet larger than expected: %s VS %s. Cropping.\n", nbInt16, max); nbInt16=max; } - - SFLDataFormat* toAudioLayer; int nbSample = nbInt16; // Do sample rate conversion int nb_sample_down = nbSample; nbSample = reSampleData(_codecSampleRate , nb_sample_down, UP_SAMPLING); #ifdef DATAFORMAT_IS_FLOAT - toAudioLayer = _floatBufferUp; #else - toAudioLayer = _dataAudioLayer; #endif - - audiolayer->playSamples(toAudioLayer, nbSample * sizeof(SFLDataFormat), true); + audiolayer->playSamples( spkrDataConverted , nbSample * sizeof(SFLDataFormat), true); // Notify (with a beep) an incoming call when there is already a call countTime += time->getSecond(); if (Manager::instance().incomingCallWaiting() > 0) { @@ -393,7 +351,6 @@ AudioRtpRTX::receiveSessionForSpkr (int& countTime) } else { countTime += time->getSecond(); } - delete adu; adu = NULL; } catch(...) { _debugException("! ARTP: receiving failed"); @@ -407,75 +364,20 @@ AudioRtpRTX::receiveSessionForSpkr (int& countTime) AudioRtpRTX::reSampleData(int sampleRate_codec, int nbSamples, int status) { if(status==UP_SAMPLING){ - //return upSampleData(sampleRate_codec, nbSamples); - return converter->upsampleData( _receiveDataDecoded , _dataAudioLayer, sampleRate_codec , _layerSampleRate , nbSamples ); + return converter->upsampleData( spkrDataDecoded , spkrDataConverted , sampleRate_codec , _layerSampleRate , nbSamples ); } else if(status==DOWN_SAMPLING){ - //return downSampleData(sampleRate_codec, nbSamples); - return converter->downsampleData( _dataAudioLayer , _intBufferDown, sampleRate_codec , _layerSampleRate , nbSamples ); + return converter->downsampleData( micData , micDataConverted , sampleRate_codec , _layerSampleRate , nbSamples ); } else return 0; } -//////////////////////////////////////////////////////////////////// -//////////// RESAMPLING FUNCTIONS ///////////////////////////////// -////////////////////////////////////////////////////////////////// - - int -AudioRtpRTX::upSampleData(int sampleRate_codec, int nbSamples) -{ - double upsampleFactor = (double) _layerSampleRate / sampleRate_codec; - int nbSamplesMax = (int) (_layerSampleRate * _layerFrameSize /1000); - if( upsampleFactor != 1 ) - { - SRC_DATA src_data; - src_data.data_in = _floatBufferDown; - src_data.data_out = _floatBufferUp; - src_data.input_frames = nbSamples; - src_data.output_frames = (int) floor(upsampleFactor * nbSamples); - src_data.src_ratio = upsampleFactor; - src_data.end_of_input = 0; // More data will come - src_short_to_float_array(_receiveDataDecoded, _floatBufferDown, nbSamples); - src_process(_src_state_spkr, &src_data); - nbSamples = ( src_data.output_frames_gen > nbSamplesMax) ? nbSamplesMax : src_data.output_frames_gen; - src_float_to_short_array(_floatBufferUp, _dataAudioLayer, nbSamples); - } - - return nbSamples; -} - - int -AudioRtpRTX::downSampleData(int sampleRate_codec, int nbSamples) -{ - double downsampleFactor = (double) sampleRate_codec / _layerSampleRate; - int nbSamplesMax = (int) (sampleRate_codec * _layerFrameSize / 1000); - if ( downsampleFactor != 1) - { - SRC_DATA src_data; - src_data.data_in = _floatBufferUp; - src_data.data_out = _floatBufferDown; - src_data.input_frames = nbSamples; - src_data.output_frames = (int) floor(downsampleFactor * nbSamples); - src_data.src_ratio = downsampleFactor; - src_data.end_of_input = 0; // More data will come - src_short_to_float_array(_dataAudioLayer, _floatBufferUp, nbSamples); - src_process(_src_state_mic, &src_data); - nbSamples = ( src_data.output_frames_gen > nbSamplesMax) ? nbSamplesMax : src_data.output_frames_gen; - src_float_to_short_array(_floatBufferDown, _intBufferDown, nbSamples); - } - return nbSamples; - -} - -//////////////////////// END RESAMPLING ////////////////////////////////////////////////////// - void AudioRtpRTX::run () { //mic, we receive from soundcard in stereo, and we send encoded //encoding before sending AudioLayer *audiolayer = Manager::instance().getAudioDriver(); - //loadCodec(_ca->getAudioCodec()); _layerFrameSize = audiolayer->getFrameSize(); // en ms _layerSampleRate = audiolayer->getSampleRate(); initBuffers(); @@ -499,7 +401,6 @@ AudioRtpRTX::run () { int countTime = 0; // for receive TimerPort::setTimer(_layerFrameSize); - //audiolayer->flushMic(); audiolayer->startStream(); _start.post(); _debug("- ARTP Action: Start\n"); @@ -517,8 +418,6 @@ AudioRtpRTX::run () { Thread::sleep(TimerPort::getTimer()); TimerPort::incTimer(_layerFrameSize); // 'frameSize' ms } - //_fstream.close(); - //unloadCodec(); //_debug("stop stream for audiortp loop\n"); audiolayer->stopStream(); } catch(std::exception &e) { diff --git a/src/audio/audiortp.h b/src/audio/audiortp.h index 6e61f6054a..a0cb003bf3 100644 --- a/src/audio/audiortp.h +++ b/src/audio/audiortp.h @@ -28,8 +28,6 @@ #include <ccrtp/rtp.h> #include <cc++/numbers.h> -#include <samplerate.h> - #include "../global.h" #include "../samplerateconverter.h" @@ -85,34 +83,16 @@ class AudioRtpRTX : public ost::Thread, public ost::TimerPort { /** Is the session symmetric or not */ bool _sym; - /** When we receive data, we decode it inside this buffer */ - int16* _receiveDataDecoded; - - /** Buffers used for send data from the mic */ - unsigned char* _sendDataEncoded; - - /** Downsampled int16 buffer */ - int16* _intBufferDown; - - /** After that we send the data inside this buffer if there is a format conversion or rate conversion */ - /** Also use for getting mic-ringbuffer data */ - SFLDataFormat* _dataAudioLayer; - - /** Downsampled float buffer */ - float32* _floatBufferDown; + /** Mic-data related buffers */ + SFLDataFormat* micData; + SFLDataFormat* micDataConverted; + unsigned char* micDataEncoded; - /** Upsampled float buffer */ - float32* _floatBufferUp; - - /** libsamplerate converter for incoming voice */ - SRC_STATE* _src_state_spkr; - - /** libsamplerate converter for outgoing voice */ - SRC_STATE* _src_state_mic; - - /** libsamplerate error */ - int _src_err; + /** Speaker-data related buffers */ + SFLDataFormat* spkrDataDecoded; + SFLDataFormat* spkrDataConverted; + /** Sample rate converter object */ SamplerateConverter* converter; /** Variables to process audio stream: sample rate for playing sound (typically 44100HZ) */ @@ -158,22 +138,6 @@ class AudioRtpRTX : public ost::Thread, public ost::TimerPort { */ int reSampleData(int sampleRate_codec, int nbSamples, int status); - /** - * Upsample the data from the clock rate of the codec to the sample rate of the layer - * @param sampleRate_codec The sample rate of the codec selected to encode/decode the data - * @param nbSamples Number of samples to process - * @return int The number of samples after process - */ - int upSampleData(int sampleRate_codec, int nbSamples); - - /** - * Downsample the data from the sample rate of the layer to the clock rate of the codec - * @param sampleRate_codec The sample rate of the codec selected to encode/decode the data - * @param nbSamples Number of samples to process - * @return int The number of samples after process - */ - int downSampleData(int sampleRate_codec, int nbSamples); - /** The audio codec used during the session */ AudioCodec* _audiocodec; }; diff --git a/src/iaxvoiplink.cpp b/src/iaxvoiplink.cpp index 1cc5a5d264..6b76c9c123 100644 --- a/src/iaxvoiplink.cpp +++ b/src/iaxvoiplink.cpp @@ -25,12 +25,10 @@ #include "manager.h" #include "audio/audiolayer.h" -#include <samplerate.h> //#include <iax/iax-client.h> #include <math.h> #include <dlfcn.h> - #define IAX_BLOCKING 1 #define IAX_NONBLOCKING 0 @@ -40,20 +38,12 @@ #define RANDOM_IAX_PORT rand() % 64000 + 1024 #define MUSIC_ONHOLD true -#define NO_MUSIC_ONHOLD false - -// from IAXC : iaxclient.h - -#define IAX__20S_8KHZ_MAX 320 //320 samples, IAX packets can have more than 20ms. -#define IAX__20S_48KHZ_MAX 1920 // 320*6 samples = 1920, 6 = 48000/8000 #define CHK_VALID_CALL if (call == NULL) { _debug("IAX: Call doesn't exists\n"); \ - return false; } - + return false; } - -IAXVoIPLink::IAXVoIPLink(const AccountID& accountID) - : VoIPLink(accountID) + IAXVoIPLink::IAXVoIPLink(const AccountID& accountID) +: VoIPLink(accountID) { _evThread = new EventThread(this); _regSession = NULL; @@ -64,19 +54,16 @@ IAXVoIPLink::IAXVoIPLink(const AccountID& accountID) audiolayer = NULL; - _receiveDataDecoded = new int16[IAX__20S_48KHZ_MAX]; - _sendDataEncoded = new unsigned char[IAX__20S_8KHZ_MAX]; + converter = new SamplerateConverter(); - // we estimate that the number of format after a conversion 8000->48000 is expanded to 6 times - _dataAudioLayer = new SFLDataFormat[IAX__20S_48KHZ_MAX]; - _floatBuffer8000 = new float32[IAX__20S_8KHZ_MAX]; - _floatBuffer48000 = new float32[IAX__20S_48KHZ_MAX]; - _intBuffer8000 = new int16[IAX__20S_8KHZ_MAX]; + int nbSamplesMax = (int) ( converter->getFrequence() * converter->getFramesize() / 1000 ); - // libsamplerate-related - _src_state_mic = src_new(SRC_SINC_BEST_QUALITY, 1, &_src_err); - _src_state_spkr = src_new(SRC_SINC_BEST_QUALITY, 1, &_src_err); + micData = new SFLDataFormat[nbSamplesMax]; + micDataConverted = new SFLDataFormat[nbSamplesMax]; + micDataEncoded = new unsigned char[nbSamplesMax]; + spkrDataConverted = new SFLDataFormat[nbSamplesMax]; + spkrDataDecoded = new SFLDataFormat[nbSamplesMax]; } @@ -87,20 +74,16 @@ IAXVoIPLink::~IAXVoIPLink() terminate(); audiolayer = NULL; - delete [] _intBuffer8000; _intBuffer8000 = NULL; - delete [] _floatBuffer48000; _floatBuffer48000 = NULL; - delete [] _floatBuffer8000; _floatBuffer8000 = NULL; - delete [] _dataAudioLayer; _dataAudioLayer = NULL; - delete [] _sendDataEncoded; _sendDataEncoded = NULL; - delete [] _receiveDataDecoded; _receiveDataDecoded = NULL; + delete [] micData; micData = NULL; + delete [] micDataConverted; micDataConverted = NULL; + delete [] micDataEncoded; micDataEncoded = NULL; - // libsamplerate-related - _src_state_mic = src_delete(_src_state_mic); - _src_state_spkr = src_delete(_src_state_spkr); + delete [] spkrDataDecoded; spkrDataDecoded = NULL; + delete [] spkrDataConverted; spkrDataConverted = NULL; } -bool + bool IAXVoIPLink::init() { // If it was done, don't do it again, until we call terminate() @@ -139,15 +122,14 @@ IAXVoIPLink::init() } if (port == IAX_FAILURE || nbTry==0) { _debug("Fail to initialize iax\n"); - + _initDone = false; } - return returnValue; } -void + void IAXVoIPLink::terminate() { // If it was done, don't do it again, until we call init() @@ -162,7 +144,7 @@ IAXVoIPLink::terminate() _initDone = false; } -void + void IAXVoIPLink::terminateIAXCall() { std::string reason = "Dumped Call"; @@ -183,7 +165,7 @@ IAXVoIPLink::terminateIAXCall() _callMap.clear(); } -void + void IAXVoIPLink::getEvent() { IAXCall* call = NULL; @@ -214,7 +196,7 @@ IAXVoIPLink::getEvent() // We've got an event before it's associated with any call iaxHandlePrecallEvent(event); } - + iax_event_free(event); } _mutexIAX.leaveMutex(); @@ -233,7 +215,7 @@ IAXVoIPLink::getEvent() _evThread->sleep(3); } -void + void IAXVoIPLink::sendAudioFromMic(void) { IAXCall* currentCall = getIAXCall(Manager::instance().getCurrentCallId()); @@ -242,7 +224,7 @@ IAXVoIPLink::sendAudioFromMic(void) // Let's mind our own business. return; } - + if( currentCall -> getAudioCodec() < 0 ) return; @@ -266,13 +248,13 @@ IAXVoIPLink::sendAudioFromMic(void) } return; } - + // Send sound here if (audiolayer) { // we have to get 20ms of data from the mic *20/1000 = /50 // rate/50 shall be lower than IAX__20S_48KHZ_MAX - int maxBytesToGet = audiolayer->getSampleRate()/50*sizeof(SFLDataFormat); + int maxBytesToGet = audiolayer->getSampleRate()* audiolayer->getFrameSize() / 1000 * sizeof(SFLDataFormat); // available bytes inside ringbuffer int availBytesFromMic = audiolayer->canGetMic(); @@ -285,59 +267,21 @@ IAXVoIPLink::sendAudioFromMic(void) // take the lowest int bytesAvail = (availBytesFromMic < maxBytesToGet) ? availBytesFromMic : maxBytesToGet; //_debug("available = %d, maxBytesToGet = %d\n", availBytesFromMic, maxBytesToGet); - + // Get bytes from micRingBuffer to data_from_mic - int nbSample = audiolayer->getMic(_dataAudioLayer, bytesAvail) / sizeof(SFLDataFormat); - - // Audio ici est PARFAIT - int16* toIAX = NULL; - //if (audiolayer->getSampleRate() != audiocodec->getClockRate() && nbSample) { - if (audiolayer->getSampleRate() != ac ->getClockRate() && nbSample) { - SRC_DATA src_data; -#ifdef DATAFORMAT_IS_FLOAT - src_data.data_in = _dataAudioLayer; -#else - src_short_to_float_array(_dataAudioLayer, _floatBuffer48000, nbSample); - src_data.data_in = _floatBuffer48000; -#endif - - // Audio parfait à ce point. - double factord = (double) ac->getClockRate() / audiolayer->getSampleRate(); - - src_data.src_ratio = factord; - src_data.input_frames = nbSample; - src_data.output_frames = (int) floor(factord * nbSample); - src_data.data_out = _floatBuffer8000; - src_data.end_of_input = 0; - - src_process(_src_state_mic, &src_data); - - nbSample = src_data.output_frames_gen; - - // Bon, l'audio en float 8000 est laid mais yé consistant. - src_float_to_short_array (_floatBuffer8000, _intBuffer8000, nbSample); - toIAX = _intBuffer8000; - - // Audio bon ici aussi.. - } else { -#ifdef DATAFORMAT_IS_FLOAT - // convert _receiveDataDecoded to float inside _receiveData - src_float_to_short_array(_dataAudioLayer, _intBuffer8000, nbSample); - toIAX = _intBuffer8000; - //if (nbSample > IAX__20S_8KHZ_MAX) { _debug("Alert from mic, nbSample %d is bigger than expected %d\n", nbSample, IAX__20S_8KHZ_MAX); } -#else - toIAX = _dataAudioLayer; // int to int -#endif - } + int nbSample = audiolayer->getMic( micData, bytesAvail ) / sizeof(SFLDataFormat); + + // resample + nbSample = converter->downsampleData( micData , micDataConverted , (int)ac ->getClockRate() , (int)audiolayer->getSampleRate() , nbSample ); // for the mono: range = 0 to IAX_FRAME2SEND * sizeof(int16) - int compSize = ac->codecEncode(_sendDataEncoded, toIAX, nbSample*sizeof(int16)); + int compSize = ac->codecEncode( micDataEncoded, micDataConverted , nbSample*sizeof(int16)); // Send it out! _mutexIAX.enterMutex(); // Make sure the session and the call still exists. if (currentCall->getSession()) { - if ( iax_send_voice(currentCall->getSession(), currentCall->getFormat(), (unsigned char*)_sendDataEncoded, compSize, nbSample) == -1) { + if ( iax_send_voice(currentCall->getSession(), currentCall->getFormat(), micDataEncoded, compSize, nbSample) == -1) { _debug("IAX: Error sending voice data.\n"); } } @@ -346,7 +290,7 @@ IAXVoIPLink::sendAudioFromMic(void) } -IAXCall* + IAXCall* IAXVoIPLink::getIAXCall(const CallID& id) { Call* call = getCall(id); @@ -358,7 +302,7 @@ IAXVoIPLink::getIAXCall(const CallID& id) -bool + bool IAXVoIPLink::sendRegister() { bool result = false; @@ -412,7 +356,7 @@ IAXVoIPLink::sendRegister() -bool + bool IAXVoIPLink::sendUnregister() { _mutexIAX.enterMutex(); @@ -434,7 +378,7 @@ IAXVoIPLink::sendUnregister() return false; } -Call* + Call* IAXVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl) { IAXCall* call = new IAXCall(id, Call::Outgoing); @@ -456,12 +400,12 @@ IAXVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl) } -bool + bool IAXVoIPLink::answer(const CallID& id) { IAXCall* call = getIAXCall(id); call->setCodecMap(Manager::instance().getCodecDescriptorMap()); - + CHK_VALID_CALL; _mutexIAX.enterMutex(); @@ -477,7 +421,7 @@ IAXVoIPLink::answer(const CallID& id) return true; } -bool + bool IAXVoIPLink::hangup(const CallID& id) { IAXCall* call = getIAXCall(id); @@ -495,7 +439,7 @@ IAXVoIPLink::hangup(const CallID& id) return true; } -bool + bool IAXVoIPLink::onhold(const CallID& id) { IAXCall* call = getIAXCall(id); @@ -503,7 +447,7 @@ IAXVoIPLink::onhold(const CallID& id) CHK_VALID_CALL; //if (call->getState() == Call::Hold) { _debug("Call is already on hold\n"); return false; } - + _mutexIAX.enterMutex(); iax_quelch_moh(call->getSession() , MUSIC_ONHOLD); _mutexIAX.leaveMutex(); @@ -512,7 +456,7 @@ IAXVoIPLink::onhold(const CallID& id) return true; } -bool + bool IAXVoIPLink::offhold(const CallID& id) { IAXCall* call = getIAXCall(id); @@ -528,7 +472,7 @@ IAXVoIPLink::offhold(const CallID& id) return true; } -bool + bool IAXVoIPLink::transfer(const CallID& id, const std::string& to) { IAXCall* call = getIAXCall(id); @@ -537,7 +481,7 @@ IAXVoIPLink::transfer(const CallID& id, const std::string& to) char callto[to.length()+1]; strcpy(callto, to.c_str()); - + _mutexIAX.enterMutex(); iax_transfer(call->getSession(), callto); _mutexIAX.leaveMutex(); @@ -546,7 +490,7 @@ IAXVoIPLink::transfer(const CallID& id, const std::string& to) // removeCall(id); } -bool + bool IAXVoIPLink::refuse(const CallID& id) { IAXCall* call = getIAXCall(id); @@ -560,7 +504,7 @@ IAXVoIPLink::refuse(const CallID& id) removeCall(id); } -bool + bool IAXVoIPLink::carryingDTMFdigits(const CallID& id, char code) { IAXCall* call = getIAXCall(id); @@ -574,7 +518,7 @@ IAXVoIPLink::carryingDTMFdigits(const CallID& id, char code) -bool + bool IAXVoIPLink::iaxOutgoingInvite(IAXCall* call) { struct iax_session *newsession; @@ -582,18 +526,18 @@ IAXVoIPLink::iaxOutgoingInvite(IAXCall* call) newsession = iax_session_new(); if (!newsession) { - _debug("IAX Error: Can't make new session for a new call\n"); - return false; + _debug("IAX Error: Can't make new session for a new call\n"); + return false; } call->setSession(newsession); /* reset activity and ping "timers" */ // iaxc_note_activity(callNo); - + std::string strNum = _user + ":" + _pass + "@" + _host + "/" + call->getPeerNumber(); char user[_user.length()+1]; strcpy(user, _user.c_str()); - + char num[strNum.length()+1]; strcpy(num, strNum.c_str()); @@ -610,7 +554,7 @@ IAXVoIPLink::iaxOutgoingInvite(IAXCall* call) } -IAXCall* + IAXCall* IAXVoIPLink::iaxFindCallBySession(struct iax_session* session) { // access to callMap shoud use that @@ -628,7 +572,7 @@ IAXVoIPLink::iaxFindCallBySession(struct iax_session* session) return NULL; // not found } -void + void IAXVoIPLink::iaxHandleCallEvent(iax_event* event, IAXCall* call) { // call should not be 0 @@ -636,192 +580,158 @@ IAXVoIPLink::iaxHandleCallEvent(iax_event* event, IAXCall* call) // CallID id = call->getCallId(); int16* output = 0; // for audio output - + switch(event->etype) { - case IAX_EVENT_HANGUP: - Manager::instance().peerHungupCall(id); - if (Manager::instance().isCurrentCall(id)) { - audiolayer->stopStream(); - // stop audio - } - removeCall(id); - break; - - case IAX_EVENT_REJECT: - //Manager::instance().peerHungupCall(id); - if (Manager::instance().isCurrentCall(id)) { - // stop audio - audiolayer->stopStream(); - } - call->setConnectionState(Call::Connected); - call->setState(Call::Error); - Manager::instance().callFailure(id); - removeCall(id); - break; - - case IAX_EVENT_ACCEPT: - // Call accepted over there by the computer, not the user yet. - if (event->ies.format) { - call->setFormat(event->ies.format); - } - break; - - case IAX_EVENT_ANSWER: - if (call->getConnectionState() != Call::Connected){ + case IAX_EVENT_HANGUP: + Manager::instance().peerHungupCall(id); + if (Manager::instance().isCurrentCall(id)) { + audiolayer->stopStream(); + // stop audio + } + removeCall(id); + break; + + case IAX_EVENT_REJECT: + //Manager::instance().peerHungupCall(id); + if (Manager::instance().isCurrentCall(id)) { + // stop audio + audiolayer->stopStream(); + } call->setConnectionState(Call::Connected); - call->setState(Call::Active); + call->setState(Call::Error); + Manager::instance().callFailure(id); + removeCall(id); + break; + case IAX_EVENT_ACCEPT: + // Call accepted over there by the computer, not the user yet. if (event->ies.format) { - // Should not get here, should have been set in EVENT_ACCEPT call->setFormat(event->ies.format); } - - Manager::instance().peerAnsweredCall(id); - //audiolayer->flushMic(); - audiolayer->startStream(); - // start audio here? - } else { - // deja connecté ? - } - break; - - case IAX_EVENT_BUSY: - call->setConnectionState(Call::Connected); - call->setState(Call::Busy); - Manager::instance().callBusy(id); - removeCall(id); - break; - - case IAX_EVENT_VOICE: - //_debug("Should have a decent value!!!!!! = %i\n" , call -> getAudioCodec()); - if( !audiolayer -> isCaptureActive()) - audiolayer->startStream(); - iaxHandleVoiceEvent(event, call); - break; - - case IAX_EVENT_TEXT: - break; - - case IAX_EVENT_RINGA: - call->setConnectionState(Call::Ringing); - Manager::instance().peerRingingCall(call->getCallId()); - break; - - case IAX_IE_MSGCOUNT: - // _debug("messssssssssssssssssssssssssssssssssssssssssssssssages\n"); - break; - case IAX_EVENT_PONG: - break; - - case IAX_EVENT_URL: - break; - - // case IAX_EVENT_CNG: ?? - // break; - - case IAX_EVENT_TIMEOUT: - break; - - case IAX_EVENT_TRANSFER: - break; - - default: - _debug("Unknown event type (in call event): %d\n", event->etype); - + break; + + case IAX_EVENT_ANSWER: + if (call->getConnectionState() != Call::Connected){ + call->setConnectionState(Call::Connected); + call->setState(Call::Active); + + if (event->ies.format) { + // Should not get here, should have been set in EVENT_ACCEPT + call->setFormat(event->ies.format); + } + + Manager::instance().peerAnsweredCall(id); + //audiolayer->flushMic(); + audiolayer->startStream(); + // start audio here? + } else { + // deja connecté ? + } + break; + + case IAX_EVENT_BUSY: + call->setConnectionState(Call::Connected); + call->setState(Call::Busy); + Manager::instance().callBusy(id); + removeCall(id); + break; + + case IAX_EVENT_VOICE: + //_debug("Should have a decent value!!!!!! = %i\n" , call -> getAudioCodec()); + //if( !audiolayer -> isCaptureActive()) + //audiolayer->startStream(); + iaxHandleVoiceEvent(event, call); + break; + + case IAX_EVENT_TEXT: + break; + + case IAX_EVENT_RINGA: + call->setConnectionState(Call::Ringing); + Manager::instance().peerRingingCall(call->getCallId()); + break; + + case IAX_IE_MSGCOUNT: + break; + case IAX_EVENT_PONG: + break; + + case IAX_EVENT_URL: + break; + + // case IAX_EVENT_CNG: ?? + // break; + + case IAX_EVENT_TIMEOUT: + break; + + case IAX_EVENT_TRANSFER: + break; + + default: + _debug("Unknown event type (in call event): %d\n", event->etype); + } } /* Handle audio event, VOICE packet received */ -void + void IAXVoIPLink::iaxHandleVoiceEvent(iax_event* event, IAXCall* call) { - // If we receive datalen == 0, some things of the jitter buffer in libiax2/iax.c - // were triggered - if (!event->datalen) { - // Skip this empty packet. - //_debug("IAX: Skipping empty jitter-buffer interpolated packet\n"); - return; + // If we receive datalen == 0, some things of the jitter buffer in libiax2/iax.c + // were triggered + if (!event->datalen) { + // Skip this empty packet. + //_debug("IAX: Skipping empty jitter-buffer interpolated packet\n"); + return; + } + + if (audiolayer) { + // On-the-fly codec changing (normally, when we receive a full packet) + // as per http://tools.ietf.org/id/draft-guy-iax-03.txt + // - subclass holds the voiceformat property. + if (event->subclass && event->subclass != call->getFormat()) { + call->setFormat(event->subclass); } + //_debug("Receive: len=%d, format=%d, _receiveDataDecoded=%p\n", event->datalen, call->getFormat(), _receiveDataDecoded); + AudioCodec* ac = call->getCodecMap().getCodec( call -> getAudioCodec() ); - if (audiolayer) { - // _debug("codec = %i\n" , call->getFormat()); - // _debug("codec = %i\n" , _audiocodec->getPayload()); - //_debug("codec = %s\n" , _audiocodec->getCodecName().c_str()); - // On-the-fly codec changing (normally, when we receive a full packet) - // as per http://tools.ietf.org/id/draft-guy-iax-03.txt - // - subclass holds the voiceformat property. - if (event->subclass && event->subclass != call->getFormat()) { - call->setFormat(event->subclass); - } - //_debug("Receive: len=%d, format=%d, _receiveDataDecoded=%p\n", event->datalen, call->getFormat(), _receiveDataDecoded); - AudioCodec* ac = call->getCodecMap().getCodec( call -> getAudioCodec() ); + unsigned char* data = (unsigned char*)event->data; + unsigned int size = event->datalen; - unsigned char* data = (unsigned char*)event->data; - unsigned int size = event->datalen; + // Decode data with relevant codec + int max = (int)( ac->getClockRate() * audiolayer->getFrameSize() / 1000 ); - if (size > IAX__20S_8KHZ_MAX) { - _debug("The size %d is bigger than expected %d. Packet cropped. Ouch!\n", size, IAX__20S_8KHZ_MAX); - size = IAX__20S_8KHZ_MAX; - } + if (size > max) { + _debug("The size %d is bigger than expected %d. Packet cropped. Ouch!\n", size, max); + size = max; + } - int expandedSize = ac->codecDecode(_receiveDataDecoded, data, size); - int nbInt16 = expandedSize/sizeof(int16); + int expandedSize = ac->codecDecode( spkrDataDecoded , data , size ); + int nbInt16 = expandedSize/sizeof(int16); - if (nbInt16 > IAX__20S_8KHZ_MAX) { - _debug("We have decoded an IAX VOICE packet larger than expected: %s VS %s. Cropping.\n", nbInt16, IAX__20S_8KHZ_MAX); - nbInt16 = IAX__20S_8KHZ_MAX; - } - - SFLDataFormat* toAudioLayer; - int nbSample = nbInt16; - int nbSampleMaxRate = nbInt16 * 6; - - if ( audiolayer->getSampleRate() != ac->getClockRate() && nbSample ) { - // Do sample rate conversion - double factord = (double) audiolayer->getSampleRate() / ac->getClockRate(); - // SRC_DATA from samplerate.h - SRC_DATA src_data; - src_data.data_in = _floatBuffer8000; - src_data.data_out = _floatBuffer48000; - src_data.input_frames = nbSample; - src_data.output_frames = (int) floor(factord * nbSample); - src_data.src_ratio = factord; - src_data.end_of_input = 0; - src_short_to_float_array(_receiveDataDecoded, _floatBuffer8000, nbSample); - - // samplerate convert, go! - src_process(_src_state_spkr, &src_data); - - nbSample = ( src_data.output_frames_gen > IAX__20S_48KHZ_MAX) ? IAX__20S_48KHZ_MAX : src_data.output_frames_gen; -#ifdef DATAFORMAT_IS_FLOAT - toAudioLayer = _floatBuffer48000; -#else - src_float_to_short_array(_floatBuffer48000, _dataAudioLayer, nbSample); - toAudioLayer = _dataAudioLayer; -#endif - - } else { - nbSample = nbInt16; -#ifdef DATAFORMAT_IS_FLOAT - // convert _receiveDataDecoded to float inside _receiveData - src_short_to_float_array(_receiveDataDecoded, _floatBuffer8000, nbSample); - toAudioLayer = _floatBuffer8000; -#else - toAudioLayer = _receiveDataDecoded; // int to int -#endif - } - audiolayer->playSamples(toAudioLayer, nbSample * sizeof(SFLDataFormat), true); - } else { - _debug("IAX: incoming audio, but no sound card open"); + if (nbInt16 > max) { + _debug("We have decoded an IAX VOICE packet larger than expected: %s VS %s. Cropping.\n", nbInt16, max); + nbInt16 = max; } + int nbSample = nbInt16; + // resample + nbInt16 = converter->upsampleData( spkrDataDecoded , spkrDataConverted , ac->getClockRate() , audiolayer->getSampleRate() , nbSample); + + audiolayer->playSamples( spkrDataConverted , nbInt16 * sizeof(SFLDataFormat), true); + + } else { + _debug("IAX: incoming audio, but no sound card open"); + } + } /** * Handle the registration process */ -void + void IAXVoIPLink::iaxHandleRegReply(iax_event* event) { if (event->etype == IAX_EVENT_REGREJ) { @@ -846,7 +756,7 @@ IAXVoIPLink::iaxHandleRegReply(iax_event* event) } } -void + void IAXVoIPLink::iaxHandlePrecallEvent(iax_event* event) { IAXCall* call = NULL; @@ -854,96 +764,96 @@ IAXVoIPLink::iaxHandlePrecallEvent(iax_event* event) std::string reason = "Error ringing user."; switch(event->etype) { - case IAX_EVENT_REGACK: - case IAX_EVENT_REGREJ: - _debug("IAX Registration Event in a pre-call setup\n"); - break; - - case IAX_EVENT_REGREQ: - // Received when someone wants to register to us!?! - // Asterisk receives and answers to that, not us, we're a phone. - _debug("Registration by a peer, don't allow it\n"); - break; - - case IAX_EVENT_CONNECT: - // We've got an incoming call! Yikes! - _debug("> IAX_EVENT_CONNECT (receive)\n"); - - id = Manager::instance().getNewCallID(); - - - call = new IAXCall(id, Call::Incoming); - - if (!call) { - _debug("! IAX Failure: unable to create an incoming call"); - return; - } + case IAX_EVENT_REGACK: + case IAX_EVENT_REGREJ: + _debug("IAX Registration Event in a pre-call setup\n"); + break; - // Setup the new IAXCall - // Associate the call to the session. - call->setSession(event->session); + case IAX_EVENT_REGREQ: + // Received when someone wants to register to us!?! + // Asterisk receives and answers to that, not us, we're a phone. + _debug("Registration by a peer, don't allow it\n"); + break; - // setCallAudioLocal(call); - call->setCodecMap(Manager::instance().getCodecDescriptorMap()); - call->setConnectionState(Call::Progressing); + case IAX_EVENT_CONNECT: + // We've got an incoming call! Yikes! + _debug("> IAX_EVENT_CONNECT (receive)\n"); + id = Manager::instance().getNewCallID(); - if (event->ies.calling_number) - call->setPeerNumber(std::string(event->ies.calling_number)); - if (event->ies.calling_name) - call->setPeerName(std::string(event->ies.calling_name)); - if (Manager::instance().incomingCall(call, getAccountID())) { - /** @todo Faudra considérer éventuellement le champ CODEC PREFS pour - * l'établissement du codec de transmission */ + call = new IAXCall(id, Call::Incoming); - // Remote lists its capabilities - int format = call->getFirstMatchingFormat(event->ies.capability); - // Remote asks for preferred codec voiceformat - int pref_format = call->getFirstMatchingFormat(event->ies.format); + if (!call) { + _debug("! IAX Failure: unable to create an incoming call"); + return; + } - // Priority to remote's suggestion. In case it's a forwarding, no transcoding - // will be needed from the server, thus less latency. - if (pref_format) - format = pref_format; + // Setup the new IAXCall + // Associate the call to the session. + call->setSession(event->session); - iax_accept(event->session, format); - iax_ring_announce(event->session); + // setCallAudioLocal(call); + call->setCodecMap(Manager::instance().getCodecDescriptorMap()); + call->setConnectionState(Call::Progressing); - addCall(call); - } else { - // reject call, unable to add it - iax_reject(event->session, (char*)reason.c_str()); - delete call; call = NULL; - } + if (event->ies.calling_number) + call->setPeerNumber(std::string(event->ies.calling_number)); + if (event->ies.calling_name) + call->setPeerName(std::string(event->ies.calling_name)); - break; - - case IAX_EVENT_HANGUP: - // Remote peer hung up - call = iaxFindCallBySession(event->session); - id = call->getCallId(); - - Manager::instance().peerHungupCall(id); - removeCall(id); - break; - - case IAX_EVENT_TIMEOUT: // timeout for an unknown session - - break; - - case IAX_IE_MSGCOUNT: - //_debug("messssssssssssssssssssssssssssssssssssssssssssssssages\n"); - break; - - default: - _debug("Unknown event type (in precall): %d\n", event->etype); + if (Manager::instance().incomingCall(call, getAccountID())) { + /** @todo Faudra considérer éventuellement le champ CODEC PREFS pour + * l'établissement du codec de transmission */ + + // Remote lists its capabilities + int format = call->getFirstMatchingFormat(event->ies.capability); + // Remote asks for preferred codec voiceformat + int pref_format = call->getFirstMatchingFormat(event->ies.format); + + // Priority to remote's suggestion. In case it's a forwarding, no transcoding + // will be needed from the server, thus less latency. + if (pref_format) + format = pref_format; + + iax_accept(event->session, format); + iax_ring_announce(event->session); + + addCall(call); + } else { + // reject call, unable to add it + iax_reject(event->session, (char*)reason.c_str()); + + delete call; call = NULL; + } + + break; + + case IAX_EVENT_HANGUP: + // Remote peer hung up + call = iaxFindCallBySession(event->session); + id = call->getCallId(); + + Manager::instance().peerHungupCall(id); + removeCall(id); + break; + + case IAX_EVENT_TIMEOUT: // timeout for an unknown session + + break; + + case IAX_IE_MSGCOUNT: + //_debug("messssssssssssssssssssssssssssssssssssssssssssssssages\n"); + break; + + default: + _debug("Unknown event type (in precall): %d\n", event->etype); } - + } -int + int IAXVoIPLink::iaxCodecMapToFormat(IAXCall* call) { CodecOrder map = call->getCodecMap().getActiveCodecs(); diff --git a/src/iaxvoiplink.h b/src/iaxvoiplink.h index ea67145ede..3a8c3fbd4a 100644 --- a/src/iaxvoiplink.h +++ b/src/iaxvoiplink.h @@ -1,8 +1,8 @@ /* - * Copyright (C) 2006-2007 Savoir-Faire Linux inc. + * Copyright (C) 2006-2007-2008 Savoir-Faire Linux inc. + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> * Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com> * Author: Yan Morin <yan.morin@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 @@ -24,9 +24,9 @@ #include "voiplink.h" #include <iax2/iax-client.h> #include "global.h" -#include <samplerate.h> #include "audio/codecDescriptor.h" +#include "samplerateconverter.h" class EventThread; class IAXCall; @@ -279,31 +279,17 @@ class IAXVoIPLink : public VoIPLink /** Connection to audio card/device */ AudioLayer* audiolayer; - /** When we receive data, we decode it inside this buffer */ - int16* _receiveDataDecoded; - /** When we send data, we encode it inside this buffer*/ - unsigned char* _sendDataEncoded; - - /** After that we send the data inside this buffer if there is a format conversion or rate conversion. */ - /* Also use for getting mic-ringbuffer data */ - SFLDataFormat* _dataAudioLayer; - - /** Buffer for 8000hz samples in conversion */ - float32* _floatBuffer8000; - /** Buffer for 48000hz samples in conversion */ - float32* _floatBuffer48000; - - /** Buffer for 8000hz samples for mic conversion */ - int16* _intBuffer8000; - - /** libsamplerate converter for incoming voice */ - SRC_STATE* _src_state_spkr; + /** Mic-data related buffers */ + SFLDataFormat* micData; + SFLDataFormat* micDataConverted; + unsigned char* micDataEncoded; - /** libsamplerate converter for outgoing voice */ - SRC_STATE* _src_state_mic; + /** Speaker-data related buffers */ + SFLDataFormat* spkrDataDecoded; + SFLDataFormat* spkrDataConverted; - /** libsamplerate error */ - int _src_err; + /** Sample rate converter object */ + SamplerateConverter* converter; }; diff --git a/src/samplerateconverter.cpp b/src/samplerateconverter.cpp index e9285a3d27..126c5d957c 100644 --- a/src/samplerateconverter.cpp +++ b/src/samplerateconverter.cpp @@ -19,71 +19,98 @@ #include "samplerateconverter.h" SamplerateConverter::SamplerateConverter( void ) { - - // libSamplerateConverter-related - // Set the converter type for the upsampling and the downsampling - // interpolator SRC_SINC_BEST_QUALITY - _src_state_mic = src_new(SRC_SINC_BEST_QUALITY, 1, &_src_err); - _src_state_spkr = src_new(SRC_SINC_BEST_QUALITY, 1, &_src_err); + // Default values + _frequence = Manager::instance().getConfigInt( AUDIO , ALSA_SAMPLE_RATE ); // 44100; + _framesize = Manager::instance().getConfigInt( AUDIO , ALSA_FRAME_SIZE ); + + init(); +} - int nbSamplesMax = (int) ( 44100 * 20 /1000); // TODO Make this generic - _floatBufferDown = new float32[nbSamplesMax]; - _floatBufferUp = new float32[nbSamplesMax]; +SamplerateConverter::SamplerateConverter( int freq , int fs ) { + _frequence = freq ; + _framesize = fs ; + + init(); } SamplerateConverter::~SamplerateConverter( void ) { - delete [] _floatBufferUp; _floatBufferUp = NULL; - delete [] _floatBufferDown; _floatBufferDown = NULL; + delete [] _floatBufferUpMic; _floatBufferUpMic = NULL; + delete [] _floatBufferDownMic; _floatBufferDownMic = NULL; + + delete [] _floatBufferUpSpkr; _floatBufferUpSpkr = NULL; + delete [] _floatBufferDownSpkr; _floatBufferDownSpkr = NULL; // libSamplerateConverter-related _src_state_mic = src_delete(_src_state_mic); _src_state_spkr = src_delete(_src_state_spkr); } +void SamplerateConverter::init( void ) { + + // libSamplerateConverter-related + // Set the converter type for the upsampling and the downsampling + // interpolator SRC_SINC_BEST_QUALITY + _src_state_mic = src_new(SRC_SINC_BEST_QUALITY, 1, &_src_err); + _src_state_spkr = src_new(SRC_SINC_BEST_QUALITY, 1, &_src_err); + + int nbSamplesMax = (int) ( getFrequence() * getFramesize() / 1000 ); + _floatBufferDownMic = new float32[nbSamplesMax]; + _floatBufferUpMic = new float32[nbSamplesMax]; + _floatBufferDownSpkr = new float32[nbSamplesMax]; + _floatBufferUpSpkr = new float32[nbSamplesMax]; +} + +//TODO Add ifdef for int16 or float32 type int SamplerateConverter::upsampleData( SFLDataFormat* dataIn , SFLDataFormat* dataOut, int samplerate1 , int samplerate2 , int nbSamples ){ double upsampleFactor = (double)samplerate2 / samplerate1 ; - int nbSamplesMax = (int) (samplerate2 * 20 /1000); // TODO get the value from the constructor - if( upsampleFactor != 1 ) + int nbSamplesMax = (int) ( samplerate2 * getFramesize() / 1000 ); + if( upsampleFactor != 1 && dataIn != NULL ) { - _debug("Begin upsample data\n"); SRC_DATA src_data; - src_data.data_in = _floatBufferDown; - src_data.data_out = _floatBufferUp; + src_data.data_in = _floatBufferDownSpkr; + src_data.data_out = _floatBufferUpSpkr; src_data.input_frames = nbSamples; src_data.output_frames = (int) floor(upsampleFactor * nbSamples); src_data.src_ratio = upsampleFactor; src_data.end_of_input = 0; // More data will come - src_short_to_float_array( dataIn , _floatBufferDown, nbSamples); + //_debug("upsample %d %d %f %d\n" , src_data.input_frames , src_data.output_frames, src_data.src_ratio , nbSamples); + src_short_to_float_array( dataIn , _floatBufferDownSpkr, nbSamples); + //_debug("upsample %d %f %d\n" , src_data.output_frames, src_data.src_ratio , nbSamples); src_process(_src_state_spkr, &src_data); + //_debug("upsample %d %d %d\n" , samplerate1, samplerate2 , nbSamples); nbSamples = ( src_data.output_frames_gen > nbSamplesMax) ? nbSamplesMax : src_data.output_frames_gen; - src_float_to_short_array(_floatBufferUp, dataOut, nbSamples); + src_float_to_short_array(_floatBufferUpSpkr, dataOut, nbSamples); + //_debug("upsample %d %d %d\n" , samplerate1, samplerate2 , nbSamples); } return nbSamples; - } +//TODO Add ifdef for int16 or float32 type int SamplerateConverter::downsampleData( SFLDataFormat* dataIn , SFLDataFormat* dataOut , int samplerate1 , int samplerate2 , int nbSamples ){ - double downsampleFactor = (double) samplerate2 / samplerate1; - int nbSamplesMax = (int) (samplerate1 * 20 / 1000); // TODO get the value from somewhere + double downsampleFactor = (double)samplerate1 / samplerate2; + //_debug("factor = %f\n" , downsampleFactor); + int nbSamplesMax = (int) ( samplerate1 * getFramesize() / 1000 ); if ( downsampleFactor != 1) { SRC_DATA src_data; - src_data.data_in = _floatBufferUp; - src_data.data_out = _floatBufferDown; + src_data.data_in = _floatBufferUpMic; + src_data.data_out = _floatBufferDownMic; src_data.input_frames = nbSamples; src_data.output_frames = (int) floor(downsampleFactor * nbSamples); src_data.src_ratio = downsampleFactor; src_data.end_of_input = 0; // More data will come - src_short_to_float_array(dataIn, _floatBufferUp, nbSamples); + //_debug("downsample %d %f %d\n" , src_data.output_frames, src_data.src_ratio , nbSamples); + src_short_to_float_array( dataIn, _floatBufferUpMic, nbSamples ); + //_debug("downsample %d %f %d\n" , src_data.output_frames, src_data.src_ratio , nbSamples); src_process(_src_state_mic, &src_data); + //_debug("downsample %d %f %d\n" , src_data.output_frames, src_data.src_ratio , nbSamples); nbSamples = ( src_data.output_frames_gen > nbSamplesMax) ? nbSamplesMax : src_data.output_frames_gen; - _debug( "return %i samples\n" , nbSamples ); - src_float_to_short_array(_floatBufferDown, dataOut, nbSamples); - _debug("Begin downsample data\n"); + //_debug("downsample %d %f %d\n" , src_data.output_frames, src_data.src_ratio , nbSamples); + src_float_to_short_array( _floatBufferDownMic , dataOut , nbSamples ); } return nbSamples; } diff --git a/src/samplerateconverter.h b/src/samplerateconverter.h index bbb5886f60..86ea175319 100644 --- a/src/samplerateconverter.h +++ b/src/samplerateconverter.h @@ -23,11 +23,13 @@ #include <math.h> #include "global.h" +#include "manager.h" class SamplerateConverter { public: /** Constructor */ SamplerateConverter( void ); + SamplerateConverter( int freq , int fs ); /** Destructor */ ~SamplerateConverter( void ); @@ -51,19 +53,28 @@ class SamplerateConverter { */ int downsampleData( SFLDataFormat* dataIn , SFLDataFormat* dataOut , int samplerate1 , int samplerate2 , int nbSamples ); - private: - /** Downsampled float buffer */ - float32* _floatBufferDown; + int getFrequence( void ) { return _frequence; } - /** Upsampled float buffer */ - float32* _floatBufferUp; + int getFramesize( void ) { return _framesize; } - /** libSamplerateConverter converter for incoming voice */ - SRC_STATE* _src_state_spkr; + private: + void init( void ); + /** Audio layer caracteristics */ + int _frequence; + int _framesize; + + /** Downsampled/Upsampled float buffers for the mic data processing */ + float32* _floatBufferDownMic; + float32* _floatBufferUpMic; /** libSamplerateConverter converter for outgoing voice */ SRC_STATE* _src_state_mic; + /** Downsampled/Upsampled float buffers for the speaker data processing */ + float32* _floatBufferDownSpkr; + float32* _floatBufferUpSpkr; + /** libSamplerateConverter converter for incoming voice */ + SRC_STATE* _src_state_spkr; /** libSamplerateConverter error */ int _src_err; }; -- GitLab