TidalCycles with Linux and Bitwig

hacker…

When I first started with TidalCycles, the pattern generating library for use with SuperDirt/SuperCollider, I had originally tried to use a Linux VM.  I had really wanted to get this working but I kept running into errors that I’m going to say were because of the Hypervisor.  I was able to get SuperCollider installed, but never able to get a sound to happen and Jack would end up crashing the VM.  During this time I had also installed things on my Windows machine and my Macbook Pro.  After getting most of my life moved to the Macbook my Windows machine opened up for more experimental usage so I decided to try this again only with Linux directly on the hardware rather than a VM.

First, I opted for Ubuntu 16.04.  My main reason for this was VMware Workstation support, as I still would like to have the ability to test workstation and server configurations on this smoking fast laptop I have.  My Macbook is great, but I only have 16GB of RAM on it, vs 64GB available on my Lenovo P50.  The Ubuntu installation went as easy as one would expect, I did not install the 3rd party extras (yet) and everything with the exception of the fingerprint reader is working after the install (including the hot-keys).

I started by following this guide for building SuperCollider from source by Brian Heim.  I’ve used this A MILLION times for testing on the VMs that I never got working so I knew that it would at least get me through SuperCollider.  I went with the jack2 option as recommended and built things without emacs support (because I have no clue how to use emacs and Atom is my digs anyways).  I dont think there are many places to mess up in that guide so just pay attention and read through the steps rather than just cut n pasting (which you should never be doing anyways…).

After successfully testing SuperCollider I moved on to Tidal.  Here is where your first pay the fuck attention really happens.  The install tells you to: sudo apt-get install supercollider sc3-plugins haskell-stack git .  Don’t do that.  The reason we build SuperCollider from source was to get the latest version and last time I ran the above code it gets something older.  Change this to: sudo apt-get install haskell-stack git , omitting git if you have already installed it (git is usually something I get right away, it could have even come with Ubuntu I didn’t pay attention).  This is where I met my first error and sputtered out a few words of curse.

stack install tidal
Run from outside a project, using implicit global project config
Using resolver: lts-11.5 from implicit global project's config file: /home/digitalohm/.stack/global-project/stack.yaml
Downloaded lts-11.5 build plan.    
AesonException "Invalid flag name: \"bytestring--lt-0_10_4\""

A web search will eventually lead you to people fixing things with magic by issuing the

stack upgrade

command but that would be too easy, and that did not work for me (although it might work for you).  stack upgrade erred out with:

Could not parse '/tmp/stack-upgrade17795/stack-1.6.5/stack.yaml':
AesonException "failed to parse field 'extra-deps': when expecting a PackageIdentifier, encountered Object instead"
See https://github.com/commercialhaskell/stack/blob/release/doc/yaml_configuration.md.

because I mean, it would be too easy right.  Murderous thoughts went through my head and I imagined cooking a Penguin rotisserie style, but eventually I found that I was not alone.  Many people eat penguins…

wget -qO- https://get.haskellstack.org/ | sh -s - -f

The above accomplished the upgrade and reissuing the stack intsall tidal  worked perfectly.

I tested things out and it was working.  I did a little cabbage patch for good measure and decided to tackle multi channel output to Bitwig.

I’m not a seasoned Bitwig user by any measure, I’m able to move around it because I’m so used to Ableton, but really it is a bit of a different beast.  I installed the community (free) edition and tried things out.  I basically copied over my startup.scd file from my Macbook Pro (see this previous post for that) and tried it out.  Through trial and error I figured out the right options for Linux which on my system ended up being:

s.options.numBuffers = 2048*2;
s.options.memSize = (512*1024);
//s.options.device = "Soundflower (64ch)";
s.options.numOutputBusChannels = 16;
s.waitForBoot {
	~dirt = SuperDirt(2, s);
	~dirt.loadSoundFiles("~/.local/share/SuperCollider/downloaded-quarks/Dirt-Samples/*");
	//s.sync;
	~dirt.start(57120, [0,2,4,6,8,10,12,14]);
};

I had to increase the numBuffers to avoid the not enough free buffers message, and for whatever reason I had to comment out the s.sync; part.  I also could not find the Linux equivalent to show the devices so I just used the default which works out of the box so much better than Soundflower (THANK YOU JACK).  With this going I used the Meter in SuperCollider to observe that it was indeed working with Tidal sending sound out a few different orbits.

From here, you should have installed and opened up Bitwig Studio, and create 8 stereo input tracks. This need to happen so that you can get things routed properly in the next step.  Cracking open qjackctl you can get into the connections option.  Go to the Audio tab and drag or click connect your routings to look like below.  One thing to note is that you need to make sure the SuperCollider connections are not going to the system outputs.  If you leave them connected then not only will get sound out of Bitwig but also directly from SuperCollider.

UPDATE 04-25-108 – I recently discovered how to make these persist with the patchbay and save the configuration as an XML.  I should be writing up how to do this shortly as well as posting the XML config for download.


Currently I’ve only tested this with 8 stereo pairs.  I really don’t need more than that atm, I mean that is 8 stereo channels from SuperCollider plus Midi controlled synths in Bitwig…yeah that should be enough.

