1// A series of BFormat UGens for SC3. UGens by Josh Parmenter 4-3-2004 2// UGen plug-ins are based on James McCartney's Pan2B and DecodeB2 UGens. 3// added interior localization, z-signal manipulation, and rotate, tilt 4// and tumble transformations. 5// B2Ster equations from 6// http://www.cyber.rdg.ac.uk/P.Sharkey/WWW/icdvrat/WWW96/Papers/keating.htm 7 8BFPanner : MultiOutUGen { 9 *categories {^#["UGens>Multichannel>Ambisonics"]} 10 } 11 12BFDecoder : UGen { 13 *categories {^#["UGens>Multichannel>Ambisonics"]} 14 } 15 16BFEncode1 : BFPanner { 17 18 *ar { arg in, azimuth=0, elevation=0, rho = 1, gain=1, wComp = 0; 19 ^this.multiNew('audio', in, azimuth, elevation, rho, gain, wComp) 20 } 21 22 init { arg ... theInputs; 23 inputs = theInputs; 24 channels = [ OutputProxy(\audio,this,0), OutputProxy(\audio,this,1), 25 OutputProxy(\audio,this,2), OutputProxy(\audio,this,3) ]; 26 ^channels 27 } 28 checkInputs { ^this.checkNInputs(1) } 29} 30 31BFEncodeSter : BFPanner { 32 33 *ar { arg l, r, azimuth=0, width = 0.5pi, elevation=0, rho = 1, gain=1, wComp = 0; 34 ^this.multiNew('audio', l, r, azimuth, width, elevation, rho, gain, wComp ) 35 } 36 37 init { arg ... theInputs; 38 inputs = theInputs; 39 channels = [ OutputProxy(\audio,this,0), OutputProxy(\audio,this,1), 40 OutputProxy(\audio,this,2), OutputProxy(\audio,this,3) ]; 41 ^channels 42 } 43 checkInputs { ^this.checkNInputs(2) } 44} 45 46BFEncode2 : BFPanner { 47 48 *ar { arg in, point_x = 1, point_y = 1, elevation=0, gain=1, wComp = 0; 49 ^this.multiNew('audio', in, point_x, point_y, elevation, gain, wComp ) 50 } 51 52 init { arg ... theInputs; 53 inputs = theInputs; 54 channels = [ OutputProxy(\audio,this,0), OutputProxy(\audio,this,1), 55 OutputProxy(\audio,this,2), OutputProxy(\audio,this,3) ]; 56 ^channels 57 } 58} 59 60// second order encoder 61FMHEncode0 : BFPanner { 62 63 *ar { arg in, azimuth=0, elevation=0, gain=1; 64 ^this.multiNew('audio', in, azimuth, elevation, gain ) 65 } 66 67 init { arg ... theInputs; 68 inputs = theInputs; 69 channels = [ OutputProxy(\audio,this,0), OutputProxy(\audio,this,1), 70 OutputProxy(\audio,this,2), OutputProxy(\audio,this,3), 71 OutputProxy(\audio,this,4), OutputProxy(\audio,this,5), 72 OutputProxy(\audio,this,6), OutputProxy(\audio,this,7), 73 OutputProxy(\audio,this,8) ]; 74 ^channels 75 } 76} 77 78FMHEncode1 : BFPanner { 79 80 *ar { arg in, azimuth=0, elevation=0, rho = 1, gain=1, wComp = 0; 81 ^this.multiNew('audio', in, azimuth, elevation, rho, gain, wComp ) 82 } 83 84 init { arg ... theInputs; 85 inputs = theInputs; 86 channels = [ OutputProxy(\audio,this,0), OutputProxy(\audio,this,1), 87 OutputProxy(\audio,this,2), OutputProxy(\audio,this,3), 88 OutputProxy(\audio,this,4), OutputProxy(\audio,this,5), 89 OutputProxy(\audio,this,6), OutputProxy(\audio,this,7), 90 OutputProxy(\audio,this,8) ]; 91 ^channels 92 } 93} 94 95FMHEncode2 : BFPanner { 96 97 *ar { arg in, point_x = 0, point_y = 0, elevation=0, gain=1, wComp = 0; 98 ^this.multiNew('audio', in, point_x, point_y, elevation, gain, wComp) 99 } 100 101 init { arg ... theInputs; 102 inputs = theInputs; 103 channels = [ OutputProxy(\audio,this,0), OutputProxy(\audio,this,1), 104 OutputProxy(\audio,this,2), OutputProxy(\audio,this,3), 105 OutputProxy(\audio,this,4), OutputProxy(\audio,this,5), 106 OutputProxy(\audio,this,6), OutputProxy(\audio,this,7), 107 OutputProxy(\audio,this,8) ]; 108 ^channels 109 } 110} 111 112BFDecode1 : BFDecoder { 113 114 *ar { arg w, x, y, z, azimuth = 0, elevation = 0, wComp = 0, mul = 1, add = 0; 115 ^this.multiNew('audio', w, x, y, z, azimuth, elevation, wComp ).madd(mul, add); 116 } 117 118 *ar1 {arg w, x, y, z, azimuth = 0, elevation = 0, maxDist = 10, distance = 10, wComp = 0, 119 mul = 1, add = 0, scaleflag = 1; 120 var dist, scaler; 121 dist = ((maxDist - distance) / 345); 122 scaler = if((scaleflag == 1), 1/((distance/maxDist)**1.5), 1); 123 ^DelayN.ar(this.multiNew('audio', w, x, y, z, azimuth, elevation, wComp ), dist, dist, scaler.reciprocal).madd(mul, add); 124 } 125 checkInputs { ^this.checkNInputs(4) } 126} 127 128/* follows Furse / Malham conventions with some tweaking (W is scaled according to x, y, z, r, s, t, u, and v. s, t, u and v are scaled by 2/3.sqrt */ 129 130FMHDecode1 : BFDecoder { 131 *ar {arg w, x, y, z, r, s, t, u, v, azimuth = 0, elevation = 0, mul = 1, add = 0; 132 ^this.multiNew('audio', w, x, y, z, r, s, t, u, v, azimuth, elevation).madd(mul, add); 133 } 134 135 *ar1 {arg w, x, y, z, r, s, t, u, v, azimuth = 0, elevation = 0, maxDist = 10, distance = 10, 136 mul = 1, add = 0, scaleflag = 1; 137 var dist, scaler; 138 dist = ((maxDist - distance) / 345); 139 scaler = if((scaleflag == 1), 1/((distance/maxDist)**1.5), 1); 140 ^DelayN.ar(this.multiNew('audio', w, x, y, z, r, s, t, u, v, azimuth, elevation ), 141 dist, dist, scaler.reciprocal).madd(mul, add); 142 } 143 /* 144 * some common speaker configs, with the appropriate components zeroed out 145 * see http://www.muse.demon.co.uk/ref/speakers.html 146 * for more information 147 */ 148 149 *stereo {arg w, y, mul = 1, add = 0; 150 var zero; 151 zero = K2A.ar(0); 152 ^this.ar(w, zero, y, zero, zero, zero, zero, zero, zero, [0.25pi, -0.25pi], 0, mul, add) 153 } 154 155 // stereo pairs 156 *square {arg w, x, y, v, mul = 1, add = 0; 157 var zero; 158 zero = K2A.ar(0); 159 ^this.ar(w, x, y, zero, zero, zero, zero, zero, v, [0.25pi, -0.25pi, 0.75pi, -0.75pi], 160 0, mul, add) 161 } 162 163 // quad clockwise 164 *quad {arg w, x, y, v, mul = 1, add = 0; 165 var zero; 166 zero = K2A.ar(0); 167 ^this.ar(w, x, y, zero, zero, zero, zero, zero, v, Array.series(4, 0.25pi, -0.5pi), 168 0, mul, add) 169 } 170 171 // point front first 172 *pentagon {arg w, x, y, u, v, mul = 1, add = 0; 173 var zero; 174 zero = K2A.ar(0); 175 ^this.ar(w, x, y, zero, zero, zero, zero, u, v, Array.series(5, 0, -0.4pi), 176 0, mul, add) 177 } 178 179 *hexagon {arg w, x, y, u, v, mul = 1, add = 0; 180 var zero; 181 zero = K2A.ar(0); 182 ^this.ar(w, x, y, zero, zero, zero, zero, u, v, 183 Array.series(6, 0.16666pi, -0.333333pi), 184 0, mul, add) 185 } 186 187 // front bisects a side 188 *octagon1 {arg w, x, y, u, v, mul = 1, add = 0; 189 var zero; 190 zero = K2A.ar(0); 191 ^this.ar(w, x, y, zero, zero, zero, zero, u, v, 192 Array.series(8, 0.125pi, -0.25pi), 193 0, mul, add) 194 } 195 196 // front is a vertex 197 *octagon2 {arg w, x, y, u, v, mul = 1, add = 0; 198 var zero; 199 zero = K2A.ar(0); 200 ^this.ar(w, x, y, zero, zero, zero, zero, u, v, 201 Array.series(8, 0, -0.25pi), 202 0, mul, add) 203 } 204 205 *cube {arg w, x, y, z, s, t, v, mul = 1, add = 0; 206 var zero; 207 zero = K2A.ar(0); 208 ^this.ar(w, x, y, z, zero, s, t, zero, v, 209 Array.series(8, 0.25pi, -0.5pi), 210 -0.25pi.dup(4) ++ 0.25pi.dup(4), mul, add) 211 } 212 213 // top, then bottom 214 *doubleHex {arg w, x, y, z, s, t, u, v, mul = 1, add = 0; 215 var zero; 216 zero = K2A.ar(0); 217 ^this.ar(w, x, y, z, zero, s, t, u, v, 218 Array.series(12, 0.16666pi, -0.33333pi), 219 0.16666pi.dup(6) ++ -0.166666pi.dup(6), mul, add) 220 } 221 // top, pentagonup, pentagondown, bottom 222 *dodecahedron {arg w, x, y, z, r, s, t, u, v, mul = 1, add = 0; 223 var zero; 224 zero = K2A.ar(0); 225 ^this.ar(w, x, y, z, r, s, t, u, v, 226 [0] ++ Array.series(10, 0.2, -0.4) ++ [0], 227 [0.5pi] ++ 0.16666pi.dup(5) ++ -0.16666pi.dup(5) ++ [-0.5pi], 228 mul, add) 229 } 230 231 checkInputs { ^this.checkNInputs(9) } 232} 233 234BFManipulate : BFPanner { 235 236 *ar { arg w, x, y, z, rotate = 0, tilt = 0, tumble = 0; 237 ^this.multiNew('audio', w, x, y, z, rotate, tilt, tumble); 238 } 239 240 init { arg ... theInputs; 241 inputs = theInputs; 242 channels = [ OutputProxy(\audio,this,0), OutputProxy(\audio,this,1), 243 OutputProxy(\audio,this,2), OutputProxy(\audio,this,3) ]; 244 ^channels 245 } 246 247 checkInputs { ^this.checkNInputs(4) } 248 249} 250 251// Rotate tilt and tumble classes, built from Rotate2. Allows w, x, y and z to be passed in, and 252// returns the new w, x, y, and z 253Rotate : BFPanner { 254 *ar {arg w, x, y, z, rotate; 255 var xout, yout; 256 #xout, yout = Rotate2.ar(x, y, rotate * -0.31830988618379); 257 ^[w, xout, yout, z]; 258 } 259 260 init { arg ... theInputs; 261 inputs = theInputs; 262 channels = [ OutputProxy(\audio,this,0), OutputProxy(\audio,this,1), 263 OutputProxy(\audio,this,2), OutputProxy(\audio,this,3) ]; 264 ^channels 265 } 266 267 checkInputs { ^this.checkNInputs(4) } 268} 269 270Tilt : BFPanner { 271 *ar {arg w, x, y, z, tilt; 272 var xout, zout; 273 #xout, zout = Rotate2.ar(x, z, tilt * -0.31830988618379); 274 ^[w, xout, y, zout]; 275 } 276 277 init { arg ... theInputs; 278 inputs = theInputs; 279 channels = [ OutputProxy(\audio,this,0), OutputProxy(\audio,this,1), 280 OutputProxy(\audio,this,2), OutputProxy(\audio,this,3) ]; 281 ^channels 282 } 283 284 checkInputs { ^this.checkNInputs(4) } 285} 286 287Tumble : BFPanner { 288 *ar {arg w, x, y, z, tilt; 289 var yout, zout; 290 #yout, zout = Rotate2.ar(y, z, tilt * -0.31830988618379); 291 ^[w, x, yout, zout]; 292 } 293 294 init { arg ... theInputs; 295 inputs = theInputs; 296 channels = [ OutputProxy(\audio,this,0), OutputProxy(\audio,this,1), 297 OutputProxy(\audio,this,2), OutputProxy(\audio,this,3) ]; 298 ^channels 299 } 300 301 checkInputs { ^this.checkNInputs(4) } 302} 303 304A2B : BFPanner { 305 306 *ar { arg a, b, c, d; 307 ^this.multiNew('audio', a, b, c, d); 308 } 309 310 init { arg ... theInputs; 311 inputs = theInputs; 312 channels = [ OutputProxy(\audio,this,0), OutputProxy(\audio,this,1), 313 OutputProxy(\audio,this,2), OutputProxy(\audio,this,3) ]; 314 ^channels 315 } 316 317 checkInputs { ^this.checkNInputs(4) } 318 319} 320 321B2A : BFPanner { 322 323 *ar { arg w, x, y, z; 324 ^this.multiNew('audio', w, x, y, z); 325 } 326 327 init { arg ... theInputs; 328 inputs = theInputs; 329 channels = [ OutputProxy(\audio,this,0), OutputProxy(\audio,this,1), 330 OutputProxy(\audio,this,2), OutputProxy(\audio,this,3) ]; 331 ^channels 332 } 333 334 checkInputs { ^this.checkNInputs(4) } 335 336} 337 338B2Ster : BFPanner { 339 *ar {arg w, x, y, mul = 1, add = 0; 340 ^this.multiNew('audio', w, x, y).madd(mul, add); 341 } 342 343 init {arg ... theInputs; 344 inputs = theInputs; 345 channels = [ OutputProxy(\audio, this, 0), OutputProxy(\audio, this, 1)]; ^channels; 346 } 347 348 checkInputs { ^this.checkNInputs(3) } 349 350 } 351 352// takes w, x, and y from a BF sig, returns a 2 channel UHJ file 353 354B2UHJ : BFPanner { 355 *ar {arg w, x, y; 356 ^this.multiNew('audio', w, x, y); 357 } 358 359 init {arg ... theInputs; 360 inputs = theInputs; 361 channels = [ OutputProxy(\audio, this, 0), OutputProxy(\audio, this, 1)]; ^channels; 362 } 363 364 checkInputs { ^this.checkNInputs(3) } 365 366 } 367 368// takes the left signal (ls) and right signal (rs) of a UHJ signal, 369// and returns w, x, and y of a BF signal 370 371UHJ2B : BFPanner { 372 *ar {arg ls, rs; 373 ^this.multiNew('audio', ls, rs); 374 } 375 376 init {arg ... theInputs; 377 inputs = theInputs; 378 ^channels = [ OutputProxy(\audio, this, 0), OutputProxy(\audio, this, 1), 379 OutputProxy(\audio, this, 2)] 380 } 381 382 checkInputs { ^this.checkNInputs(2) } 383 384 } 385 386BFFreeVerb { 387 *ar {arg w, x, y, z, mix = 1.0, room = 0.5, damp = 0.5, diffuse = 0.1, mul = 1.0, add = 0.0; 388 var a, b, c, d; 389 #a, b, c, d = B2A.ar(w, x, y, z); 390 #a, b, c, d = FreeVerb.ar([a, b, c, d], mix, room, damp, mul, add); 391 a = a + (b + c * diffuse); 392 b = b + (c + d * diffuse); 393 c = c + (d + a * diffuse); 394 d = d + (a + b * diffuse); 395 ^A2B.ar(a, b, c, d); 396 } 397 } 398 399BFGVerb { 400 *ar {arg w, x, y, z, diffuse = 0.0, roomsize = 10, revtime = 3, damping = 0.5, 401 inputbw = 0.5, drylevel = 0.0, earlyreflevel = 0.7, taillevel = 0.5, 402 maxroomsize = 300, mul = 1, add = 0; 403 var a, b, c, d, al, ar, bl, br, cl, cr, dl, dr; 404 #a, b, c, d = B2A.ar(w, x, y, z); 405 #a, b, c, d = DelayN.ar([a, b, c, d], 0.001, Array.fill(4, {Rand.new(0.0, 0.001)})); 406 #ar, al = GVerb.ar(a, roomsize, revtime, damping, inputbw, 45, drylevel, 407 earlyreflevel, taillevel, maxroomsize, mul); 408 #br, bl = GVerb.ar(b, roomsize, revtime, damping, inputbw, 45, drylevel, 409 earlyreflevel, taillevel, maxroomsize, mul); 410 #cr, cl = GVerb.ar(c, roomsize, revtime, damping, inputbw, 45, drylevel, 411 earlyreflevel, taillevel, maxroomsize, mul); 412 #dr, dl = GVerb.ar(d, roomsize, revtime, damping, inputbw, 45, drylevel, 413 earlyreflevel, taillevel, maxroomsize, mul); 414 a = ar + al + (br + cl * diffuse); 415 b = br + bl + (cr + dl * diffuse); 416 c = cr + cl + (dr + al * diffuse); 417 d = dr + dl + (ar + bl * diffuse); 418 ^A2B.ar(a, b, c, d) + add; 419 } 420 } 421 422 423 424 425////////////////////////////////////////////////////////////////////////////////////////////// 426// Classes from SuperCollider 2 are in the file AmbisonicsSC2 427////////////////////////////////////////////////////////////////////////////////////////////// 428 429 430 431