zynaddsubfx

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

commit bd461ef7d62e00b9eeb6a23267bd847b4d964398
parent 13f7a55881503c16bfaa756ff7f332934eef96e9
Author: fundamental <[email protected]>
Date:   Tue, 17 Jan 2012 17:03:48 -0500

OutMgr: Resampling/low latency glitches resolved

- Cheap resampling integrated into OutMgr as previous solution was sub-par
- OutMgr no longer assumes that synth->buffersize >= IO's frame_size

Diffstat:
Msrc/Nio/JackEngine.cpp | 3+++
Msrc/Nio/JackEngine.h | 3+--
Msrc/Nio/OutMgr.cpp | 70++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
3 files changed, 54 insertions(+), 22 deletions(-)

diff --git a/src/Nio/JackEngine.cpp b/src/Nio/JackEngine.cpp @@ -171,6 +171,9 @@ bool JackEngine::openAudio() if((NULL != audio.ports[0]) && (NULL != audio.ports[1])) { audio.jackSamplerate = jack_get_sample_rate(jackClient); audio.jackNframes = jack_get_buffer_size(jackClient); + samplerate = audio.jackSamplerate; + bufferSize = audio.jackNframes; + //Attempt to autoConnect when specified if(Nio::autoConnect) { diff --git a/src/Nio/JackEngine.h b/src/Nio/JackEngine.h @@ -46,8 +46,7 @@ class JackEngine:public AudioOut, MidiIn void setAudioEn(bool nval); bool getAudioEn() const; - unsigned int getSamplerate() { return audio.jackSamplerate; } - unsigned int getBuffersize() { return audio.jackNframes; } + int getBuffersize() { return audio.jackNframes; } std::string clientName(); int clientId(); diff --git a/src/Nio/OutMgr.cpp b/src/Nio/OutMgr.cpp @@ -1,6 +1,7 @@ #include "OutMgr.h" #include <algorithm> #include <iostream> +#include <cassert> #include "AudioOut.h" #include "Engine.h" #include "EngineMgr.h" @@ -68,10 +69,7 @@ const Stereo<float *> OutMgr::tick(unsigned int frameSize) pthread_mutex_unlock(&(master.mutex)); addSmps(outl, outr); } - Stereo<float *> ans = priBuffCurrent; - ans.l -= frameSize; - ans.r -= frameSize; - //cout << storedSmps() << '=' << frameSize << endl; + stales = frameSize; return priBuf; } @@ -118,28 +116,50 @@ string OutMgr::getSink() const return "ERROR"; } +//performs linear interpolation +static float interpolate(const float *data, size_t len, float pos) +{ + assert(len > (size_t)pos+1); + const int l_pos = (int)pos, + r_pos = l_pos + 1; + const float leftness = pos - l_pos; + return data[l_pos] * leftness + data[r_pos] * (1.0f - leftness); +} + +//perform a cheap linear interpolation for resampling +//This will result in some distortion at frame boundries +//returns number of samples produced +static size_t resample(float *dest, const float *src, float s_in, float s_out, size_t elms) +{ + size_t out_elms = elms*s_out/s_in; + float r_pos = 0.0f; + for(int i = 0; i < (int)out_elms; ++i, r_pos += s_in/s_out) + dest[i] = interpolate(src, elms, r_pos); + + return out_elms; +} + void OutMgr::addSmps(float *l, float *r) { //allow wave file to syphon off stream wave->push(Stereo<float *>(l, r), synth->buffersize); - if(currentOut->getSampleRate() != (size_t)synth->samplerate) { //we need to resample - //cout << "BAD RESAMPLING" << endl; - Stereo<Sample> smps(Sample(synth->buffersize, l), Sample( - synth->buffersize, - r)); - smps.l.resample(synth->samplerate, currentOut->getSampleRate()); - smps.r.resample(synth->samplerate, currentOut->getSampleRate()); - memcpy(priBuffCurrent.l, smps.l.c_buf(), synth->bufferbytes); - memcpy(priBuffCurrent.r, smps.r.c_buf(), synth->bufferbytes); + const int s_out = currentOut->getSampleRate(), + s_sys = synth->samplerate; + + if(s_out != s_sys) { //we need to resample + const size_t steps = resample(priBuffCurrent.l, l, s_sys, s_out, synth->buffersize); + resample(priBuffCurrent.r, r, s_sys, s_out, synth->buffersize); + + priBuffCurrent.l += steps; + priBuffCurrent.r += steps; } else { //just copy the samples memcpy(priBuffCurrent.l, l, synth->bufferbytes); memcpy(priBuffCurrent.r, r, synth->bufferbytes); + priBuffCurrent.l += synth->buffersize; + priBuffCurrent.r += synth->buffersize; } - priBuffCurrent.l += synth->buffersize; - priBuffCurrent.r += synth->buffersize; - stales += synth->buffersize; } void OutMgr::removeStaleSmps() @@ -147,9 +167,19 @@ void OutMgr::removeStaleSmps() if(!stales) return; - //memset is possibly unneeded - memset(priBuf.l, '0', 4096 * sizeof(float)); - memset(priBuf.r, '0', 4096 * sizeof(float)); - priBuffCurrent = priBuf; + const int leftover = storedSmps() - stales; + + assert(leftover>-1); + + //leftover samples [seen at very low latencies] + if(leftover) { + memmove(priBuf.l, priBuffCurrent.l - leftover, leftover * sizeof(float)); + memmove(priBuf.r, priBuffCurrent.r - leftover, leftover * sizeof(float)); + priBuffCurrent.l = priBuf.l + leftover; + priBuffCurrent.r = priBuf.r + leftover; + } + else + priBuffCurrent = priBuf; + stales = 0; }