Skip to content
This repository was archived by the owner on Nov 23, 2023. It is now read-only.

Commit d4cc42b

Browse files
include_drum argument to notochord (refactor get_samplers into query to support this); more tidal examples and custom MIDI target
1 parent efad23d commit d4cc42b

5 files changed

Lines changed: 441 additions & 168 deletions

File tree

Lines changed: 119 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,134 @@
1-
-- Notochord feed and query demo patterns
1+
-- Notochord feed demo patterns
22
:set
33

4-
-- notochord chooses the pitch,
5-
-- anchored by the mallet at MIDI pitch 48
6-
p "choosepitch"
7-
$ ncinst "7 [9*3 [17 25]]"
8-
# ncpitch "-1 [48 -1]"
9-
# ncvel (range 30 120 $ sine)
10-
# cps 1
11-
# legato 4
4+
hush
5+
6+
do
7+
once $ ncreset "1"
8+
p "choosepitch"
9+
$ ncinst " 8 [ 13*3 [17 27 ]]"
10+
# ncpitch "-1 [[60 -1 60] -1 ]"
11+
# ncvel (range 120 70 $ sine)
12+
# cps 1
13+
# legato 3.99
14+
15+
do
16+
once $ ncreset "1"
17+
-- notochord chooses the pitch,
18+
-- anchored by the mallet at MIDI pitch 60
19+
-- instrument is patterned: clavinet, marimba, organ, jazz guitar
20+
p "choosepitch"
21+
$ ncinst " 8 [ 13*3 [17 27]]"
22+
# ncpitch "-1 [[60 -1 60] -1 ]"
23+
# ncvel (range 120 70 $ sine)
24+
# cps 1
25+
# legato 3.99
26+
27+
do
28+
once $ ncreset "1"
29+
-- notochord chooses pitch and velocity
30+
-- celesta / music box
31+
p "chooseperf"
32+
$ ncinst (slow 1 "9 [9*3 [11 11]]")
33+
# legato 0.99
34+
# cps 1
35+
-- # cps "<1 2 0.3333 0.6667>"
1236

1337
hush
1438

15-
-- notochord chooses the instrumentation
16-
p "chooseinst"
17-
$ ncinst "-1"
18-
<| ncpitch (run 8 + 48)
19-
# ncvel (range 30 120 $ sine)
20-
# cps 1
21-
# legato 0.5
39+
-- idea: pitchwithin, which converts ncpitch to include_pitch
40+
do
41+
once $ ncreset "1"
42+
-- notochord chooses the instrumentation
43+
p "chooseinst"
44+
$ ncinst "-1"
45+
<| ncpitch (slow 4 (run 32 + 36))
46+
# ncvel (range 30 99 $ slow 4 saw)
47+
# cps 1
48+
# legato 0.99
2249

