|
- /*
- 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 <algorithm>
- #include <iostream>
- #include <cassert>
- #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<float *> 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<AudioOut *>(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<float *>(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;
- }
-
- }
|