commit 8e30753aebf515b3e323034f712092c278e6491b
parent 0d4a565a6d84ce8fb9c40a55f6097e1a85c9f0ac
Author: fundamental <mark.d.mccurry@gmail.com>
Date: Sat, 19 Dec 2009 23:12:00 -0500
Nio: Sample processing reordering to reduce latency
- This should reduce the time that it takes from requested sample to delivered
sample
- This also is a good be more clean than the other solution, so that is a step
in the right direction
Diffstat:
11 files changed, 100 insertions(+), 73 deletions(-)
diff --git a/src/Nio/AlsaEngine.cpp b/src/Nio/AlsaEngine.cpp
@@ -107,6 +107,7 @@ void *AlsaEngine::_AudioThread(void *arg)
void *AlsaEngine::AudioThread()
{
RunStuff();
+ return NULL;
}
diff --git a/src/Nio/AudioOut.cpp b/src/Nio/AudioOut.cpp
@@ -26,8 +26,8 @@ using namespace std;
#include "AudioOut.h"
AudioOut::AudioOut(OutMgr *out)
- :samplerate(SAMPLE_RATE),nsamples(SOUND_BUFFER_SIZE),
- manager(out),enabled(false)
+ :samplerate(SAMPLE_RATE),bufferSize(SOUND_BUFFER_SIZE),
+ usePartial(false),manager(out),enabled(false)
{
pthread_mutex_init(&outBuf_mutex, NULL);
pthread_cond_init (&outBuf_cv, NULL);
@@ -40,20 +40,65 @@ AudioOut::~AudioOut()
void AudioOut::out(Stereo<Sample> smps)
{
- if(samplerate != SAMPLE_RATE) {
- smps.l().resample(SAMPLE_RATE,samplerate);//we need to resample
- smps.r().resample(SAMPLE_RATE,samplerate);//we need to resample
+#warning TODO check for off by one errors
+ pthread_mutex_lock(&outBuf_mutex);
+ if(samplerate != SAMPLE_RATE) { //we need to resample
+ smps.l().resample(SAMPLE_RATE,samplerate);
+ smps.r().resample(SAMPLE_RATE,samplerate);
+ }
+
+ if(usePartial) { //we have a partial to use
+ smps.l() = partialIn.l().append(smps.l());
+ smps.r() = partialIn.r().append(smps.r());
+ }
+
+ if(smps.l().size() == bufferSize) { //sized just right
+ outBuf.push(smps);
+ usePartial = false;
+ pthread_cond_signal(&outBuf_cv);
+ }
+ else if(smps.l().size() > bufferSize) { //store overflow
+ outBuf.push(Stereo<Sample>(smps.l().subSample(0,bufferSize),
+ smps.r().subSample(0,bufferSize)));
+ partialIn = Stereo<Sample>(smps.l().subSample(bufferSize,smps.l().size()),
+ smps.r().subSample(bufferSize,smps.r().size()));
+ usePartial = true;
+ pthread_cond_signal(&outBuf_cv);
+ }
+ else { //underflow
+ partialIn = smps;
+ usePartial = true;
}
+ pthread_mutex_unlock(&outBuf_mutex);
+}
+void AudioOut::setSamplerate(int _samplerate)
+{
pthread_mutex_lock(&outBuf_mutex);
- outBuf.push_back(smps);
- pthread_cond_signal(&outBuf_cv);
+ usePartial = false;
+ samplerate = _samplerate;
+ //hm, the queue does not have a clear
+ while(!outBuf.empty())
+ outBuf.pop();
pthread_mutex_unlock(&outBuf_mutex);
+
}
-const Stereo<Sample> AudioOut::popOne()
+void AudioOut::setBufferSize(int _bufferSize)
+{
+ pthread_mutex_lock(&outBuf_mutex);
+ usePartial = false;
+ bufferSize = _bufferSize;
+ //hm, the queue does not have a clear
+ while(!outBuf.empty())
+ outBuf.pop();
+ pthread_mutex_unlock(&outBuf_mutex);
+};
+
+
+const Stereo<Sample> AudioOut::getNext()
{
- const int BUFF_SIZE = 6;
+ const unsigned int BUFF_SIZE = 6;
Stereo<Sample> ans;
pthread_mutex_lock(&outBuf_mutex);
bool isEmpty = outBuf.empty();
@@ -71,7 +116,7 @@ const Stereo<Sample> AudioOut::popOne()
{
pthread_mutex_lock(&outBuf_mutex);
ans = outBuf.front();
- outBuf.pop_front();
+ outBuf.pop();
if(outBuf.size()+manager->getRunning()<BUFF_SIZE)
manager->requestSamples(BUFF_SIZE - (outBuf.size()
+ manager->getRunning()));
@@ -82,40 +127,3 @@ const Stereo<Sample> AudioOut::popOne()
current=ans;
return ans;
}
-
-//hopefully this does not need to be called
-//(it can cause a horrible mess with the current starvation behavior)
-void AudioOut::putBack(const Stereo<Sample> smp)
-{
- pthread_mutex_lock(&outBuf_mutex);
- outBuf.push_front(smp);
- pthread_mutex_unlock(&outBuf_mutex);
-}
-
-const Stereo<Sample> AudioOut::getNext(int smps)
-{
-
- if(smps<1)
- smps = nsamples;
-
- Stereo<Sample> ans = popOne();
-
- //if everything is perfectly configured, this should not need to loop
- while(ans.l().size()!=smps) {
- if(ans.l().size() > smps) {
- //subsample/putback excess
-#warning TODO check for off by one errors
- putBack(Stereo<Sample>(ans.l().subSample(smps,ans.l().size()),
- ans.l().subSample(smps,ans.l().size())));
- ans = Stereo<Sample>(ans.l().subSample(0,smps),
- ans.l().subSample(0,smps));
- }
- else {
- Stereo<Sample> next = popOne();
- ans.l().append(next.l());
- ans.r().append(next.r());
- }
- }
-
- return ans;
-}
diff --git a/src/Nio/AudioOut.h b/src/Nio/AudioOut.h
@@ -23,7 +23,7 @@
#include "../Misc/Stereo.h"
#include "../Samples/Sample.h"
-#include <deque>
+#include <queue>
#include <pthread.h>
#include "OutMgr.h"
#include "../Misc/Atomic.h"
@@ -50,29 +50,30 @@ class AudioOut
/**Sets the Sample Rate of this Output
* (used for getNext()).*/
- void setSamplerate(int _samplerate) {samplerate=_samplerate;};
+ void setSamplerate(int _samplerate);
/**Sets the Samples required per Out of this driver
* (used for getNext()).*/
- void setNsamples(int _nsamples) {nsamples=_nsamples;};
+ void setBufferSize(int _bufferSize);
protected:
- const Stereo<Sample> popOne();
void putBack(const Stereo<Sample> smp);
/**Get the next sample for output.
* (has nsamples sampled at a rate of samplerate)*/
- virtual const Stereo<Sample> getNext(int smps = -1);
+ virtual const Stereo<Sample> getNext();
int samplerate;
- int nsamples;
+ int bufferSize;
- std::deque<Stereo<Sample> > outBuf;
- const Sample * curSmp;
- Stereo<Sample> current;
+ std::queue<Stereo<Sample> > outBuf;
pthread_mutex_t outBuf_mutex;
pthread_cond_t outBuf_cv;
+ Stereo<Sample> partialIn;/**<used for taking in samples with a different samplerate/buffersize*/
+ bool usePartial;
+ Stereo<Sample> current;/**<used for xrun defence*/
+
OutMgr *manager;
//thread resources
pthread_t pThread;
diff --git a/src/Nio/JackEngine.cpp b/src/Nio/JackEngine.cpp
@@ -82,9 +82,12 @@ bool JackEngine::Start()
openAudio();
if (NULL != jackClient)
{
+ setBufferSize(jack_get_buffer_size(jackClient));
int chk;
jack_set_error_function(_errorCallback);
jack_set_info_function(_infoCallback);
+ if(jack_set_buffer_size_callback(jackClient, _bufferSizeCallback, this))
+ cerr << "Error setting the bufferSize callback" << endl;
if ((chk = jack_set_xrun_callback(jackClient, _xrunCallback, this)))
cerr << "Error setting jack xrun callback" << endl;
if (jack_set_process_callback(jackClient, _processCallback, this))
@@ -192,8 +195,10 @@ bool JackEngine::processAudio(jack_nframes_t nframes)
}
}
- Stereo<Sample> smp = getNext(nframes);
+ Stereo<Sample> smp = getNext();
//cout << "smp size of: " << smp.l().size() << endl;
+
+ //Assumes smp.l().size() == nframes
memcpy(audio.portBuffs[0], smp.l().c_buf(), smp.l().size()*sizeof(REALTYPE));
memcpy(audio.portBuffs[1], smp.r().c_buf(), smp.r().size()*sizeof(REALTYPE));
return true;
@@ -215,3 +220,15 @@ void JackEngine::_infoCallback(const char *msg)
{
cerr << "Jack info message: " << msg << endl;
}
+
+int JackEngine::_bufferSizeCallback(jack_nframes_t nframes, void *arg)
+{
+ return static_cast<JackEngine*>(arg)->bufferSizeCallback(nframes);
+}
+
+int JackEngine::bufferSizeCallback(jack_nframes_t nframes)
+{
+ cerr << "Jack buffer resized" << endl;
+ setBufferSize(nframes);
+ return 0;
+}
diff --git a/src/Nio/JackEngine.h b/src/Nio/JackEngine.h
@@ -50,6 +50,8 @@ class JackEngine : public AudioOut
int processCallback(jack_nframes_t nframes);
static int _processCallback(jack_nframes_t nframes, void *arg);
+ int bufferSizeCallback(jack_nframes_t nframes);
+ static int _bufferSizeCallback(jack_nframes_t nframes, void *arg);
static void _errorCallback(const char *msg);
static void _infoCallback(const char *msg);
static int _xrunCallback(void *arg);
diff --git a/src/Nio/OssEngine.cpp b/src/Nio/OssEngine.cpp
@@ -46,8 +46,6 @@ OssEngine::OssEngine(OutMgr *out)
smps = new short[SOUND_BUFFER_SIZE * 2];
memset(smps, 0, sizeof(short) * SOUND_BUFFER_SIZE * 2);
-
- cerr << "hello?" << endl;
}
OssEngine::~OssEngine()
@@ -60,7 +58,6 @@ bool OssEngine::openAudio()
{
int snd_bitsize = 16;
snd_handle = open(config.cfg.LinuxOSSWaveOutDev, O_WRONLY, 0);
- cerr << config.cfg.LinuxOSSWaveOutDev << endl;
if(snd_handle == -1) {
cerr << "ERROR - I can't open the "
<< config.cfg.LinuxOSSWaveOutDev << '.' << endl;
@@ -81,12 +78,11 @@ bool OssEngine::Start()
return true;
if(!openAudio())
return false;
+ enabled = true;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&pThread, &attr, _AudioThread, this);
- cout << "Starting Oss";
- enabled = true;
return true;
}
diff --git a/src/Nio/OutMgr.cpp b/src/Nio/OutMgr.cpp
@@ -93,10 +93,11 @@ void *OutMgr::outputThread()
if(!defaultOut->Start())//there should be a better failsafe
cerr << "ERROR: The default Audio Output Failed to Open!" << endl;
+
//setup
- running=true;
- init=true;
- bool doWait=false;
+ running = true;
+ init = true;
+ bool doWait = false;
while(running()) {
if(false) {
@@ -189,7 +190,7 @@ void OutMgr::remove(AudioOut *out)
pthread_mutex_unlock(&mutex);
}
-int OutMgr::getRunning()
+unsigned int OutMgr::getRunning()
{
return numRequests();
}
diff --git a/src/Nio/OutMgr.h b/src/Nio/OutMgr.h
@@ -32,7 +32,7 @@ class OutMgr
int requestSamples(unsigned int n=1);
/**Return the number of building samples*/
- int getRunning();
+ unsigned int getRunning();
void run();
diff --git a/src/Nio/WavEngine.cpp b/src/Nio/WavEngine.cpp
@@ -128,7 +128,7 @@ const Stereo<Sample> WavEngine::getNext()
}
pthread_mutex_lock(&outBuf_mutex);
ans = outBuf.front();
- outBuf.pop_front();
+ outBuf.pop();
pthread_mutex_unlock(&outBuf_mutex);
return ans;
}
diff --git a/src/Samples/Sample.cpp b/src/Samples/Sample.cpp
@@ -1,7 +1,7 @@
/*
ZynAddSubFX - a software synthesizer
- Sample.C - Object for storing information on samples
+ Sample.cpp - Object for storing information on samples
Copyright (C) 2009-2009 Mark McCurry
Author: Mark McCurry
@@ -134,7 +134,6 @@ void Sample::resample(const unsigned int rate, const unsigned int nrate)
if(rate == nrate)
return; //no resampling here
else {//resampling occurs here
- int itr = 0;
float ratio = (nrate * 1.0) / (rate * 1.0);
int nBufferSize = (int)bufferSize * ratio;
@@ -155,7 +154,7 @@ void Sample::resample(const unsigned int rate, const unsigned int nrate)
}
}
-void Sample::append(const Sample &smp)
+Sample &Sample::append(const Sample &smp)
{
int nbufferSize = bufferSize + smp.bufferSize;
float *nbuffer = new float[nbufferSize];
@@ -166,6 +165,7 @@ void Sample::append(const Sample &smp)
buffer = nbuffer;
bufferSize = nbufferSize;
+ return *this;
}
Sample Sample::subSample(int a, int b) const
diff --git a/src/Samples/Sample.h b/src/Samples/Sample.h
@@ -61,8 +61,9 @@ class Sample
/**Change the sampling rate of the Sample*/
void resample(const unsigned int rate, const unsigned int nrate);
- /**Appends another Sample to this Sample*/
- void append(const Sample &smp);
+ /**Appends another Sample to this Sample
+ * @return this*/
+ Sample &append(const Sample &smp);
/**Gets a subsample from a to b*/
Sample subSample(int a, int b) const;