PD Patches Overview

This is an overview of the current patches in the synth, built with Pure Data (libpd) v0.46-6.

[main]

The top-level patch that is parsed by libpd on the c++ front end. This is where the DSP audio chain including the [clock], [wavetable] synth, all [voice]’s, and mixer are initialized.

[clock]

The clock is built on a [phasor~] running at audiorate. We use a Beats Per Minute calculation to set the frequency. There are three distinct pulses:

  1. Heartbeat is the base clock. This triggers every time a 16 step pattern with a noteStep of 1 takes to go cycle 16 times, calculated from the BPM.
  2. Step represents the amount of time a single 16 step pattern with a noteStep of 1 takes to cycle one time.
  3. Metro Blip is a 4/4 pulse, representing quarter notes.

To extract control-rate information used in triggering events and whatnot, we use [edgem~], a patch that gives the ability to pass a multiplier to [edge~], used for measuring various fractions of time from the Heartbeat phasor.

[wavetable]

The wavetable is the raw waveform arrays later used by [tabread~]. We create arrays 4096 values in length with via sinesum in a message, with the exception of Noise, which we generate via C++, as we have more control over random numbers.

[voice $1] 1 thru 8

Currently there are 8 static voices. There’s a potential for more, we’ll see how much overhead we have in the optimization phase. A voice is made up of a sequencer and a synth, which then gets piped through a mixer-track.

[sequencer]

The first component of the voice is the sequencer, which has two arrays that contain note and velocity pairs.

in [getPosition], we use clockPhase to calculate the position of the sequencer. This ensures that every sequencer is in sync. This is also where we apply swing and noteStep.

[synth]

Now onto the juicy parts that make actual sound.

Once we have the note and velocity pairs from the sequencer, they get handled by the [poly-tone~] patch. Here we see if we’re set to polyphonic or mono, and do all the voice packing, handling, and distribution to the 8 voices. (Could be more in the future depending on overhead). When the synth is in a monophonic state, it only uses the first voice, and the rest are switched off.

[tone~]

Inside [tone~] we the guts of what makes up each voice. After unpacking the note and velocity pairs, that data is used to trigger an ADSR, and frequencey envelope. Depending if “hold” is engaged sets if the release phase of the envelope gets triggered – this is used in the Mono Synth.

The wavetable uses whichever waveform type is selected, combined with the incoming note frequency, to generate a signal using [tabread~]

The frequency envelope uses a noteOn event to start an attack phase which modifies the pitch of each note.

[svf~] State Variable Filter

We utilize the [svf~] object packaged with Cyclone. It is a 4-type filter which generates the outputs for all filter types at once, allowing us to cross-fade between them.

A filter envelope is triggered from note-attack to sweep the filter cutoff

We use [mix], based on the same concept of glm::mix, or lerp(), which linear interpolates filter values so they blend between envelope states if the cut or res are modified.

In order to implement a crossfader between all filter value types, another custom patch is created:

[switcher $1]

A switcher in every voice disabled DSP 2 seconds after the last note is released.

[mixer-track]

Finally, the audio is converted to stereo and mixed with balance and volume additionall, we bus the audio to an fx-send for later use in the master.

[delay]

Tape delay.

[mixer]

Finally, the mixer takes all the audio from each voice and mixes them down. Here we have another final filter on the master LR, and put them through a compressor and a limiter, and finally from here, the sound goes to the DAC.

Local Controls

Our local test-bed allows any value to be tested, note velocities and patterns randomly created, and effects tweaked. This screenshot is controlling only one synth (s1). The local controls also support midi i/o for external interfaces.

A local mixer with graphical controls offers the ability to test output, fx bus, compressor settings, and fx controls.

Filter UI design

Each UI element went through a transformation from Knobs to something more intuitive.

Each one went through several iterations.

Here is a SVF mixer that didn’t make it into the app.

Sometimes we’ll try a direction and abandon it, but certain ones like this stay in the back of your head. We’ve talked about bringing this one back in some way.

Was super important to play with the app to get a feel for how well a control worked. Hard to tell if an idea is good sometimes without testing it.

Flexible codebase really helped.

Design mockups for a SVF (State Variable Filter) with the following functionality:

  • Filter Mode
    • Modes: 1. Low Pass, 2. Band Pass, 3. Hi Pass, 4. Notch.
    • Value: Float, 0.0f - 3.0f, interpolates between each filter mode
  • Filter Cutoff Freq
    • Value: Float, 0 - 3000 range
  • Filter Resonance Amt
    • Value: Float, 0 - 1
  • Filter Envelope Attack Length
    • Value: Float, 0 - 2000ms range
  • Filter Attack Curve
    • Value: Float -1 - 1 value range
  • Filter Envelope Cutoff Frequency Start
    • Value: Float, 0 - 3000 range


SVF UI Mockups

Implementing Filter Mode as a circular slider component. Res and Cutoff are X/Y inside the circular slider ring.

Illustrations of various filter ctrl states.

Futher illustrations depicting interpolations between filter states.

Filter Envelope Editor

PD Patches

Main filter patch

Filter mode crossfader (for circular control)

Filter Envelope

[mix~]

[mix~help]

[video] Skeletal UI control structure demo

Archives