Skip to content

Instantly share code, notes, and snippets.

@mox1
Last active August 29, 2015 13:57
Show Gist options
  • Save mox1/9752463 to your computer and use it in GitHub Desktop.
Save mox1/9752463 to your computer and use it in GitHub Desktop.
Audio Ring Buffer - used within SSRadio. Supports atomic reads and writes.
/*
* 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_ */
/*
* 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