f@0: // f@0: // AccessiblePeakMeter.h f@0: // f@0: // Author: Fiore Martin f@0: // Started from IPlugMultiTargets example in WDL-OL, by Oli Larkin - https://github.com/olilarkin/wdl-ol f@0: // f@0: // Licensed under the Cockos WDL License, see README.txt f@0: // f@0: f@0: #ifndef __ACCESSIBLEPEAKMETER__ f@0: #define __ACCESSIBLEPEAKMETER__ f@0: f@0: #include "IPlug_include_in_plug_hdr.h" f@0: #include "stk/include/SineWave.h" f@0: #include "stk/include/Envelope.h" f@0: #include "stk/include/ADSR.h" f@0: #include "stk/include/RtAudio.h" f@0: #include "stk/include/RtWvOut.h" f@0: f@0: #include f@0: #include f@0: f@0: f@0: const int DAC_BUFFER_SIZE = 64; f@0: const int SONIFICATION_TYPE_CLIPPING = 1; f@0: const int SONIFICATION_TYPE_CONTINUOUS = 0; f@0: const double BEEP_TIME = 0.2; f@0: const int MAX_CHANNELS = 2; f@0: f@0: /* f@0: This is a slightly hacked version of the AccessiblePeakMeter. The sonification is sent to f@0: the defualt audio card rather than to the plugin output. The input audio signal is still f@0: routed to the plugin out put with nomodification besides the amplitude, modified according f@0: to the dry parameter. f@0: f@0: When Reset() is called for the first time on the plugin, a new thread is spawned. f@0: The VST plugin then continuously shares information about the input audio with the thread. f@0: The thread continously polls this information and plays the sonification on the default audio interface. f@0: f@0: DacRoutine() : is the function execute by the new thread f@0: DacSynced : is the struct with all shared variables between the new thread and the VST plugin f@0: Sonification: is the structure with all the sonification stuff. In this version it is only f@0: accessed by the new thread. f@0: f@0: DacThread struct does RAAI on the resource "thread" f@0: f@0: */ f@0: f@0: f@0: f@0: /* DacRouting is the function executed by the thread that plays the sonification to the dac */ f@0: void DacRoutine(); f@0: f@0: /* the sonification struct contains all the variables for sonification. f@0: It is accessed exclusively by the sonification thread */ f@0: struct Sonification { f@0: stk::SineWave ugen[MAX_CHANNELS]; f@0: f@0: /* resets all the ugens. Type and ugen freq are not affected by reset f@0: as well as sample rate, when srate is less than 0 */ f@0: void reset(double srate = -1.0){ f@0: for (int i = 0; i < MAX_CHANNELS; i++){ f@0: ugen[i].reset(); f@0: f@0: /* continous sonification */ f@0: continous.isOn[i] = true; f@0: stk::Envelope & coe = continous.envelope[i]; f@0: coe.setValue(1.0); // setValue also sets the target f@0: coe.setTime(0.2); f@0: f@0: /* clipping sonification */ f@0: clipping.maxDiff[i] = 0.0; f@0: stk::ADSR & cle = clipping.envelope[i]; f@0: cle.setValue(0.0); f@0: cle.setReleaseTime(BEEP_TIME); f@0: cle.setAttackTime(0.01); f@0: cle.setSustainLevel(1); f@0: f@0: /* f@0: the sample is fixed to 44100 when the handle to the soundcard is created f@0: the sonification is independent from changes in the daw sample rate anyway f@0: if (srate > 0.0){ f@0: ugen[i].setSampleRate(srate); f@0: coe.setSampleRate(srate); f@0: cle.setSampleRate(srate); f@0: } f@0: */ f@0: } f@0: } f@0: f@0: struct Continous { f@0: stk::Envelope envelope[MAX_CHANNELS]; f@0: bool isOn[MAX_CHANNELS]; f@0: } continous; f@0: f@0: struct Clipping { f@0: double maxDiff[MAX_CHANNELS]; f@0: stk::ADSR envelope[MAX_CHANNELS]; f@0: } clipping; f@0: f@0: Sonification() { f@0: reset(); f@0: } f@0: } sSonification; f@0: f@0: /* this structure is used to pass information back and forth between f@0: the puginthread and the sonification thread, with direct access to the sound card */ f@0: struct DacSynced { f@0: bool die; f@0: double wet; f@0: double sonifFreq[MAX_CHANNELS]; f@0: int type; f@0: double maxClippingDiff[MAX_CHANNELS]; f@0: int sonificationType; f@0: DacSynced() f@0: : die(false), f@0: wet(0.5), f@0: sonificationType( SONIFICATION_TYPE_CLIPPING ) f@0: { f@0: for (int i = 0; i < MAX_CHANNELS; i++){ f@0: sonifFreq[i] = 0.0; f@0: maxClippingDiff[i] = 0.0; f@0: } f@0: } f@0: } sDacSynced; f@0: f@0: f@0: f@0: /* mutex to sync the vst plugin thread with the thread f@0: that writes directly into the soundcard (DacThread) f@0: */ f@0: std::mutex sDacMutex; f@0: f@0: f@0: /*sPrevSonificationType keeps track of the sonification type. Useful for DacThread f@0: to assess whether the sonification has changed from the last loop cycle */ f@0: int sPrevSonificationType = SONIFICATION_TYPE_CLIPPING; f@0: f@0: struct DacThread { f@0: std::thread t; f@0: bool started = false; f@0: f@0: /* when this struct goes out of scope (user * f@0: * exits daw) The thread is stopped and joined. */ f@0: ~DacThread(){ f@0: sDacMutex.lock(); f@0: sDacSynced.die = true; f@0: sDacMutex.unlock(); f@0: f@0: if (t.joinable()) f@0: t.join(); f@0: } f@0: f@0: } sDacThread; f@0: f@0: class AccessiblePeakMeter : public IPlug f@0: { f@0: public: f@0: f@0: AccessiblePeakMeter(IPlugInstanceInfo instanceInfo); f@0: ~AccessiblePeakMeter(); f@0: f@0: void Reset(); f@0: void OnParamChange(int paramIdx); f@0: f@0: void ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames); f@0: bool HostRequestingAboutBox(); f@0: f@0: static const double DRY_DEFAULT; f@0: static const double WET_DEFAULT; f@0: static const int SONIFICATION_TYPE_DEFAULT; f@0: static const double METERDECAY_DEFAULT; f@0: static const double THRESHOLD_DEFAULT; f@0: static const double BEEP_TIME; f@0: static const double DB_RANGE; f@0: static const double SONIFICATION_RANGE; f@0: static const int NO_BEEP; f@0: static const int NUM_PRESETS = 2; f@0: static const double MIN_SONIFICATION_FREQ; f@0: static const int NUM_KNOB_FRAMES; f@0: static const double CLIPPING_CEILING_SNAP; f@0: f@0: private: f@0: /* parameters on the GUI */ f@0: double mDry; f@0: double mWet; f@0: double mMeterDecayRate; f@0: double mThreshold; f@0: f@0: double mSampleRate; f@0: f@0: double mPrevPeak[MAX_CHANNELS]; f@0: int mMeterIdx[MAX_CHANNELS]; f@0: f@0: int mSonificationType; f@0: f@0: void addContinuousSonification(double** inputs, double** outputs, int nFrames); f@0: void addClippingSonification(double** inputs, double** outputs, int nFrames); f@0: }; f@0: f@0: #endif //__ACCESSIBLEPEAKMETER__