Using Tidal to control modular synths with CV

Because SuperCollider can output DC voltages directly, I wanted to see how far I can get controlling a modular synth with CV. This allows me to bypass many of the Midi-to-CV tools, which I haven't had the best experiences with...

So far, I have quantised pitch, envelope generator, and trigger instruments.

d1 $ pitch "0 10 8 1" # scale "<12 31 8>" # orbit 0

pitch allows a pattern of note values. scale sets the amount of notes per octave. The pitch and scale values will be converted to 1v/octave. Both pitch and scale can be paternised for some microtonal madness...

d1 $ env "-1 0.8@2 0.3@3 0.8@4" # orbit 0

env will take -1/1 patterns and apply a smoothing transition on each step of the cycle. This output will be a bit 'steppy', and works best when put through a slew or glissando module.

d1 $ trigger "0 1 0 0 1 1 0 1" # orbit 0

trigger will take a 0/1 pattern and return +5v signals for the 1 values. Use -1 if you need a -5v.

I posted the code here if anyone is interested:


I gave this a try out today. Really cool!

I got all 3 synth defs to work as expected pretty quickly. I got hung up on audio routing in Supercollider for quite a while however. I'm a bit unclear as to the right startup.scd settings and how they relate to these orbit numbers.

I have a UAD Apollo and I'd like these going out of the ADAT connection to an Expert Sleepers module in my modular rack. The first ADAT channel is the 11th output on my sound card.

I setup the startup file as follows (I'm only placing the relevant items here):

s.options.numOutputBusChannels = 12;
~dirt.start(57120, [0,10,12,14,16,18]);

All of the orbits (0 through 11) head out of my sound card sequentially though. There is no jump between orbits 0 and 2 as I would expect. I then decided to keep tying larger orbits and when I got to # orbit 10 I was getting an error message from SC saying:
WARNING: SuperDirt: event falls out of existing orbits, index (1)
but it worked! The audio was routed to the correct ADAT destination. I could not get # orbit 2 to send out correctly (or any of the others either) though.

So it seems like an error in my orbit assignment in my startup? Or a bug perhaps? Or maybe it's weird behavior because your synth defs are mono I assume and orbits are definite as stereo pairs?

Let me know if you have no idea and maybe I can flag some others over.
And regardless, thanks for sharing these!


1 Like

Ahh.. I reallise that I am not using orbit semantically. I think I will change it to channel or something else...

Right now, orbit/channel needs to match to an output on your device, by index. Here is what I use:

I have a aggregate device that has an ES-8, which I use for tidal, so 0-15 would be used as orbits/channels Depending on how your audio devices output are setup, those indexes will need to reflect that.

I'm still using stereo channels in my startup.scd, Besides for catering to how many channels you have, I don't think anything special needs to be setup for this. It can get a bit confusing when outputting both CV and stereo audio tracks. You have to keep a mental note of which grouped outputs are stereo audio, and mono CV.

Thanks for this, I'll update the code/documentation to make it a bit more clear. I'm going to have a crack at an ADSR envelope in SuperDirt next.


I've updated the synth defs to use channel instead of orbit now...

1 Like

Ah, I see.
So I have to do something like this:

s.options.numOutputBusChannels = 16;
~dirt.start(57120, [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]); 

And great about the auto synth def. I was going to suggest that!
Very cool.

Please post here if you make updates if you don't mind. I'd love to follow along :wink:

s.options.numOutputBusChannels = 16;
~dirt.start(57120, [0, 2, 4, 6, 8, 10, 12, 14]);

I still use my start up like this, so that my audio channels are stereo. I ran into heaps of strange gain issues when I tried running mono only for everything

With this setup, I can still route audio to # orbit 4,5,6 etc as stereo channels -- as well as route # channel 0, 1, 2, 3 as mono CV signals. I think its because I'm skipping the superDirt orbit output, and jumping straight to CV output to the audio card itself (as superDirt doesn't have capability of DC atm?)

Will continue to post, but hit a bit of a road block with ADSR -- any help would be much appreciated! :dizzy:

That makes sense re: channels thanks.
What road block did you hit?

I'm not totally sure how to map a tidal cycle against a super collider envelope...

Env.adsr(0.001, 0.2, 0.25, 1) will output a new ADSR envelope

Screen Shot 2020-05-17 at 2.27.31 pm

I tried looking at various existing synths and issues, but i end up a bit more confused than I started... I think I need to get current position of envelope related to cycle length, and then turn that position into a 0/1 value...

  SynthDef(\adsr, { | out, channel, attack = 0.1, decay = 0.5, sustain = 0.5, release = 0.5 |
    var env = Env.adsr(attack, decay, sustain, release);
    var gen =, index???));,;

Thats more or less my work in progress...

Similar issue:

I think this needs to be used somewhere:

Wow, I got it in the end, this is much nicer...

d4 $ adsr 0.001 0.2 0.25 1 # x 0

Screen Shot 2020-05-20 at 7.06.24 pm

You can patternise these crazily too, but the envelope will only generate once per cycle...

d5 $ adsr "<0.05 0.9>" "<0 0.4>" 1 1 # x 0

Screen Shot 2020-05-20 at 6.46.32 pm

Will update the repo... I made a clock instrument too!


I'm super interested in this, although I don't fully understand it! I'd love to have a way to control the envelopes when using things like chop so that you can have a crossfade between slices, for instance. (I had a response to that question from Julian (telephon) in another forum, but I can't find that right now and couldn't make it work anyway.)

Which sound card are you using. I am asking since last time I checked that kind perk, not all cards were supporting DC voltage out.

Hmm.. I'm not sure exactly what you mean -- if you end up finding that example, I'd love to have a look...

I have an ES-8. There are a few other audio interfaces that have DC-coupled inputs/outputs -- but yes, they are required for this.

Pretty sure that all newish universal audio interface have DC coupled outs. Apollo X6 + for sure.

1 Like

Thank you for posting this, I just picked up the Expert Sleepers ES-9 and want to integrate Tidal with my modular so this is very helpful to know.


This is exactly where I checked, btw their website also hold a resource page for all "known" cards that support DC ouputs. Not a 100% reliable source, but AFAIK anyone owning a MOTU (not me) should be good to go. The page I am talking about:

Amazing, that ES-9 looks really nice, I have the older ES-8...

I've just started giving this code a workout myself, if you need any help getting this working, let me know. Also, any feedback/suggestions are welcome.

Oh, also, some of the code shared above is already deprecated... The latest is here:

Thanks @mashaal!
Glad you got it sorted.

I got wrapped up in some other stuff for I'm going to give this new update a go soon.


1 Like

After playing with the adsr, I realise that I have it hard set to only produce one envelope per cycle. I think I can update this so the occurrence of the envelope in the cycle can be patterned too., like a triggered adsr..

d1 $ trig "1 ~ 1 1 ~ 1 ~" # adsr (range 0.1 1 sine) 0.5 0.5 0.5 

In that example the attack time would grow for each triggered envelope over course of the cycle.

1 Like