1Bus { 2 3 var <rate, <index, <numChannels, <server; 4 var mapSymbol; 5 6 *control { arg server, numChannels=1; 7 var alloc; 8 server = server ? Server.default; 9 alloc = server.controlBusAllocator.alloc(numChannels); 10 if(alloc.isNil) { 11 error("Meta_Bus:control: failed to get a control bus allocated." 12 + "numChannels:" + numChannels + "server:" + server.name); 13 ^nil 14 }; 15 ^this.new(\control, alloc, numChannels, server) 16 } 17 18 *audio { arg server, numChannels=1; 19 var alloc; 20 server = server ? Server.default; 21 alloc = server.audioBusAllocator.alloc(numChannels); 22 if(alloc.isNil) { 23 error("Meta_Bus:audio: failed to get an audio bus allocated." 24 + "numChannels:" + numChannels + "server:" + server.name); 25 ^nil 26 }; 27 ^this.new(\audio, alloc, numChannels, server) 28 } 29 30 *alloc { arg rate, server, numChannels=1; 31 ^this.perform(rate ? \audio, server, numChannels) 32 } 33 34 *new { arg rate=\audio, index=0, numChannels=2, server; 35 ^super.newCopyArgs(rate, index, numChannels, server ? Server.default) 36 } 37 38 *newFrom { arg bus, offset, numChannels=1; 39 if(offset > bus.numChannels or: { numChannels + offset > bus.numChannels }) { 40 Error("Bus:newFrom tried to reach outside the channel range of %".format( bus )).throw 41 }; 42 ^this.new(bus.rate, bus.index + offset, numChannels) 43 } 44 45 isSettable { 46 ^rate != \audio 47 } 48 49 set { arg ... values; // shouldn't be larger than this.numChannels 50 if(index.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 51 if(this.isSettable) { 52 server.sendBundle(nil, (["/c_set"] 53 ++ values.collect({ arg v, i; [index + i , v] }).flat)) 54 } { 55 error("Cannot set an audio rate bus") 56 } 57 } 58 59 setMsg { arg ... values; 60 if(index.isNil) { Error("Cannot construct a % for a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 61 if(this.isSettable) { 62 ^["/c_set"] ++ values.collect({ arg v, i; [index + i , v] }).flat 63 } { 64 error("Cannot set an audio rate bus") 65 } 66 } 67 68 setn { arg values; 69 if(index.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 70 // could throw an error if values.size > numChannels 71 if(this.isSettable) { 72 server.sendBundle(nil, 73 ["/c_setn", index, values.size] ++ values) 74 } { 75 error("Cannot set an audio rate bus") 76 } 77 } 78 79 setnMsg { arg values; 80 if(index.isNil) { Error("Cannot construct a % for a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 81 if(this.isSettable) { 82 ^["/c_setn", index, values.size] ++ values 83 } { 84 error("Cannot set an audio rate bus") 85 } 86 } 87 88 setAt { |offset ... values| 89 if(index.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 90 // shouldn't be larger than this.numChannels - offset 91 if(this.isSettable) { 92 server.sendBundle(nil, (["/c_set"] 93 ++ values.collect({ arg v, i; [index + offset + i , v] }).flat)) 94 } { 95 error("Cannot set an audio rate bus") 96 } 97 } 98 99 setnAt { |offset, values| 100 if(index.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 101 // could throw an error if values.size > numChannels 102 if(this.isSettable) { 103 server.sendBundle(nil, 104 ["/c_setn", index + offset, values.size] ++ values) 105 } { 106 error("Cannot set an audio rate bus") 107 } 108 } 109 110 setPairs { | ... pairs| 111 if(index.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 112 if(this.isSettable) { 113 server.sendBundle(nil, (["/c_set"] 114 ++ pairs.clump(2).collect({ arg pair; [pair[0] + index, pair[1]] }).flat)) 115 } { 116 error("Cannot set an audio rate bus") 117 } 118 } 119 120 get { arg action; 121 if(index.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 122 if(numChannels == 1) { 123 action = action ? { |vals| "Bus % index: % value: %.\n".postf(rate, index, vals); }; 124 OSCFunc({ |message| 125 // The response is of the form [/c_set, index, value]. 126 // We want "value," which is at index 2. 127 action.value(message[2]) 128 }, \c_set, server.addr, argTemplate: [index]).oneShot; 129 server.listSendMsg([\c_get, index]) 130 } { 131 this.getn(numChannels, action) 132 } 133 } 134 135 getn { arg count, action; 136 if(index.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 137 action = action ? { |vals| "Bus % index: % values: %.\n".postf(rate, index, vals) }; 138 OSCFunc({ |message| 139 // The response is of the form [/c_set, index, count, ...values]. 140 // We want the values, which are at indexes 3 and above. 141 action.value(message[3..]) 142 }, \c_setn, server.addr, argTemplate: [index]).oneShot; 143 server.listSendMsg([\c_getn, index, count ? numChannels]) 144 } 145 146 getMsg { 147 if(index.isNil) { Error("Cannot construct a % for a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 148 ^[\c_get, index] 149 } 150 151 getnMsg { arg count; 152 if(index.isNil) { Error("Cannot construct a % for a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 153 ^[\c_getn, index, count ? numChannels] 154 } 155 156 getSynchronous { 157 if(index.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 158 if (this.isSettable.not) { 159 Error("Bus-getSynchronous only works for control-rate busses").throw 160 } { 161 ^server.getControlBusValue(index) 162 } 163 } 164 165 getnSynchronous { |count| 166 if(index.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 167 if (this.isSettable.not) { 168 Error("Bus-getnSynchronous only works for control-rate busses").throw 169 } { 170 ^server.getControlBusValues(index, count ? numChannels) 171 } 172 } 173 174 setSynchronous { |... values| 175 if(index.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 176 if (this.isSettable.not) { 177 Error("Bus-setSynchronous only works for control-rate busses").throw 178 } { 179 if (values.size == 1) { 180 server.setControlBusValue(index, values[0]) 181 } { 182 server.setControlBusValues(index, values) 183 } 184 } 185 } 186 187 setnSynchronous { |values| 188 if(index.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 189 if (this.isSettable.not) { 190 Error("Bus-setnSynchronous only works for control-rate busses").throw 191 } { 192 server.setControlBusValues(index, values) 193 } 194 } 195 196 fill { arg value, numChans; 197 if(index.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 198 // could throw an error if numChans > numChannels 199 server.sendBundle(nil, ["/c_fill", index, numChans, value]) 200 } 201 202 fillMsg { arg value; 203 if(index.isNil) { Error("Cannot construct a % for a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 204 ^["/c_fill", index, numChannels, value] 205 } 206 207 free { arg clear = false; 208 if(index.isNil) { (this.class.name ++ " has already been freed").warn; ^this }; 209 if(rate == \audio) { 210 server.audioBusAllocator.free(index) 211 } { 212 server.controlBusAllocator.free(index); 213 if(clear) { this.setAll(0) } 214 }; 215 index = nil; 216 numChannels = nil; 217 mapSymbol = nil; 218 } 219 220 // allow reallocation 221 222 alloc { 223 if(rate === 'audio') { 224 index = server.audioBusAllocator.alloc(numChannels) 225 } { 226 index = server.controlBusAllocator.alloc(numChannels) 227 }; 228 mapSymbol = nil; 229 } 230 231 realloc { 232 var r, n; 233 if(index.notNil) { 234 r = rate; 235 n = numChannels; 236 this.free; 237 rate = r; 238 numChannels = n; 239 this.alloc; 240 } { 241 Error("Cannot % a % that has been freed".format(thisMethod.name, this.class.name)).throw 242 } 243 } 244 245 // alternate syntaxes 246 setAll { arg value; 247 if(index.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 248 this.fill(value, numChannels) 249 } 250 251 value_ { arg value; 252 if(index.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 253 this.fill(value, numChannels) 254 } 255 256 printOn { arg stream; 257 stream << this.class.name << "(" <<* 258 [rate, index, numChannels, server] <<")" 259 } 260 261 storeOn { arg stream; 262 stream << this.class.name << "(" <<* 263 [rate.asCompileString, index, numChannels, server.asCompileString] <<")" 264 } 265 266 == { arg aBus; 267 ^this.compareObject(aBus, #[\index, \numChannels, \rate, \server]) 268 } 269 270 hash { 271 ^this.instVarHash(#[\index, \numChannels, \rate, \server]) 272 } 273 274 isAudioOut { // audio interface 275 ^(rate === \audio and: { index < server.options.firstPrivateBus}) 276 } 277 278 ar { arg numChannels=(this.numChannels), offset=0; 279 if(rate == \audio) { 280 ^In.ar(index + offset, numChannels) 281 } { 282 ^K2A.ar( In.kr(index + offset, numChannels) ) 283 } 284 } 285 286 kr { arg numChannels=(this.numChannels), offset=0; 287 if(rate == \audio) { 288 ^A2K.kr( In.ar(index + offset, numChannels) ) 289 } { 290 ^In.kr(index + offset, numChannels) 291 } 292 } 293 294 play { arg target=0, outbus, fadeTime, addAction=\addToTail; 295 if(index.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; 296 if(this.isAudioOut.not) { 297 // returns a Synth 298 ^{ this.ar }.play(target, outbus, fadeTime, addAction) 299 } 300 } 301 302 asUGenInput { ^this.index } 303 asControlInput { ^this.index } 304 305 asMap { 306 ^mapSymbol ?? { 307 if(index.isNil) { MethodError("bus not allocated.", this).throw }; 308 mapSymbol = if(rate == \control) { "c" } { "a" }; 309 mapSymbol = (mapSymbol ++ index).asSymbol; 310 } 311 } 312 313 subBus { arg offset, numChannels=1; 314 ^this.class.newFrom(this, offset, numChannels) 315 } 316 317} 318