2350
-- drums; notochord chooses the velocities
2451
p "choosevel"
2552
$ ncinst "129"
26-
<| ncpitch "[36*4, 56*8, [~ 38]*2]"
53+
<| ncpitch "[35*4, 44*8, [~ 38]*2]"
54+
# cps 1
55+
# legato 0.99
56+
57+
hush
58+
59+
p "choosevel"
60+
$ ncinst "233"
61+
<| ncpitch "[[35 ~ 35 35], 44*4, [~ 38]*2]"
2762
# cps 1
28-
# legato 1
63+
# legato 0.99
64+
65+
66+
hush
67+
68+
do
69+
once $ ncreset "1"
70+
-- 909; notochord chooses the velocities and drums (except kick)
71+
p "choosedrum"
72+
$ ncinst "155"
73+
<| stack [ncpitch "35*4" # ncvel "100", ncpitch "[-1*8, [~ -1]*2]" # ncvel "-1" # nudge "0.003"]
74+
# cps 1
75+
# legato 0.99
76+
77+
do
78+
once $ ncreset "1"
79+
-- jazz drums; notochord chooses pitch and velocity
80+
p "choosedrum"
81+
$ ncinst "234"
82+
<| ncpitch "[-1]*12"
83+
# cps 1
84+
# legato 2.99
85+
# nudge "0.003"
86+
87+
hush
88+
89+
-- sound effects kit
90+
p "sfx"
91+
$ ncinst "185"
92+
<| ncpitch "[[~ -1]*4]"
93+
# cps 1
94+
# legato 1.99
95+
# nudge "0.003"
96+
97+
do
98+
once $ ncreset "1"
99+
-- velocity ramp; notochord chooses the instrumentation + pitch
100+
p "dynamicsonly"
101+
$ ncinst "[-1]*8"
102+
# ncvel (range 1 127 $ slow 2 saw)
103+
# cps 1
104+
# legato 3.99
29105

30106
hush
31107

108+
-- reset hidden state of notochord
32109
once $ ncreset "1"
33110

34-
-- p "dirt" $ s "supervibe"
111+
-- timbres of heaven drums:
112+
-- 129 = Don's Std Kit
113+
-- 130 = Std Kit 2
114+
-- 137 = Room Drum Kit
115+
-- 145 = Power Drums
116+
-- 153 = Electronic Kit
117+
-- 154 = TR-808 Elec. Kit
118+
-- 155 = TR-909 Elec. Kit
119+
-- 156 = Dance Kit
120+
-- 161 = Jazz Drum Kit
121+
-- 169 = Brush Kit
122+
-- 177 = Orchestra Kit
123+
-- 179 = Symphony Kit
124+
-- 185 = GM2 SFX Kit
125+
-- 228 = PSR-520 Std kit
126+
-- 229 = Don's XG Std Kit
127+
-- 230 = XG Room Kit
128+
-- 231 = XG Rock Kit
129+
-- 232 = XG Electronic Kit
130+
-- 233 = XG Analog Kit
131+
-- 234 = XG Jazz Kit
132+
-- 235 = XG Brush Kit
133+
-- 236 = XG Symphony Kit
134+
-- 256 = CM-64/32L (SC-55)

examples/notochord/tidalcycles/tidal-notochord.scd

Lines changed: 143 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,137 @@ thisProcess.openUDPPort(6789); // from tidal
33
~python = NetAddr.new("127.0.0.1", 9999); // to python
44
)
55

