22
33NotoOutput {
44 var <port, <sema, <nAnon;
5- *new { | deviceName=nil , portName=nil , anonInstruments=8 |
5+ *new { | deviceName=nil , portName=nil , anonInstruments=32 |
66 ^super.new.init(deviceName, portName, anonInstruments)
77 }
88
@@ -13,8 +13,8 @@ NotoOutput {
1313 deviceName = deviceName?"IAC Driver" ;
1414 portName = portName?"Bus 1" ;
1515 },
16- \linux , { "Notochord: TODO: default MIDI device on Linux" .postln },
17- \windows , { "Notochord: TODO: default MIDI device on Windows" .postln }
16+ \linux , { "Notochord: TODO: default MIDI output device on Linux" .postln },
17+ \windows , { "Notochord: TODO: default MIDI output device on Windows" .postln }
1818 );
1919 nAnon = anonInstruments;
2020 port = MIDIOut .newByName(deviceName, portName).latency_(0 );
@@ -25,10 +25,37 @@ NotoOutput {
2525 128 .do{arg note; 16 .do{arg chan;
2626 port.noteOff(chan, note, vel)}}
2727 }
28+
29+ isDrum { | inst |
30+ inst;
31+ ^ ((inst>128 )&&(inst<=256 )) || (inst>(256 +nAnon))
32+ }
2833}
2934
30- NotoDAWOutput : NotoOutput {
31- //TODO
35+ NotoMappingOutput : NotoOutput {
36+ var <>instrumentMap, <>drumMap;
37+
38+ init { | deviceName, portName, anonInstruments |
39+ super.init(deviceName, portName, anonInstruments);
40+ instrumentMap = Dictionary .new;
41+ drumMap = Dictionary .new;
42+ }
43+
44+ send { | inst, pitch, vel |
45+ var channel;
46+ channel = instrumentMap.at(inst);
47+ (isDrum(inst) && drumMap.includesKey(pitch)).if{
48+ pitch = drumMap.at(pitch)};
49+ channel.isNil.if{
50+ "WARNING: unmapped instrument in NotoDAWOutput.send" .postln; ^nil };
51+ // pitch.isNil.if{
52+ // "WARNING: unmapped drum in NotoDAWOutput.send".postln; ^nil};
53+ (vel>0 ).if{
54+ port.noteOn(channel, pitch, vel);
55+ }{
56+ port.noteOff(channel, pitch);
57+ };
58+ }
3259}
3360
3461NotoFluidOutput : NotoOutput {
@@ -42,15 +69,9 @@ NotoFluidOutput : NotoOutput {
4269 instChannels = TwoWayIdentityDictionary .new;
4370 }
4471
45- isDrum { | inst |
46- inst;
47- ^ ((inst>128 )&&(inst<=256 )) || (inst>(256 +nAnon))
48- }
49-
5072 send { | inst, pitch, vel |
5173 var channel;
5274 sema.wait;
53- inst;
5475 // check if this instrument has a channel
5576 channel = instChannels.at(inst);
5677 channel.isNil.if{
@@ -99,27 +120,44 @@ NotoFluidOutput : NotoOutput {
99120
100121// MIDI input API
101122NotoInput {
102- var <device ;
123+ var <deviceUID ;
103124
104- *new { | deviceName |
105- ^super.new.init(deviceName)
125+ *new { | deviceName, portName |
126+ ^super.new.init(deviceName, portName )
106127 }
107128
108- init { | deviceName |
129+ init { | deviceName, portName |
130+ var device;
109131 MIDIClient .initialized.not.if{MIDIClient .init};
110132 MIDIIn .connectAll;
111133
134+ Platform .case(
135+ \osx , {
136+ deviceName = deviceName?"IAC Driver" ;
137+ },
138+ \linux , { "Notochord: TODO: default MIDI input device on Linux" .postln },
139+ \windows , { "Notochord: TODO: default MIDI input device on Windows" .postln }
140+ );
141+
112142 device = MIDIClient .sources.detect{
113- |e| e.device.containsi(deviceName)
114- }.uid;
143+ |e| e.device.containsi(deviceName) && portName.isNil.if{true }{e.device.containsi(portName)}
144+ };
145+ device.isNil.if{
146+ "WARNING: NotoInput: MIDI input device not found" .postln;
147+ ("available sources are: " ++MIDIClient .sources).postln;
148+ deviceUID=0 ;
149+ }{
150+ ("NotoInput: using MIDI input device \" " ++deviceName++"\" " ).postln;
151+ deviceUID=device.uid;
152+ }
115153 }
116154
117155 noteOn { |fn|
118- MIDIdef .noteOn(\input_on ++device , fn, srcID: device );
156+ MIDIdef .noteOn(\input_on ++deviceUID , fn, srcID: deviceUID).permanent_( true );
119157 }
120158
121159 noteOff { |fn|
122- MIDIdef .noteOff(\input_off ++device , fn, srcID: device );
160+ MIDIdef .noteOff(\input_off ++deviceUID , fn, srcID: deviceUID).permanent_( true );
123161 }
124162}
125163
@@ -149,7 +187,7 @@ Notochord {
149187 pendingQueries = 0 ;
150188 };
151189
152- }, "notochord/query_return" );
190+ }, "notochord/query_return" ).permanent_( true ) ;
153191
154192 // initial handler which just prints the argument dict
155193 handler = _ .postln;
@@ -162,7 +200,8 @@ Notochord {
162200 argKeys = [
163201 \allow_end ,
164202 \min_time , \max_time , \min_vel , \max_vel ,
165- \include_instrument , \exclude_instrument ,
203+ \include_inst , \exclude_inst ,
204+ \allow_anon ,
166205 \include_pitch , \exclude_pitch , \include_drum ,
167206 \instrument_temp , \pitch_temp ,
168207 \rhythm_temp , \timing_temp , \velocity_temp ,
@@ -251,8 +290,8 @@ Notochord {
251290// C. forecasted events which may not play
252291// A requires: schedule, once locked in: feed, finally play
253292// B requires: schedule, once locked in: query_feed, finally play
254- // C requires: schedule, if locked in: feed/lay (treat as A),
255- // -- if anything is scheduled between this event and the source event , cancel
293+ // C requires: schedule, once locked in: as A
294+ // -- but if anything else gets scheduled before it , cancel
256295
257296// alternate view:
258297//
@@ -276,16 +315,4 @@ Notochord {
276315// this would feed an event and then query *only* delta to next event
277316// a separate query would complete the event just-in-time, *only* if it isn't
278317// first pre-empted, which prevents wasting time on other attributes
279- // gets hairy when there are other constraints though
280-
281-
282- /*(
283- ~noto = Notochord();
284- ~input = NotoInput();
285- ~output = NotoOutput();
286-
287-
288- ~input.callback = {};
289- ~noto.callback = {};
290-
291- )*/
318+ // gets hairy when there are other constraints though
0 commit comments