|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613 |
- /*
- 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 version 2 of the GNU General Public License
- as published by the Free Software Foundation.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License (version 2 or later) for more details.
-
- You should have received a copy of the GNU General Public License (version 2)
- along with this program; if not, write to the Free Software Foundation,
- Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- */
-
- #include <cmath>
- #include <cstdlib>
- #include <cstdio>
- #include <cassert>
- #include "../globals.h"
- #include "SUBnote.h"
- #include "../Misc/Util.h"
-
- SUBnote::SUBnote(SUBnoteParameters *parameters,
- Controller *ctl_,
- float freq,
- float velocity,
- int portamento_,
- int midinote,
- bool besilent)
- :SynthNote(freq, velocity, portamento_, midinote, besilent)
- {
- pars = parameters;
- ctl = ctl_;
- NoteEnabled = ON;
- setup(freq, velocity, portamento_, midinote);
- }
-
- void SUBnote::setup(float freq,
- float velocity,
- int portamento_,
- int midinote,
- bool legato)
- {
- 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) {
- numstages = pars->Pnumstages;
- stereo = pars->Pstereo;
- start = pars->Pstart;
- firsttick = 1;
- }
- int pos[MAX_SUB_HARMONICS];
-
- if(pars->Pfixedfreq == 0)
- basefreq = freq;
- else {
- basefreq = 440.0f;
- int fixedfreqET = pars->PfixedfreqET;
- if(fixedfreqET != 0) { //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);
- }
- }
- float detune = getdetune(pars->PDetuneType,
- pars->PCoarseDetune,
- pars->PDetune);
- basefreq *= powf(2.0f, detune / 1200.0f); //detune
- // basefreq*=ctl->pitchwheel.relfreq;//pitch wheel
-
- //global filter
- GlobalFilterCenterPitch = pars->GlobalFilter->getfreq() //center freq
- + (pars->PGlobalFilterVelocityScale / 127.0f
- * 6.0f) //velocity sensing
- * (VelF(velocity,
- pars->PGlobalFilterVelocityScaleFunction)
- - 1);
-
- if(!legato) {
- GlobalFilterL = NULL;
- GlobalFilterR = NULL;
- GlobalFilterEnvelope = NULL;
- }
-
- int harmonics = 0;
-
- //select only harmonics that desire to compute
- for(int n = 0; n < MAX_SUB_HARMONICS; ++n) {
- if(pars->Phmag[n] == 0)
- continue;
- pos[harmonics++] = n;
- }
- if(!legato)
- firstnumharmonics = numharmonics = harmonics;
- else {
- if(harmonics > firstnumharmonics)
- numharmonics = firstnumharmonics;
- else
- numharmonics = harmonics;
- }
-
-
- if(numharmonics == 0) {
- NoteEnabled = OFF;
- return;
- }
-
-
- if(!legato) {
- lfilter = new bpfilter[numstages * numharmonics];
- if(stereo != 0)
- rfilter = new bpfilter[numstages * numharmonics];
- }
-
- //how much the amplitude is normalised (because the harmonics)
- float reduceamp = 0.0f;
-
- for(int n = 0; n < numharmonics; ++n) {
- 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
- float bw =
- powf(10, (pars->Pbandwidth - 127.0f) / 127.0f * 4) * numstages;
-
- //Bandwidth Scale
- bw *= powf(1000 / freq, (pars->Pbwscale - 64.0f) / 64.0f * 3.0f);
-
- //Relative BandWidth
- bw *= powf(100, (pars->Phrelbw[pos[n]] - 64.0f) / 64.0f);
-
- if(bw > 25.0f)
- bw = 25.0f;
-
- //try to keep same amplitude on all freqs and bw. (empirically)
- float gain = sqrt(1500.0f / (bw * freq));
-
- float hmagnew = 1.0f - pars->Phmag[pos[n]] / 127.0f;
- float hgain;
-
- switch(pars->Phmagtype) {
- case 1:
- hgain = expf(hmagnew * logf(0.01f));
- break;
- case 2:
- hgain = expf(hmagnew * logf(0.001f));
- break;
- case 3:
- hgain = expf(hmagnew * logf(0.0001f));
- break;
- case 4:
- hgain = expf(hmagnew * logf(0.00001f));
- break;
- default:
- hgain = 1.0f - hmagnew;
- }
- gain *= hgain;
- reduceamp += hgain;
-
- for(int nph = 0; nph < numstages; ++nph) {
- float amp = 1.0f;
- if(nph == 0)
- amp = gain;
- initfilter(lfilter[nph + n * numstages], freq, bw, amp, hgain);
- if(stereo != 0)
- initfilter(rfilter[nph + n * numstages], freq, bw, amp, hgain);
- }
- }
-
- if(reduceamp < 0.001f)
- reduceamp = 1.0f;
- volume /= reduceamp;
-
- oldpitchwheel = 0;
- oldbandwidth = 64;
- if(!legato) {
- if(pars->Pfixedfreq == 0)
- initparameters(basefreq);
- else
- initparameters(basefreq / 440.0f * freq);
- }
- else {
- if(pars->Pfixedfreq == 0)
- freq = basefreq;
- else
- freq *= basefreq / 440.0f;
-
- if(pars->PGlobalFilterEnabled != 0) {
- globalfiltercenterq = pars->GlobalFilter->getq();
- GlobalFilterFreqTracking = pars->GlobalFilter->getfreqtracking(
- basefreq);
- }
- }
-
- oldamplitude = newamplitude;
- }
-
- void SUBnote::legatonote(float freq, float velocity, int portamento_,
- int midinote, bool externcall)
- {
- // Manage legato stuff
- if(legato.update(freq, velocity, portamento_, midinote, externcall))
- return;
-
- setup(freq, velocity, portamento_, midinote, true);
- }
-
- SUBnote::~SUBnote()
- {
- if(NoteEnabled != OFF)
- KillNote();
- }
-
- /*
- * Kill the note
- */
- void SUBnote::KillNote()
- {
- if(NoteEnabled != OFF) {
- delete [] lfilter;
- lfilter = NULL;
- if(stereo != 0)
- delete [] rfilter;
- rfilter = NULL;
- delete AmpEnvelope;
- delete FreqEnvelope;
- delete BandWidthEnvelope;
- delete GlobalFilterL;
- delete GlobalFilterR;
- delete GlobalFilterEnvelope;
- NoteEnabled = OFF;
- }
- }
-
-
- /*
- * 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)
- {
- 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)
- {
- AmpEnvelope = new Envelope(pars->AmpEnvelope, freq);
- if(pars->PFreqEnvelopeEnabled != 0)
- FreqEnvelope = new Envelope(pars->FreqEnvelope, freq);
- else
- FreqEnvelope = NULL;
- if(pars->PBandWidthEnvelopeEnabled != 0)
- BandWidthEnvelope = new Envelope(pars->BandWidthEnvelope, freq);
- else
- BandWidthEnvelope = NULL;
- if(pars->PGlobalFilterEnabled != 0) {
- globalfiltercenterq = pars->GlobalFilter->getq();
- GlobalFilterL = Filter::generate(pars->GlobalFilter);
- if(stereo)
- GlobalFilterR = Filter::generate(pars->GlobalFilter);
- GlobalFilterEnvelope = new Envelope(pars->GlobalFilterEnvelope,
- freq);
- GlobalFilterFreqTracking = pars->GlobalFilter->getfreqtracking(basefreq);
- }
- 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()
- {
- if((FreqEnvelope != NULL) || (BandWidthEnvelope != NULL)
- || (oldpitchwheel != ctl->pitchwheel.data)
- || (oldbandwidth != ctl->bandwidth.data)
- || (portamento != 0)) {
- float envfreq = 1.0f;
- float envbw = 1.0f;
- float gain = 1.0f;
-
- if(FreqEnvelope != NULL) {
- envfreq = FreqEnvelope->envout() / 1200;
- envfreq = powf(2.0f, envfreq);
- }
- envfreq *= ctl->pitchwheel.relfreq; //pitch wheel
- if(portamento != 0) { //portamento is used
- envfreq *= ctl->portamento.freqrap;
- if(ctl->portamento.used == 0) //the portamento has finished
- portamento = 0; //this note is no longer "portamented"
- ;
- }
-
- if(BandWidthEnvelope != NULL) {
- envbw = BandWidthEnvelope->envout();
- envbw = powf(2, envbw);
- }
- envbw *= ctl->bandwidth.relbw; //bandwidth controller
-
- float tmpgain = 1.0f / sqrt(envbw * envfreq);
-
- for(int n = 0; n < numharmonics; ++n) {
- overtone_rolloff[n] = computerolloff(overtone_freq[n] * envfreq);
- }
- for(int n = 0; n < numharmonics; ++n)
- for(int nph = 0; nph < numstages; ++nph) {
- if(nph == 0)
- gain = tmpgain;
- else
- gain = 1.0f;
- computefiltercoefs(lfilter[nph + n * numstages],
- lfilter[nph + n * numstages].freq * envfreq,
- lfilter[nph + n * numstages].bw * envbw,
- gain);
- }
- if(stereo != 0)
- for(int n = 0; n < numharmonics; ++n)
- for(int nph = 0; nph < numstages; ++nph) {
- if(nph == 0)
- gain = tmpgain;
- else
- gain = 1.0f;
- computefiltercoefs(
- rfilter[nph + n * numstages],
- rfilter[nph + n
- * numstages].freq * envfreq,
- rfilter[nph + n * numstages].bw * envbw,
- gain);
- }
-
-
- oldbandwidth = ctl->bandwidth.data;
- oldpitchwheel = ctl->pitchwheel.data;
- }
- newamplitude = volume * AmpEnvelope->envout_dB() * 2.0f;
-
- //Filter
- if(GlobalFilterL != NULL) {
- float globalfilterpitch = GlobalFilterCenterPitch
- + GlobalFilterEnvelope->envout();
- float filterfreq = globalfilterpitch + ctl->filtercutoff.relfreq
- + GlobalFilterFreqTracking;
- filterfreq = Filter::getrealfreq(filterfreq);
-
- GlobalFilterL->setfreq_and_q(filterfreq,
- globalfiltercenterq * ctl->filterq.relq);
- if(GlobalFilterR != NULL)
- GlobalFilterR->setfreq_and_q(
- filterfreq,
- globalfiltercenterq
- * ctl->filterq.relq);
- }
- }
-
- /*
- * Note Output
- */
- int SUBnote::noteout(float *outl, float *outr)
- {
- memcpy(outl, denormalkillbuf, synth->bufferbytes);
- memcpy(outr, denormalkillbuf, synth->bufferbytes);
-
- if(NoteEnabled == OFF)
- return 0;
-
- float tmprnd[synth->buffersize];
- float tmpsmp[synth->buffersize];
- //left channel
- for(int i = 0; i < synth->buffersize; ++i)
- tmprnd[i] = RND * 2.0f - 1.0f;
- 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(lfilter[nph + n * numstages], tmpsmp);
- for(int i = 0; i < synth->buffersize; ++i)
- outl[i] += tmpsmp[i] * rolloff;
- }
-
- if(GlobalFilterL != NULL)
- GlobalFilterL->filterout(&outl[0]);
-
- //right channel
- if(stereo != 0) {
- for(int i = 0; i < synth->buffersize; ++i)
- tmprnd[i] = RND * 2.0f - 1.0f;
- 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(rfilter[nph + n * numstages], tmpsmp);
- for(int i = 0; i < synth->buffersize; ++i)
- outr[i] += tmpsmp[i] * rolloff;
- }
- if(GlobalFilterR != NULL)
- GlobalFilterR->filterout(&outr[0]);
- }
- else
- memcpy(outr, outl, synth->bufferbytes);
-
- if(firsttick != 0) {
- 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 = 0;
- }
-
- 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;
- }
-
- /*
- * Relase Key (Note Off)
- */
- void SUBnote::relasekey()
- {
- AmpEnvelope->relasekey();
- if(FreqEnvelope)
- FreqEnvelope->relasekey();
- if(BandWidthEnvelope)
- BandWidthEnvelope->relasekey();
- if(GlobalFilterEnvelope)
- GlobalFilterEnvelope->relasekey();
- }
-
- /*
- * Check if the note is finished
- */
- int SUBnote::finished() const
- {
- if(NoteEnabled == OFF)
- return 1;
- else
- return 0;
- }
|