diff options
author | Gerald Begumisa <ben_g@users.sourceforge.net> | 2008-02-27 19:21:41 +0000 |
---|---|---|
committer | Gerald Begumisa <ben_g@users.sourceforge.net> | 2008-02-27 19:21:41 +0000 |
commit | 0937742d2f5689c93efca3a5a56e8b36f81152c7 (patch) | |
tree | a0c817c8b45c888c88d020583a45a5ec65a225dd /orkbasecxx | |
parent | 9483156b326918005bb42751cc0e37a16b3e3e41 (diff) |
Added support for storage of stereo files - typically files containing 2 separate channels. Added the config.xml variable StereoRecording which needs to be set to true for this to work. Added another config.xml variable TapeNumChannels which should be set to 2 to test this. If TapeNumChannels is set to 1, default behaviour happens. Currently only 1 and 2 are the only supported values for TapeNumChannels.
git-svn-id: https://oreka.svn.sourceforge.net/svnroot/oreka/trunk@526 09dcff7a-b715-0410-9601-b79a96267cd0
Diffstat (limited to 'orkbasecxx')
-rw-r--r-- | orkbasecxx/AudioCapture.cpp | 118 | ||||
-rw-r--r-- | orkbasecxx/AudioCapture.h | 19 | ||||
-rw-r--r-- | orkbasecxx/Config.cpp | 10 | ||||
-rw-r--r-- | orkbasecxx/Config.h | 6 | ||||
-rw-r--r-- | orkbasecxx/audiofile/LibSndFileFile.cpp | 115 |
5 files changed, 248 insertions, 20 deletions
diff --git a/orkbasecxx/AudioCapture.cpp b/orkbasecxx/AudioCapture.cpp index 706c657..77b9e95 100644 --- a/orkbasecxx/AudioCapture.cpp +++ b/orkbasecxx/AudioCapture.cpp @@ -22,33 +22,105 @@ AudioChunk::AudioChunk() { m_details.Clear(); m_pBuffer = NULL; + m_numChannels = 0; + m_pChannelAudio = NULL; } -AudioChunk::~AudioChunk() +AudioChunk::AudioChunk(int numChannels) { - if(m_pBuffer) + m_details.Clear(); + m_pBuffer = NULL; + m_pChannelAudio = NULL; + if(numChannels <= 0) { - free(m_pBuffer); + // Force at least one channel to resolve this possible + // error condition + numChannels = 1; + } + + m_numChannels = numChannels; + m_pChannelAudio = (void**)calloc(m_numChannels, sizeof(void*)); + m_details.m_channel = 100; + + if(!m_pChannelAudio) + { + CStdString numBytesString; + + numBytesString.Format("%d", m_numChannels*sizeof(void*)); + throw("AudioChunk::AudioChunk(numChannels) could not allocate a buffer of size " + numBytesString); } } +AudioChunk::~AudioChunk() +{ + FreeAll(); +} + void AudioChunk::ToString(CStdString& string) { - string.Format("encoding:%d numBytes:%u ts:%u ats:%u seq:%u rtp-pt:%d ch:%u", - m_details.m_encoding, m_details.m_numBytes, m_details.m_timestamp, m_details.m_arrivalTimestamp, - m_details.m_sequenceNumber, m_details.m_rtpPayloadType, m_details.m_channel); + if(!m_numChannels) + { + string.Format("encoding:%d numBytes:%u ts:%u ats:%u seq:%u rtp-pt:%d ch:%u", + m_details.m_encoding, m_details.m_numBytes, m_details.m_timestamp, m_details.m_arrivalTimestamp, + m_details.m_sequenceNumber, m_details.m_rtpPayloadType, m_details.m_channel); + } + else + { + string.Format("encoding:%d numBytesPerChannel:%u numChannels:%d ts:%u ats:%u seq:%u rtp-pt:%d", + m_details.m_encoding, m_details.m_numBytes, m_numChannels, m_details.m_timestamp, + m_details.m_arrivalTimestamp, m_details.m_sequenceNumber, m_details.m_rtpPayloadType); + } } -void* AudioChunk::CreateBuffer(AudioChunkDetails& details) +void AudioChunk::FreeAll() { if(m_pBuffer) { free(m_pBuffer); m_pBuffer = NULL; } + if(m_numChannels) + { + for(int i = 0; i < m_numChannels; i++) + { + if(m_pChannelAudio[i]) + { + free(m_pChannelAudio[i]); + } + } + + free(m_pChannelAudio); + m_pChannelAudio = NULL; + } +} + +void AudioChunk::CreateMultiChannelBuffers(AudioChunkDetails& details) +{ + if(!m_numChannels) + { + return; + } + + for(int i = 0; i < m_numChannels; i++) + { + m_pChannelAudio[i] = calloc(details.m_numBytes, 1); + if(!m_pChannelAudio[i]) + { + CStdString exception; + + exception.Format("AudioChunk::CreateMultiChannelBuffers failed to calloc buffer of size:%d for channel:%d", details.m_numBytes, i); + throw(exception); + } + } +} + +void* AudioChunk::CreateBuffer(AudioChunkDetails& details) +{ + FreeAll(); if(details.m_numBytes) { m_pBuffer = calloc(details.m_numBytes, 1); + CreateMultiChannelBuffers(details); } if (!m_pBuffer) { @@ -86,6 +158,38 @@ void AudioChunk::SetBuffer(void* pBuffer, AudioChunkDetails& details) } } +void AudioChunk::SetBuffer(void* pBuffer, AudioChunkDetails& details, int chan) +{ + CStdString exception; + int chanIdx = 0; + + if(chan > m_numChannels || chan < 1) + { + exception.Format("AudioChunk::SetBuffer: invalid channel %d", chan); + throw(exception); + } + + chanIdx = chan - 1; + if(m_pChannelAudio[chanIdx]) + { + free(m_pChannelAudio[chanIdx]); + m_pChannelAudio[chanIdx] = NULL; + } + if(details.m_numBytes && pBuffer) + { + m_pChannelAudio[chanIdx] = malloc(details.m_numBytes); + if(!m_pChannelAudio[chanIdx]) + { + exception.Format("AudioChunk::SetBuffer: failed to allocate buffer of size:%d for channel:%d channelidx:%d", details.m_numBytes, chan, chanIdx); + throw(exception); + } + else + { + memcpy(m_pChannelAudio[chanIdx], pBuffer, details.m_numBytes); + } + } +} + int AudioChunk::GetNumSamples() { switch(m_details.m_encoding) diff --git a/orkbasecxx/AudioCapture.h b/orkbasecxx/AudioCapture.h index 1d6ae78..972e428 100644 --- a/orkbasecxx/AudioCapture.h +++ b/orkbasecxx/AudioCapture.h @@ -55,7 +55,8 @@ public: unsigned int m_sequenceNumber; unsigned int m_sampleRate; char m_rtpPayloadType; // -1 if none - unsigned char m_channel; // 0 if mono, 1 or 2 if stereo + unsigned char m_channel; // 0 if mono, 1 or 2 if stereo, 100 if we have + // separated multiple channels }; /** @@ -65,21 +66,31 @@ class DLL_IMPORT_EXPORT_ORKBASE AudioChunk { public: AudioChunk(); + AudioChunk(int numChannels); ~AudioChunk(); void ToString(CStdString&); + /** Creates n zeroed buffers where n=m_numChannels */ + void CreateMultiChannelBuffers(AudioChunkDetails& details); + /** Allocate a new empty buffer (zeroed) */ void* CreateBuffer(AudioChunkDetails& details); /** Copy external buffer to internal buffer. Create internal buffer if necessary */ void SetBuffer(void* pBuffer, AudioChunkDetails& details); + /** Copy external buffer to internal buffer. Create internal buffer if necessary */ + void SetBuffer(void* pBuffer, AudioChunkDetails& details, int chan); + /** Computes the Root-Mean-Square power value of the buffer */ double ComputeRms(); /** Compute the RMS decibel value of the buffer with a 0 dB reference being the maximum theoretical RMS power of the buffer (2^15) */ double ComputeRmsDb(); + /** Free's all memory allocated */ + void FreeAll(); + int GetNumSamples(); int GetNumBytes(); int GetSampleRate(); @@ -90,6 +101,12 @@ public: void * m_pBuffer; + //========================================================== + // Additions to support separate audio for multiple channels + + int m_numChannels; + void ** m_pChannelAudio; + private: AudioChunkDetails m_details; }; diff --git a/orkbasecxx/Config.cpp b/orkbasecxx/Config.cpp index 7699525..2da7141 100644 --- a/orkbasecxx/Config.cpp +++ b/orkbasecxx/Config.cpp @@ -68,6 +68,8 @@ Config::Config() m_allowAutomaticRecording = ALLOW_AUTOMATIC_RECORDING_DEFAULT; m_captureFileSizeLimitKb = CAPTURE_FILE_SIZE_LIMIT_KB_DEFAULT; m_partyFilter.clear(); + m_stereoRecording = STEREO_RECORDING_DEFAULT; + m_tapeNumChannels = TAPE_NUM_CHANNELS_DEFAULT; m_tapeDurationMinimumSec = TAPE_DURATION_MINIMUM_SEC_DEFAULT; } @@ -141,6 +143,8 @@ void Config::Define(Serializer* s) s->BoolValue(ALLOW_AUTOMATIC_RECORDING_PARAM, m_allowAutomaticRecording); // only valid in non-lookback mode s->IntValue(CAPTURE_FILE_SIZE_LIMIT_KB_PARAM, m_captureFileSizeLimitKb); s->CsvValue(PARTY_FILTER_PARAM, m_partyFilter); + s->BoolValue(STEREO_RECORDING_PARAM, m_stereoRecording); + s->IntValue(TAPE_NUM_CHANNELS_PARAM, m_tapeNumChannels); s->IntValue(TAPE_DURATION_MINIMUM_SEC_PARAM, m_tapeDurationMinimumSec); } @@ -185,6 +189,12 @@ void Config::Validate() throw(exc); } } + if(m_tapeNumChannels < 0) + { + CStdString exception; + exception.Format("Config::Validate: please set a valid number for TapeNumChannels - currently:%d", m_tapeNumChannels); + throw(exception); + } } CStdString Config::GetClassName() diff --git a/orkbasecxx/Config.h b/orkbasecxx/Config.h index 0713c47..561e238 100644 --- a/orkbasecxx/Config.h +++ b/orkbasecxx/Config.h @@ -104,6 +104,10 @@ #define CAPTURE_FILE_SIZE_LIMIT_KB_DEFAULT 300000 #define PARTY_FILTER_PARAM "PartyFilter" #define PARTY_FILTER_DEFAULT "" +#define STEREO_RECORDING_PARAM "StereoRecording" +#define STEREO_RECORDING_DEFAULT false +#define TAPE_NUM_CHANNELS_PARAM "TapeNumChannels" +#define TAPE_NUM_CHANNELS_DEFAULT 2 #define TAPE_DURATION_MINIMUM_SEC_PARAM "TapeDurationMinimumSec" #define TAPE_DURATION_MINIMUM_SEC_DEFAULT 0 @@ -166,6 +170,8 @@ public: bool m_allowAutomaticRecording; int m_captureFileSizeLimitKb; std::list<CStdString> m_partyFilter; + bool m_stereoRecording; + int m_tapeNumChannels; int m_tapeDurationMinimumSec; private: diff --git a/orkbasecxx/audiofile/LibSndFileFile.cpp b/orkbasecxx/audiofile/LibSndFileFile.cpp index c7377fd..6250057 100644 --- a/orkbasecxx/audiofile/LibSndFileFile.cpp +++ b/orkbasecxx/audiofile/LibSndFileFile.cpp @@ -19,12 +19,12 @@ LibSndFileFile::LibSndFileFile(int fileFormat) { - m_fileInfo.format = fileFormat; + m_fileInfo.format = fileFormat; m_fileInfo.frames = 0; - m_fileInfo.samplerate = 0; - m_fileInfo.channels = 0; - m_fileInfo.sections = 0; - m_fileInfo.seekable = 0; + m_fileInfo.samplerate = 0; + m_fileInfo.channels = 0; + m_fileInfo.sections = 0; + m_fileInfo.seekable = 0; m_pFile = NULL; m_numChunksWritten = 0; m_mode = READ; @@ -43,7 +43,21 @@ void LibSndFileFile::Open(CStdString& filename, fileOpenModeEnum mode, bool ster m_filename = filename + ".wav"; } m_mode = mode; - stereo ? m_fileInfo.channels = 2 : m_fileInfo.channels = 1; + if(CONFIG.m_stereoRecording == true) + { + if(CONFIG.m_tapeNumChannels > 1) + { + m_fileInfo.channels = CONFIG.m_tapeNumChannels; + } + else + { + m_fileInfo.channels = 1; + } + } + else + { + m_fileInfo.channels = 1; + } if(m_sampleRate == 0) { m_sampleRate = sampleRate; @@ -82,18 +96,95 @@ void LibSndFileFile::WriteChunk(AudioChunkRef chunkRef) { if( chunkRef->GetEncoding() == AlawAudio || chunkRef->GetEncoding() == UlawAudio) { - if(sf_write_raw(m_pFile, chunkRef->m_pBuffer, chunkRef->GetNumSamples()) != chunkRef->GetNumSamples()) + // We have faith that whoever is producing these chunks created them + // with the same number of channels that we have opened the soundfile + // with above - this is enforced in the RtpMixer + if(chunkRef->m_numChannels > 0 && CONFIG.m_stereoRecording == true) + { + int numBytes = 0; + unsigned char *muxAudio = NULL; + unsigned char *wrPtr = NULL; + + numBytes = chunkRef->GetNumSamples() * chunkRef->m_numChannels; + muxAudio = (unsigned char*)malloc(numBytes); + wrPtr = muxAudio; + + if(!muxAudio) + { + CStdString exception; + + exception.Format("sf_write_raw failed for stereo write: could not allocate %d bytes", numBytes); + throw(exception); + } + + for(int x = 0; x < chunkRef->GetNumSamples(); x++) + { + for(int i = 0; i < chunkRef->m_numChannels; i++) + { + *wrPtr++ = (unsigned char)*((unsigned char*)(chunkRef->m_pChannelAudio[i])+x); + } + } + + if(sf_write_raw(m_pFile, muxAudio, numBytes) != numBytes) + { + CStdString numChunksWrittenString = IntToString(m_numChunksWritten); + free(muxAudio); + throw(CStdString("sf_write_raw failed, audio file " + m_filename + " could not be written after " + numChunksWrittenString + " chunks written")); + } + free(muxAudio); + } + else { - CStdString numChunksWrittenString = IntToString(m_numChunksWritten); - throw(CStdString("sf_write_raw failed, audio file " + m_filename + " could not be written after " + numChunksWrittenString + " chunks written")); + if(sf_write_raw(m_pFile, chunkRef->m_pBuffer, chunkRef->GetNumSamples()) != chunkRef->GetNumSamples()) + { + CStdString numChunksWrittenString = IntToString(m_numChunksWritten); + throw(CStdString("sf_write_raw failed, audio file " + m_filename + " could not be written after " + numChunksWrittenString + " chunks written")); + } } } else if (chunkRef->GetEncoding() == PcmAudio) { - if(sf_write_short(m_pFile, (short*)chunkRef->m_pBuffer, chunkRef->GetNumSamples()) != chunkRef->GetNumSamples()) + // We have faith that whoever is producing these chunks created them + // with the same number of channels that we have opened the soundfile + // with above - this is enforced in the RtpMixer + if(chunkRef->m_numChannels > 0 && CONFIG.m_stereoRecording == true) + { + int numShorts = 0; + short *muxAudio = NULL; + short *wrPtr = NULL; + + numShorts = chunkRef->GetNumSamples()*chunkRef->m_numChannels; + muxAudio = (short*)calloc(numShorts, sizeof(short)); + wrPtr = muxAudio; + if(!muxAudio) + { + CStdString exception; + + exception.Format("sf_write_raw failed for stereo write: could not allocate %d bytes", numShorts*sizeof(short)); + throw(exception); + } + for(int x = 0; x < chunkRef->GetNumSamples(); x++) + { + for(int i = 0; i < chunkRef->m_numChannels; i++) + { + *wrPtr++ = (short)*((short*)(chunkRef->m_pChannelAudio[i])+x); + } + } + if(sf_write_short(m_pFile, muxAudio, numShorts) != numShorts) + { + CStdString numChunksWrittenString = IntToString(m_numChunksWritten); + free(muxAudio); + throw(CStdString("sf_write_short failed, audio file " + m_filename + " could not be written after " + numChunksWrittenString + " chunks written")); + } + free(muxAudio); + } + else { - CStdString numChunksWrittenString = IntToString(m_numChunksWritten); - throw(CStdString("sf_write_short failed, audio file " + m_filename + " could not be written after " + numChunksWrittenString + " chunks written")); + if(sf_write_short(m_pFile, (short*)chunkRef->m_pBuffer, chunkRef->GetNumSamples()) != chunkRef->GetNumSamples()) + { + CStdString numChunksWrittenString = IntToString(m_numChunksWritten); + throw(CStdString("sf_write_short failed, audio file " + m_filename + " could not be written after " + numChunksWrittenString + " chunks written")); + } } } m_numChunksWritten++; |