|
- /*
- ZynAddSubFX - a software synthesizer
-
- SUBnote.cpp - The "subtractive" synthesizer
- Copyright (C) 2002-2005 Nasca Octavian Paul
- Author: Nasca Octavian Paul
-
- 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 <cmath>
- #include <cstdlib>
- #include <cstdio>
- #include <cassert>
- #include <iostream>
- #include "../globals.h"
- #include "SUBnote.h"
- #include "Envelope.h"
- #include "ModFilter.h"
- #include "../Containers/ScratchString.h"
- #include "../Params/Controller.h"
- #include "../Params/SUBnoteParameters.h"
- #include "../Params/FilterParams.h"
- #include "../Misc/Time.h"
- #include "../Misc/Util.h"
- #include "../Misc/Allocator.h"
-
- #ifndef M_PI
- # define M_PI 3.14159265358979323846 /* pi */
- #endif
-
- namespace zyncarla {
-
- SUBnote::SUBnote(const SUBnoteParameters *parameters, SynthParams &spars)
- :SynthNote(spars), pars(*parameters),
- AmpEnvelope(nullptr),
- FreqEnvelope(nullptr),
- BandWidthEnvelope(nullptr),
- GlobalFilter(nullptr),
- GlobalFilterEnvelope(nullptr),
- NoteEnabled(true),
- lfilter(nullptr), rfilter(nullptr),
- wm(nullptr)
- {
- setup(spars.frequency, spars.velocity, spars.portamento, spars.note);
- }
-
- float SUBnote::setupFilters(int *pos, bool automation)
- {
- //how much the amplitude is normalised (because the harmonics)
- float reduceamp = 0.0f;
-
- for(int n = 0; n < numharmonics; ++n) {
- const float freq = basefreq * pars.POvertoneFreqMult[pos[n]];
- overtone_freq[n] = freq;
- overtone_rolloff[n] = computerolloff(freq);
-
- //the bandwidth is not absolute(Hz); it is relative to frequency
- const float bw = SUBnoteParameters::convertBandwidth(pars.Pbandwidth,
- numstages, freq, pars.Pbwscale, pars.Phrelbw[pos[n]]);
-
- //try to keep same amplitude on all freqs and bw. (empirically)
- const float hgain = SUBnoteParameters::convertHarmonicMag(pars.Phmag[pos[n]],
- pars.Phmagtype);
- const float gain = hgain * sqrt(1500.0f / (bw * freq));
-
- reduceamp += hgain;
-
- for(int nph = 0; nph < numstages; ++nph) {
- float amp = 1.0f;
- if(nph == 0)
- amp = gain;
- initfilter(lfilter[nph + n * numstages], freq + OffsetHz, bw,
- amp, hgain, automation);
- if(stereo)
- initfilter(rfilter[nph + n * numstages], freq + OffsetHz, bw,
- amp, hgain, automation);
- }
- }
-
- if(reduceamp < 0.001f)
- reduceamp = 1.0f;
-
- return reduceamp;
- }
-
- void SUBnote::setup(float freq,
- float velocity,
- int portamento_,
- int midinote,
- bool legato)
- {
- this->velocity = velocity;
- portamento = portamento_;
- NoteEnabled = ON;
- volume = powf(0.1f, 3.0f * (1.0f - pars.PVolume / 96.0f)); //-60 dB .. 0 dB
- volume *= VelF(velocity, pars.PAmpVelocityScaleFunction);
- if(pars.PPanning != 0)
- panning = pars.PPanning / 127.0f;
- else
- panning = RND;
-
- if(!legato) { //normal note
- numstages = pars.Pnumstages;
- stereo = pars.Pstereo;
- start = pars.Pstart;
- firsttick = 1;
- }
-
-
- if(pars.Pfixedfreq == 0)
- basefreq = freq;
- else {
- basefreq = 440.0f;
- int fixedfreqET = pars.PfixedfreqET;
- if(fixedfreqET) { //if the frequency varies according the keyboard note
- float tmp = (midinote - 69.0f) / 12.0f
- * (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f);
- if(fixedfreqET <= 64)
- basefreq *= powf(2.0f, tmp);
- else
- basefreq *= powf(3.0f, tmp);
- }
- }
- int BendAdj = pars.PBendAdjust - 64;
- if (BendAdj % 24 == 0)
- BendAdjust = BendAdj / 24;
- else
- BendAdjust = BendAdj / 24.0f;
- float offset_val = (pars.POffsetHz - 64)/64.0f;
- OffsetHz = 15.0f*(offset_val * sqrtf(fabsf(offset_val)));
- float detune = getdetune(pars.PDetuneType,
- pars.PCoarseDetune,
- pars.PDetune);
- basefreq *= powf(2.0f, detune / 1200.0f); //detune
- // basefreq*=ctl.pitchwheel.relfreq;//pitch wheel
-
- int pos[MAX_SUB_HARMONICS];
- int harmonics;
-
- pars.activeHarmonics(pos, harmonics);
-
- if(!legato) //normal note
- firstnumharmonics = numharmonics = harmonics;
- else {
- if(harmonics > firstnumharmonics)
- numharmonics = firstnumharmonics;
- else
- numharmonics = harmonics;
- }
-
-
- if(numharmonics == 0) {
- NoteEnabled = false;
- return;
- }
-
-
- if(!legato) { //normal note
- lfilter = memory.valloc<bpfilter>(numstages * numharmonics);
- if(stereo)
- rfilter = memory.valloc<bpfilter>(numstages * numharmonics);
- }
-
- //how much the amplitude is normalised (because the harmonics)
- float reduceamp = setupFilters(pos, false);
- oldreduceamp = reduceamp;
-
- volume /= reduceamp;
-
- oldpitchwheel = 0;
- oldbandwidth = 64;
- if(!legato) { //normal note
- if(pars.Pfixedfreq == 0)
- initparameters(basefreq, wm);
- else
- initparameters(basefreq / 440.0f * freq, wm);
- }
- else {
- if(pars.Pfixedfreq == 0)
- freq = basefreq;
- else
- freq *= basefreq / 440.0f;
-
- if(GlobalFilter)
- GlobalFilter->updateNoteFreq(basefreq);
- }
-
- oldamplitude = newamplitude;
- }
-
- SynthNote *SUBnote::cloneLegato(void)
- {
- SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity,
- portamento, legato.param.midinote, true};
- return memory.alloc<SUBnote>(&pars, sp);
- }
-
- void SUBnote::legatonote(LegatoParams pars)
- {
- // Manage legato stuff
- if(legato.update(pars))
- return;
-
- try {
- setup(pars.frequency, pars.velocity, pars.portamento, pars.midinote,
- true);
- } catch (std::bad_alloc &ba) {
- std::cerr << "failed to set legato note parameter in SUBnote: " << ba.what() << std::endl;
- }
- }
-
- SUBnote::~SUBnote()
- {
- if(NoteEnabled)
- KillNote();
- }
-
- /*
- * Kill the note
- */
- void SUBnote::KillNote()
- {
- if(NoteEnabled) {
- memory.devalloc(numstages * numharmonics, lfilter);
- if(stereo)
- memory.devalloc(numstages * numharmonics, rfilter);
- memory.dealloc(AmpEnvelope);
- memory.dealloc(FreqEnvelope);
- memory.dealloc(BandWidthEnvelope);
- memory.dealloc(GlobalFilter);
- memory.dealloc(GlobalFilterEnvelope);
- NoteEnabled = false;
- }
- }
-
-
- /*
- * Compute the filters coefficients
- */
- void SUBnote::computefiltercoefs(bpfilter &filter,
- float freq,
- float bw,
- float gain)
- {
- if(freq > synth.samplerate_f / 2.0f - 200.0f)
- freq = synth.samplerate_f / 2.0f - 200.0f;
-
-
- float omega = 2.0f * PI * freq / synth.samplerate_f;
- float sn = sinf(omega);
- float cs = cosf(omega);
- float alpha = sn * sinh(LOG_2 / 2.0f * bw * omega / sn);
-
- if(alpha > 1)
- alpha = 1;
- if(alpha > bw)
- alpha = bw;
-
- filter.b0 = alpha / (1.0f + alpha) * filter.amp * gain;
- filter.b2 = -alpha / (1.0f + alpha) * filter.amp * gain;
- filter.a1 = -2.0f * cs / (1.0f + alpha);
- filter.a2 = (1.0f - alpha) / (1.0f + alpha);
- }
-
-
- /*
- * Initialise the filters
- */
- void SUBnote::initfilter(bpfilter &filter,
- float freq,
- float bw,
- float amp,
- float mag,
- bool automation)
- {
- if(!automation) {
- filter.xn1 = 0.0f;
- filter.xn2 = 0.0f;
-
- if(start == 0) {
- filter.yn1 = 0.0f;
- filter.yn2 = 0.0f;
- }
- else {
- float a = 0.1f * mag; //empirically
- float p = RND * 2.0f * PI;
- if(start == 1)
- a *= RND;
- filter.yn1 = a * cosf(p);
- filter.yn2 = a * cosf(p + freq * 2.0f * PI / synth.samplerate_f);
-
- //correct the error of computation the start amplitude
- //at very high frequencies
- if(freq > synth.samplerate_f * 0.96f) {
- filter.yn1 = 0.0f;
- filter.yn2 = 0.0f;
- }
- }
- }
-
- filter.amp = amp;
- filter.freq = freq;
- filter.bw = bw;
- computefiltercoefs(filter, freq, bw, 1.0f);
- }
-
- /*
- * Do the filtering
- */
-
- inline void SubFilterA(const float coeff[4], float &src, float work[4])
- {
- work[3] = src*coeff[0]+work[1]*coeff[1]+work[2]*coeff[2]+work[3]*coeff[3];
- work[1] = src;
- src = work[3];
- }
-
- inline void SubFilterB(const float coeff[4], float &src, float work[4])
- {
- work[2] = src*coeff[0]+work[0]*coeff[1]+work[3]*coeff[2]+work[2]*coeff[3];
- work[0] = src;
- src = work[2];
- }
-
- //This dance is designed to minimize unneeded memory operations which can result
- //in quite a bit of wasted time
- void SUBnote::filter(bpfilter &filter, float *smps)
- {
- assert(synth.buffersize % 8 == 0);
- float coeff[4] = {filter.b0, filter.b2, -filter.a1, -filter.a2};
- float work[4] = {filter.xn1, filter.xn2, filter.yn1, filter.yn2};
-
- for(int i = 0; i < synth.buffersize; i += 8) {
- SubFilterA(coeff, smps[i + 0], work);
- SubFilterB(coeff, smps[i + 1], work);
- SubFilterA(coeff, smps[i + 2], work);
- SubFilterB(coeff, smps[i + 3], work);
- SubFilterA(coeff, smps[i + 4], work);
- SubFilterB(coeff, smps[i + 5], work);
- SubFilterA(coeff, smps[i + 6], work);
- SubFilterB(coeff, smps[i + 7], work);
- }
- filter.xn1 = work[0];
- filter.xn2 = work[1];
- filter.yn1 = work[2];
- filter.yn2 = work[3];
- }
-
- /*
- * Init Parameters
- */
- void SUBnote::initparameters(float freq, WatchManager *wm)
- {
- //TODO populate this base string
- ScratchString pre;
- AmpEnvelope = memory.alloc<Envelope>(*pars.AmpEnvelope, freq,
- synth.dt(), wm, (pre+"AmpEnvelope/").c_str);
-
- if(pars.PFreqEnvelopeEnabled)
- FreqEnvelope = memory.alloc<Envelope>(*pars.FreqEnvelope, freq,
- synth.dt(), wm, (pre+"FreqEnvelope/").c_str);
-
- if(pars.PBandWidthEnvelopeEnabled)
- BandWidthEnvelope = memory.alloc<Envelope>(*pars.BandWidthEnvelope,
- freq, synth.dt(), wm, (pre+"BandWidthEnvelope/").c_str);
-
- if(pars.PGlobalFilterEnabled) {
- GlobalFilterEnvelope =
- memory.alloc<Envelope>(*pars.GlobalFilterEnvelope, freq,
- synth.dt(), wm, (pre+"GlobalFilterEnvelope/").c_str);
-
- GlobalFilter = memory.alloc<ModFilter>(*pars.GlobalFilter, synth, time, memory, stereo, freq);
-
- GlobalFilter->updateSense(velocity, pars.PGlobalFilterVelocityScale,
- pars.PGlobalFilterVelocityScaleFunction);
-
- GlobalFilter->addMod(*GlobalFilterEnvelope);
- }
- computecurrentparameters();
- }
-
- /*
- * Compute how much to reduce amplitude near nyquist or subaudible frequencies.
- */
- float SUBnote::computerolloff(float freq)
- {
- const float lower_limit = 10.0f;
- const float lower_width = 10.0f;
- const float upper_width = 200.0f;
- float upper_limit = synth.samplerate / 2.0f;
-
- if (freq > lower_limit + lower_width &&
- freq < upper_limit - upper_width)
- return 1.0f;
- if (freq <= lower_limit || freq >= upper_limit)
- return 0.0f;
- if (freq <= lower_limit + lower_width)
- return (1.0f - cosf(M_PI * (freq - lower_limit) / lower_width)) / 2.0f;
- return (1.0f - cosf(M_PI * (freq - upper_limit) / upper_width)) / 2.0f;
- }
-
- /*
- * Compute Parameters of SUBnote for each tick
- */
- void SUBnote::computecurrentparameters()
- {
- //Recompute parameters for realtime automation
- if(pars.time && pars.last_update_timestamp == pars.time->time()) {
- //A little bit of copy/paste for now
-
- int pos[MAX_SUB_HARMONICS];
- int harmonics;
-
- pars.activeHarmonics(pos, harmonics);
-
- bool delta_harmonics = (harmonics != numharmonics);
- if(delta_harmonics) {
- memory.devalloc(lfilter);
- memory.devalloc(rfilter);
-
- firstnumharmonics = numharmonics = harmonics;
- lfilter = memory.valloc<bpfilter>(numstages * numharmonics);
- if(stereo)
- rfilter = memory.valloc<bpfilter>(numstages * numharmonics);
- }
-
- float reduceamp = setupFilters(pos, !delta_harmonics);
- volume = volume*oldreduceamp/reduceamp;
- oldreduceamp = reduceamp;
- }
-
- if(FreqEnvelope || BandWidthEnvelope
- || (oldpitchwheel != ctl.pitchwheel.data)
- || (oldbandwidth != ctl.bandwidth.data)
- || portamento) {
- float envfreq = 1.0f;
- float envbw = 1.0f;
-
- if(FreqEnvelope) {
- envfreq = FreqEnvelope->envout() / 1200;
- envfreq = powf(2.0f, envfreq);
- }
-
- envfreq *=
- powf(ctl.pitchwheel.relfreq, BendAdjust); //pitch wheel
-
- //Update frequency while portamento is converging
- if(portamento) {
- envfreq *= ctl.portamento.freqrap;
- if(!ctl.portamento.used) //the portamento has finished
- portamento = false;
- }
-
- if(BandWidthEnvelope) {
- envbw = BandWidthEnvelope->envout();
- envbw = powf(2, envbw);
- }
-
- envbw *= ctl.bandwidth.relbw; //bandwidth controller
-
-
- //Recompute High Frequency Dampening Terms
- for(int n = 0; n < numharmonics; ++n)
- overtone_rolloff[n] = computerolloff(overtone_freq[n] * envfreq);
-
-
- //Recompute Filter Coefficients
- float tmpgain = 1.0f / sqrt(envbw * envfreq);
- computeallfiltercoefs(lfilter, envfreq, envbw, tmpgain);
- if(stereo)
- computeallfiltercoefs(rfilter, envfreq, envbw, tmpgain);
-
-
- oldbandwidth = ctl.bandwidth.data;
- oldpitchwheel = ctl.pitchwheel.data;
- }
- newamplitude = volume * AmpEnvelope->envout_dB() * 2.0f;
-
- //Filter
- if(GlobalFilter)
- GlobalFilter->update(ctl.filtercutoff.relfreq,
- ctl.filterq.relq);
- }
-
- void SUBnote::computeallfiltercoefs(bpfilter *filters, float envfreq,
- float envbw, float gain)
- {
- for(int n = 0; n < numharmonics; ++n)
- for(int nph = 0; nph < numstages; ++nph)
- computefiltercoefs(filters[nph + n * numstages],
- filters[nph + n * numstages].freq * envfreq,
- filters[nph + n * numstages].bw * envbw,
- nph == 0 ? gain : 1.0);
- }
-
- void SUBnote::chanOutput(float *out, bpfilter *bp, int buffer_size)
- {
- float tmprnd[buffer_size];
- float tmpsmp[buffer_size];
-
- //Initialize Random Input
- for(int i = 0; i < buffer_size; ++i)
- tmprnd[i] = RND * 2.0f - 1.0f;
-
- //For each harmonic apply the filter on the random input stream
- //Sum the filter outputs to obtain the output signal
- for(int n = 0; n < numharmonics; ++n) {
- float rolloff = overtone_rolloff[n];
- memcpy(tmpsmp, tmprnd, synth.bufferbytes);
-
- for(int nph = 0; nph < numstages; ++nph)
- filter(bp[nph + n * numstages], tmpsmp);
-
- for(int i = 0; i < synth.buffersize; ++i)
- out[i] += tmpsmp[i] * rolloff;
- }
- }
-
- /*
- * Note Output
- */
- int SUBnote::noteout(float *outl, float *outr)
- {
- memcpy(outl, synth.denormalkillbuf, synth.bufferbytes);
- memcpy(outr, synth.denormalkillbuf, synth.bufferbytes);
-
- if(!NoteEnabled)
- return 0;
-
- if(stereo) {
- chanOutput(outl, lfilter, synth.buffersize);
- chanOutput(outr, rfilter, synth.buffersize);
-
- if(GlobalFilter)
- GlobalFilter->filter(outl, outr);
-
- } else {
- chanOutput(outl, lfilter, synth.buffersize);
-
- if(GlobalFilter)
- GlobalFilter->filter(outl, 0);
-
- memcpy(outr, outl, synth.bufferbytes);
- }
-
- if(firsttick) {
- int n = 10;
- if(n > synth.buffersize)
- n = synth.buffersize;
- for(int i = 0; i < n; ++i) {
- float ampfadein = 0.5f - 0.5f * cosf(
- (float) i / (float) n * PI);
- outl[i] *= ampfadein;
- outr[i] *= ampfadein;
- }
- firsttick = false;
- }
-
- if(ABOVE_AMPLITUDE_THRESHOLD(oldamplitude, newamplitude))
- // Amplitude interpolation
- for(int i = 0; i < synth.buffersize; ++i) {
- float tmpvol = INTERPOLATE_AMPLITUDE(oldamplitude,
- newamplitude,
- i,
- synth.buffersize);
- outl[i] *= tmpvol * panning;
- outr[i] *= tmpvol * (1.0f - panning);
- }
- else
- for(int i = 0; i < synth.buffersize; ++i) {
- outl[i] *= newamplitude * panning;
- outr[i] *= newamplitude * (1.0f - panning);
- }
-
- oldamplitude = newamplitude;
- computecurrentparameters();
-
- // Apply legato-specific sound signal modifications
- legato.apply(*this, outl, outr);
-
- // Check if the note needs to be computed more
- if(AmpEnvelope->finished() != 0) {
- for(int i = 0; i < synth.buffersize; ++i) { //fade-out
- float tmp = 1.0f - (float)i / synth.buffersize_f;
- outl[i] *= tmp;
- outr[i] *= tmp;
- }
- KillNote();
- }
- return 1;
- }
-
- /*
- * Release Key (Note Off)
- */
- void SUBnote::releasekey()
- {
- AmpEnvelope->releasekey();
- if(FreqEnvelope)
- FreqEnvelope->releasekey();
- if(BandWidthEnvelope)
- BandWidthEnvelope->releasekey();
- if(GlobalFilterEnvelope)
- GlobalFilterEnvelope->releasekey();
- }
-
- /*
- * Check if the note is finished
- */
- bool SUBnote::finished() const
- {
- return !NoteEnabled;
- }
-
- void SUBnote::entomb(void)
- {
- AmpEnvelope->forceFinish();
- }
-
- }
|