diff --git a/debian/Release b/debian/Release index 7fb4d2cd9ab47a7db36db62a5fbbdea976a4655d..5bbfec2cc9ae328804760f3a3782a036edea9761 100644 --- a/debian/Release +++ b/debian/Release @@ -1,7 +1,7 @@ Archive : unstable -Version: 0.9 +Version: 0.9.1 Component : universe Origin : SFLphone Label : sflphone -Architecture : all +Architecture : i386 diff --git a/debian/arch b/debian/arch deleted file mode 100644 index 5a9a476a8b7b3a362fa7925b74bda4a771cba262..0000000000000000000000000000000000000000 --- a/debian/arch +++ /dev/null @@ -1 +0,0 @@ -i386 diff --git a/debian/autopackage.sh b/debian/autopackage.sh index 4d6248ca2dd49d3256469f817de477754a8b04d8..b3cb825990fc7ea91066b8cab1ec5d60e7a745cb 100755 --- a/debian/autopackage.sh +++ b/debian/autopackage.sh @@ -42,7 +42,7 @@ cp $bindir/sflphoned $sfldir$bindir strip $sfldir$bindir/sflphoned cp $bindir/sflphone-gtk $sfldir$bindir strip $sfldir$bindir/sflphone-gtk -ln -sf $sfldir$bindir/sflphone-gtk $sfldir$bindir/sflphone +ln -sf $bindir/sflphone-gtk $sfldir$bindir/sflphone #/usr/lib mkdir -p $sfldir/usr/lib/sflphone/codecs @@ -76,8 +76,10 @@ cp changelog.Debian.gz $sfldir$sharedir/doc/sflphone cp copyright $sfldir$sharedir/doc/sflphone cp TODO $sfldir$sharedir/doc/sflphone -# DEBIAN files mkdir -p $debdir +cp debian-binary $sfldir +cp postrm $debdir +# DEBIAN files # Create control file control="$debdir/control" touch $control @@ -99,4 +101,4 @@ fakeroot dpkg --build $sfldir ${sfldir}_$2.deb # Clean up the generated stuff echo "Clean up ... " -#rm -rf $sfldir +rm -rf $sfldir diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index b2b6d3bc6cbd494a5becc148f8774450befdb577..0000000000000000000000000000000000000000 --- a/debian/changelog +++ /dev/null @@ -1,70 +0,0 @@ -sflphone (0.9.1) unstable; urgency=low - * Add a search tool in the history - * Migrate some gtk_entry_new to sexy_icon_entry_new - * Bug fix (Ticket #78): The voicemail password isn't displayed anymore in - the history tab - * Add the SIP registration expire value in the user file. - - -- Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> Thu, 22 May 2008 11:14:25 -0500 - -sflphone (0.9.0) unstable; urgency=low - * Add history features - * Call date - * Call duration - * Mouse events in the history tab - * Smooth switch from the history tab to the calls tab - * Remove most of GTK-Critical warnings - - -- Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> Wed, 13 May 2008 16:58:25 -0500 - -sflphone (0.9-2008-06-06) unstable; urgency=low - * Audio bug correction: capture stopped after a few minutes of conversation - with USB Plantronics sound card - - -- Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> Tue, 06 May 2008 16:58:25 -0500 - -sflphone (0.9-2008-05-06) unstable; urgency=low - * Bug correction: account creation with the assistant - * GTK+ warnings removal - * libnotify warnings removal - * Remove aliasing on the SFLphone logo - - -- Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> Mon, 05 May 2008 16:58:25 -0500 - -sflphone (0.9) unstable; urgency=low - * Clean dependencies ( removal of libboost ) - * Several GTK improvement and updates - -account window - -configuration window - * Migrate from GtkCheckMenuItem to GtkImageMenuItem - * ALSA standard I/O transfers: MMAP instead of R/W - * Fix speex audio quality - * IAX2 protocol - -Fix hold/unhold situation - -Add on hold music - * SIP protocol - -Ringtone on incoming call - -Fix transfer situation - * Add desktop notification ( libnotify ) - * Improve the system tray icon behaviour - * Improve registration error handling - * Register/unregister from the account window takes effect without starting back SFLphone - * Compilation warnings removal - * Call history - * Add an account configuration wizard - - -- Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> Wed, 30 Apr 2008 16:58:25 -0500 - - - -sflphone (0.8.2 ) unstable; urgency=low - - * Internationalization of the GTK GUI - * English / French - * STUN support - * Slight modifications of the graphical interface ( tooltips, dialpad, ...) - - - -- Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> Fri, 21 Mar 2008 11:37:53 -0500 - - diff --git a/debian/control b/debian/control index aa80a7f5a06dafd35c55efe11a5bb68577f51864..bf0d3fd666e70203a245c34ab00b4b6b6f0ee61b 100644 --- a/debian/control +++ b/debian/control @@ -2,18 +2,18 @@ Source: sflphone Maintainer: SavoirFaireLinux Inc <emmanuel.milou@savoirfairelinux.com> Section: gnome Priority: optional -Standards-Version: 0.9 +Build-Depends: debhelper (>> 3.0.0) +Standards-Version: 0.9.1 Package: sflphone -Architecture: i386 Section: gnome Priority: optional +Architecture: i386 Essential: no -Depends: libgcc1 , libsamplerate0 (>=0.1.2) , libdbus-glib-1-2 (>= 0.73), libexpat1 , libgtk2.0-0 , gnome-common , libc6 (>= 2.3.6-6) , libglib2.0-0 (>= 2.12.0) , libosip2-3, libexosip2-5, libcommoncpp2-1.5.3-0 , libccrtp1-1.5-1 , libiax0 , libgtkglext1 +Depends: libgcc1 , libsamplerate0 (>=0.1.2) , libdbus-glib-1-2 (>= 0.73), libexpat1 , libgtk2.0-0 , libc6 (>= 2.3.6-6) , libglib2.0-0 (>= 2.12.0) , libosip2-2, libexosip2-4, libcommoncpp2-1.6-0 , libccrtp1-1.6-0 , sflphone-iax2 , libgsm1 (>=1.0.10) , libspeex1 (>=1.1.12) , dbus-c++-1 (>=0.5.0) , libsexy2 (>=0.1.11) Homepage: http://www.sflphone.org -Description: SFLphone - Answer the call - SFLphone is meant to be a robust enterprise-class desktop phone. It is design with a hundred-calls-a-day receptionist in mind. It can work for you, too. - . +Description: SIP and IAX2 compatible softphone + SFLphone is meant to be a robust enterprise-class desktop phone. SFLphone is released under the GNU General Public License. - . - SFLphone is being developed by the global community, and maintained by Savoir-faire Linux, a Montreal, Quebec, Canada-based Linux consulting company. + SFLphone is being developed by the global community, and maintained by + Savoir-faire Linux, a Montreal, Quebec, Canada-based Linux consulting company. diff --git a/debian/debian-binary b/debian/debian-binary new file mode 100644 index 0000000000000000000000000000000000000000..cd5ac039d67e0bdadb17976e4ac39f0ffe6bb6e4 --- /dev/null +++ b/debian/debian-binary @@ -0,0 +1 @@ +2.0 diff --git a/debian/postrm b/debian/postrm new file mode 100755 index 0000000000000000000000000000000000000000..56801250786e23c392e1598602ed8593b4c64ff5 --- /dev/null +++ b/debian/postrm @@ -0,0 +1,15 @@ +#!/bin/sh + +# debian/postrm postremoval script for SFLphone + +if [ "$1" = "purge" ] +then + + # remove the user config file + rm -f $HOME/.sflphone/sflphonedrc + +fi + +exit 0 + +## ------------------------------------------------- diff --git a/debian/rules b/debian/rules index efde01dd03e95702fef9e90f0c6e0c0cfd5ed9bf..110e88d2256c17cad8b2f0c36a9cddc8b2c05a54 100755 --- a/debian/rules +++ b/debian/rules @@ -35,10 +35,12 @@ endif config.status: configure dh_testdir # Add here commands to configure the package. - autoreconf --install - CFLAGS="$(CFLAGS)" CXX="$(CXX)" ./configure --prefix=/usr --with-debug +#autoreconf --install +#CFLAGS="$(CFLAGS)" CXX="$(CXX)" ./configure --prefix=/usr --with-debu + ./configure --prefix=/ +#gnome-autogen.sh --prefix=/usr --with-debug cd sflphone-gtk - gnome-autogen.sh --prefix=/usr + ./configure --prefix=/ #Architecture @@ -48,7 +50,8 @@ build-arch: build-arch-stamp build-arch-stamp: config.status # Add here commands to compile the arch part of the package. - #$(MAKE) + $(MAKE) && (cd sflphone-gtk) + $(MAKE) touch build-arch-stamp build-indep: build-indep-stamp @@ -96,7 +99,7 @@ install-arch: # Add here commands to install the arch part of the package into # debian/tmp. - $(MAKE) install DESTDIR=$(CURDIR)/debian/sflphoned + $(MAKE) install DESTDIR=$(CURDIR)/debian/sflphone/usr dh_install -s # Must not depend on anything. This is to be called by diff --git a/debian/sflphone.install b/debian/sflphone.install new file mode 100644 index 0000000000000000000000000000000000000000..7e7915dafd83894ad61e23a94368b0baca82fa00 --- /dev/null +++ b/debian/sflphone.install @@ -0,0 +1,29 @@ +usr/bin +usr/bin/sflphoned +usr/bin/sflphone-gtk +usr/bin/sflphone + +#/usr/lib +usr/lib/sflphone/codecs +usr/lib/sflphone/codecs/libcodec_* + +#/usr/share/applications +usr/share/applications +usr/share/applications/sflphone.desktop +usr/share/dbus-1/services +usr/share/dbus-1/services/org.sflphone.SFLphone.service +usr/share/pixmaps +usr/share/pixmaps/sflphone.png + +usr/share/sflphone/* +usr/share/sflphone/ringtones/* + +usr/share/locale/fr/LC_MESSAGES +usr/share/locale/fr/LC_MESSAGES/sflphone.mo +usr/share/locale/es/LC_MESSAGES +usr/share/locale/es/LC_MESSAGES/sflphone.mo + +changelog.Debian.gz +copyright +TODO + diff --git a/sflphone-gtk/src/assistant.c b/sflphone-gtk/src/assistant.c index e50d043c89c298d21a00b3480fc64f189c17d279..27c066cc6e4c8fd888af2ea69fd8a8a6a8c22be3 100644 --- a/sflphone-gtk/src/assistant.c +++ b/sflphone-gtk/src/assistant.c @@ -41,11 +41,13 @@ set_account_type( GtkWidget* widget , gpointer data ) static void close_callback( void ) { gtk_widget_destroy(wiz->assistant); + g_free(wiz); wiz = NULL; } static void cancel_callback( void ) { gtk_widget_destroy(wiz->assistant); + g_free(wiz); wiz = NULL; } static void @@ -98,6 +100,7 @@ enable_stun( GtkWidget* widget ) void build_wizard( void ) { +if(!wiz){ wiz = ( struct _wizard* )g_malloc( sizeof( struct _wizard)); current = g_new0(account_t, 1); current->properties = g_hash_table_new(NULL, g_str_equal); @@ -116,14 +119,14 @@ build_wizard( void ) build_iax_account_configuration(); build_registration_error(); build_summary(); - + g_signal_connect(G_OBJECT(wiz->assistant), "close" , G_CALLBACK(close_callback), NULL); g_signal_connect(G_OBJECT(wiz->assistant), "cancel" , G_CALLBACK(cancel_callback), NULL); gtk_widget_show_all(wiz->assistant); gtk_assistant_update_buttons_state(GTK_ASSISTANT(wiz->assistant)); - + } } GtkWidget* diff --git a/src/Makefile.am b/src/Makefile.am index d5e84d57d0c0e921bbc8ca3b315efd894203168f..5915d0166af82089b66206bcbe3e8750b86c9cda 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -31,10 +31,10 @@ endif ALSAFLAG= -lasound PULSEAUDIO_LIBS=-lpulse -SUBDIRS = audio config dbus $(ZEROCONFDIR)# contact memmanager video mixer +SUBDIRS = audio config dbus $(ZEROCONFDIR) #contact memmanager video mixer sflphoned_SOURCES = eventthread.cpp main.cpp voiplink.cpp \ - managerimpl.cpp observer.cpp \ + managerimpl.cpp observer.cpp samplerateconverter.cpp \ account.cpp sipaccount.cpp accountcreator.cpp \ sipvoiplink.cpp call.cpp sipcall.cpp \ $(IAXSOURCES) @@ -61,7 +61,7 @@ libsflphone_la_SOURCES = noinst_LTLIBRARIES = libsflphone.la noinst_HEADERS = managerimpl.h manager.h global.h observer.h eventthread.h user_cfg.h \ - voiplink.h \ + voiplink.h samplerateconverter.h \ account.h sipaccount.h accountcreator.h \ sipvoiplink.h call.h sipcall.h \ $(IAXHEADERS) diff --git a/src/audio/audiortp.cpp b/src/audio/audiortp.cpp index cb30737f116a1af7dc9242722248b747137f630c..641eb4f841a5ebfc05ea3fb7c06828ca6a36d4fa 100644 --- a/src/audio/audiortp.cpp +++ b/src/audio/audiortp.cpp @@ -38,7 +38,6 @@ #include "ringbuffer.h" #include "../user_cfg.h" #include "../sipcall.h" -#include <samplerate.h> //////////////////////////////////////////////////////////////////////////////// // AudioRtp @@ -102,9 +101,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(); @@ -118,13 +114,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 () { @@ -145,39 +134,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( _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() ); @@ -254,7 +238,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 @@ -267,50 +250,38 @@ AudioRtpRTX::sendSessionFromMic(int timestamp) int maxBytesToGet = _layerSampleRate * _layerFrameSize * sizeof(SFLDataFormat) / 1000; // available bytes inside ringbuffer int availBytesFromMic = audiolayer->canGetMic(); - //printf("availBytesFromMic = %i \n", availBytesFromMic); // take the lowest int bytesAvail = (availBytesFromMic < maxBytesToGet) ? availBytesFromMic : maxBytesToGet; - //printf("max bytes to get: %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) { @@ -334,54 +305,44 @@ AudioRtpRTX::receiveSessionForSpkr (int& countTime) return; } - int payload = adu->getType(); // codec type - unsigned char* data = (unsigned char*)adu->getData(); // data in char + //int payload = adu->getType(); // codec type + 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); + unsigned int max = (unsigned int)(_codecSampleRate * _layerFrameSize / 1000); if ( size > max ) { - _debug("We have received from RTP a packet larger than expected: %s VS %s\n", size, max); + _debug("We have received from RTP a packet larger than expected: %d VS %d\n", size, max); _debug("The packet size has been cropped\n"); 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 if (nbInt16 > max) { - _debug("We have decoded an RTP packet larger than expected: %s VS %s. Cropping.\n", nbInt16, max); + _debug("We have decoded an RTP packet larger than expected: %d VS %d. 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 int layer = audiolayer->getLayerType(); //_debug(" interface %i - ALSA = %i\n" , layer, ALSA); if( CHECK_INTERFACE( layer, ALSA ) ) - audiolayer->playSamples(toAudioLayer, nbSample * sizeof(SFLDataFormat), true); + audiolayer->playSamples( spkrDataConverted, nbSample * sizeof(SFLDataFormat), true); else - audiolayer->putMain( toAudioLayer, nbSample * sizeof(SFLDataFormat) ); + audiolayer->putMain( spkrDataConverted, nbSample * sizeof(SFLDataFormat) ); // Notify (with a beep) an incoming call when there is already a call countTime += time->getSecond(); if (Manager::instance().incomingCallWaiting() > 0) { @@ -394,7 +355,6 @@ AudioRtpRTX::receiveSessionForSpkr (int& countTime) } else { countTime += time->getSecond(); } - delete adu; adu = NULL; } catch(...) { _debugException("! ARTP: receiving failed"); @@ -407,72 +367,21 @@ AudioRtpRTX::receiveSessionForSpkr (int& countTime) int AudioRtpRTX::reSampleData(int sampleRate_codec, int nbSamples, int status) { - if(status==UP_SAMPLING) - return upSampleData(sampleRate_codec, nbSamples); - else if(status==DOWN_SAMPLING) - return downSampleData(sampleRate_codec, 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); + if(status==UP_SAMPLING){ + return converter->upsampleData( spkrDataDecoded , spkrDataConverted , sampleRate_codec , _layerSampleRate , 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); + else if(status==DOWN_SAMPLING){ + return converter->downsampleData( micData , micDataConverted , sampleRate_codec , _layerSampleRate , nbSamples ); } - return nbSamples; - + else + return 0; } -//////////////////////// 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(); @@ -496,7 +405,6 @@ AudioRtpRTX::run () { int countTime = 0; // for receive TimerPort::setTimer(_layerFrameSize); - //audiolayer->flushMic(); audiolayer->startStream(); _start.post(); _debug("- ARTP Action: Start\n"); @@ -514,8 +422,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 0b735ac890414ed4a27b7556432d18111b0779d1..a0cb003bf35566a6251361edf0b0a3b5ef0a5ce5 100644 --- a/src/audio/audiortp.h +++ b/src/audio/audiortp.h @@ -28,8 +28,8 @@ #include <ccrtp/rtp.h> #include <cc++/numbers.h> -#include <samplerate.h> #include "../global.h" +#include "../samplerateconverter.h" #define UP_SAMPLING 0 #define DOWN_SAMPLING 1 @@ -83,33 +83,17 @@ 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; + /** Mic-data related buffers */ + SFLDataFormat* micData; + SFLDataFormat* micDataConverted; + unsigned char* micDataEncoded; - /** 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; - - /** Upsampled float buffer */ - float32* _floatBufferUp; - - /** libsamplerate converter for incoming voice */ - SRC_STATE* _src_state_spkr; + /** Speaker-data related buffers */ + SFLDataFormat* spkrDataDecoded; + SFLDataFormat* spkrDataConverted; - /** libsamplerate converter for outgoing voice */ - SRC_STATE* _src_state_mic; - - /** libsamplerate error */ - int _src_err; + /** Sample rate converter object */ + SamplerateConverter* converter; /** Variables to process audio stream: sample rate for playing sound (typically 44100HZ) */ int _layerSampleRate; @@ -154,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 1cc5a5d26404bfddf3848f6279e82961acc1e4c0..6b76c9c123dc6bcdfae3761b2cdb33facedd8b45 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 ea67145ede0534d38fd60a0dca6d392dd3b04250..3a8c3fbd4a11b183e6cd58b4f99a0cce81cc238d 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 new file mode 100644 index 0000000000000000000000000000000000000000..126c5d957cf7a9712bfbf4b105a0d817d6f653aa --- /dev/null +++ b/src/samplerateconverter.cpp @@ -0,0 +1,116 @@ +/* + * 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 "samplerateconverter.h" + +SamplerateConverter::SamplerateConverter( void ) { + // Default values + _frequence = Manager::instance().getConfigInt( AUDIO , ALSA_SAMPLE_RATE ); // 44100; + _framesize = Manager::instance().getConfigInt( AUDIO , ALSA_FRAME_SIZE ); + + init(); +} + +SamplerateConverter::SamplerateConverter( int freq , int fs ) { + + _frequence = freq ; + _framesize = fs ; + + init(); +} + +SamplerateConverter::~SamplerateConverter( void ) { + + 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 * getFramesize() / 1000 ); + if( upsampleFactor != 1 && dataIn != NULL ) + { + SRC_DATA src_data; + 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 + //_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(_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)samplerate1 / samplerate2; + //_debug("factor = %f\n" , downsampleFactor); + int nbSamplesMax = (int) ( samplerate1 * getFramesize() / 1000 ); + if ( downsampleFactor != 1) + { + SRC_DATA src_data; + 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 + //_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("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 new file mode 100644 index 0000000000000000000000000000000000000000..86ea175319c44fd36660d3fdf8b33a7ef9de1e46 --- /dev/null +++ b/src/samplerateconverter.h @@ -0,0 +1,83 @@ +/* + * 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_RATE_H +#define _SAMPLE_RATE_H + +#include <samplerate.h> +#include <math.h> + +#include "global.h" +#include "manager.h" + +class SamplerateConverter { + public: + /** Constructor */ + SamplerateConverter( void ); + SamplerateConverter( int freq , int fs ); + /** Destructor */ + ~SamplerateConverter( void ); + + /** + * Upsample from the samplerate1 to the samplerate2 + * @param data The data buffer + * @param SamplerateConverter1 The lower sample rate + * @param SamplerateConverter2 The higher sample rate + * @param nbSamples The number of samples to process + * @return int The number of samples after the operation + */ + int upsampleData( SFLDataFormat* dataIn , SFLDataFormat* dataOut , int samplerate1 , int samplerate2 , int nbSamples ); + + /** + * Downsample from the samplerate1 to the samplerate2 + * @param data The data buffer + * @param SamplerateConverter1 The lower sample rate + * @param SamplerateConverter2 The higher sample rate + * @param nbSamples The number of samples to process + * @return int The number of samples after the operation + */ + int downsampleData( SFLDataFormat* dataIn , SFLDataFormat* dataOut , int samplerate1 , int samplerate2 , int nbSamples ); + + int getFrequence( void ) { return _frequence; } + + int getFramesize( void ) { return _framesize; } + + 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; +}; + +#endif //_SAMPLE_RATE_H + diff --git a/src/voiplink.cpp b/src/voiplink.cpp index 59aca9d19989c81e01e7a5da13eb410cf880366d..1a762036ed9a44f0c84b2b0458da5dfabc09d22e 100644 --- a/src/voiplink.cpp +++ b/src/voiplink.cpp @@ -25,8 +25,9 @@ #include "voiplink.h" #include "manager.h" -VoIPLink::VoIPLink(const AccountID& accountID) : _accountID(accountID), _localIPAddress("127.0.0.1"), _localPort(0), _registrationError(NO_ERROR), _initDone(false) +VoIPLink::VoIPLink(const AccountID& accountID) : _accountID(accountID), _localIPAddress("127.0.0.1"), _localPort(0), _initDone(false) { + _registrationError = NO_ERROR; } VoIPLink::~VoIPLink (void)