The SupeDirtMixer v1.0 is out now!

I created this mixer UI for SuperDirt. My primary goal in developing this mixer was to eliminate the need for using a DAW like Ableton during my live coding sessions. Previously, I had been using Ableton solely for mixing in a live setup, so I wanted to streamline the process.

This mixer makes tonal depth, maintaining clean frequency separation, and creating a rich stereo panorama possible, all of which are easily accessible within SuperDirt. In essence, this mixer replaces the default values of the orbits defaultParentEvents. As a result, it empowers users to adjust parameters like gain, allowing values to range from 0 to 1.5 instead of being limited to 1.0. These modified defaultParentEvent values persist until they are overwritten on the TidalCycles side.

24 Likes

WHAAAAAAAAAAAT !!!! omg this is amazing !!!

3 Likes

image

2 Likes

Nice work! When I can make some more Tidal time soon I need to check this out.

1 Like

Daaaamn this meme gave me some chills and then I needed to laugh. Is this already retro? :smiley:
Now we may be 1 louder xD

1 Like

This is very cool, thanks for releasing it. Any chance the gui is mappable to something like Nanokontrol?

1 Like

Originally I only planned to make the presets changeable with OSC, but this feature is quite similar to make the values for gain, pan and reverb adjustable as well. It wasn't a big deal, so with the latest version these parts of the ui are mappable now.

(
     // Currently the ose listener only listens to the SuperDirt default port 57120
     var superDirtOSC = NetAddr.new("127.0.0.1", 57120);

     // You can change the preset an
     superDirtOSC.sendMsg("/SuperDirtMixer/loadPreset", "Default.csv");

     // You can change the gain, pan and reverb values with OSC
     // The first value is the orbit number and the second value is the new value that will be set in the orbit defaultParentEvent
     superDirtOSC.sendMsg("/SuperDirtMixer/gain", 0, 1.4);
     superDirtOSC.sendMsg("/SuperDirtMixer/pan", 0, 0.7);
     superDirtOSC.sendMsg("/SuperDirtMixer/reverb", 0, 0.4);
)

And to set mute and solo you can do it the standard way (something like this):

tidalNetAddr = NetAddr.new("127.0.0.1", 6010);

tidalNetAddr.sendMsg("/mute", orbitIndex );
tidalNetAddr.sendMsg("/unmute", orbitIndex );

tidalNetAddr.sendMsg("/solo", orbitIndex );
tidalNetAddr.sendMsg("/unsolo", orbitIndex );)

But how to map a MIDI controller to this I recommend the official documentation: OSC | Tidal Cycles

1 Like

I installed it using Quarks.install("https://github.com/thgrund/SuperDirtMixer").

I was going to try it before putting it in the startup.scd, by running this code (had to increase memory because otherwise it would not start the server):

(
s.options.numBuffers = 16 * s.options.numBuffers;
s.options.memSize = 32 * s.options.memSize;
s.options.maxNodes = 32 * s.options.maxNodes;
s.waitForBoot {
	~dirt = SuperDirt(2, s);
	~dirt.start(57120, 0 ! 14);
	// More SuperDirt ...

	// Initialize the SuperDirtMixer
	~mixer = SuperDirtMixer(~dirt, 6010);

	// You can adjust parameters before you use the ui
	~mixer.orbitLabels = ["d1 - Lead", "d2 - Bass", "d3 - Key", "d4 - Pad"];
	~mixer.enableMasterPeakRMS(0)
}
)

When running:

~mixer.gui

I get the following error:

ERROR: Message 'synth' not understood.
Perhaps you misspelled 'switch', or meant to call 'synth' on another receiver?
RECEIVER:
Instance of GlobalDirtEffect {    (0x55f58d7e24a8, gc=18, fmt=00, flg=00, set=03)
  instance variables [7]
    name : Symbol 'dirt_global_eq'
    paramNames : instance of Array (0x55f58cb582f8, size=1, set=2)
    numChannels : Integer 2
    state : instance of Event (0x55f58d7e3478, size=5, set=3)
    alwaysRun : false
    synth : instance of Synth (0x55f58a5c9a78, size=6, set=3)
    defName : nil
}
ARGS:
CALL STACK:
	DoesNotUnderstandError:reportError
		arg this = <instance of DoesNotUnderstandError>
	Nil:handleError
		arg this = nil
		arg error = <instance of DoesNotUnderstandError>
	Thread:handleError
		arg this = <instance of Thread>
		arg error = <instance of DoesNotUnderstandError>
	Object:throw
		arg this = <instance of DoesNotUnderstandError>
	Object:doesNotUnderstand
		arg this = <instance of GlobalDirtEffect>
		arg selector = 'synth'
		arg args = [*0]
	SuperDirtMixer:gui
		arg this = <instance of SuperDirtMixer>
		var window = nil
		var v = nil
		var composite = <instance of CompositeView>
		var freqScope = <instance of FreqScopeView>
		var orbitUIElements = nil
		var masterFunc = nil
		var meterResp = nil
		var masterOutResp = nil
		var equiView = nil
		var setEQuiValues = nil
		var activeOrbit = <instance of DirtOrbit>
		var presetFile = 'Default.csv'
		var orbitLevelIndicators = [*0]
		var panKnobs = [*0]
		var panNumBoxs = [*0]
		var gainSliders = [*0]
		var gainNumBoxs = [*0]
		var reverbKnobs = [*0]
		var eqButtons = <instance of List>
		var orbitMixerViews = [*0]
		var setOrbitEQValues = nil
		var leftMasterIndicator = <instance of LevelIndicator>
		var rightMasterIndicator = <instance of LevelIndicator>
	Interpreter:interpretPrintCmdLine
		arg this = <instance of Interpreter>
		var res = nil
		var func = <instance of Function>
		var code = "~mixer.gui"
		var doc = <instance of ScelDocument>
		var ideClass = nil
	Process:interpretPrintCmdLine
		arg this = <instance of Main>
