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 }