Last active
August 29, 2015 13:57
-
-
Save mox1/9752463 to your computer and use it in GitHub Desktop.
Audio Ring Buffer - used within SSRadio. Supports atomic reads and writes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* AudioBuffer2.h | |
* | |
* Created on: Jul 30, 2013 | |
* Author: mox1 | |
*/ | |
#ifndef AUDIOBUFFER2_H_ | |
#define AUDIOBUFFER2_H_ | |
#include <Poco/Types.h> | |
#include <Poco/Mutex.h> | |
#include <Poco/Event.h> | |
/*library includes */ | |
// Note power of two buffer size | |
#define kNumPointsInMyBuffer 1024 | |
class AudioBuffer2 { | |
public: | |
AudioBuffer2(size_t size,int sample_rate,int channels); | |
virtual ~AudioBuffer2(); | |
bool writeIntoBuffer(Poco::Int16 *myData, int numsamples); | |
Poco::Int32 readFromBuffer(Poco::Int16 *dst, int numsamples); | |
bool sampleRateChange(int in_sample); | |
bool numChannelChange(int in_channel); | |
bool maxBuffered(void); | |
void dump(int len); | |
void waitOnData(int milliseconds); | |
Poco::Int32 msLeft(); | |
void reset(int sample_rate, int channels); | |
void wind_back(int bytes); | |
int cur_sample_rate; | |
int cur_channels; | |
private: | |
Poco::Mutex lock; | |
Poco::Event gotData; | |
//index for writing | |
Poco::UInt32 currentIndex; | |
//index for reading | |
Poco::UInt32 readIndex; | |
Poco::UInt32 sizeOfBuffer; | |
Poco::Int16 *data; | |
//current amount of un-read data in buffer | |
volatile Poco::UInt32 available; | |
//this holds the maximum amount of "unread" data we want | |
Poco::UInt32 max_buf; | |
Poco::Int32 dropouts; | |
}; | |
#endif /* AUDIOBUFFER2_H_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* AudioBuffer2.cpp | |
* | |
* Created on: Jul 30, 2013 | |
* Author: mox1 | |
*/ | |
#include "AudioBuffer2.h" | |
#include "../SatLogging.h" | |
/*library includes */ | |
/*poco*/ | |
#include <Poco/Mutex.h> | |
#include <stdlib.h> | |
/* size must be a power of 2!!!!!! */ | |
AudioBuffer2::AudioBuffer2(size_t size,int sample_rate,int channels) { | |
// Initialize the ring buffer | |
sizeOfBuffer = size; | |
currentIndex = 0; | |
readIndex = 0; | |
data = (Poco::Int16 *) calloc(1,(size * sizeof(Poco::Int16))); | |
available = 0; | |
cur_sample_rate = sample_rate; | |
cur_channels = channels; | |
max_buf = size / 4; | |
Poco::Mutex lock(); | |
Poco::Event gotData(); | |
dropouts = 0; | |
} | |
AudioBuffer2::~AudioBuffer2() { | |
// TODO Auto-generated destructor stub | |
free(data); | |
data = NULL; | |
} | |
/* Reset back to a clean state */ | |
void AudioBuffer2::reset(int sample_rate, int channels) { | |
lock.lock(); | |
currentIndex = 0; | |
readIndex = 0; | |
available = 0; | |
cur_sample_rate = sample_rate; | |
cur_channels = channels; | |
dropouts = 0; | |
lock.unlock(); | |
//incase we are waiting on audio, signal this to try again | |
gotData.set(); | |
} | |
bool AudioBuffer2::sampleRateChange(int in_sample) { | |
if(in_sample == cur_sample_rate) return false; | |
return true; | |
} | |
bool AudioBuffer2::numChannelChange(int in_channel) { | |
if(in_channel == cur_channels) return false; | |
return true; | |
} | |
// A little function to write into the buffer | |
//this | |
bool AudioBuffer2::writeIntoBuffer(Poco::Int16 *myData, int numsamples) { | |
// -1 for our binary modulo in a moment | |
int buffLen = sizeOfBuffer - 1; | |
int lastWrittenSample = currentIndex; | |
int idx; | |
Poco::UInt32 total = (numsamples*cur_channels); | |
lock.lock(); | |
if(cur_channels == -1) { | |
SSDEBUG("HIT potential race condition, avoided!?!?!"); | |
lock.unlock(); | |
return false; | |
} | |
//make sure buffer has enough room | |
if((sizeOfBuffer - available) < total) { | |
//SSDEBUG("Buffer is full, can't hold any more! (%?d asked, %?d avail)",total,(sizeOfBuffer-available)); | |
lock.unlock(); | |
return false; | |
} | |
for (Poco::UInt32 i=0; i < total; ++i) { | |
// modulo will automagically wrap around our index | |
idx = (i + lastWrittenSample) & buffLen; | |
data[idx] = myData[i]; | |
} | |
// Update the current index of our ring buffer. | |
available += total; | |
currentIndex += total; | |
currentIndex &= sizeOfBuffer - 1; | |
lock.unlock(); | |
//Signal data has showed up | |
gotData.set(); | |
return true; | |
} | |
/* This now returns the number of actual bytes placed into the buffer | |
* Yes, I realize you ask in "samples" but some audio platforms (aka android) | |
* need to know how much | |
* | |
* returns number of bytes placed into dst (0 is valid, negative numbers are error) | |
*/ | |
Poco::Int32 AudioBuffer2::readFromBuffer(Poco::Int16 *dst, int numsamples) { | |
// -1 for our binary modulo in a moment | |
int buffLen = sizeOfBuffer - 1; | |
int lastReadSample = readIndex; | |
int idx; | |
lock.lock(); | |
if(cur_channels == -1) { | |
SSDEBUG("HIT potential race condition, avoided!?!?!"); | |
lock.unlock(); | |
return 0; | |
} | |
Poco::UInt32 total = (numsamples*cur_channels); | |
if (available == 0) { | |
SSDEBUG("No audio data available!"); | |
dropouts++; | |
lock.unlock(); | |
return 0; | |
} | |
/*previously this was a return false, | |
* now simply fill buffer with what we have | |
*/ | |
if (total > available) { | |
SSDEBUG("Not enough data available! (%?d asked, %?d avail)",total,available); | |
total = available; | |
} | |
for (Poco::UInt32 i=0; i < total; ++i) { | |
// modulo will automagically wrap around our index | |
idx = (i + lastReadSample) & buffLen; | |
dst[i] = data[idx]; | |
} | |
// Update the current index of our ring buffer. | |
available -= total; | |
readIndex += total; | |
readIndex &= sizeOfBuffer - 1; | |
//SSDEBUG("Available is now %?d",available); | |
lock.unlock(); | |
//Return total bytes that were written into output buffer | |
return total * sizeof(Poco::Int16); | |
} | |
//Calculate how much buffer is left, in ms | |
Poco::Int32 AudioBuffer2::msLeft() { | |
return (available / (cur_sample_rate/1000) ); | |
} | |
void AudioBuffer2::dump(int len) { | |
SSDEBUG("AudioBuffer [%?d - %?d]",currentIndex-len,len); | |
//pLogger->dump("TEST\n",&data[currentIndex-len],len); | |
} | |
void AudioBuffer2::waitOnData(int milliseconds) { | |
//Reset event | |
gotData.reset(); | |
//wait a max | |
gotData.tryWait(milliseconds); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment