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:
86
Source/GrainEngine.h
Normal file
86
Source/GrainEngine.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
#include <JuceHeader.h>
|
||||
#include "GrainVoice.h"
|
||||
|
||||
class GrainEngine
|
||||
{
|
||||
public:
|
||||
static constexpr int maxVoices = 8;
|
||||
|
||||
GrainEngine();
|
||||
|
||||
void prepare (double sampleRate, int samplesPerBlock);
|
||||
void processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages);
|
||||
void loadSample (const juce::File& file);
|
||||
void loadSample (const void* data, size_t dataSize, const juce::String& formatName);
|
||||
|
||||
const juce::AudioBuffer<float>& getSampleBuffer() const { return sampleBuffer; }
|
||||
double getSampleRate() const { return currentSampleRate; }
|
||||
bool hasSample() const { return sampleBuffer.getNumSamples() > 0; }
|
||||
juce::String getSamplePath() const { return loadedSamplePath; }
|
||||
|
||||
// Root note — which MIDI note the sample represents (default C4 = 60)
|
||||
std::atomic<int> rootNote { 60 };
|
||||
|
||||
// Grain parameters (GUI → audio)
|
||||
std::atomic<float> position { 0.5f };
|
||||
std::atomic<float> grainSizeMs { 100.0f };
|
||||
std::atomic<float> density { 10.0f };
|
||||
std::atomic<float> pitchSemitones { 0.0f };
|
||||
std::atomic<float> pan { 0.0f };
|
||||
std::atomic<float> posScatter { 0.0f };
|
||||
std::atomic<float> sizeScatter { 0.0f };
|
||||
std::atomic<float> pitchScatter { 0.0f };
|
||||
std::atomic<float> panScatter { 0.0f };
|
||||
std::atomic<int> direction { 0 };
|
||||
std::atomic<bool> freeze { false };
|
||||
|
||||
// ADSR
|
||||
std::atomic<float> attackTime { 0.01f };
|
||||
std::atomic<float> decayTime { 0.1f };
|
||||
std::atomic<float> sustainLevel { 1.0f };
|
||||
std::atomic<float> releaseTime { 0.3f };
|
||||
|
||||
// Filter
|
||||
std::atomic<int> filterType { 0 }; // 0=LP, 1=HP, 2=BP
|
||||
std::atomic<float> filterCutoff { 20000.0f };
|
||||
std::atomic<float> filterReso { 0.707f };
|
||||
|
||||
// Reverb
|
||||
std::atomic<float> reverbSize { 0.0f };
|
||||
std::atomic<float> reverbDecay { 0.0f };
|
||||
|
||||
// Master
|
||||
std::atomic<float> masterVolume { 1.0f };
|
||||
|
||||
// VU meter levels (audio → GUI)
|
||||
std::atomic<float> vuLevelL { 0.0f };
|
||||
std::atomic<float> vuLevelR { 0.0f };
|
||||
|
||||
// Grain visualization
|
||||
struct ActiveGrainInfo { int startSample; int lengthSamples; float progress; };
|
||||
std::vector<ActiveGrainInfo> getActiveGrainInfo() const;
|
||||
|
||||
private:
|
||||
std::array<GrainVoice, maxVoices> voices;
|
||||
juce::AudioBuffer<float> sampleBuffer;
|
||||
juce::String loadedSamplePath;
|
||||
double currentSampleRate = 44100.0;
|
||||
double sourceSampleRate = 44100.0;
|
||||
int currentBlockSize = 512;
|
||||
|
||||
juce::AudioFormatManager formatManager;
|
||||
|
||||
// Global effects
|
||||
juce::dsp::StateVariableTPTFilter<float> filter;
|
||||
juce::Reverb reverb;
|
||||
juce::Reverb::Parameters reverbParams;
|
||||
|
||||
void handleMidiEvent (const juce::MidiMessage& msg);
|
||||
void handleNoteOff (int note);
|
||||
void syncVoiceParameters();
|
||||
|
||||
// Sustain pedal
|
||||
bool sustainPedalDown = false;
|
||||
std::array<bool, maxVoices> sustainedVoices {}; // voices held by pedal
|
||||
};
|
||||
Reference in New Issue
Block a user