Helping a newbie to plugin development

I know the basics of programming, and understand the core structure of programming in C++. I know how to program, but all is self taught in the last couple of months.

I want to make a sequencer that doesn’t have any exact parallel in any of the other plugins in all the VCV Rack community, but I don’t really understand how the code works for a sequencer. I do, however, understand how the graphic part of the code works, but just to make it show in Rack, i.e. initialize the plugin, add the name of it and the module, compile it, make it show in Rack (I made the graphics needed like some knobs, jack input/output, and the panel), tweak knobs and patch cables that doesn’t do anything. But don’t how to actually make it do anything.

How can I learn the process of building a sequencer?
Is there any specific resource or do I have to learn all about DSP and C++ from scratch? :sweat_smile:

The first module I’m trying to make is a sequencer inspired by the Ladik S-180 & Ladik S-183. If anyone wants to help me, you are more than welcome, the plugin is in it’s very early stages and I’m not the best at either the code or design.

Thanks for your help. :slightly_smiling_face:


So a sequencer is, unlike dealing with oscillators and analogue audio signals, extremely straightforward. The internals range from simply storing parameters in the parameters array (if you have a knobby sequencer, like SEQ-3 where there are no preset banks and every step is visible always) or some kind of internal array if you have a beefy pattern store like @marc_boule’s pattern or gate seq’s.

A sequencer is dealing with a small set of values that are fixed in time (whereas a VCO or VCF is dealing with “oscillating” values that only have meaning in relation to their proximity to another in a period of time). At its most basic you put a SchmittTrigger in for a clock, test it, and advance the pattern when the trigger fires. All a sequencer really does is hold on to some CVs for a time, and then output different CVs when the clock pulses.

You can see the source code to my Turing Machine clone, which shows off checking clock pulses and outputting different notes over time. SEQ-3 is also open source and is a common base for other sequencer plugins, which is a bit more advanced in that you can manually program the steps.

Sequencers tend to differ more in their usability and for certain purposes than in difficulty of implementation; dTroy and SEQ-3 are harder to do pitches with because you have to try and learn what the knob means in semitones, while say PhraseSeq16 offers a single octave piano display that makes keying notes in obvious (but lacks knobs, making it harder to key in exact voltages for filter sweeps.) It’s likely that many sequencers could be built around the same model class internally and just offer different face plates for how notes are entered.


Feel free to ask me anything about my sequencers, it will be my pleasure to answer as best I can. After having spent much time trying to figure out how best to handle resets, clocks and run states (i.e. run switches and run cables), as they relate to synchronization and missing first steps, etc, I am planning on writing a short document to expose the design principles I use in Impromptu sequencers and Clocked, and perhaps even to have a template pseudo code to show the structure of the important parts of my sequencers. Topics such as run toggling, clock schmitt trigger and how it is muted by run, clock ignore on reset, gate re-trigger on reset, and many other related topics will be covered. Cheers!


That would be awesome Marc!

Sounds like using the PulseGenerator, AND’ed with checking if the SchmittTrigger changed to positive this sample :thinking: At least, I do sample the Schmitt trigger’s current state and again after feeding it inputs to see if ALAN should step forward. I’m not sure if this is idiomatic.

Well in my case, it’s more like guarding the sampling of the schmitt trigger by the run state. In my sequencers I now have:

if (running)
    if (clockTrigger.process(inputs[CLOCK_INPUT].value))
        // process the clock (i.e. advance sequencer, etc.)

I used to do it the other way (swap both lines above), but this new way it is much better, since when we turn off RUN on Clocked (which pauses its outputs), and then back on again, it’s now impossible to miss a clock transition in the sequencer that is caused by routing delays.


Just to update this thread also, the manual section is now finished, and at the end of the section there is a link to some code excerpts.