1// see MIDIResponder help for all classes on this page 2 3 4MIDIResponder { 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 } 25 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 } 36} 37 38 39 40NoteOnResponder : MIDIResponder { 41 classvar <norinit = false,<nonr; 42 43 *new { arg function, src, chan, num, veloc, install=true,swallowEvent=false; 44 num = num.isNumber.if({ num.asInteger }, num); 45 num = num.isCollection.if({ num.collect(_.asInteger) }, num); 46 ^super.new.function_(function) 47 .matchEvent_(MIDIEvent(nil, src, chan, num, veloc)) 48 .swallowEvent_(swallowEvent) 49 .init(install) 50 } 51 *initialized { ^norinit } 52 *responders { ^nonr } 53 *init { 54 if(MIDIClient.initialized.not,{ MIDIIn.connectAll }); 55 if(norinit.not) { 56 MIDIIn.addFuncTo(\noteOn, { arg src, chan, note, veloc; 57 nonr.any({ arg r; 58 r.respond(src,chan,note,veloc) 59 }); 60 }) 61 }; 62 norinit = true; 63 nonr = []; 64 } 65 *add { arg resp; 66 nonr = nonr.add(resp); 67 } 68 *remove { arg resp; 69 nonr.remove(resp); 70 } 71 learn { 72 var oneShot; 73 oneShot = this.class.new({ |src,chan,num,value| 74 this.matchEvent_(MIDIEvent(nil,src,chan,nil,nil)); 75 oneShot.remove; 76 },nil,nil,nil,nil,true,true) 77 } 78} 79 80NoteOffResponder : NoteOnResponder { 81 classvar <noffinit = false,<noffr; 82 83 *init { 84 if(MIDIClient.initialized.not,{ MIDIIn.connectAll }); 85 if(noffinit.not) { 86 MIDIIn.addFuncTo(\noteOff, { arg src, chan, note, veloc; 87 noffr.any({ arg r; 88 r.respond(src,chan,note,veloc) 89 }); 90 }) 91 }; 92 noffinit = true; 93 noffr = []; 94 } 95 *initialized { ^noffinit } 96 *responders { ^noffr } 97 98 *add { arg resp; 99 noffr = noffr.add(resp); 100 } 101 *remove { arg resp; 102 noffr.remove(resp); 103 } 104} 105 106CCResponder : MIDIResponder { 107 classvar <ccinit = false,<ccr,<ccnumr; 108 109 *new { arg function, src, chan, num, value, install=true,swallowEvent=false; 110 num = num.isNumber.if({ num.asInteger }, num); 111 num = num.isCollection.if({ num.collect(_.asInteger) }, num); 112 ^super.new.function_(function).swallowEvent_(swallowEvent) 113 .matchEvent_(MIDIEvent(nil, src, chan, num, value)) 114 .init(install) 115 } 116 *initialized { ^ccinit } 117 *responders { ^ccnumr.select(_.notNil).flat ++ ccr } 118 *add { arg resp; 119 var temp; 120 if(this.initialized.not,{ this.init }); 121 if((temp = resp.matchEvent.ctlnum).isNumber) { 122 ccnumr[temp] = ccnumr[temp].add(resp); 123 } { 124 ccr = ccr.add(resp); 125 }; 126 } 127 *remove { arg resp; 128 var temp; 129 if((temp = resp.matchEvent.ctlnum).isNumber) { 130 ccnumr[temp].remove(resp) 131 } { 132 ccr.remove(resp); 133 }; 134 } 135 *init { 136 if(MIDIClient.initialized.not,{ MIDIIn.connectAll }); 137 if(ccinit.not) { 138 MIDIIn.addFuncTo(\control, { arg src,chan,num,val; 139 // first try cc num specific 140 // then try non-specific (matches any cc ) 141 [ccnumr[num], ccr].any({ |stack| 142 stack.notNil and: {stack.any({ |r| r.respond(src,chan,num,val) })} 143 }) 144 }); 145 }; 146 ccinit = true; 147 ccr = []; 148 ccnumr = Array.newClear(128); 149 } 150 learn { 151 var oneShot; 152 oneShot = CCResponder({ |src,chan,num,value| 153 this.matchEvent_(MIDIEvent(nil,src,chan,num,nil)); 154 oneShot.remove; 155 },nil,nil,nil,nil,true,true) 156 } 157 158 matchEvent_ { |midiEvent| 159 // if ctlnum changes from non-number to number, or vice versa, 160 // this responder is going to move between ccr and ccnumr 161 if(matchEvent.notNil and: 162 { matchEvent.ctlnum.isNumber !== midiEvent.ctlnum.isNumber }) 163 { 164 this.remove; 165 matchEvent = midiEvent; 166 this.class.add(this); 167 } { 168 matchEvent = midiEvent; 169 } 170 } 171} 172 173TouchResponder : MIDIResponder { 174 classvar <touchinit = false,<touchr; 175 176 *new { arg function, src, chan, value, install=true,swallowEvent=false; 177 ^super.new.function_(function).swallowEvent_(swallowEvent) 178 .matchEvent_(MIDIEvent(nil, src, chan, nil, value)) 179 .init(install) 180 } 181 *init { 182 if(MIDIClient.initialized.not,{ MIDIIn.connectAll }); 183 if(touchinit.not) { 184 MIDIIn.addFuncTo(\touch, { arg src, chan, val; 185 touchr.any({ arg r; 186 r.respond(src,chan,nil,val) 187 }) 188 }) 189 }; 190 touchinit = true; 191 touchr = []; 192 } 193 value { arg src,chan,num,val; 194 // num is irrelevant 195 function.value(src,chan,val); 196 } 197 *initialized { ^touchinit } 198 *responders { ^touchr } 199 200 *add { arg resp; 201 touchr = touchr.add(resp); 202 } 203 *remove { arg resp; 204 touchr.remove(resp); 205 } 206 learn { 207 var oneShot; 208 oneShot = this.class.new({ |src,chan,num,value| 209 this.matchEvent_(MIDIEvent(nil,src,chan,nil,nil)); 210 oneShot.remove; 211 },nil,nil,nil,true,true) 212 } 213} 214 215BendResponder : TouchResponder { 216 classvar <bendinit = false,<bendr; 217 218 *init { 219 if(MIDIClient.initialized.not,{ MIDIIn.connectAll }); 220 if(bendinit.not) { 221 MIDIIn.addFuncTo(\bend, { arg src, chan, val; 222 bendr.any({ arg r; 223 r.respond(src,chan,nil,val) 224 }); 225 }) 226 }; 227 bendinit = true; 228 bendr = []; 229 } 230 *initialized { ^bendinit } 231 *responders { ^bendr } 232 233 *add { arg resp; 234 bendr = bendr.add(resp); 235 } 236 *remove { arg resp; 237 bendr.remove(resp); 238 } 239} 240 241/* 242NoteOnOffResponder 243 the note on function would return an object which is stored. 244 when a matching note off event occurs, the object is passed into the note off function 245PolyTouchResponder 246*/ 247 248 249 250ProgramChangeResponder : MIDIResponder { 251 classvar <pcinit = false,<pcr; 252 253 *new { arg function, src, chan, value, install=true; 254 ^super.new.function_(function) 255 .matchEvent_(MIDIEvent(nil, src.asMIDIInPortUID, chan, nil, value)) 256 .init(install) 257 } 258 *init { 259 if(MIDIClient.initialized.not,{ MIDIIn.connectAll }); 260 if(pcinit.not) { 261 MIDIIn.addFuncTo(\program, { arg src, chan, val; 262 pcr.do({ arg r; 263 if(r.matchEvent.match(src, chan, nil, val)) 264 { r.value(src,chan,val) }; 265 }); 266 }) 267 }; 268 pcinit = true; 269 pcr = []; 270 } 271 value { arg src,chan,val; 272 function.value(src,chan,val); 273 } 274 *initialized { ^pcinit } 275 *responders { ^pcr } 276 277 *add { arg resp; 278 pcr = pcr.add(resp); 279 } 280 *remove { arg resp; 281 pcr.remove(resp); 282 } 283} 284