zynaddsubfx

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

OutMgr.cpp (7762B)


      1 /*
      2   ZynAddSubFX - a software synthesizer
      3 
      4   OutMgr.cpp - Audio Output Manager
      5   Copyright (C) 2016 Mark McCurry
      6 
      7   This program is free software; you can redistribute it and/or
      8   modify it under the terms of the GNU General Public License
      9   as published by the Free Software Foundation; either version 2
     10   of the License, or (at your option) any later version.
     11 */
     12 #include "OutMgr.h"
     13 #include <algorithm>
     14 #include <iostream>
     15 #include <cassert>
     16 #include "AudioOut.h"
     17 #include "Engine.h"
     18 #include "EngineMgr.h"
     19 #include "InMgr.h"
     20 #include "WavEngine.h"
     21 #include "../Misc/Master.h"
     22 #include "../Misc/Util.h" //for set_realtime()
     23 using namespace std;
     24 
     25 namespace zyn {
     26 
     27 OutMgr &OutMgr::getInstance(const SYNTH_T *synth)
     28 {
     29     static OutMgr instance(synth);
     30     return instance;
     31 }
     32 
     33 #if HAVE_BG_SYNTH_THREAD
     34 void *
     35 OutMgr::_refillThread(void *arg)
     36 {
     37     return static_cast<OutMgr *>(arg)->refillThread();
     38 }
     39 
     40 void *
     41 OutMgr::refillThread()
     42 {
     43     refillLock();
     44     while (bgSynthEnabled) {
     45         refillSmps(stales + synth.buffersize);
     46         refillWakeup();
     47         refillWait();
     48     }
     49     refillUnlock();
     50     return 0;
     51 }
     52 
     53 void
     54 OutMgr::setBackgroundSynth(bool enable)
     55 {
     56     void *dummy;
     57 
     58     if (bgSynthEnabled == enable)
     59         return;
     60     if (bgSynthEnabled) {
     61         refillLock();
     62         bgSynthEnabled = false;
     63         refillWakeup();
     64         refillUnlock();
     65 
     66         pthread_join(bgSynthThread, &dummy);
     67     } else {
     68         refillLock();
     69         bgSynthEnabled = true;
     70         refillUnlock();
     71 
     72         pthread_create(&bgSynthThread, 0, &_refillThread, this);
     73     }
     74 }
     75 #endif
     76 
     77 OutMgr::OutMgr(const SYNTH_T *synth_)
     78     :wave(new WavEngine(*synth_)),
     79       priBuf(new float[FRAME_SIZE_MAX],
     80              new float[FRAME_SIZE_MAX]),
     81       priBuffCurrent(priBuf), master(NULL), stales(0), synth(*synth_)
     82 {
     83     assert(synth_);
     84     currentOut = NULL;
     85 
     86     //init samples
     87     outr = new float[synth.buffersize];
     88     outl = new float[synth.buffersize];
     89     memset(outl, 0, synth.bufferbytes);
     90     memset(outr, 0, synth.bufferbytes);
     91 
     92 #if HAVE_BG_SYNTH_THREAD
     93     pthread_mutex_init(&bgSynthMtx, 0);
     94     pthread_cond_init(&bgSynthCond, 0);
     95     bgSynthEnabled = false;
     96 #endif
     97     midiFlushOffset = 0;
     98 
     99     /* at any stales value, there should be space for synth.buffersize samples */
    100     maxStoredSmps = FRAME_SIZE_MAX - (FRAME_SIZE_MAX % synth.buffersize);
    101     assert(maxStoredSmps > (unsigned int)synth.buffersize);
    102     maxStoredSmps -= synth.buffersize;
    103 }
    104 
    105 OutMgr::~OutMgr()
    106 {
    107 #if HAVE_BG_SYNTH_THREAD
    108     setBackgroundSynth(false);
    109 #endif
    110 
    111     delete wave;
    112     delete [] priBuf.l;
    113     delete [] priBuf.r;
    114     delete [] outr;
    115     delete [] outl;
    116 #if HAVE_BG_SYNTH_THREAD
    117     pthread_cond_destroy(&bgSynthCond);
    118     pthread_mutex_destroy(&bgSynthMtx);
    119 #endif
    120 }
    121 
    122 void OutMgr::refillSmps(unsigned int smpsLimit)
    123 {
    124     InMgr &midi = InMgr::getInstance();
    125 
    126     while(smpsLimit > curStoredSmps()) {
    127         refillUnlock();
    128         if(!midi.empty() &&
    129            !midi.flush(midiFlushOffset, midiFlushOffset + synth.buffersize)) {
    130           midiFlushOffset += synth.buffersize;
    131         } else {
    132           midiFlushOffset = 0;
    133         }
    134         master->AudioOut(outl, outr);
    135         refillLock();
    136         addSmps(outl, outr);
    137     }
    138 }
    139 
    140 /* Sequence of a tick
    141  * 1) Lets remove old/stale samples
    142  * 2) Apply applicable MIDI events
    143  * 3) Lets see if we need to generate samples
    144  * 4) Lets generate some
    145  * 5) Goto 2 if more are needed
    146  * 6) Lets return those samples to the primary and secondary outputs
    147  * 7) Lets wait for another tick
    148  */
    149 Stereo<float *> OutMgr::tick(unsigned int frameSize)
    150 {
    151     auto retval = priBuf;
    152     //SysEv->execute();
    153     refillLock();
    154     /* cleanup stales, if any */
    155     if(frameSize + stales > maxStoredSmps)
    156         removeStaleSmps();
    157 #if HAVE_BG_SYNTH_THREAD
    158     /* check if backround sampling is enabled */
    159     if(bgSynthEnabled) {
    160         assert(frameSize <= (unsigned int)synth.buffersize);
    161         /* wait for background samples to complete, if any */
    162         while(frameSize + stales > curStoredSmps())
    163             refillWait();
    164     } else {
    165 #endif
    166         /* check if drivers ask for too many samples */
    167         assert(frameSize + stales <= maxStoredSmps);
    168         /* produce samples foreground, if any */
    169         refillSmps(frameSize + stales);
    170 #if HAVE_BG_SYNTH_THREAD
    171     }
    172 #endif
    173     retval.l += stales;
    174     retval.r += stales;
    175     stales += frameSize;
    176 #if HAVE_BG_SYNTH_THREAD
    177     if(bgSynthEnabled) {
    178         /* start refill thread again, if any */
    179         refillWakeup();
    180     }
    181 #endif
    182     refillUnlock();
    183     return retval;
    184 }
    185 
    186 AudioOut *OutMgr::getOut(string name)
    187 {
    188     return dynamic_cast<AudioOut *>(EngineMgr::getInstance().getEng(name));
    189 }
    190 
    191 string OutMgr::getDriver() const
    192 {
    193     return currentOut->name;
    194 }
    195 
    196 bool OutMgr::setSink(string name)
    197 {
    198     AudioOut *sink = getOut(name);
    199 
    200     if(!sink)
    201         return false;
    202 
    203     if(currentOut)
    204         currentOut->setAudioEn(false);
    205 
    206     currentOut = sink;
    207     currentOut->setAudioEn(true);
    208 
    209     bool success = currentOut->getAudioEn();
    210 
    211     //Keep system in a valid state (aka with a running driver)
    212     if(!success)
    213         (currentOut = getOut("NULL"))->setAudioEn(true);
    214 
    215     return success;
    216 }
    217 
    218 string OutMgr::getSink() const
    219 {
    220     if(currentOut)
    221         return currentOut->name;
    222     else {
    223         cerr << "BUG: No current output in OutMgr " << __LINE__ << endl;
    224         return "ERROR";
    225     }
    226     return "ERROR";
    227 }
    228 
    229 void OutMgr::setAudioCompressor(bool isEnabled)
    230 {
    231     currentOut->isOutputCompressionEnabled=isEnabled;
    232 }
    233 
    234 bool OutMgr::getAudioCompressor(void)
    235 {
    236     return currentOut->isOutputCompressionEnabled;
    237 }
    238 
    239 void OutMgr::setMaster(Master *master_)
    240 {
    241     master=master_;
    242 }
    243 
    244 void OutMgr::applyOscEventRt(const char *msg)
    245 {
    246     master->applyOscEvent(msg);
    247 }
    248 
    249 //perform a cheap linear interpolation for resampling
    250 //This will result in some distortion at frame boundaries
    251 //returns number of samples produced
    252 static size_t resample(float *dest,
    253                        const float *src,
    254                        float s_in,
    255                        float s_out,
    256                        size_t elms)
    257 {
    258     size_t out_elms = elms * s_out / s_in;
    259     float  r_pos    = 0.0f;
    260     for(int i = 0; i < (int)out_elms; ++i, r_pos += s_in / s_out)
    261         dest[i] = interpolate(src, elms, r_pos);
    262 
    263     return out_elms;
    264 }
    265 
    266 void OutMgr::addSmps(float *l, float *r)
    267 {
    268     //allow wave file to syphon off stream
    269     wave->push(Stereo<float *>(l, r), synth.buffersize);
    270 
    271     const int s_out = currentOut->getSampleRate(),
    272               s_sys = synth.samplerate;
    273 
    274     if(s_out != s_sys) { //we need to resample
    275         const size_t steps = resample(priBuffCurrent.l,
    276                                       l,
    277                                       s_sys,
    278                                       s_out,
    279                                       synth.buffersize);
    280         resample(priBuffCurrent.r, r, s_sys, s_out, synth.buffersize);
    281 
    282         priBuffCurrent.l += steps;
    283         priBuffCurrent.r += steps;
    284     }
    285     else { //just copy the samples
    286         memcpy(priBuffCurrent.l, l, synth.bufferbytes);
    287         memcpy(priBuffCurrent.r, r, synth.bufferbytes);
    288         priBuffCurrent.l += synth.buffersize;
    289         priBuffCurrent.r += synth.buffersize;
    290     }
    291 }
    292 
    293 void OutMgr::removeStaleSmps()
    294 {
    295     if(!stales)
    296         return;
    297 
    298     const int leftover = curStoredSmps() - stales;
    299 
    300     assert(leftover > -1);
    301 
    302     //leftover samples [seen at very low latencies]
    303     if(leftover) {
    304         memmove(priBuf.l, priBuffCurrent.l - leftover, leftover * sizeof(float));
    305         memmove(priBuf.r, priBuffCurrent.r - leftover, leftover * sizeof(float));
    306         priBuffCurrent.l = priBuf.l + leftover;
    307         priBuffCurrent.r = priBuf.r + leftover;
    308     }
    309     else
    310         priBuffCurrent = priBuf;
    311 
    312     stales = 0;
    313 }
    314 
    315 }