6-
// (
7-
// s.waitForBoot{
8-
// ~dirt = SuperDirt.new;
9-
// ~dirt.loadSoundFiles("~/Music/sample/dirt/*");
10-
// ~dirt.start(57120);
11-
// };
12-
// )
13-
//
14-
// ~dirt.soundLibrary.getEvent('matrix_bass', 1)
15-
16-
17-
// for sending MIDI to ableton
6+
// notochord uses General MIDI (https://en.wikipedia.org/wiki/General_MIDI)
7+
// instrument and drum mappings.
8+
// instrument 1-128 are the GM melodic instruments, 129-256 are drumkits
9+
10+
// Option 1: map up to 16 notochord instruments to specific MIDI channels:
11+
(
12+
~m1 = MIDIOut.newByName("IAC Driver", "Bus 1").latency_(0);
13+
// this will automatically restrict the instruments notochord can sample
14+
// to the keys of ~midi_map
15+
// (but make sure not to use any other instruments in tidal either)
16+
~midi_map = (
17+
// notochord instrument (1-256): midi channel (1-16)
18+
1: 1, //grand piano -> channel 1
19+
13: 2, //marimba -> channel 2
20+
// 19: 3, //rock organ -> channel 3
21+
// 26: 4, //acoustic guitar (steel) -> channel 4
22+
// 39: 5, //synth bass 1 -> channel 5
23+
// 43: 6, //cello -> channel 6
24+
// 47: 7, //harp -> channel 7
25+
// 54: 8, //voice oohs -> channel 8
26+
// 57: 9, //trumpet -> channel 9
27+
129:10, //standard drumkit -> channel 10 (drums)
28+
// 67:11, //tenor sax -> channel 11
29+
// 76:12, //pan flute -> channel 12
30+
// 82:13, //saw lead -> channel 13
31+
// 90:14, //warm pad -> channel 14
32+
// 109:15, //kalimba -> channel 15
33+
// 119:16, //synth drum -> channel 16
34+
);
35+
// this will automatically restrict the pitches notochord can sample
36+
// when using a drum instrument to the keys of ~drum_map
37+
// (but make sure not to use any other drums in tidal either)
38+
// this example is based on the default 808 rack in Ableton
39+
~drum_map = (
40+
//general MIDI number: desired MIDI number
41+
36:36, //electric bass drum -> C1
42+
37:37, //side stick -> C#1
43+
40:38, //electric snare -> D1
44+
39:39, //hand clap -> D#1
45+
75:40, //clave -> E1
46+
45:41, //low tom -> F1
47+
42:42, //closed hat-> F#1
48+
47:43, //mid tom -> G1
49+
70:44, //maracas -> G#1
50+
50:45, //hi tom -> A1
51+
46:46, //open hat -> A#1
52+
64:47, //low conga -> B1
53+
63:48, //mid conga -> C2
54+
51:49, //ride cymbal 1 -> C#2
55+
62:50, //hi conga -> D2
56+
56:51, //cowbell -> D#2
57+
);
58+
~release_all = {arg vel=0;
59+
[~m1].do{arg port; 128.do{arg note; 16.do{arg chan;
60+
port.noteOff(chan, note, vel)}}}
61+
};
62+
~midi_send = {arg inst, pitch, vel;
63+
var channel = ~midi_map[inst]-1;
64+
~is_drum.(inst).if{pitch = ~drum_map[pitch]};
65+
channel.isNil.if{"nil channel in mid_send"};
66+
pitch.isNil.if{"nil pitch in mid_send"};
67+
(vel>0).if{
68+
~m1.noteOn(channel, pitch, vel);
69+
}{
70+
~m1.noteOff(channel, pitch);
71+
};
72+
};
73+
)
74+
75+
// Option 2: send MIDI to fluidsynth
76+
// example fluidsynth terminal commands:
77+
// fluidsynth -a coreaudio -m coremidi -v -o midi.autoconnect=1 -o synth.midi-bank-select=mma ~/Downloads/FluidR3_GM_GS.sf2
78+
// fluidsynth -a coreaudio -m coremidi -v -o midi.autoconnect=1 -o synth.midi-bank-select=xg "/Users/victor/Downloads/Timbres of Heaven (XGM) 4.00(G)/Timbres of Heaven (XGM) 4.00(G).sf2"
79+
(
80+
~drum_map = nil;
81+
~midi_map = nil;
82+
~bank_xg = true;
83+
~channel_lru = LinkedList.fill(16, {arg i; i});
84+
~inst_channels = TwoWayIdentityDictionary.new;
85+
~sema = Semaphore(1);
86+
87+
MIDIClient.init;
88+
~m1 = MIDIOut.newByName("IAC Driver", "Bus 1").latency_(0);
89+
90+
~release_all = {arg vel=0;
91+
[~m1].do{arg port; 128.do{arg note; 16.do{arg chan;
92+
port.noteOff(chan, note, vel)}}}
93+
};
94+
~midi_send = {arg inst, pitch, vel;
95+
var channel;
96+
~sema.wait;
97+
// check if this instrument has a channel
98+
channel = ~inst_channels.at(inst);
99+
channel.isNil.if{
100+
// if not get least recently used channel
101+
channel = ~channel_lru.popFirst;
102+
// and change the bank+program
103+
~is_drum.(inst).if{
104+
~m1.control(channel, 0, ~bank_xg.if{120}{1}); //drum
105+
~m1.control(channel, 32, 0);
106+
~m1.program(channel, inst-129); //program
107+
}{
108+
~m1.control(channel, 0, 0); //melodic
109+
~m1.control(channel, 32, 0);
110+
~m1.program(channel, inst-1); //program
111+
};
112+
// TODO: anonymous instruments?
113+
114+
}{
115+
~channel_lru.remove(channel)
116+
};
117+
// send the event
118+
(vel>0).if{
119+
~m1.noteOn(channel, pitch, vel);
120+
}{
121+
~m1.noteOff(channel, pitch);
122+
};
123+
// update the inst / channel mappings
124+
~channel_lru.add(channel);
125+
~inst_channels.remove(channel);
126+
~inst_channels[inst] = channel;
127+
~sema.signal;
128+
};
129+
)
130+
131+
// Option 3: send MIDI to GM.als
132+
// this is an Ableton set which maps general midi onto ~30 synths
133+
// ask victor for the set. requires Live 9 suite or higher
18134
(
135+
~drum_map = nil;
136+
~midi_map = nil;
19137
MIDIClient.init;
20138
// MIDIClient.destinations
21139
~m1 = MIDIOut.newByName("IAC Driver", "Bus 1").latency_(0);
@@ -86,57 +204,6 @@ MIDIClient.init;
86204
};
87205
)
88206

