/* ============================================================================== 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::pi) { currentAngle -= 2.0 * MathConstants::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::pi; } void BaseVoice::advanceControlFrame() { currentVolumeSequenceFrame = settingRefs->volumeSequence.nextIndexOf (currentVolumeSequenceFrame); if (currentVolumeSequenceFrame == FrameSequence::SHOULD_RETIRE) { currentEnvelopeLevel = 0; clearCurrentNote(); angleDelta = 0.0; } }