If things go well (and I didn’t miss a step, because I’m documenting from memory mostly) then you should see activity in all the channels you configured in Bitwig

Each channel is an individual d(n) in Tidal using the orbits 0-7.

Next is MIDI and here is where I had the most fails and went from fantasizing about cooking Penguins to just all out extinction level massacre.  Also during all of this I ended up in the #Bitwig channel of #FreeNode so I might be idling there (you can mostly find me in talk.lurk.org).  TO MAKE A LONG AND FRUSTRATING STORY SHORT you need to do a few things.  For whatever reason the Jack MIDI does not seem to work in Ubuntu.  I was able to see MIDI coming out of Tidal/SuperDirt in the MIDI Monitor, but Ubuntu wasn’t reading anything.  This guy riot has a post up that goes over how to get this working and it went pretty much verbatim.  For this configuration just run sudo modprobe snd_virmidi midi_devs=1 and then run a2jmidid .  If you get any errors about it not existing, don’t freak out, just install it.

At this point I should point that I upgraded to Tidal 1.0-Dev and SuperDirt 1.0-Dev.  I did this because the new SuperDirt MIDI is way more stable and MIDI is my thing.  The SuperDirt upgrade was extremely easy, but I know that a lot of people have problems with it.  I believe there are multiple ways to get this done and I’m not saying my way is correct but here is how I did it.

First navigate to your SuperColllider install directory, mine was located at ~/.local/share/SuperCollider/downloaded-quarks
Then remove that motherfucker with rm -rf SuperDirt
Next, clone that new motherfucker using git clone -b 1.0-dev https://github.com/musikinformatik/SuperDirt.git
THEN, cd into SuperDirt and checkout this certain commit using git checkout 900f8060bf95215dc4825d7f4425ef1acf819cb0 .  The reason for this is that this is the commit that I KNOW to be working at least for all the MIDI stuff I’ve been playing with lately.  Until I get past a couple performances in May I really don’t want to test out other things.  Once I pass those performances then I’ll upgrade to the lastest 1.0-Dev commit and test away.

The process for Tidal is pretty similar, and again this is the route that got me working on the Macbook so I tried to repeat it.

First, navigate to the stack install directory.  Mine is located at: ~/.stack/indices/Hackage/packages
Remove the tidal folder, noting what version was on there, at this time it was 0.9.8
Next, git the dev branch using git clone https://github.com/tidalcycles/Tidal.git -b 1.0-dev
Create a new tidal directory with mkdir tidal and then move the cloned repo inside of it while renaming it to the 0.9.8 version.  mv Tidal tidal/0.9.8
Now reinstall Tidal using stack install tidal . From what I understand, stack will look to the directory that exists to install the repo.  This might be completely wrong, if it is please someone school me.

At this point, I tested the MIDI configuration in SuperDirt and Tidal and everything worked, or at least no errors were thrown.

The SuperDirt code is:

MIDIClient.init;
MIDIClient.destinations;
~midiOut = MIDIOut.newByName("Midi Through", "Midi Through Port-0");
~midiOut.latency = 0.0;
~dirt.soundLibrary.addMIDI(\midi, ~midiOut);

and the new Tidal code is:

(midicmd, midicmd_p) = pS "midicmd" (Nothing)
(midichan, midichan_p) = pF "midichan" (Nothing)
(progNum, progNum_p) = pF "progNum" (Nothing)
(val, val_p) = pF "val" (Nothing)
(uid, uid_p) = pF "uid" (Nothing)
(array, array_p) = pF "array" (Nothing)
(frames, frames_p) = pF "frames" (Nothing)
(seconds, seconds_p) = pF "seconds" (Nothing)
(minutes, minutes_p) = pF "minutes" (Nothing)
(hours, hours_p) = pF "hours" (Nothing)
(frameRate, frameRate_p) = pF "frameRate" (Nothing)
(songPtr, songPtr_p) = pF "songPtr" (Nothing)
(ctlNum, ctlNum_p) = pF "ctlNum" (Nothing)
(control, control_p) = pF "control" (Nothing)

OK…now back to whatever it was I was talking about…oh yes Bitwig MIDI.  Now with that out of the way, you’ll notice I used the “Midi Through Port-0” in my configuration.  Change this as you see fit, I really have no idea what it looks like otherwise, my whole goal for this is a self contained system with not external hardware needed.  Go back into qjackctl and this time go to the MIDI tab.  Connect the port to the Virtual Raw MIDI 1-0 crated at a2midid step above

If at this point you think this is a ALOT, recognize that it’s a lot more doing it blindly or even documenting it :D, but do not fret, because we are almost there.  Now that you have this much going, Bitwig is ready to recieve your MIDI.  So you could go into Bitwig and configure your MIDI like so:

and you are off…or are you?

