Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / SCClassLibrary / Common / Control / MIDIResponder.sc
blob49cfb0d22fe0e3c6a56ace0ca731e145af457c78
1 // see MIDIResponder help for all classes on this page
4 MIDIResponder {
5         var     <>function,<>swallowEvent=false,
6                 <>matchEvent;           // for matching ports, channels, and parameters
7         init { arg install;
8                 if(this.class.initialized.not,{ this.class.init });
9                 matchEvent.port = matchEvent.port.asMIDIInPortUID;
10                 if(install,{this.class.add(this);});
11         }
12         respond { arg src,chan,num,value;
13                 if(this.match(src,chan,num,value),{
14                         this.value(src,chan,num,value)
15                         ^swallowEvent
16                 });
17                 ^false;
18         }
19         match { arg src,chan,num,value;
20                 ^matchEvent.match(src,chan,num,value);
21         }
22         value { arg src,chan,a,b;
23                 function.value(src, chan, a, b)
24         }
26         remove {
27                 this.class.remove(this)
28         }
29         *removeAll {
30                 if(this == MIDIResponder,{
31                         this.allSubclasses.do({ |responderClass| responderClass.removeAll })
32                 },{
33                         this.init
34                 })
35         }
40 NoteOnResponder : MIDIResponder {
41         classvar <norinit = false,<nonr;
43         *new { arg function, src, chan, num, veloc, install=true,swallowEvent=false;
44                 ^super.new.function_(function)
45                         .matchEvent_(MIDIEvent(nil, src, chan, num, veloc))
46                         .swallowEvent_(swallowEvent)
47                         .init(install)
48         }
49         *initialized { ^norinit }
50         *responders { ^nonr }
51         *init {
52                 if(MIDIClient.initialized.not,{ MIDIIn.connectAll });
53                 if(norinit.not) {
54                         MIDIIn.addFuncTo(\noteOn, { arg src, chan, note, veloc;
55                                 nonr.any({ arg r;
56                                         r.respond(src,chan,note,veloc)
57                                 });
58                         })
59                 };
60                 norinit = true;
61                 nonr = [];
62         }
63         *add { arg resp;
64                 nonr = nonr.add(resp);
65         }
66         *remove { arg resp;
67                 nonr.remove(resp);
68         }
69         learn {
70                 var oneShot;
71                 oneShot = this.class.new({ |src,chan,num,value|
72                                         this.matchEvent_(MIDIEvent(nil,src,chan,nil,nil));
73                                         oneShot.remove;
74                                 },nil,nil,nil,nil,true,true)
75         }
78 NoteOffResponder : NoteOnResponder {
79         classvar <noffinit = false,<noffr;
81         *init {
82                 if(MIDIClient.initialized.not,{ MIDIIn.connectAll });
83                 if(noffinit.not) {
84                         MIDIIn.addFuncTo(\noteOff, { arg src, chan, note, veloc;
85                                 noffr.any({ arg r;
86                                         r.respond(src,chan,note,veloc)
87                                 });
88                         })
89                 };
90                 noffinit = true;
91                 noffr = [];
92         }
93         *initialized { ^noffinit }
94         *responders { ^noffr }
96         *add { arg resp;
97                 noffr = noffr.add(resp);
98         }
99         *remove { arg resp;
100                 noffr.remove(resp);
101         }
104 CCResponder : MIDIResponder {
105         classvar <ccinit = false,<ccr,<ccnumr;
107         *new { arg function, src, chan, num, value, install=true,swallowEvent=false;
108                 ^super.new.function_(function).swallowEvent_(swallowEvent)
109                         .matchEvent_(MIDIEvent(nil, src, chan, num, value))
110                         .init(install)
111         }
112         *initialized { ^ccinit }
113         *responders { ^ccnumr.select(_.notNil).flat ++ ccr }
114         *add { arg resp;
115                 var temp;
116                 if(this.initialized.not,{ this.init });
117                 if((temp = resp.matchEvent.ctlnum).isNumber) {
118                         ccnumr[temp] = ccnumr[temp].add(resp);
119                 } {
120                         ccr = ccr.add(resp);
121                 };
122         }
123         *remove { arg resp;
124                 var temp;
125                 if((temp = resp.matchEvent.ctlnum).isNumber) {
126                         ccnumr[temp].remove(resp)
127                 } {
128                         ccr.remove(resp);
129                 };
130         }
131         *init {
132                 if(MIDIClient.initialized.not,{ MIDIIn.connectAll });
133                 if(ccinit.not) {
134                         MIDIIn.addFuncTo(\control, { arg src,chan,num,val;
135                                 // first try cc num specific
136                                 // then try non-specific (matches any cc )
137                                 [ccnumr[num], ccr].any({ |stack|
138                                         stack.notNil and: {stack.any({ |r| r.respond(src,chan,num,val) })}
139                                 })
140                         });
141                 };
142                 ccinit = true;
143                 ccr = [];
144                 ccnumr = Array.newClear(128);
145         }
146         learn {
147                 var oneShot;
148                 oneShot = CCResponder({ |src,chan,num,value|
149                                         this.matchEvent_(MIDIEvent(nil,src,chan,num,nil));
150                                         oneShot.remove;
151                                 },nil,nil,nil,nil,true,true)
152         }
154         matchEvent_ { |midiEvent|
155                         // if ctlnum changes from non-number to number, or vice versa,
156                         // this responder is going to move between ccr and ccnumr
157                 if(matchEvent.notNil and:
158                                 { matchEvent.ctlnum.isNumber !== midiEvent.ctlnum.isNumber })
159                 {
160                         this.remove;
161                         matchEvent = midiEvent;
162                         this.class.add(this);
163                 } {
164                         matchEvent = midiEvent;
165                 }
166         }
169 TouchResponder : MIDIResponder {
170         classvar <touchinit = false,<touchr;
172         *new { arg function, src, chan, value, install=true,swallowEvent=false;
173                 ^super.new.function_(function).swallowEvent_(swallowEvent)
174                         .matchEvent_(MIDIEvent(nil, src, chan, nil, value))
175                         .init(install)
176         }
177         *init {
178                 if(MIDIClient.initialized.not,{ MIDIIn.connectAll });
179                 if(touchinit.not) {
180                         MIDIIn.addFuncTo(\touch, { arg src, chan, val;
181                                 touchr.any({ arg r;
182                                         r.respond(src,chan,nil,val)
183                                 })
184                         })
185                 };
186                 touchinit = true;
187                 touchr = [];
188         }
189         value { arg src,chan,num,val;
190                 // num is irrelevant
191                 function.value(src,chan,val);
192         }
193         *initialized { ^touchinit }
194         *responders { ^touchr }
196         *add { arg resp;
197                 touchr = touchr.add(resp);
198         }
199         *remove { arg resp;
200                 touchr.remove(resp);
201         }
202         learn {
203                 var oneShot;
204                 oneShot = this.class.new({ |src,chan,num,value|
205                                         this.matchEvent_(MIDIEvent(nil,src,chan,nil,nil));
206                                         oneShot.remove;
207                                 },nil,nil,nil,true,true)
208         }
211 BendResponder : TouchResponder {
212         classvar <bendinit = false,<bendr;
214         *init {
215                 if(MIDIClient.initialized.not,{ MIDIIn.connectAll });
216                 if(bendinit.not) {
217                         MIDIIn.addFuncTo(\bend, { arg src, chan, val;
218                                 bendr.any({ arg r;
219                                         r.respond(src,chan,nil,val)
220                                 });
221                         })
222                 };
223                 bendinit = true;
224                 bendr = [];
225         }
226         *initialized { ^bendinit }
227         *responders { ^bendr }
229         *add { arg resp;
230                 bendr = bendr.add(resp);
231         }
232         *remove { arg resp;
233                 bendr.remove(resp);
234         }
238 NoteOnOffResponder
239         the note on function would return an object which is stored.
240         when a matching note off event occurs, the object is passed into the note off function
241 PolyTouchResponder
246 ProgramChangeResponder : MIDIResponder {
247         classvar <pcinit = false,<pcr;
249         *new { arg function, src, chan, value, install=true;
250                 ^super.new.function_(function)
251                         .matchEvent_(MIDIEvent(nil, src.asMIDIInPortUID, chan, nil, value))
252                         .init(install)
253         }
254         *init {
255                 if(MIDIClient.initialized.not,{ MIDIIn.connectAll });
256                 if(pcinit.not) {
257                         MIDIIn.addFuncTo(\program, { arg src, chan, val;
258                                 pcr.do({ arg r;
259                                         if(r.matchEvent.match(src, chan, nil, val))
260                                         { r.value(src,chan,val) };
261                                 });
262                         })
263                 };
264                 pcinit = true;
265                 pcr = [];
266         }
267         value { arg src,chan,val;
268                 function.value(src,chan,val);
269         }
270         *initialized { ^pcinit }
271         *responders { ^pcr }
273         *add { arg resp;
274                 pcr = pcr.add(resp);
275         }
276         *remove { arg resp;
277                 pcr.remove(resp);
278         }