Files
InstaGrain/Source/GrainVoice.cpp
hariel1985 55b5f89ac5 Initial release — InstaGrain granular synthesizer v1.0
8-voice polyphonic granular synth (VST3/AU/LV2) with:
- 128 grain pool per voice, Hann windowing, linear interpolation
- Root note selector, sample rate correction, sustain pedal (CC64)
- Scatter controls, direction modes (Fwd/Rev/PingPong), freeze
- ADSR envelope, global filter (LP/HP/BP), reverb
- Waveform display with grain visualization
- Drag & drop sample loading, full state save/restore
- CI/CD for Windows/macOS/Linux

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 17:26:06 +01:00

79 sor
2.0 KiB
C++

#include "GrainVoice.h"
GrainVoice::GrainVoice() {}
void GrainVoice::prepare (double sampleRate)
{
currentSampleRate = sampleRate;
cloud.prepare (sampleRate);
adsr.setSampleRate (sampleRate);
}
void GrainVoice::noteOn (int midiNote, float velocity)
{
currentNote = midiNote;
velocityGain = velocity;
voiceActive = true;
// Pitch offset relative to sample's root note
cloud.midiPitchOffset = (float) (midiNote - rootNote.load());
// Update ADSR
adsrParams.attack = attackTime.load();
adsrParams.decay = decayTime.load();
adsrParams.sustain = sustainLevel.load();
adsrParams.release = releaseTime.load();
adsr.setParameters (adsrParams);
adsr.noteOn();
cloud.reset();
}
void GrainVoice::noteOff()
{
adsr.noteOff();
}
void GrainVoice::forceStop()
{
adsr.reset();
voiceActive = false;
currentNote = -1;
cloud.reset();
}
void GrainVoice::processBlock (juce::AudioBuffer<float>& output, int numSamples,
const juce::AudioBuffer<float>& sourceBuffer)
{
if (! voiceActive) return;
// Render cloud into temp buffer
juce::AudioBuffer<float> voiceBuffer (output.getNumChannels(), numSamples);
voiceBuffer.clear();
cloud.processBlock (voiceBuffer, numSamples, sourceBuffer);
// Compute ADSR envelope once per sample (not per channel!)
std::vector<float> envBuffer ((size_t) numSamples);
for (int i = 0; i < numSamples; ++i)
envBuffer[(size_t) i] = adsr.getNextSample() * velocityGain;
// Apply envelope and mix into output
for (int ch = 0; ch < output.getNumChannels(); ++ch)
{
const float* voiceData = voiceBuffer.getReadPointer (ch);
float* outData = output.getWritePointer (ch);
for (int i = 0; i < numSamples; ++i)
outData[i] += voiceData[i] * envBuffer[(size_t) i];
}
// Check if ADSR has finished
if (! adsr.isActive())
{
voiceActive = false;
currentNote = -1;
cloud.reset();
}
}