mirror of
https://github.com/yokemura/Magical8bitPlug2.git
synced 2025-05-24 23:00:21 -04:00
227 lines
5.7 KiB
C++
227 lines
5.7 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
BaseVoice.cpp
|
|
Created: 11 Nov 2019 9:38:44pm
|
|
Author: 除村 武志
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
#include "BaseVoice.h"
|
|
|
|
//---------------------------------------------
|
|
//
|
|
// Base Voice
|
|
// (Abstract)
|
|
//
|
|
//---------------------------------------------
|
|
BaseVoice::BaseVoice (SettingRefs* sRefs)
|
|
{
|
|
settingRefs = sRefs;
|
|
}
|
|
|
|
bool BaseVoice::canPlaySound (SynthesiserSound* sound)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void BaseVoice::startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int currentPitchBendPosition)
|
|
{
|
|
|
|
noteNumber = midiNoteNumber;
|
|
|
|
envelopePhase = kEnvelopePhaseA;
|
|
currentAngle = 0.0;
|
|
currentEnvelopeLevel = 0.0;
|
|
|
|
ampByVelocityAndGain = * (settingRefs->gain) * velocity; // velocity value range is 0.0f-1.0f
|
|
|
|
calculateAngleDelta();
|
|
|
|
//
|
|
// Envelope
|
|
//
|
|
double srate = getSampleRate();
|
|
|
|
if (* (settingRefs->attack) == 0 ) { attack_slope = 1.0; }
|
|
else
|
|
{
|
|
attack_slope = 1.0 / (* (settingRefs->attack) * srate);
|
|
}
|
|
|
|
if (* (settingRefs->decay) == 0 ) { decay_slope = 1.0; }
|
|
else
|
|
{
|
|
decay_slope = 1.0 / (* (settingRefs->decay) * srate);
|
|
}
|
|
|
|
sustain_level = * (settingRefs->suslevel);
|
|
|
|
if (* (settingRefs->release) == 0 ) { release_slope = 1.0; }
|
|
else
|
|
{
|
|
release_slope = 1.0 / (* (settingRefs->release) * srate);
|
|
}
|
|
|
|
//
|
|
// Control frame
|
|
//
|
|
controlFrameTimer = 0;
|
|
currentVolumeSequenceFrame = 0;
|
|
|
|
if (settingRefs->isVolumeSequenceEnabled())
|
|
{
|
|
currentEnvelopeLevel = (float) (settingRefs->volumeSequence.valueAt (0)) / 15.0f;
|
|
}
|
|
}
|
|
|
|
void BaseVoice::stopNote (float, bool allowTailOff)
|
|
{
|
|
if (!allowTailOff)
|
|
{
|
|
clearCurrentNote();
|
|
angleDelta = 0.0;
|
|
return;
|
|
}
|
|
|
|
if (settingRefs->isVolumeSequenceEnabled())
|
|
{
|
|
if (settingRefs->volumeSequence.hasRelease)
|
|
{
|
|
currentVolumeSequenceFrame = settingRefs->volumeSequence.releaseSequenceStartIndex;
|
|
currentEnvelopeLevel = (float) (settingRefs->volumeSequence.valueAt (0)) / 15.0f;
|
|
}
|
|
else
|
|
{
|
|
clearCurrentNote();
|
|
angleDelta = 0.0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
envelopePhase = kEnvelopePhaseR;
|
|
}
|
|
|
|
void BaseVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples)
|
|
{
|
|
if (angleDelta == 0.0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
while (--numSamples >= 0)
|
|
{
|
|
//
|
|
// Envelope
|
|
//
|
|
if (settingRefs->isVolumeSequenceEnabled())
|
|
{
|
|
currentEnvelopeLevel = (float) (settingRefs->volumeSequence.valueAt (currentVolumeSequenceFrame)) / 15.0f;
|
|
}
|
|
else
|
|
{
|
|
switch (envelopePhase)
|
|
{
|
|
case kEnvelopePhaseA:
|
|
currentEnvelopeLevel += attack_slope;
|
|
|
|
if (currentEnvelopeLevel > 1.0)
|
|
{
|
|
currentEnvelopeLevel = 1.0;
|
|
envelopePhase = kEnvelopePhaseD;
|
|
}
|
|
|
|
break;
|
|
|
|
case kEnvelopePhaseD:
|
|
currentEnvelopeLevel -= decay_slope;
|
|
|
|
if (currentEnvelopeLevel < sustain_level)
|
|
{
|
|
envelopePhase = kEnvelopePhaseS;
|
|
currentEnvelopeLevel = sustain_level;
|
|
}
|
|
|
|
break;
|
|
|
|
case kEnvelopePhaseS:
|
|
currentEnvelopeLevel = sustain_level;
|
|
break;
|
|
|
|
case kEnvelopePhaseR:
|
|
currentEnvelopeLevel -= release_slope;
|
|
|
|
if (currentEnvelopeLevel < 0)
|
|
{
|
|
currentEnvelopeLevel = 0;
|
|
clearCurrentNote();
|
|
angleDelta = 0.0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Write to buffer
|
|
//
|
|
float currentSample = voltageForAngle (currentAngle) * currentEnvelopeLevel * ampByVelocityAndGain;
|
|
|
|
for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
|
|
outputBuffer.addSample (i, startSample, currentSample);
|
|
|
|
//
|
|
// Advance phase
|
|
//
|
|
calculateAngleDelta();
|
|
|
|
currentAngle += angleDelta;
|
|
|
|
while (currentAngle > 2.0 * MathConstants<float>::pi)
|
|
{
|
|
currentAngle -= 2.0 * MathConstants<float>::pi;
|
|
}
|
|
|
|
//
|
|
// Advance control frame
|
|
//
|
|
controlFrameTimer += 1.0 / getSampleRate();
|
|
|
|
if (controlFrameTimer >= controlFrameLength)
|
|
{
|
|
advanceControlFrame();
|
|
|
|
while (controlFrameTimer >= controlFrameLength)
|
|
{
|
|
controlFrameTimer -= controlFrameLength;
|
|
}
|
|
}
|
|
|
|
onFrameAdvanced();
|
|
|
|
++startSample;
|
|
}
|
|
}
|
|
|
|
void BaseVoice::calculateAngleDelta()
|
|
{
|
|
auto cyclesPerSecond = MidiMessage::getMidiNoteInHertz (noteNumber);
|
|
auto cyclesPerSample = cyclesPerSecond / getSampleRate();
|
|
|
|
angleDelta = cyclesPerSample * 2.0 * MathConstants<double>::pi;
|
|
}
|
|
|
|
void BaseVoice::advanceControlFrame()
|
|
{
|
|
currentVolumeSequenceFrame = settingRefs->volumeSequence.nextIndexOf (currentVolumeSequenceFrame);
|
|
|
|
if (currentVolumeSequenceFrame == FrameSequence::SHOULD_RETIRE)
|
|
{
|
|
currentEnvelopeLevel = 0;
|
|
clearCurrentNote();
|
|
angleDelta = 0.0;
|
|
}
|
|
}
|