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>
This commit is contained in:
78
Source/GrainVoice.cpp
Normal file
78
Source/GrainVoice.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user