diff --git a/configure.ac b/configure.ac index 100f5d33d..6ee3762e6 100644 --- a/configure.ac +++ b/configure.ac @@ -38,6 +38,7 @@ AC_CONFIG_FILES([src/Makefile \ src/config/Makefile \ src/dbus/Makefile \ src/zeroconf/Makefile \ + src/plug-in/audiorecorder/Makefile \ src/plug-in/Makefile]) dnl Unitary test section diff --git a/src/Makefile.am b/src/Makefile.am index 16725b63f..53945f899 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -91,6 +91,7 @@ libsflphone_la_LIBADD = \ ./dbus/libdbus.la \ ./config/libconfig.la \ ./plug-in/libplugin.la \ + ./plug-in/audiorecorder/libaudiorecorder.la \ $(IAX_LIBS) libsflphone_la_SOURCES = diff --git a/src/audio/pulselayer.h b/src/audio/pulselayer.h index 828a6b849..521ff82e3 100644 --- a/src/audio/pulselayer.h +++ b/src/audio/pulselayer.h @@ -22,7 +22,7 @@ #include "audiolayer.h" #include "audiostream.h" -#include "audiorecord.h" +#include "plug-in/audiorecorder/audiorecord.h" #define PLAYBACK_STREAM_NAME "SFLphone out" #define CAPTURE_STREAM_NAME "SFLphone in" diff --git a/src/global.h b/src/global.h index 259f34e76..0321db6e1 100644 --- a/src/global.h +++ b/src/global.h @@ -32,7 +32,7 @@ typedef float float32; typedef short int16; -useful typedefs. +//useful typedefs. typedef signed short SINT16; typedef signed int SINT32; diff --git a/src/plug-in/Makefile.am b/src/plug-in/Makefile.am index 53bf6b050..7555fe7f3 100644 --- a/src/plug-in/Makefile.am +++ b/src/plug-in/Makefile.am @@ -1,5 +1,7 @@ include ../../globals.mak +SUBDIRS=audiorecorder + noinst_LTLIBRARIES = libplugin.la libplugin_la_SOURCES = \ diff --git a/src/plug-in/audiorecorder/Makefile.am b/src/plug-in/audiorecorder/Makefile.am new file mode 100644 index 000000000..37a2ce4ed --- /dev/null +++ b/src/plug-in/audiorecorder/Makefile.am @@ -0,0 +1,7 @@ +include $(top_srcdir)/globals.mak + +noinst_LTLIBRARIES = libaudiorecorder.la + +libaudiorecorder_la_SOURCES = \ + audiorecord.cpp + diff --git a/src/plug-in/audiorecorder/audiorecord.cpp b/src/plug-in/audiorecorder/audiorecord.cpp new file mode 100644 index 000000000..65f638039 --- /dev/null +++ b/src/plug-in/audiorecorder/audiorecord.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2008 Savoir-Faire Linux inc. + * Author: Alexandre Savard + * + * 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 "audiorecord.h" + +AudioRecord::AudioRecord(){ + sndSmplRate_ = 44100; + channels_ = 1; + byteCounter_ = 0; + +} + + +void AudioRecord::setSndSamplingRate(int smplRate){ + sndSmplRate_ = smplRate; +} + + +void AudioRecord::openFile(std::string fileName, FILE_TYPE type, SOUND_FORMAT format) { + + channels_ =1; + fileType_ = type; + byteCounter_ = 0; + sndFormat_ = format; + + bool result = false; + + if(fileType_ == FILE_RAW){ + result = setRawFile( fileName.c_str() ); + } + else if (fileType_ == FILE_WAV){ + result = setWavFile( fileName.c_str() ); + } + +} + + +void AudioRecord::closeFile() { + + if (fp == 0) return; + + if (fileType_ == FILE_RAW) + fclose(fp); + else if (fileType_ == FILE_WAV) + this->closeWavFile(); + +} + + +bool AudioRecord::isOpenFile() { + + if(fp) + return true; + else + return false; +} + + +bool AudioRecord::setRawFile(const char *fileName) { + + char name[8192]; + strncpy(name, fileName, 8192); + if ( strstr(name, ".raw") == NULL) strcat(name, ".raw"); + fp = fopen(name, "wb"); + if ( !fp ) { + cout << "AudioRecord: could not create RAW file: " << name << '.'; + return false; + } + + if ( sndFormat_ != INT16 ) { // TODO need to change INT16 to SINT16 + sndFormat_ = INT16; + cout << "AudioRecord: using 16-bit signed integer data format for file " << name << '.'; + } + + cout << "AudioRecord: creating RAW file: " << name; + return true; +} + + +bool AudioRecord::setWavFile(const char *fileName) { + + char name[8192]; + strncpy(name, fileName, 8192); + if ( strstr(name, ".wav") == NULL) strcat(name, ".wav"); + fp = fopen(name, "wb"); + if ( !fp ) { + cout << "AudioRecord: could not create WAV file: " << name; + return false; + } + + struct wavhdr hdr = {"RIF", 44, "WAV", "fmt", 16, 1, 1, + 44100, 0, 2, 16, "dat", 0}; + hdr.riff[3] = 'F'; + hdr.wave[3] = 'E'; + hdr.fmt[3] = ' '; + hdr.data[3] = 'a'; + hdr.num_chans = channels_; + if ( sndFormat_ == INT16 ) { // TODO need to write INT16 to SINT16 + hdr.bits_per_samp = 16; + } + hdr.bytes_per_samp = (SINT16) (channels_ * hdr.bits_per_samp / 8); + hdr.bytes_per_sec = (SINT32) (hdr.sample_rate * hdr.bytes_per_samp); + + + if ( fwrite(&hdr, 4, 11, fp) != 11 ) { + cout << "AudioRecord: could not write WAV header for file " << name << '.'; + return false; + } + + cout << "AudioRecord: creating WAV file: " << name; + return true; +} + + +void AudioRecord::closeWavFile() +{ + int bytes_per_sample = 1; + if ( sndFormat_ == INT16 ) + bytes_per_sample = 2; + + + SINT32 bytes = byteCounter_ * channels_ * bytes_per_sample; + fseek(fp, 40, SEEK_SET); // jump to data length + fwrite(&bytes, 4, 1, fp); + + bytes = byteCounter_ * channels_ * bytes_per_sample + 44; // + 44 for the wave header + fseek(fp, 4, SEEK_SET); // jump to file size + fwrite(&bytes, 4, 1, fp); + fclose( fp ); +} + + +void AudioRecord::recData(SFLDataFormat* buffer, int nSamples) { + + if (fp == 0){ + cout << "AudioRecord: Can't record data, a file has not yet been opened!"; + return; + } + + if ( sndFormat_ == INT16 ) { // TODO change INT16 to SINT16 + if (nSamples <= 1){ + if ( fwrite(buffer, 2, 1, fp) != 1) + cout << "AudioRecord: Could not record data!"; + } + else { + for ( int k=0; k + * + * 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 +#include + +#include "global.h" + +using namespace std; + + +// structure for the wave header +struct wavhdr { + char riff[4]; // "RIFF" + SINT32 file_size; // in bytes + char wave[4]; // "WAVE" + char fmt[4]; // "fmt " + SINT32 chunk_size; // in bytes (16 for PCM) + SINT16 format_tag; // 1=PCM, 2=ADPCM, 3=IEEE float, 6=A-Law, 7=Mu-Law + SINT16 num_chans; // 1=mono, 2=stereo + SINT32 sample_rate; + SINT32 bytes_per_sec; + SINT16 bytes_per_samp; // 2=16-bit mono, 4=16-bit stereo + SINT16 bits_per_samp; + char data[4]; // "data" + SINT32 data_length; // in bytes +}; + +class AudioRecord +{ + +public: + + AudioRecord(); + + void setSndSamplingRate(int smplRate); + + /** + * Check if no otehr file is opened, then create a new one + * @param fileName A string containing teh file (with/without extension) + * @param type The sound file format (FILE_RAW, FILE_WAVE) + * @param format Internal sound format (INT16 / INT32) + */ + void openFile(std::string fileName, FILE_TYPE type, SOUND_FORMAT format); + + /** + * Close the opend recording file. If wave: cout the number of byte + */ + void closeFile(); + + /** + * Check if a file is already opened + */ + bool isOpenFile(); + + /** + * Record a chunk of data in an openend file + * @param buffer The data chunk to be recorded + * @param nSamples Number of samples (number of bytes) to be recorded + */ + void recData(SFLDataFormat* buffer, int nSamples); // TODO ad the data to rec + +protected: + + /** + * Set the header for raw files + */ + bool setRawFile(const char* fileName); + + /** + * Set the header for wave files + */ + bool setWavFile(const char* fileName); + + /** + * Compute the number of byte recorded and close the file + */ + void closeWavFile(); + + /** + * Pointer to the recorded file + */ + FILE *fp; //file pointer + + /** + * File format (RAW / WAVE) + */ + FILE_TYPE fileType_; + + /** + * Sound format (SINT16/SINT32) + */ + SOUND_FORMAT sndFormat_; + + /** + * Number of channels + */ + int channels_; + + /** + * Number f byte recorded + */ + unsigned long byteCounter_; + + /** + * Sampling rate + */ + int sndSmplRate_; + +}; diff --git a/test/Makefile.am b/test/Makefile.am index 86e70dbc3..b3aaf8805 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,6 +1,6 @@ include ../globals.mak -bin_PROGRAMS = configurationTester pluginmanagerTester +bin_PROGRAMS = configurationTester pluginmanagerTester audiorecorderTester OBJECT_FILES= \ ../src/sflphoned-managerimpl.o \ @@ -17,6 +17,7 @@ OBJECT_FILES= \ ../src/sflphoned-eventthread.o \ ../src/sflphoned-useragent.o \ ../src/plug-in/pluginmanager.o \ + ../src/plug-in/audiorecorder/audiorecord.o \ ../src/sflphoned-samplerateconverter.o configurationTester_SOURCES = \ @@ -29,6 +30,12 @@ pluginmanagerTester_SOURCES = \ pluginmanagerTest.cpp \ TestMain.cpp +audiorecorderTester_SOURCES = \ + audiorecorderTest.h \ + audiorecorderTest.cpp \ + TestMain.cpp + + configurationTester_LDADD = \ ../src/libsflphone.la \ $(SFLPHONE_LIBS) $(ZEROCONFLIB) $(LIB_DNSSD) $(IAX_LIBS) \ @@ -41,6 +48,7 @@ configurationTester_LDADD = \ @DBUSCPP_LIBS@ \ @SAMPLERATE_LIBS@ \ $(PJSIP_LIBS) \ + -luuid \ $(OBJECT_FILES) pluginmanagerTester_LDADD = \ @@ -55,5 +63,21 @@ pluginmanagerTester_LDADD = \ @DBUSCPP_LIBS@ \ @SAMPLERATE_LIBS@ \ $(PJSIP_LIBS) \ + -luuid \ + $(OBJECT_FILES) + +audiorecorderTester_LDADD = \ + ../src/libsflphone.la \ + $(SFLPHONE_LIBS) $(ZEROCONFLIB) $(LIB_DNSSD) $(IAX_LIBS) \ + @ALSA_LIBS@ \ + @PULSEAUDIO_LIBS@ \ + @CPPUNIT_LIBS@ \ + @CCEXT2_LIBS@ \ + @CCGNU2_LIBS@ \ + @CCRTP_LIBS@ \ + @DBUSCPP_LIBS@ \ + @SAMPLERATE_LIBS@ \ + $(PJSIP_LIBS) \ + -luuid \ $(OBJECT_FILES) diff --git a/test/audiorecorderTest.cpp b/test/audiorecorderTest.cpp new file mode 100644 index 000000000..8a9327cb5 --- /dev/null +++ b/test/audiorecorderTest.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Author: Emmanuel Milou + * + * 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 +#include + +#include "audiorecorderTest.h" + +using std::cout; +using std::endl; + +void AudioRecorderTest::setUp(){ + // Instanciate the object + _ar = new AudioRecord(); +} + +void AudioRecorderTest::testRecordData(){ + + FILE_TYPE ft = FILE_WAV; + SOUND_FORMAT sf = INT16; + _ar->setSndSamplingRate(44100); + _ar->openFile("theWavFile.wav",ft,sf); + + cout << "file opened!\n"; + + SFLDataFormat buf [2]; + for (SFLDataFormat i = -32768; i < 32767; i++ ){ + buf[0] = i; + buf[1] = i; + recAudio.recData(buf,2); + } + + recAudio.closeFile(); + + +} + +void AudioRecorderTest::tearDown(){ + // Delete the audio recorder module + delete _ar; _ar = NULL; +} diff --git a/test/audiorecorderTest.h b/test/audiorecorderTest.h new file mode 100644 index 000000000..216b65707 --- /dev/null +++ b/test/audiorecorderTest.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Author: Emmanuel Milou + * + * 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. + */ + +// Cppunit import +#include +#include +#include +#include + +#include + +// Application import +#include "plug-in/pluginmanager.h" +#include "plug-in/audiorecorder/audiorecord.h" + +/* + * @file audiorecorderTest.cpp + * @brief Regroups unitary tests related to the plugin manager. + */ + +#ifndef _AUDIORECORDER_TEST_ +#define _AUDIORECORDER_TEST_ + +class AudioRecorderTest : public CppUnit::TestCase { + + /* + * Use cppunit library macros to add unit test the factory + */ + CPPUNIT_TEST_SUITE( AudioRecorderTest ); + CPPUNIT_TEST( testRecordData ); + CPPUNIT_TEST_SUITE_END(); + + public: + AudioRecorderTest() : CppUnit::TestCase("Audio Recorder Tests") {} + + /* + * Code factoring - Common resources can be initialized here. + * This method is called by unitcpp before each test + */ + void setUp(); + + /* + * Code factoring - Common resources can be released here. + * This method is called by unitcpp after each test + */ + inline void tearDown(); + + void testRecordData(); + + private: + AudioRecord *_ar; +}; + +/* Register our test module */ +CPPUNIT_TEST_SUITE_REGISTRATION( AudioRecorderTest ); + +#endif