HelpBrowser: path box becomes a more conventional search box
[supercollider.git] / SCClassLibrary / Common / Control / MIDIResponder.sc
blob7ae4017fcb6a9a5ebcfeea81589860ab4c4751a6
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                 norinit = true;
54                 nonr = [];
55                 MIDIIn.addFuncTo(\noteOn, { arg src, chan, note, veloc;
56                         nonr.any({ arg r;
57                                 r.respond(src,chan,note,veloc)
58                         });
59                 })
60         }
61         *add { arg resp;
62                 nonr = nonr.add(resp);
63         }
64         *remove { arg resp;
65                 nonr.remove(resp);
66         }
67         learn {
68                 var oneShot;
69                 oneShot = this.class.new({ |src,chan,num,value|
70                                         this.matchEvent_(MIDIEvent(nil,src,chan,nil,nil));
71                                         oneShot.remove;
72                                 },nil,nil,nil,nil,true,true)
73         }
76 NoteOffResponder : NoteOnResponder {
77         classvar <noffinit = false,<noffr;
79         *init {
80                 if(MIDIClient.initialized.not,{ MIDIIn.connectAll });
81                 noffinit = true;
82                 noffr = [];
83                 MIDIIn.addFuncTo(\noteOff, { arg src, chan, note, veloc;
84                         noffr.any({ arg r;
85                                 r.respond(src,chan,note,veloc)
86                         });
87                 })
88         }
89         *initialized { ^noffinit }
90         *responders { ^noffr }
92         *add { arg resp;
93                 noffr = noffr.add(resp);
94         }
95         *remove { arg resp;
96                 noffr.remove(resp);
97         }
100 CCResponder : MIDIResponder {
101         classvar <ccinit = false,<ccr,<ccnumr;
103         *new { arg function, src, chan, num, value, install=true,swallowEvent=false;
104                 ^super.new.function_(function).swallowEvent_(swallowEvent)
105                         .matchEvent_(MIDIEvent(nil, src, chan, num, value))
106                         .init(install)
107         }
108         *initialized { ^ccinit }
109         *responders { ^ccnumr.select(_.notNil).flat ++ ccr }
110         *add { arg resp;
111                 var temp;
112                 if(this.initialized.not,{ this.init });
113                 if((temp = resp.matchEvent.ctlnum).isNumber) {
114                         ccnumr[temp] = ccnumr[temp].add(resp);
115                 } {
116                         ccr = ccr.add(resp);
117                 };
118         }
119         *remove { arg resp;
120                 var temp;
121                 if((temp = resp.matchEvent.ctlnum).isNumber) {
122                         ccnumr[temp].remove(resp)
123                 } {
124                         ccr.remove(resp);
125                 };
126         }
127         *init {
128                 if(MIDIClient.initialized.not,{ MIDIIn.connectAll });
129                 ccinit = true;
130                 ccr = [];
131                 ccnumr = Array.newClear(128);
132                 MIDIIn.addFuncTo(\control, { arg src,chan,num,val;
133                         // first try cc num specific
134                         // then try non-specific (matches any cc )
135                         [ccnumr[num], ccr].any({ |stack|
136                                 stack.notNil and: {stack.any({ |r| r.respond(src,chan,num,val) })}
137                         })
138                 });
139         }
140         learn {
141                 var oneShot;
142                 oneShot = CCResponder({ |src,chan,num,value|
143                                         this.matchEvent_(MIDIEvent(nil,src,chan,num,nil));
144                                         oneShot.remove;
145                                 },nil,nil,nil,nil,true,true)
146         }
148         matchEvent_ { |midiEvent|
149                         // if ctlnum changes from non-number to number, or vice versa,
150                         // this responder is going to move between ccr and ccnumr
151                 if(matchEvent.notNil and:
152                                 { matchEvent.ctlnum.isNumber !== midiEvent.ctlnum.isNumber })
153                 {
154                         this.remove;
155                         matchEvent = midiEvent;
156                         this.class.add(this);
157                 } {
158                         matchEvent = midiEvent;
159                 }
160         }
163 TouchResponder : MIDIResponder {
164         classvar <touchinit = false,<touchr;
166         *new { arg function, src, chan, value, install=true,swallowEvent=false;
167                 ^super.new.function_(function).swallowEvent_(swallowEvent)
168                         .matchEvent_(MIDIEvent(nil, src, chan, nil, value))
169                         .init(install)
170         }
171         *init {
172                 if(MIDIClient.initialized.not,{ MIDIIn.connectAll });
173                 touchinit = true;
174                 touchr = [];
175                 MIDIIn.addFuncTo(\touch, { arg src, chan, val;
176                         touchr.any({ arg r;
177                                 r.respond(src,chan,nil,val)
178                         })
179                 })
180         }
181         value { arg src,chan,num,val;
182                 // num is irrelevant
183                 function.value(src,chan,val);
184         }
185         *initialized { ^touchinit }
186         *responders { ^touchr }
188         *add { arg resp;
189                 touchr = touchr.add(resp);
190         }
191         *remove { arg resp;
192                 touchr.remove(resp);
193         }
194         learn {
195                 var oneShot;
196                 oneShot = this.class.new({ |src,chan,num,value|
197                                         this.matchEvent_(MIDIEvent(nil,src,chan,nil,nil));
198                                         oneShot.remove;
199                                 },nil,nil,nil,true,true)
200         }
203 BendResponder : TouchResponder {
204         classvar <bendinit = false,<bendr;
206         *init {
207                 if(MIDIClient.initialized.not,{ MIDIIn.connectAll });
208                 bendinit = true;
209                 bendr = [];
210                 MIDIIn.addFuncTo(\bend, { arg src, chan, val;
211                         bendr.any({ arg r;
212                                 r.respond(src,chan,nil,val)
213                         });
214                 })
215         }
216         *initialized { ^bendinit }
217         *responders { ^bendr }
219         *add { arg resp;
220                 bendr = bendr.add(resp);
221         }
222         *remove { arg resp;
223                 bendr.remove(resp);
224         }
228 NoteOnOffResponder
229         the note on function would return an object which is stored.
230         when a matching note off event occurs, the object is passed into the note off function
231 PolyTouchResponder
236 ProgramChangeResponder : MIDIResponder {
237         classvar <pcinit = false,<pcr;
239         *new { arg function, src, chan, value, install=true;
240                 ^super.new.function_(function)
241                         .matchEvent_(MIDIEvent(nil, src.asMIDIInPortUID, chan, nil, value))
242                         .init(install)
243         }
244         *init {
245                 if(MIDIClient.initialized.not,{ MIDIIn.connectAll });
246                 pcinit = true;
247                 pcr = [];
248                 MIDIIn.addFuncTo(\program, { arg src, chan, val;
249                         pcr.do({ arg r;
250                                 if(r.matchEvent.match(src, chan, nil, val))
251                                         { r.value(src,chan,val) };
252                         });
253                 })
254         }
255         value { arg src,chan,val;
256                 function.value(src,chan,val);
257         }
258         *initialized { ^pcinit }
259         *responders { ^pcr }
261         *add { arg resp;
262                 pcr = pcr.add(resp);
263         }
264         *remove { arg resp;
265                 pcr.remove(resp);
266         }