/* ZynAddSubFX - a software synthesizer OutMgr.cpp - Audio Output Manager Copyright (C) 2016 Mark McCurry This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "OutMgr.h" #include #include #include #include "AudioOut.h" #include "Engine.h" #include "EngineMgr.h" #include "InMgr.h" #include "WavEngine.h" #include "../Misc/Master.h" #include "../Misc/Util.h" //for set_realtime() using namespace std; namespace zyncarla { OutMgr &OutMgr::getInstance(const SYNTH_T *synth) { static OutMgr instance(synth); return instance; } OutMgr::OutMgr(const SYNTH_T *synth_) :wave(new WavEngine(*synth_)), priBuf(new float[4096], new float[4096]), priBuffCurrent(priBuf), master(NULL), stales(0), synth(*synth_) { assert(synth_); currentOut = NULL; //init samples outr = new float[synth.buffersize]; outl = new float[synth.buffersize]; memset(outl, 0, synth.bufferbytes); memset(outr, 0, synth.bufferbytes); } OutMgr::~OutMgr() { delete wave; delete [] priBuf.l; delete [] priBuf.r; delete [] outr; delete [] outl; } /* Sequence of a tick * 1) Lets remove old/stale samples * 2) Apply appliciable midi events * 3) Lets see if we need to generate samples * 4) Lets generate some * 5) Goto 2 if more are needed * 6) Lets return those samples to the primary and secondary outputs * 7) Lets wait for another tick */ const Stereo OutMgr::tick(unsigned int frameSize) { InMgr &midi = InMgr::getInstance(); //SysEv->execute(); removeStaleSmps(); int i=0; while(frameSize > storedSmps()) { if(!midi.empty()) { midi.flush(i*synth.buffersize, (i+1)*synth.buffersize); } master->AudioOut(outl, outr); addSmps(outl, outr); i++; } stales = frameSize; return priBuf; } AudioOut *OutMgr::getOut(string name) { return dynamic_cast(EngineMgr::getInstance().getEng(name)); } string OutMgr::getDriver() const { return currentOut->name; } bool OutMgr::setSink(string name) { AudioOut *sink = getOut(name); if(!sink) return false; if(currentOut) currentOut->setAudioEn(false); currentOut = sink; currentOut->setAudioEn(true); bool success = currentOut->getAudioEn(); //Keep system in a valid state (aka with a running driver) if(!success) (currentOut = getOut("NULL"))->setAudioEn(true); return success; } string OutMgr::getSink() const { if(currentOut) return currentOut->name; else { cerr << "BUG: No current output in OutMgr " << __LINE__ << endl; return "ERROR"; } return "ERROR"; } void OutMgr::setMaster(Master *master_) { master=master_; } void OutMgr::applyOscEventRt(const char *msg) { master->applyOscEvent(msg); } //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(l, r), synth.buffersize); 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; } } void OutMgr::removeStaleSmps() { if(!stales) return; 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; } }