summaryrefslogtreecommitdiff
path: root/orkbasecxx
diff options
context:
space:
mode:
authorGerald Begumisa <ben_g@users.sourceforge.net>2008-02-27 19:21:41 +0000
committerGerald Begumisa <ben_g@users.sourceforge.net>2008-02-27 19:21:41 +0000
commit0937742d2f5689c93efca3a5a56e8b36f81152c7 (patch)
treea0c817c8b45c888c88d020583a45a5ec65a225dd /orkbasecxx
parent9483156b326918005bb42751cc0e37a16b3e3e41 (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.cpp118
-rw-r--r--orkbasecxx/AudioCapture.h19
-rw-r--r--orkbasecxx/Config.cpp10
-rw-r--r--orkbasecxx/Config.h6
-rw-r--r--orkbasecxx/audiofile/LibSndFileFile.cpp115
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++;