Magical8bitPlug2/Source/BaseVoice.cpp
除村 武志 941a120dea Added code
2020-02-26 17:13:02 +09:00

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;
}
}