In this configuration you will indeed get MIDI from SuperDirt to Bitwig, however yow won’t be able to separate it and that isn’t the end goal here . At this point I had to do a lot of research and finally ended up at this github by Thomas Helzle.  Thankfully Thomas has done all the research in his project for getting Bitwig working with Processing so we just had to do a little copy and paste action.  Originally I had tried to create my own controller script but failed, I’m still having issues getting it to show up, however I just backed up the original Generic controller and created a new one like this:

loadAPI(2);
host.setShouldFailOnDeprecatedUse(true);

host.defineController("Generic", "MIDI Keyboard", "1.0", "6E55D132-1846-4C64-9F97-48041F2D9B96");
host.defineMidiPorts(1, 1);


var LOWEST_CC = 1;
var HIGHEST_CC = 119;

function init()
{
	MultiCon   = host.getMidiInPort(0).createNoteInput("Omni",  "??????");
	MultiCon1  = host.getMidiInPort(0).createNoteInput("Ch 1",  "?0????");
	MultiCon2  = host.getMidiInPort(0).createNoteInput("Ch 2",  "?1????");
	MultiCon3  = host.getMidiInPort(0).createNoteInput("Ch 3",  "?2????");
	MultiCon4  = host.getMidiInPort(0).createNoteInput("Ch 4",  "?3????");
	MultiCon5  = host.getMidiInPort(0).createNoteInput("Ch 5",  "?4????");
	MultiCon6  = host.getMidiInPort(0).createNoteInput("Ch 6",  "?5????");
	MultiCon7  = host.getMidiInPort(0).createNoteInput("Ch 7",  "?6????");
	MultiCon8  = host.getMidiInPort(0).createNoteInput("Ch 8",  "?7????");
	MultiCon9  = host.getMidiInPort(0).createNoteInput("Ch 9",  "?8????");
	MultiCon10 = host.getMidiInPort(0).createNoteInput("Ch 10", "?9????");
	MultiCon11 = host.getMidiInPort(0).createNoteInput("Ch 11", "?A????");
	MultiCon12 = host.getMidiInPort(0).createNoteInput("Ch 12", "?B????");
	MultiCon13 = host.getMidiInPort(0).createNoteInput("Ch 13", "?C????");
	MultiCon14 = host.getMidiInPort(0).createNoteInput("Ch 14", "?D????");
	MultiCon15 = host.getMidiInPort(0).createNoteInput("Ch 15", "?E????");
	MultiCon16 = host.getMidiInPort(0).createNoteInput("Ch 16", "?F????");

	MultiCon.setShouldConsumeEvents(false);
	MultiCon1.setShouldConsumeEvents(false);
	MultiCon2.setShouldConsumeEvents(false);
	MultiCon3.setShouldConsumeEvents(false);
	MultiCon4.setShouldConsumeEvents(false);
	MultiCon5.setShouldConsumeEvents(false);
	MultiCon6.setShouldConsumeEvents(false);
	MultiCon7.setShouldConsumeEvents(false);
	MultiCon8.setShouldConsumeEvents(false);
	MultiCon9.setShouldConsumeEvents(false);
	MultiCon10.setShouldConsumeEvents(false);
	MultiCon11.setShouldConsumeEvents(false);
	MultiCon12.setShouldConsumeEvents(false);
	MultiCon13.setShouldConsumeEvents(false);
	MultiCon14.setShouldConsumeEvents(false);
	MultiCon15.setShouldConsumeEvents(false);
	MultiCon16.setShouldConsumeEvents(false);

	host.getMidiInPort(0).setMidiCallback(onMidi);
	host.getMidiInPort(0).setSysexCallback(onSysex);

   // Make CCs 2-119 freely mappable for all 16 Channels
   userControls = host.createUserControls((HIGHEST_CC - LOWEST_CC + 1)*16);


   for(var i=LOWEST_CC; i<=HIGHEST_CC; i++)
   {
      for (var j=1; j<=16; j++) {
         // Create the index variable c
         var c = i - LOWEST_CC + (j-1) * (HIGHEST_CC-LOWEST_CC+1);
         // Set a label/name for each userControl
         userControls.getControl(c).setLabel("CC " + i + " - Channel " + j);
      }
   }
}

function onMidi(status, data1, data2) {
	 //printMidi(status, data1, data2);
	 //println(MIDIChannel(status));

   if (isChannelController(status)) {
      if (data1 >= LOWEST_CC && data1 <= HIGHEST_CC) {
         var index = data1 - LOWEST_CC + (HIGHEST_CC * MIDIChannel(status));
         userControls.getControl(index).set(data2, 128);
      }
   }
}

function onSysex(data) {
	//printSysex(data);
}

function exit() {
   // nothing to do here 😉
}

Now, you should have the ability to send MIDI to do the different channels via the midichan parameter in Tidal.  You can test this using the following code

d1 $ note 0 # midichan 0 # s "midi"
d2 $ note 10 # midichan 1 # s "midi"

You should be able to set your MIDI channels in Bitwig and it should look like this:

And there you have it, my half assed attempt to recall installation and configuration of Tidal/SuperDirt/SuperCollider and Bitwig.

Please if you run into any problems, typos, etc, locate me in #Bitwig or on talk.lurk.org

Leave a Reply

Your email address will not be published. Required fields are marked *