zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

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:
Msrc/Nio/AlsaEngine.cpp | 1+
Msrc/Nio/AudioOut.cpp | 102+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/Nio/AudioOut.h | 19++++++++++---------
Msrc/Nio/JackEngine.cpp | 19++++++++++++++++++-
Msrc/Nio/JackEngine.h | 2++
Msrc/Nio/OssEngine.cpp | 6+-----
Msrc/Nio/OutMgr.cpp | 9+++++----
Msrc/Nio/OutMgr.h | 2+-
Msrc/Nio/WavEngine.cpp | 2+-
Msrc/Samples/Sample.cpp | 6+++---
Msrc/Samples/Sample.h | 5+++--
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;