Hi all!

How do people do multichannel stuff?

To start with - say if I've got 4 channels, then I want d1 to go to 1st channel, d2 to go to 2nd, d3 to 3rd, etc...?

And i guess it would be much harder to spatialise it, so for example to have d1 moving around the room/through the different channels at different points..?

Thanks in advance!

1 Like

Firstly, you'll want to look at separating audio outputs in superdirt and supercollider, as per:

Then you can use # orbit to choose your stereo output pairs - orbit is patternable as it turns out, so this puts output on a different stereo pair each cycle:

# orbit "<0 1 2>"

Then just route your sc outputs wherever you want them to appear

1 Like

Hey thanks.

I'm getting it ready to use 16 speakers - this is what I've done, could you check it please? These lines are what I've changed from the supercollider startup file

s.options.numOutputBusChannels = 16;

~dirt = SuperDirt(16, s);

~dirt.start(57120, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);

So then I run this, if I wanted to send one pattern the first speaker I would write # orbit "0.0625"

then say if I wanted to send another pattern to the 12th speaker, I would write # orbit "0.75"

...if my maths is right?

Thanks again!

I initially thought this is more complicated than it needs to be, but by the end of this post I realised there were a few ways to skin this cat :wink: - here's what I initially thought you would do:

Option 1:
Inside s.reboot {

s.options.numOutputBusChannels = 16;

Then inside s.waitForBoot:

// leave this at 2 (stereo channel per orbit)
~dirt = SuperDirt(2, s); // two output channels, increase if you want to pan across more channels
~dirt.start(57120, [0, 2, 4, 6, 8, 10, 12, 14] );   // start listening on port 57120, create 8 stereo busses

then you have # orbit 0..7 each with a stereo pair of speakers (use # pan [0|1] to access individual speakers.

Option 2:
realistically, another option would be to make each orbit a single speaker, might look like:
leave sc output channels at 16 -> inside s.waitForBoot:

~dirt = SuperDirt(1, s); // mono output channel
~dirt.start(57120, [0,1, 2,3, 4,5, 6,7, 8,9, 10,11, 12,13, 14,15] );   // start listening on port 57120, create 16 mono busses

Then you use # orbit 0..15 to access each individual speaker, # pan does nothing

Option 3:
then of course you could use a single orbit with 16 speakers which is what I think you've proposed. sc output channels stay at 16 -> inside s.waitForBoot:

~dirt = SuperDirt(16, s); // 16ch output
~dirt.start(57120, [0] );   // start listening on port 57120, create 16 mono busses

Then you use # orbit 0 to access the bank of speakers, and use divisions of 1/16 in # pan to access each individual speaker

Disclaimer: all of this is wholly untested - without knowing exactly what your plan is,it's hard to say which would work best but I feel like option 3 might be most flexible (I'm not sure if you can specify a pattern to play on multiple orbits for example, which you'd need to pan smoothly I think)

Ok, I will try all these and get back to you. I only have 1 hour in the spatial sound lab tomorrow to test it out so just wanted to pre empt some of my confusion......... Thank you so much

So with the option 3 as you wrote - not quite sure the difference between pan and orbit

are you saying to do the following to send this pattern to the 2nd speaker only (if my maths is right? 0 is first speaker, so second speaker is 0.0625?) -

d1 $ s "bd*8" # pan 0.0625

then I don't understand where # orbit 0 comes in?

Sorry if this is tedious, I'm very inexperienced with sound terms / speaker technical jargon & set ups etc. In absolute laymans terms would be great

yeah - here's a tip though, don't worry about your maths and use fractions for readability

d1 $ s "bd*8" # pan (1/16)

pan through the full range of speakers (adjust the fractions if you want to limit the speakers)

d1 $ s "bd*8" # pan (range (0/16) (16/16) (slow 8 saw))

a new and undocumented-ish feature in current tidal - using panbus on a long sample (note it goes from -1 to 1 instead of 0 to 1) so that it will pan even though you're only playing an event once every 4 cycles:

    $ s "[bev/4]"    
    # panbus 1 (range (-8/8) (8/8) (slow 4 saw))   
    # legato 1

well, this is where my lack of experience/knowledge with this comes in -
In a default setup, an orbit refers to a pair of stereo channels, there are multiple orbits which effectively map directly to each dN $
The orbits allow global effects (such as delay, reverb) to be applied in different amounts to different orbits.
In the config we've setup though, there is only a single orbit - I don't know if tidal will definitely just loop on the single available orbit, or whether it will send to non-existent orbits (I'd expect it'll do the former), but by defining # orbit 0 it was like a safeguard that it will go exactly where you wanted it to

Yes this would work fine. If you want to send a sound to a particular speaker by number without doing the maths, you can also use channel, e.g. d1 $ sound "bd" # channel "5" to go to the sixth speaker (since it starts counting from 0).

You can test this without having to be on an actual multichannel system. Just start superdirt with these settings and open a scope (click on numbers on bottom right, and "show scope"). Then run something like this and you should see the kick move around: d1 $ channel "0 .. 15" # sound "bd"

Because bd is a stereo sample, it will play across adjacent channels. There should be a way to handle how stereo samples are played but I think it's a bit buggy:

I wouldn't worry too much about orbits. With the above config you are creating 18 but you could stick with the usual 12 (which are assigned from d1 to d12) by replacing the dirt.start line with ~dirt.start(57120, 0 ! 12); (which would be the same as having twelve zeroes). That's a bit less resource intensive. You could reduce the number of orbits as cleary says if you have problems with cpu usage but I think the usage is low, and then you will have problems with using reverb and delays.

1 Like

Looking at option 3, which is 16 channel output with orbit 0 and pan aware to access bank of speakers, and option 2, mono output channel, Is it possible to have both, where i can send discrete output through one speaker and have a pan aware orbit, and toggle between the two?

Also on panning, is it possible to reduce the width of the source such that superdirt doesn't spread the audio across too many speakers? I remember looking through old threads of superdirt panning in the past and it didn't seem to be resolved.

Short answer is, I don't know I'm afraid. I don't have access to anything to test so everything I've posted to this point is purely a guess, I'm not comfortable extrapolating those guesses further

I think you can't vary the number of channels to pan between per orbit, but can send to a single channel as I described earlier.

There's some discussion of the parameters available to control pan width / splay etc here:

It'd be great if you could join the thread there if you have trouble or success.. When I get access to multichannel systems there's never enough time to work through it properly. I did get to work on a multichannel system last week but ended up using 7 raspberry pis outputting 2 channels each :slight_smile:

One of the problems is with the idea of stereo. Since samples are recorded in stereo, and playback is meant to be stereo on two speakers, once there's more than 2 speakers, it's difficult to decide how to split the 2 stereo sources across multiple speakers. Outside of supercollider, one way i've tried to get past that is to use a surround sound bed and place these two speakers as sound sources within that bed. And to pan would be to move those sound sources within the bed itself. That way option 1, which is the default works out of the box and panning would be to tie the positional parameters into tidalcycles for patterning.

1 Like

It might be total overkill, but I know that there are good plugins for ambisonics or spatialization on SuperCollider: It might be possible to adapt these to SuperDirt.

1 Like

@mmmoth On that issue we discuss pan width/splay for dealing with samples containing more than one channel.
@raph there's an issue exploring ambisonics here

I thought i'd just note this down here. If any audio effect is applied to a B format encoding, there's a chance it will alter the spatialization. IIRC trond lossius recommends applying effects to A format first. There's the IEM plugins which have omni/multi versions of compressors and reverb, and i assume those take this into account or i've been using those wrong.

I've been working with the multichannel (4) setup, having it set up as a follows:

s.options.numOutputBusChannels = 4; 
~dirt = SuperDirt(1, s); // two output channels, increase if you want to pan across more channels
~dirt.start(57120, [0, 1, 2, 3]);

It allows me to send audio to a patternable # orbit " 0 1 2 3"

the other option I explored is:

s.options.numOutputBusChannels = 4; 
~dirt = SuperDirt(4, s); // two output channels, increase if you want to pan across more channels
~dirt.start(57120, 0 ! 12);

It allows me to pan audio to a patternable # pan " 0 0.25 0.5 0.75"

I have some questions for both options:

  • for the first option, with orbit, can I at some point in my chain fork the chain so I can send it to multiple orbits? what if I wanted a sound to come from all speakers instead of just one, or maybe 2?
d1 $ s "kick*4"
      # fork  (# orbit "0 3 2 3"  # orbit "1 1 1 1")

would always make it sound from speaker 1, but also from 0 3 2 and 3 on different beats.
Can this be achieved?

In the panning option I'm stuck with a similar problem. I know this is not a bug...
Samples can be mono or stereo. Stereo samples will play across at least two speakers, mono samples can be played on a single speaker. But how to eg. spread a stereo sample across 1&4(L) and 2&3(R) in this situation (or 1&2(F) and 3&4(B) for that matter). Or how to make a sample sound from all speakers.

Having read the thread, I'm effectively thinking of the ATK-plugins which I have successfully used in reaper, and which exist for SuperCollider, but it seams like a big hack for a small setup I'm working on...
It would involve sending the orbits into ATK, and having a tidal # atk function delivering the spatialised audio to SC which would output it depending on the output situation.

d1 $ s "kick*4"
  # atk 4 /// atk outputprofile 4?
  # pan 0.5 // (center)
  # fade 0 // (front)
  # focus  1 // ("how "wide" is the sound)

The good thing is that you would be able to work on a scene with headphones, and later on swap the output profile to your modest 4 speaker or crazy 64 speaker array, and it would have the same spatialisation (and a different degree of experience)

but for now I'm looking into the hopefully much easier forking option, to get some patterns on combinations of speakers

1 Like

Hi @kaosbeat,

I would keep exploring option 2 rather than 1. You can still use e.g. # channel "0 1 2" etc for addressing specific channels then, IIRC.

There's some discussion about panning stereo samples here: let's sort out the panning scheme · Issue #36 · musikinformatik/SuperDirt · GitHub

You can see I was pretty confused by the end of it, but all the options should be covered..

Also exploration/examples of using superdirt with atk here: Support for Ambisonics · Issue #141 · musikinformatik/SuperDirt · GitHub

Here's an example from @munshkr for booting superdirt with binaural panning with ATK GitHub - munshkr/nobounds

We used it in a performance where I was in a local venue with multichannel system and @munshkr, @irisS and @cortesana.malinali were remote, monitoring on headphones with binaural panning.

For this you could just do :

# orbit "[0 3 2 3, 1]"


# channel "[0 3 2 3, 1]"
1 Like