#include "SynthVoice.h" //============================================================================== NeuralSynthVoice::NeuralSynthVoice(NeuralSharedParams& sp) : shared(sp) {} //============================================================================== void NeuralSynthVoice::prepare(const juce::dsp::ProcessSpec& spec) { setWaveform(0); tempBlock = juce::dsp::AudioBlock(heapBlock, spec.numChannels, spec.maximumBlockSize); processorChain.prepare(spec); adsr.setSampleRate(spec.sampleRate); } //============================================================================== void NeuralSynthVoice::noteStarted() { auto velocity = getCurrentlyPlayingNote().noteOnVelocity.asUnsignedFloat(); auto freqHz = (float)getCurrentlyPlayingNote().getFrequencyInHertz(); processorChain.get().setFrequency(freqHz, true); juce::ADSR::Parameters p; p.attack = shared.attack->load(); p.decay = shared.decay->load(); p.sustain = shared.sustain->load(); p.release = shared.release->load(); adsr.setParameters(p); adsr.noteOn(); } //============================================================================== void NeuralSynthVoice::notePitchbendChanged() { auto freqHz = (float)getCurrentlyPlayingNote().getFrequencyInHertz(); processorChain.get().setFrequency(freqHz, true); } //============================================================================== void NeuralSynthVoice::noteStopped(bool allowTailOff) { adsr.noteOff(); //Triggers release phase } //============================================================================== void NeuralSynthVoice::notePressureChanged() {} void NeuralSynthVoice::noteTimbreChanged() {} void NeuralSynthVoice::noteKeyStateChanged() {} //============================================================================== void NeuralSynthVoice::renderNextBlock(juce::AudioBuffer& outputBuffer, int startSample, int numSamples) { if (!adsr.isActive()) clearCurrentNote(); if (waveform != -1) { setWaveform(waveform); waveform = -1; } auto block = tempBlock.getSubBlock(0, (size_t)numSamples); block.clear(); juce::dsp::ProcessContextReplacing context(block); processorChain.process(context); // 3. Apply ADSR envelope to tempBlock std::vector channelPtrs; for (size_t ch = 0; ch < tempBlock.getNumChannels(); ++ch) channelPtrs.push_back(tempBlock.getChannelPointer(ch)); juce::AudioBuffer buffer(channelPtrs.data(), static_cast(tempBlock.getNumChannels()), static_cast(tempBlock.getNumSamples())); adsr.applyEnvelopeToBuffer(buffer, 0, numSamples); juce::dsp::AudioBlock(outputBuffer) .getSubBlock((size_t)startSample, (size_t)numSamples) .add(tempBlock); } void NeuralSynthVoice::setWaveform(int waveformType) { auto& osc = processorChain.template get(); switch (waveformType) { case 0: osc.initialise([](float x) { return std::sin(x); }); break; case 1: osc.initialise([](float x) { return x / juce::MathConstants::pi; }); // Saw break; case 2: osc.initialise([](float x) { return x < 0.0f ? -1.0f : 1.0f; }); // Square break; case 3: osc.initialise([](float x) { return 2.0f * std::abs(2.0f * (x / juce::MathConstants::twoPi) - 1.0f) - 1.0f; }); // Triangle break; } }