Send and receive MIDI
Tidal can send and receive MIDI messages. You can talk with external synthesisers and hardware and receive data from your controllers. All the mapping is done directly through the language in an intuitive way.
SuperDirt MIDI
Prerequisites
The prerequisites require recent versions of Tidal and SuperDirt:
- Upgrade to the latest Tidal (this post assumes version 0.9.10 or greater)
- Make sure you have the latest SuperDirt quark. Uninstalling and reinstalling the SuperDirt quark might be easiest. See this page for details on how to update Quarks.
Initialization
To begin, you’ll start in SuperCollider. Start up SuperDirt as you normally would. Then, in SuperCollider eval the following code:
You should now see a list of the system MIDI devices in SuperCollider’s post window. The output will look something like this:
Take note that these MIDI devices have two parts to their names. You will need both parts in the next step, which is to actually connect to the MIDI device. Eval the following line:
Above, we have stored a reference to the device in a variable named ~midiOut
.
Finally, define the name of the “synth” in Tidal you will use to control this device. Below, we will call it mydevice
. Eval the following line:
Optionally, you can define a latency value on your device:
That’s it for initialization. You should now have a MIDI device connected in SuperDirt, running as a synth named mydevice
.
Usage in Tidal
Note Patterns
Now we can start writing some Tidal patterns to control the MIDI device. Let’s send it a trivial note pattern:
That should play a simple four-note pattern. Notice we’re just using the synth name mydevice
to send notes to the MIDI device.
You can also use the note-name and octave notation:
Alternatively to using n
or note
to pass MIDI notes, you can use the midinote
function:
The only difference is that with midinote
notes are specified with numbers from 0 (C(-1)) to 127 (G9). In TidalCycles, note 0 is C5, and in MIDI, C5 is note 60, so it’s easy to translate from one system to the other by adding or subtracting 60
to the note value.
The last example could be rewritten as:
MIDI Channels
Use the function midichan
to set the MIDI channel.
The default MIDI channel is 1. SuperDirt MIDI channels are indexed starting at zero, so MIDI channel 1 is midichan 0:
If your synth is listening on a different channel, let’s say, MIDI channel 5, you would use midichan 4
:
Notice that midichan
accepts a pattern of numbers, so you can use a pattern to play on different MIDI channels:
The above pattern plays notes “0 2” on channel 1 and “4 7” on channel 5.
CC Params
To send a CC param to your synth, the best way to do it in the new SuperDirt MIDI is with a different Tidal pattern. To create this pattern, you’ll be using two new SuperDirt MIDI params:
ccn
: the CC param number you want to control:ccn 30
ccv
: the value to send to the CC param, ranging from 0 to 127:ccv 64
Here’s a full example, sending a value of 64 to CC param 30:
You can of course also specify the MIDI channel with midichan
:
You can specify patterns of CC values:
Note that the left-most pattern defines the rhythm in this case when using #
.
If you have a specific feature on your device that listens on a specific CC number, you can give it a friendly name if you wish:
If you have many CC params you want to control at once, a stack works well:
Each device has its own MIDI chart implementation, but there are a few CC numbers that are standard and should work the same in most devices:
CC number | Function |
---|---|
1 | Modulation wheel |
2 | Breath controller |
7 | Volume |
10 | Pan |
11 | Expression pedal |
64 | Sustain pedal (<=63 Off, >=64 On) |
There is also the function cc
, which allows us to pass both the number and the value in a single string:
Velocity
MIDI velocity can be set using amp
and gain
. They work in a similar way to when used with samples, being amp
linear and gain
exponential.
Default velocity is 50
, default amp
is 0.4
and default gain
is 1
.
In the following tables you can see how distinct amp
and gain
values affect velocity:
Amp value | MIDI velocity value |
---|---|
0 | 0 |
0.1 | 12 |
0.2 | 25 |
0.3 | 38 |
0.4 | 50 |
0.5 | 63 |
0.6 | 76 |
0.7 | 88 |
0.8 | 101 |
0.9 | 114 |
1 | 127 |
Gain value | MIDI velocity value |
---|---|
<=0.3 | 0 |
0.4 | 1 |
0.5 | 3 |
0.6 | 6 |
0.7 | 12 |
0.8 | 20 |
0.9 | 33 |
1 | 50 |
1.1 | 74 |
1.2 | 105 |
>=1.3 | 127 |
Pitch modulation
Pitch modulation can be controlled using the midibend
function.
Note that usually a pitch wheel sends a number between -8192
and 8191
, but here all numbers are positive, so the range is from 0
to 16383
, being 8192
the neutral middle point.
You can simulate the movement of the pitch wheel in various ways.
Supposing your device is called mydevice
and receives MIDI messages on channel 5
, in this example the sound will start in a C note, and gradually increase pitch to the limit of the pitch bend modulation:
Now, we start at the minimum of the pitch bend modulation, and move fast to the neutral point, where we will sustain the pitch for the remaining of the cycle:
Aftertouch
Aftertouch is used to modify the character of a sound that is already playing, usually by applying more or less pressure on a MIDI controller keyboard keys.
To pass aftertouch MIDI messages to a device, use the function miditouch
.
Aftertouch has a range from 0
to 127
, and default value is 0
.
In this example, once a note is playing, we set aftertouch from 0
to 64
and backwards several times in a cycle, as if we were pressing the C key of a MIDI controller.
Modulation wheel
There isn’t a specific function to send modulation wheel messages, but CC 1
is used for modulation wheel, so we can use that:
Here, we start the cycle with mod wheel set to 0
, go up to maximum, then down to the minimum, and end the cycle at 0
again.
Program change
Program change messages can be sent with the progNum
function.
If you called you device mydevice
, and it’s receiving program change messages through MIDI channel 14
, you can change it’s program/pattern by issuing a command like this:
NRPN parameters
To send NRPN parameters, use functions nrpnn
and nrpnv
.
NRPN numbers are composed of two bytes: MSB (Most Significant Byte) and LSB (Less Significant Byte). When using nrpnn
, you write those two bytes in a single number. To calculate the number you have to use, multiply the first byte (MSB) by 128
, and then add the second byte (LSB). For example, if your device manual says that reverb send is set by NRPN MSP 2 and NRPN LSB 6 (or 2:6), you need to give nrpnn
a 2*128+6=262
.
NRPN values also have two bytes, which allows for more precision than CC messages. The valid rang for nrpnv
is from 0
to 16384
. However, note that many devices will just ignore this extra precision.
nrpnn
and nrpnv
are patternable, but notice that their argument is of type Pattern Int
, so you have to make sure to pass them Int
s and not Double
s:
sine
is giving us Double
s, so we need to convert them to Int
s before feeding them to nrpnv
. We do this by using floor
, which is a Haskell function that rounds down a number, and <$>
which applies a function (in this case floor
) to all the elements in a collection.
There is also the function nrpn
, which allows us to pass both the number and the value in a single string:
Tidal-Midi
The older tidal-midi
Haskell module is not currently working (although it might return). Use the other existing solutions.
Synchronising MIDI clock
It is often important to send MIDI clock events to synchronize tempo between devices. Tidal can’t sync its tempo to MIDI clock events that it receives, but it can act as a MIDI clock source. The following sections show two alternatives for sending MIDI clock events that follow the tempo of Tidal.
Synchronising MIDI clock using the Link protocol
Since version 1.9, Tidal uses the Link protocol for scheduling events. Link is a technology that synchronizes musical beat, tempo, and phase across multiple applications. It was originally developed by a company called Ableton, but is open source and now implemented in a wide range of music software. We can use Link to synchronize Tidal with a separate program that will act as the MIDI clock source.
This is the preferred method for sending MIDI clock events as it is easy, performant, stable, and has fewer quirks than Synchronising MIDI clock via Tidal.
Ableton Live as the MIDI clock source
Ableton Live can synchronize with Tidal over Link and simultaneously send MIDI clock messages.
To achieve this, follow both instructions:
- Turn on Link sync in Ableton Live. See Synchronizing via Link.
- Turn the MIDI device on as a sync destination in Live’s Link/Tempo/MIDI Preferences. See Synchronizing External MIDI Devices to Live.
SuperCollider as the MIDI clock source
We can use Link to synchronize Tidal with SuperCollider and set up SuperCollider to send MIDI clock events. This method was inspired by jamshark70’s thread. This requires extending SuperCollider with a new class LinkToMidiClock
.
First decide if the SuperCollider class should be available only to your user account or to all users on the machine. Then find the corresponding extensions directory by running one of these lines in SuperCollider:
Create a file LinkToMidiClock.sc
in the selected extensions directory and save it with this content:
Reboot SuperCollider or use Language > Recompile Class Library
.
We are now ready to follow the initialization guide. We will use the MIDI device variable named ~midiOut
from the initialization in the examples below.
After the MIDI device is initialized, create a LinkClock in SuperCollider.
You can check that Tidal and SuperCollider have connected over Link by checking the number of Link peers:
Then, create a LinkToMidiClock
that is connected to the MIDI device ~midiOut
and the LinkClock
~lc
.
MIDI clock events will be sent continously after we tell it to start, until we tell it to stop.
Note: If SuperCollider and Tidal don’t connect over Link, try starting Tidal before the LinkClock is created, but after SuperDirt is started. Alternatively, try creating the LinkClock before starting Tidal. This has anecdotally worked in some cases. Please report your findings in the TidalCycles version 1.9.0 nnouncement thread.
For more details on Tidal’s integration with Link, see Multi-User Tidal.
Synchronising MIDI clock via Tidal
We can alternatively use Tidal and SuperDirt MIDI for sending MIDI clock events. The advantage is that it also works in older versions of Tidal, but the method is somewhat more complicated.
Set up SuperDirt MIDI by following the initialization guide.
When that is done, you can start sending MIDI clock messages, 48 per cycle, like this:
Your MIDI device should adjust its BPM to Tidal’s cps. It’s then a good idea to send a stop
message like this:
and then finally a start message to start the MIDI clock at the right time. The following sends a start message every fourth cycle:
Once everything’s started and in sync, it’s probably best to stop sending the start messages to avoid glitching:
However now if you do hush, the midiclock
will stop as well as all the other patterns. To avoid this, you can overwrite the hush function with a version that silences particular patterns:
You will probably find that the downbeats for SuperDirt and your MIDI devices don’t align. As a starting point, set MIDI latency in supercollider to 0:
Make sure any offset on the MIDI side is also set to 0, then gradually adjust one of them until they align. If they stay in alignment when you change the cps, all is good!
Controller Input
Tidal 1.0.0 now has support for external input, using the OSC protocol. Here’s a quick guide to getting it going, including using a simple ‘bridge’ for getting MIDI input working.
Setup
To use MIDI, you don’t have to worry too much about mapping OSC. However, you do have to run something to convert MIDI into OSC (Tidal is listening for OSC messages). Here’s how to do that using SuperCollider. First, with Tidal and SuperDirt already running, run the below code block in SuperCollider:
Usage
You should then be able to run a pattern such as the following, that uses CC value 12
:
If you want to use MIDI in a pattern forming statement, you may find it helpful to segment
the input first, as the raw pattern coming from your MIDI device will be at very high resolution. This example takes only one value per cycle & remaps the value with the range
function:
Renaming MIDI notes
In case you have a MIDI drum machine, where the bassdrum is on MIDI note 231 and you don’t want to write 231
every time, you could either do this:
Another approach is using inhabit
, you pass it a list of names and patterns, like this:
You could also hide the midi channel in there so you don’t have to type it each time
Note that the 232
bit is a pattern, so you could have one name trigger more than one event e.g.
Alternative with Pure Data
The above SuperCollider instructions are most convenient if you’re using SuperDirt, but as an alternative you can use Pure Data to convert midi to OSC. You can get Pure Data from here (the vanilla
version is recommended). Then, download this file. Then if you start Tidal, open that file in Pure Data, and configure your MIDI device in Pure Data, things should start working.