/* ============================================================================== shifter_voice.cpp Created: 25 Oct 2025 2:09:42pm Author: mickl ============================================================================== */ #include "shifter_voice.h" static inline float mtof(float m) { return powf(2, (m - 69.0f) / 12.0f) * 440.0f; } void ShifterVoice::Init(float sample_rate) { sample_rate_ = sample_rate; portamento_.Init(sample_rate_, 0.05f); //default portamento time amplitude_envelope_.Init(sample_rate_); amplitude_envelope_.SetAttackTime(0.2f); amplitude_envelope_.SetDecayTime(0.1f); amplitude_envelope_.SetReleaseTime(.05f); onoff_ = false; overflow_ = false; current_midi = 60; current_period_ = sample_rate_ / mtof((float)current_midi); current_amplitude = 0.0f; period_counter = 0.0f; panning = 0.5f; //on reset, make sure envelope is not running while (amplitude_envelope_.IsRunning()) { amplitude_envelope_.Process(false); } } bool ShifterVoice::IsActive() { return amplitude_envelope_.IsRunning(); } void ShifterVoice::Trigger(int midi_note) { current_midi = midi_note; // Retrigger envelope amplitude_envelope_.Retrigger(false); onoff_ = true; float pan_min = 0; float pan_max = 1; if(pan_width < .5f) pan_max = .1 + (pan_width * 2 * .9f); else pan_min = (pan_width - .5f) * 2 * .9f; bool pan_left = rand() % 2; panning = rand() / (float)RAND_MAX; panning = pan_min + (panning * (pan_max - pan_min)); if (pan_left) { panning = .5 - panning * .5f; } else { panning = .5 + panning * .5f; } } void ShifterVoice::Release() { onoff_ = false; } void ShifterVoice::Process() { current_amplitude = amplitude_envelope_.Process(onoff_); current_period_ = sample_rate_ / mtof(portamento_.Process((float)current_midi + pitch_adjust)); period_counter++; overflow_ = period_counter >= current_period_; if (overflow_) { period_counter -= current_period_; } } float ShifterVoice::CurrentAmplitude() { return current_amplitude; } float ShifterVoice::CurrentPeriod() { return current_period_; } bool ShifterVoice::PeriodOverflow() { return overflow_; } void ShifterVoice::SetPortamentoTime(float time) { portamento_.SetHtime(time); } void ShifterVoice::SetAdsrTimes(float attack, float decay, float release) { amplitude_envelope_.SetAttackTime(attack); amplitude_envelope_.SetDecayTime(decay); amplitude_envelope_.SetReleaseTime(release); amplitude_envelope_.SetSustainLevel(1.0); } float ShifterVoice::GetPanning(int channel) const { switch (channel) { default: case 0: return panning < .5 ? 1.0 : 2.0f - (panning * 2.0f); case 1: return panning > .5 ? 1.0 : panning * 2.0f; } } void ShifterVoice::SetPitchAdjust(float adj) { pitch_adjust = adj; }