Skip to content

Instantly share code, notes, and snippets.

@jjylik
Created February 21, 2021 16:05
Show Gist options
  • Save jjylik/053daad19bb758b8bc1c497a4eb2433a to your computer and use it in GitHub Desktop.
Save jjylik/053daad19bb758b8bc1c497a4eb2433a to your computer and use it in GitHub Desktop.
SOUL patch with a simple sine synth with LFO detune
/*
== SOUL example code ==
== Author: Jules ==
*/
/// Title: A simple MPE sine-wave synthesiser example
///
/// A simple sine-wave synthesiser featuring pitch-bend support, using a basic
/// envelope and voice-allocator.
// Modified from the example to add a detune LFO https://github.com/soul-lang/SOUL/tree/master/examples/patches/SineSynth
graph SineSynth [[ main ]]
{
input smoothedGain.volume [[ name: "Volume", min: -40, max: 0, init: -6, step: 1 ]];
input event soul::midi::Message midiIn;
input DetuneParamProcessor.detuneDepth [[ name: "Detune amount", min: 0, max: 100, init: 1, step: 1 ]];
output stream float audioOut;
let
{
voices = Voice[8];
voiceAllocator = soul::voice_allocators::Basic(8);
smoothedGain = soul::gain::SmoothedGainParameter (0.5f);
gainProcessor = soul::gain::DynamicGain (float);
}
connection
{
DetuneParamProcessor.detuneDepthOut -> SineOsc.detuneDepth;
midiIn -> soul::midi::MPEParser -> voiceAllocator;
// Plumb the voice allocator to the voices array
voiceAllocator.voiceEventOut -> voices.noteOn,
voices.noteOff,
voices.pitchBend;
DetuneParamProcessor.detuneDepthOut -> voices.detuneDepth;
// Sum the voices audio out to the output
voices -> gainProcessor.in;
smoothedGain -> gainProcessor.gain;
gainProcessor -> audioOut;
}
}
processor DetuneParamProcessor
{
input event float detuneDepth;
output event float detuneDepthOut;
event detuneDepth (float newValue)
{
detuneDepthScaled = newValue/10.0f;
onUpdate();
}
float detuneDepthScaled = 0.0f;
void onUpdate()
{
detuneDepthOut << detuneDepthScaled;
}
}
processor SineOsc
{
input event
{
soul::note_events::NoteOn noteOn;
soul::note_events::NoteOff noteOff;
soul::note_events::PitchBend pitchBend;
float detuneDepth;
}
input stream float detune;
output stream float audioOut;
event detuneDepth (float f)
{
detuneAmountUsed = f;
}
event noteOn (soul::note_events::NoteOn e)
{
notePitch = e.note;
bendSemitones = 0.0f;
calculatePhaseIncrement();
}
event noteOff (soul::note_events::NoteOff e) {}
event pitchBend (soul::note_events::PitchBend e)
{
bendSemitones = e.bendSemitones;
calculatePhaseIncrement();
}
float notePitch, bendSemitones, phase, phaseIncrement;
float detuneAmountUsed = 1.0f;
void calculatePhaseIncrement()
{
let noteFrequency = soul::noteNumberToFrequency (notePitch + bendSemitones);
phaseIncrement = float (noteFrequency * twoPi * processor.period);
}
void run()
{
loop
{
phase = addModulo2Pi (phase, phaseIncrement+(detune*detuneAmountUsed));
audioOut << sin (phase);
advance();
}
}
}
//==============================================================================
graph Voice
{
input event
{
soul::note_events::NoteOn noteOn;
soul::note_events::NoteOff noteOff;
soul::note_events::PitchBend pitchBend;
float detuneDepth;
}
output stream float audioOut;
namespace LFO = soul::oscillators::lfo;
let lfo = LFO::Processor (LFO::Shape::sine, LFO::Polarity::bipolar, 0.1f, 1.0f);
let
{
amplitudeEnvelope = soul::envelope::FixedAttackReleaseEnvelope (0.2f, 0.02f, 0.1f);
attenuator = soul::gain::DynamicGain (float);
}
connection
{
noteOn -> SineOsc.noteOn;
noteOff -> SineOsc.noteOff;
pitchBend -> SineOsc.pitchBend;
lfo.out -> SineOsc.detune;
noteOn, noteOff -> amplitudeEnvelope.noteIn;
SineOsc.audioOut -> attenuator.in;
amplitudeEnvelope.levelOut -> attenuator.gain;
attenuator -> audioOut;
detuneDepth -> SineOsc.detuneDepth;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment