// our patch 22.5/100.0 => float head_width; head_width / 2.0 => float head_radius; 1.0 => float hrtf_xy; 0.333 => float hrtf_z; 331.3 => float sound_speed_k; // m/sec 300.0 => float default_max_distance; // in meters (should be function of sound source) 20.5 => float temperature; // in C; query this real-time sound_speed_k * Math.sqrt(1 + temperature / 273.15) => float sound_speed; class Point { float x, y, z; 0.0 => x => y => z; public void set(float dx, float dy, float dz) { dx => x; dy => y; dz => z; } public void set(Point p) { p.x => x; p.y => y; p.z => z; } public void move(float dx, float dy, float dz) { x + dx => x; y + dy => y; z + dz => z; } public float distance(float dx, float dy, float dz) { Math.sqrt(Math.pow(x - dx, 2) + Math.pow(y - dy, 2) + Math.pow(z - dz, 2)) => float d; return d; } } class BinAural { Delay d_left, d_right; Gain g_left, g_right; Point loc; .6 => float bin_gain => g_left.gain => g_right.gain; d_left.max(5::second); d_right.max(5::second); public float src_gain() { return bin_gain; } public void connect(UGen l, UGen r) { d_left => g_left => l; d_right => g_right => r; MoveXYZ(0, head_width, 0); } public void set_gain(float g) { g => bin_gain; } // distance from implied origin of (0, 0, 0) fun float distance(float dx, float dy, float dz) { Math.sqrt(dx * dx + dy * dy + dz * dz) => float d; return d; } // farthest relative distance from origin the sound is perceptible (override me) fun float max_distance() { return bin_gain * default_max_distance; } fun float dissipation(float azimuth, float phi, float g, float d) { max_distance() => float max_distance; Math.min(d, max_distance) => d; g - g * (Math.pow(d,1/2.0) / Math.pow(max_distance,1/2.0)) => float new_g; Math.max(new_g, 0.00) => new_g; Math.sin(azimuth) => float sin_azimuth; Math.cos(azimuth) => float cos_azimuth; head_radius => float a; sound_speed => float c; 0.1 => float a_min; 5 * pi / 6.0 => float theta_min; Math.cos((azimuth/theta_min) * pi) => float cos_theta; (1 + a_min / 2.0) + ((1 - a_min / 2.0) * cos_theta) => float alpha; (1.0 + alpha * a / 2 * c) / (1 + a / 2 * c) => float xyhk; <<< azimuth, xyhk >>>; // (cos_theta + 1.0) / 2.0 => xyhk; // behind head if (cos_azimuth < 0.0) { // new_g * .667 => new_g; } Math.cos(phi) => float zhk; // sound above the plane of the ears if (zhk >= 0) { zhk / 2.0 => zhk; // sound below the plane of the ears } else { zhk / -1.0 => zhk; } // <<< "dis", theta, xyhk >>>; // new_g - new_g * hrtf_xy * xyhk - new_g * hrtf_z * zhk=> new_g; new_g * xyhk - new_g * hrtf_z * zhk=> new_g; return Math.max(new_g, 0.0); } public void MoveXYZ(Point p) { MoveXYZ(p.x, p.y, p.z); } public void MoveXYZ(float dx, float dy, float dz) { // compute radius from head-center to location distance(dx, dy, dz) => float r; // <<< "distance", dx, dy, dz, r >>>; pi / 2.0 => float theta; pi / 2.0 => float phi; if (r > 0) { Math.acos(dz / r) => phi; Math.acos(dx / r) => theta; if (dy < 0) { 2 * pi - theta => theta; } } distance(dx + head_radius, dy, dz) => float left; distance(dx - head_radius, dy, dz) => float right; Math.max(left, .01) => left; Math.max(right, .01) => right; (theta + 3 * pi / 2) % (2 * pi) => float azi_left; (2 * pi - azi_left) % (2 * pi) => float azi_right; <<< dx, dy, dz, theta, azi_left, azi_right >>>; dissipation(azi_left, phi, src_gain(), left) => g_left.gain; dissipation(azi_right, phi, src_gain(), right) => g_right.gain; (left / sound_speed)::second => d_left.delay; (right / sound_speed)::second => d_right.delay; // <<< dx, dy, theta >>>; // <<< g_left.gain(), g_right.gain(), left / sound_speed, right / sound_speed >>>; loc.set(dx, dy, dz); } public void Circle(float o_x, float o_y, float phi, float theta, float radius, int n, float total_time) { radius * Math.cos(phi) => float dz; (2 * pi) / n => float chunk; total_time / n => float time_chunk; for (0 => int i; i < n; i++) { o_x + (radius * Math.cos(theta) * Math.sin(phi)) => float dx; o_y + (radius * Math.sin(theta) * Math.sin(phi)) => float dy; MoveXYZ(dx, dy, dz); theta + chunk => theta; // <<< "circle t", dx, dy, theta / pi, Math.cos(theta) >>>; time_chunk::ms => now; total_time - time_chunk => total_time; } total_time::ms => now; } // linear motion public void MoveTo(float x1, float y1, float z1, int iterations, float time_amount) { Math.fabs(x1 - loc.x) => float dx; Math.fabs(y1 - loc.y) => float dy; Math.fabs(z1 - loc.z) => float dz; distance(dx, dy, dz) => float delta; if (delta <= 0) { MoveXYZ(x1, y1, z1); time_amount::ms => now; return; } (x1 - loc.x) / iterations => dx; (y1 - loc.y) / iterations => dy; (z1 - loc.z) / iterations => dz; time_amount / iterations => float time_chunk; Point p; p.set(loc); for (; iterations > 0; iterations--) { MoveXYZ(p); p.move(dx, dy, dz); time_chunk::ms => now; time_amount - time_chunk => time_amount; } MoveXYZ(x1, y1, z1); time_amount::ms => now; } } class MyBuffer extends BinAural { Gain g; SndBuf snd; 1.5 => float target_gain; public void connect(string s, UGen left, UGen right) { 1.8 => g.gain; s => snd.read; 1.0 => snd.gain; snd => g; g => d_left => g_left => left; g => d_right => g_right => right; end(); } public void play(dur length) { 0 => snd.pos; length => now; } public void play() { 0 => snd.pos; } public void end() { snd.samples() => snd.pos; } public void continuous_play() { while (true) { 0 => float time_chunk; if (Std.rand2f(0.0, 1.0) > .85) { 7.4 => time_chunk; } else { 18.59 => time_chunk; } <<< "tc", time_chunk >>>; spork ~play(time_chunk::second); (time_chunk - .1)::second => now; } } } 0 => int maraca; 4 => int water_drops; 12 => int coke_can; 15 => int nickel_in_mug; 16 => int dime_in_mug; 17 => int quarter_in_mug; 22 => int tuned_bamboo_chimes; class BinShaker extends BinAural { Shakers shake => JCRev r; // set the reverb mix .025 => r.mix; public void connect(UGen left, UGen right) { r => d_left => g_left => left; r => d_right => g_right => right; // MoveXYZ(0, 0, 0); } public void Play(int which, int pitch, float total_time) { which => shake.which; Std.mtof(pitch) => shake.freq; now + total_time::ms => time end_time; while (now < end_time) { ShakeIt(); } } public void ShakeIt() { Std.rand2f( 0.8, 1.3 ) => shake.noteOn; if( Std.randf() > 0.8 ) { 500::ms => now; } else if( Std.randf() > .85 ) { 250::ms => now; } else if( Std.randf() > -0.8 ) { .125::second => now; } else { 1 => int i => int pick_dir; // how many times 4 * Std.rand2( 1, 5 ) => int pick; 0.0 => float pluck; 0.7 / pick => float inc; // time loop for( ; i < pick; i++ ) { 75::ms => now; Std.rand2f(.2,.3) + i*inc => pluck; pluck + -.2 * pick_dir => shake.noteOn; // simulate pluck direction !pick_dir => pick_dir; } // let time pass for final shake 75::ms => now; } } } // mandated by the society for the prevention of deafness in computer generated sound Dyno dl => dac.left; Dyno dr => dac.right; dl.limit(); dr.limit(); <<< "--------------" >>>; // load buffers up front MyBuffer bee, rain, thunder, beast; rain.connect("data/light_rain_forest.wav", dl, dr); thunder.connect("data/thunderclap.wav", dl, dr); beast.connect("data/animal_4f.wav", dl, dr); bee.connect("data/BumbleBee.wav", dl, dr); spork ~bee.play(18.4::second); bee.Circle(0, 0, pi/2, pi/2, 10.0, 8, 8000); me.exit(); // bring in bee from front, right bee.MoveXYZ(85, 70, 25); 2::second => now; bee.MoveTo(50, 70, 20, 10, 3000); 3::second => now; bee.MoveTo(15, 50, 15, 10, 3000); 1.8::second => now; bee.MoveTo(10, 15, 5, 10, 2000); // bring in thunder from front, left spork ~thunder.play(); thunder.MoveXYZ(-30, 110, 30); spork ~thunder.MoveTo(-10, 60, 25, 50, 20000); 3::second => now; spork ~bee.play(18.4::second); bee.MoveXYZ(10, 15, 4); 1::second => now; bee.MoveTo(-15, 15, 3, 10, 2000); 2::second => now; bee.MoveTo(-4, 5, 1, 20, 3000); 2::second => now; // rain also front, left, but closer spork ~rain.play(); rain.MoveXYZ(-10, 80, 10); spork ~rain.MoveTo(-10, 50, 10, 20, 32000); // bee circles and then flies away bee.Circle(-2, 0, pi/2, pi, 3.0, 64, 4000); bee.MoveTo(-100, -80, 30, 20, 3000); 6::second => now;