Using "Let" to give midi note numbers names (probably really obvious!)

I'm just getting started with sending midi, initially to a TR-8 drum machine and a micro-brute synth.
The drum machine uses specific midi notes for each drum sound. I'd like to be able to type the drum name rather than the number, so I can program patterns easier and see what's happening.

At the moment, for example, sending 4 kick drums looks like this

d1 $ n "231*4" # sound "tr8" # midichan 9

What I'd like to be able to do is this:

let BD = 231

d1 $ n "BD*4" # sound "tr8 # midichan 9

... and get the same result.
But not sure how to execute the "let" bit, or why it's not allowing me to do it.
I suspect I'm missing somehting obvious!
Any help gratefully accepted.

1 Like

It's not obvious, but Haskell can't so easily peek inside the mininotation (the stuff in quotes) to see "variables" there. But you can write a substitution function yourself

s2n :: String -> Note
s2n "BD" = 231
s2n _ = 0

d1 $ n (s2n <$> "BD*4") # sound "tr8" # midichan 9

Another approach is using inhabit, you pass it a list of names and patterns, like this:

let drum pat = sound (inhabit [("bd", "231"), ("sd", "232")] pat)

d1 $ drum "bd sd" # midichan 9

You could also hide the midi channel in there so you don't have to type it each time

let drum pat = sound (inhabit [("bd", "231"), ("sd", "232")] pat) # midichan 9

d1 $ drum "bd sd"
d2 $ drum "bd*3 sd*2"

Note that the "232" bit is a pattern, so you could have one name trigger more than one event e.g.

let drum pat = sound (inhabit [("bd", "231"), ("rush", "232*8"), ("sd", "232")] pat) # midichan 9

d1 $ drum "bd sd rush"

Thanks both, that's really helpful.

Could this be extended to allow variant sounds, like it's done with samples -- bd producing bd:0, but also allowing bd:1 mapped to a different note, and so forth? This would be great for drum machines that offer different sounds per "instrument"; more generally, putting together arbitrary groups of sounds and selecting from them with patterns should be fun.

Described the use case in this comment: Writing addon library for MIDI instruments - #10 by johanw. (Learning some Haskell would make sense, but until then..)

Would inhabit be a reasonable way to write such a lookup? The point that a name could "trigger more than one event" is intriguing.

Yes you could do this sort of thing:

let drum pat = inhabit [("bd", sound "kick:1"), ("sd", sound "snare" # n 3 # squiz 1.2)] pat

This works great, and clearly has lots of uses, many thanks!

However, I'm looking for something slightly different .. namely, for a way to select MIDI notes in the same way that n "1 10 3 4" # s "bd hh" plays samples 2 and 11 from a folder named "bd", then 4 and 5 from folder "hh".

Could bd and hh here be redefined to pick from lists of MIDI notes, instead of lists of samples -- allowing numbers to be passed in? Maybe using (abusing?) scale would work, I'll try that :slight_smile:

(If this could be combined with inhabit to put a pattern on each position, even better.)

1 Like

@johanw curious, what is your current setup for midi and sound selection?

Thanks for asking :smiley: I have been experimenting with these, and all work pretty well: the Elektron Analog Four synth, using "multimap" to have 128 (drum) sounds available over four channels; with the Groove Agent drum machine that comes with Cubase; and with the Frau Angelico drum machine from Standuino (now Bastl Instruments). All over MIDI.

It's the Analog Four that is the most fun and challenging of these, since there is a lot of sounds to choose from.

(Edit: on a Mac, and using Cubase to monitor and record both midi and audio. A "Blackhole" audio device receives sound from SuperCollider. Eedit: sync doesn't work, but so far it's been OK to align to the timeline after recording.)

1 Like

cool. yeah, i'm using ableton and i would love to eventually get to that same kind of 128-multimap level with my setup, having just a ton of samples preloaded and use patterns to select them. right now, i'm using combinations of stacked instrument groups or drum racks and then scanning through them by assigning them to macros. it's okay but it seems like a workaround to something more powerful or intuitive.

That sounds a lot more sophisticated than where I'm at so far. But here's a little discovery. With toScale here, the following introduces "ht" as a scale and plays through five different "hihat tip" sounds in Groove Agent:

let scale = getScale (scaleTable ++ [ ("ht", (map (subtract 60) [76,77,79,81,83])) ])

d1 $ n (scale "ht" "0 [2 1] ~ 3") # s "midi"

The midi notes for the sounds are 76, 77, 79, 81, 83.

It's nice that it works, but it feels kinda wrong.


Here's more convenient notation. For Groove Agent, but I guess should work with any midi instrument.

  1. Make "scales" out of hihat tip, hihat shank, toms (ht, hs, td); each has five sounds available (well, only four toms, the fifth is a "sidestick").
let scale = getScale (scaleTable ++ [ ("ht", (map (subtract 60) [76,77,79,81,83])),
                                      ("hs", (map (subtract 60) [64,65,67,69,71])),
                                      ("td", (map (subtract 60) [48,45,43,41,37])) ])
  1. shorthand gaht = Groove Agent hihat tip, etc. May add midi channels here.
let gaht x = n (scale "ht" x) # s "midi"
let gahs x = n (scale "hs" x) # s "midi"
let gatd x = n (scale "td" x) # s "midi"
  1. use
d1 $ fast 1.5 $ stack [
  gaht "~ [~ 2] ~ 2",
  gahs "2 ~ [1 0] ~",
  gatd "[4] ~ 3 3"

nice! i will have to give this a shot. i'll report back :wink:

When I try this code with Tidal 1.8.0 and supercollider 3.12.2 it doesn't work. No midi notes outputted.
Is it normal or am I missing something?

I haven't yet upgraded from 1.7.1, so can't really say -- sure hope it will still work.