89-
// for sending MIDI to fluidsynth
90-
// fluidsynth -a coreaudio -m coremidi -v -o midi.autoconnect=1 -o synth.midi-bank-select=mma ~/Downloads/FluidR3_GM_GS.sf2
91-
(
92-
~channel_lru = LinkedList.fill(16, {arg i; i});
93-
~inst_channels = TwoWayIdentityDictionary.new;
94-
~sema = Semaphore(1);
95-
96-
MIDIClient.init;
97-
~m1 = MIDIOut.newByName("IAC Driver", "Bus 1").latency_(0);
98-
99-
~release_all = {arg vel=0;
100-
[~m1].do{arg port; 128.do{arg note; 16.do{arg chan;
101-
port.noteOff(chan, note, vel)}}}
102-
};
103-
~midi_send = {arg inst, pitch, vel;
104-
var channel;
105-
~sema.wait;
106-
// check if this instrument has a channel
107-
channel = ~inst_channels.at(inst);
108-
channel.isNil.if{
109-
// if not get least recently used channel
110-
channel = ~channel_lru.popFirst;
111-
// and change the bank+program
112-
((inst>128)&&(inst<=256))||(inst>=265).if{
113-
~m1.control(channel, 0, 1); //drum
114-
~m1.control(channel, 32, 0);
115-
~m1.program(channel, inst-128); //program
116-
}{
117-
~m1.control(channel, 0, 0); //melodic
118-
~m1.control(channel, 32, 0);
119-
~m1.program(channel, inst-1); //program
120-
};
121-
// TODO: anonymous instruments?
122-
123-
}{
124-
~channel_lru.remove(channel)
125-
};
126-
// send the event
127-
(vel>0).if{
128-
~m1.noteOn(channel, pitch, vel);
129-
}{
130-
~m1.noteOff(channel, pitch);
131-
};
132-
// update the inst / channel mappings
133-
~channel_lru.add(channel);
134-
~inst_channels.remove(channel);
135-
~inst_channels[inst] = channel;
136-
~sema.signal;
137-
};
138-
)
139-
140207
(
141208
~oLatency = 0.1; // should match value at tidal boot
142209
~ncLatency = ~oLatency-0.02; // slightly shorter than oLatency
@@ -145,6 +212,11 @@ MIDIClient.init;
145212
~events = Dictionary.new;
146213
~event_idx = 0;
147214

215+
// check if an instrument is a drumkit
216+
~is_drum = {arg inst;
217+
((inst>128)&&(inst<=256))||(inst>=265)
218+
};
219+
148220
// handle OSC input from tidal
149221
OSCdef(\from_tidal, {
150222
arg msg, time, src;
@@ -271,6 +343,14 @@ OSCdef(\from_tidal, {
271343
query.add(\min_vel); query.add(1)
272344
};
273345
query.add(\fix_time); query.add(event[\time]);
346+
~midi_map.notNil.if{
347+
query.add(\include_instrument);
348+
query.add("%JSON:"++JSON.stringify(~midi_map.keys.asList))
349+
};
350+
~drum_map.notNil.if{
351+
query.add(\include_drum);
352+
query.add("%JSON:"++JSON.stringify(~drum_map.keys.asList))
353+
};
274354
query
275355
};
276356

notochord/notochord/data.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def __init__(self, data_dir, batch_len, transpose=5, speed=0.1, glob='**/*.pkl',
2121
self.prog_start_token = 0
2222
# self.clamp_time = clamp_time
2323
self.testing = False
24-
self.max_test_len = 10000
24+
self.max_test_len = 2048
2525

2626
def __len__(self):
2727
return len(self.files)

0 commit comments

Comments
 (0)