/* ZynAddSubFX - a software synthesizer SynthNote.cpp - Abstract Synthesizer Note Instance Copyright (C) 2016 Mark McCurry 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 "SynthNote.h" #include "../globals.h" #include #include #include namespace zyncarla { SynthNote::SynthNote(SynthParams &pars) :memory(pars.memory), legato(pars.synth, pars.frequency, pars.velocity, pars.portamento, pars.note, pars.quiet), ctl(pars.ctl), synth(pars.synth), time(pars.time) {} SynthNote::Legato::Legato(const SYNTH_T &synth_, float freq, float vel, int port, int note, bool quiet) :synth(synth_) { // Initialise some legato-specific vars msg = LM_Norm; fade.length = (int)(synth.samplerate_f * 0.005f); // 0.005f seems ok. if(fade.length < 1) fade.length = 1; // (if something's fishy) fade.step = (1.0f / fade.length); decounter = -10; param.freq = freq; param.vel = vel; param.portamento = port; param.midinote = note; lastfreq = 0.0f; silent = quiet; } int SynthNote::Legato::update(LegatoParams pars) { if(pars.externcall) msg = LM_Norm; if(msg != LM_CatchUp) { lastfreq = param.freq; param.freq = pars.frequency; param.vel = pars.velocity; param.portamento = pars.portamento; param.midinote = pars.midinote; if(msg == LM_Norm) { if(silent) { fade.m = 0.0f; msg = LM_FadeIn; } else { fade.m = 1.0f; msg = LM_FadeOut; return 1; } } if(msg == LM_ToNorm) msg = LM_Norm; } return 0; } void SynthNote::Legato::apply(SynthNote ¬e, float *outl, float *outr) { if(silent) // Silencer if(msg != LM_FadeIn) { memset(outl, 0, synth.bufferbytes); memset(outr, 0, synth.bufferbytes); } try { switch (msg) { case LM_CatchUp: // Continue the catch-up... if (decounter == -10) decounter = fade.length; //Yea, could be done without the loop... for (int i = 0; i < synth.buffersize; ++i) { decounter--; if (decounter < 1) { // Catching-up done, we can finally set // the note to the actual parameters. decounter = -10; msg = LM_ToNorm; LegatoParams pars{param.freq, param.vel, param.portamento, param.midinote, false}; note.legatonote(pars); break; } } break; case LM_FadeIn: // Fade-in if (decounter == -10) decounter = fade.length; silent = false; for (int i = 0; i < synth.buffersize; ++i) { decounter--; if (decounter < 1) { decounter = -10; msg = LM_Norm; break; } fade.m += fade.step; outl[i] *= fade.m; outr[i] *= fade.m; } break; case LM_FadeOut: // Fade-out, then set the catch-up if (decounter == -10) decounter = fade.length; for (int i = 0; i < synth.buffersize; ++i) { decounter--; if (decounter < 1) { for (int j = i; j < synth.buffersize; ++j) { outl[j] = 0.0f; outr[j] = 0.0f; } decounter = -10; silent = true; // Fading-out done, now set the catch-up : decounter = fade.length; msg = LM_CatchUp; //This freq should make this now silent note to catch-up/resync //with the heard note for the same length it stayed at the //previous freq during the fadeout. float catchupfreq = param.freq * (param.freq / lastfreq); LegatoParams pars{catchupfreq, param.vel, param.portamento, param.midinote, false}; note.legatonote(pars); break; } fade.m -= fade.step; outl[i] *= fade.m; outr[i] *= fade.m; } break; default: break; } } catch (std::bad_alloc &ba) { std::cerr << "failed to apply legato: " << ba.what() << std::endl; } } void SynthNote::setVelocity(float velocity_) { legato.setSilent(true); //Let legato.update(...) returns 0. LegatoParams pars{legato.getFreq(), velocity_, legato.getPortamento(), legato.getMidinote(), true}; try { legatonote(pars); } catch (std::bad_alloc &ba) { std::cerr << "failed to set velocity to legato note: " << ba.what() << std::endl; } legato.setDecounter(0); //avoid chopping sound due fade-in } }