^^ ERROR: Message 'synth' not understood.
Perhaps you misspelled 'switch', or meant to call 'synth' on another receiver?
RECEIVER: GlobalDirtEffect('dirt_global_eq', [ 'reverbWet' ])

Am I missing something?

Hey @loopier! Thanks for trying out the SuperDirtMixer!
When everything works fine, then you should receive these three messages:

---- initialize SuperDirtMixer ----
loading synthdefs in /Users/mrreason/Development/SuperCollider/SuperDirtMixer/classes/../synths/synth.scd
SuperDirtMixer was successfully initialized

I assume you do not see the loading synthdefs line in your console?

And what happens if you try to execute the SynthDef definition in the synths folder manually?

Or so to say, are you able to run these line of codes?

(
var numChannels = ~dirt.numChannels;

SynthDef("dirt_global_eq2", { |out, dryBus, gate = 1, reverbWet|
		var sound;
		var in = In.ar(dryBus, ~dirt.numChannels);
		//ReplaceOut.ar(dryBus, in * (1 - gobalCutoffDry));

		// Dry/Wet
		// TODO: disable if no value was received
		sound = in.equi;
		DirtPause.ar(sound, graceTime:4);

		ReplaceOut.ar(dryBus, (in * (1 - reverbWet)) + (sound * reverbWet ));

	}, [\ir]).add;

)

I do get a similar line to this (I'm on Linux):

---- initialize SuperDirtMixer ----
loading synthdefs in /home/r/.local/share/SuperCollider/downloaded-quarks/SuperDirtMixer/classes/../synths/synth.scd
SuperDirtMixer was successfully initialized

The code runs fine, but I keep getting the same error when I evaluate ~mixer.gui.

I might have an idea. So what do you get after booting the server and initializate the SuperDirtMixer, when you evaluate this line:

~dirt.orbits[0].globalEffects[0].synth

Same thing:

ERROR: Message 'synth' not understood.
Perhaps you misspelled 'switch', or meant to call 'synth' on another receiver?
RECEIVER:
Instance of GlobalDirtEffect {    (0x55b223135458, gc=08, fmt=00, flg=00, set=03)
  instance variables [7]
    name : Symbol 'dirt_global_eq'
    paramNames : instance of Array (0x55b2257c0218, size=1, set=2)
    numChannels : Integer 2
    state : instance of Event (0x55b224cb7218, size=5, set=3)
    alwaysRun : false
    synth : instance of Synth (0x55b22313e608, size=6, set=3)
    defName : nil
}
ARGS:
CALL STACK:
	DoesNotUnderstandError:reportError
		arg this = <instance of DoesNotUnderstandError>
	Nil:handleError
		arg this = nil
		arg error = <instance of DoesNotUnderstandError>
	Thread:handleError
		arg this = <instance of Thread>
		arg error = <instance of DoesNotUnderstandError>
	Object:throw
		arg this = <instance of DoesNotUnderstandError>
	Object:doesNotUnderstand
		arg this = <instance of GlobalDirtEffect>
		arg selector = 'synth'
		arg args = [*0]
	Interpreter:interpretPrintCmdLine
		arg this = <instance of Interpreter>
		var res = nil
		var func = <instance of Function>
		var code = "~dirt.orbits[0].globalEffect..."
		var doc = <instance of ScelDocument>
		var ideClass = nil
	Process:interpretPrintCmdLine
		arg this = <instance of Main>
^^ ERROR: Message 'synth' not understood.
Perhaps you misspelled 'switch', or meant to call 'synth' on another receiver?
RECEIVER: GlobalDirtEffect('dirt_global_eq', [ 'reverbWet' ])

And what do you receive when you evaluate this @loopier :

~dirt.orbits[0].globalEffects

@loopier stupid me...I added one minor change to SuperDirt and then forgot it. The mixer ui is not usable without doing this change in SuperDirt:

I created a pull request, but you can change this in your SuperDirt quark files on your computer.

1 Like

Ah, that worked. Thanks!

3 Likes

Glad to hear that! Happy mixing :slight_smile:

1 Like

This is very nice.

As you have asked for comments:

  • there is a convenient library for JSON that allows you to ave and load dictionaries, maybe that is useful for saving presets? GitHub - musikinformatik/JSONlib: A JSON de- and encoder for SuperCollider
  • the EQui is in the dependency list, but I think it isn't automatically installed – you may give it a try by replacing it with the full URL (I've never tried it, but could work).
  • you are iterating over all orbits to set the default environment – there is a convenient method to do this: ~dirt.set(name, value, name, value ...). Or, if you have a list of key value pairs: ~dirt.set(*pairs).
3 Likes

Thank you very much for your post!

And thanks for the comments! I was not aware of the JSONlib quark and it looks really promising. This would make it a lot easier to create presets and make them scalable. And it would help me to fix the defaultParentEvent related DRY pattern issues that I have. I mean in the end I could just save the complete defaultParentEvent as a preset in JSON for each orbit. And the decoding of this would be really simple and straight forward.

For EQui it is not in my dependency list:
image
But I will give it a try with the git url. This is provided by the SuperCollider documentation so I assume this should work (as you mentioned): Using Quarks | SuperCollider 3.12.2 Help

And I love the set shorthand! Something that I will definitely checkout.

1 Like

You may run into troubles because there are functions and Buses in the defaultParentEvent that can't be written to json. One could separate these out using a dictionary with a parent (with all "private" stuff in the parent).

It is in the quark's dependency list:

Actually, there is also TidalVST, which may not be necessary?