// jeff smith March 2008 // // Dedicated to the memory of The Skipper, Gilligan, and Mary Anne // // // Do everything in Chuck. No preprocessing. No post processing. As a result, everything is // very extensible. // // // The score for this composition exists at the bottom of the program. // // constants // A3 220 => float A3; A3 => float fundamental; // fundamental pitch must be >= 22 56 => int fund; // scale 0 => int P1; 1 => int m2; 2 => int M2; 3 => int m3; 4 => int M3; 5 => int P4; 6 => int a4; 7 => int P5; 8 => int m6; 9 => int M6; 10 => int m7; 11 => int M7; 12 => int P8; 12 => int diatonic_scale; 6 => int num_overtones; // in ms 2500 => int bar; bar / 2 => int half_note; bar / 4 => int quarter_note; bar / 8 => int eighth_note; // dynamics 0.1 => float dyn_ppp; 0.2 => float dyn_pp; 0.3 => float dyn_p; 0.45 => float dyn_mp; 0.55 => float dyn_mf; 0.7 => float dyn_f; 0.8 => float dyn_ff; 0.9 => float dyn_fff; // first 31 prime numbers [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113] @=> int primes[]; 4 => int num_channels; -1 => int room_left; -1 => int room_front; // -1 => int room_bottom; 0 => int room_bottom; 1 => int room_right; 1 => int room_back; // 1 => int room_top; 0 => int room_top; room_right - room_left => int room_width; room_back - room_front => int room_depth; room_top - room_bottom => int room_height; // radius of our room room_width / 2.0 => float room_radius; // angle from origins to top, mid, and bottom planes // [pi / 4.0, pi / 2.0, 3.0 * pi / 4.0] @=> float z_angle[]; // [4, 8, 4, 0] @=> int points_per_plane[]; [ pi / 2.0] @=> float z_angle[]; [4, 0] @=> int points_per_plane[]; // first x,y channel will be right,front, and then we proceed // counter-clockwise around the room -pi/4.0 => float xy_angle_phase; [ // top four "channels/top/fr.wav", "channels/top/br.wav", "channels/top/bl.wav", "channels/top/fl.wav", // middle eight "channels/middle/fr.wav", "channels/middle/r.wav", "channels/middle/br.wav", "channels/middle/b.wav", "channels/middle/bl.wav", "channels/middle/l.wav", "channels/middle/fl.wav", "channels/middle/f.wav", // bottom four "channels/bottom/fr.wav", "channels/bottom/br.wav", "channels/bottom/bl.wav", "channels/bottom/fl.wav", "" ] @=> string channel_files[]; fun int min(int a, int b) { if (a < b) return a; return b; } class AChannelWave { WvOut ch; public void init(string s) { if (s == "") { <<< "error: channel file names" >>>; me.exit(); } s => ch.wavFilename; } public void close(string s) { s => ch.closeFile; } } // this class, later a global variable, streams audio to wave files // representing our different channels class NChannelWave { true => int write_to_file; true => int debug_on_dac; new AChannelWave[num_channels] @=> AChannelWave chnls[]; Gain dl, dr; if (debug_on_dac) { .9 => dl.gain; .9 => dr.gain; dr => dac.right; dl => dac.left; } public void init(int write_to_wav_file) { if (!write_to_wav_file && dac.channels() != num_channels) { <<< "number of channels doesn't match dac:", num_channels, dac.channels() >>>; me.exit(); } write_to_wav_file => write_to_file; // == !write_to_dac if (!write_to_file) return; // this order is material and must correspond to ChannelSphere.c_connect loop for (0 => int i; i < num_channels; i++) { chnls[i].init(channel_files[i]); } } public void done() { if (!write_to_file) return; for (0 => int i; i < num_channels; i++) { chnls[i].close(channel_files[i]); } } public void connect(Gain g, float x, int i /* channel_index */) { if (i < 0 || i >= num_channels) { <<< "connect channel error", i >>>; me.exit(); } if (!write_to_file) { g => dac.chan(i); return; } g => chnls[i].ch; if (!debug_on_dac) { chnls[i].ch => blackhole; return; } // debug on two-channel dac while writing wav files if (x == 0) { chnls[i].ch => dl; chnls[i].ch => dr; } else if (x < 0) { chnls[i].ch => dl; } else { chnls[i].ch => dr; } } } fun float distance(float dx, float dy, float dz) { dx * dx => dx; dy * dy => dy; dz * dz => dz; Math.sqrt(dx + dy + dz) => float d; return d; } class ChannelPoint { float x,y,z; 0.0 => x => y => z; Gain g; 0.0 => g.gain; 0.0 => float set_gain; // To compute the gain of a given channel in panning, we'll compute the distance from a point // of the desired pan location to the channel. The gain for that channel will be the inverse // distance of the channel to that panning point. The channel radius, therefore, defines // the maximum distance from a panning point to the channel. A larger radius, therefore, // will expand the reach of the channel, and will result in more channels being utilized // for a given panning point. 1.1 => float channel_radius; public float gain_at_point(float x2, float y2, float z2) { distance(x - x2, y - y2, z - z2) => float d; // if d > channel_radius, return zero return Math.max(0.0, channel_radius - d); } public void connect(float x1, float y1, float z1, float gn, UGen in, NChannelWave out, int index) { x1 => x; y1 => y; z1 => z; Set(gn); in => g; out.connect(g, x, index); } // a channel radius of less than half (cr <= 0.50) room_width will mean that a // XYZ pan of (0,0,0) will yield no sound. public void SetChannelRadius(float cr) { if (cr < 0 || cr > 1.0) { <<< "set distance scale error", cr >>>; return; } // 0.6 < channel_radius <= room_width Math.max(0.6, cr * room_width) => channel_radius; } public void Set(float gn) { // save gain value; set gain filter, however, to 0 <= gn <= 1.0 gn => set_gain; Math.max(0.0, Math.min(1.0, gn)) => g.gain; } public void Amplify(float a) { Set(a * set_gain); } public void debug_gains() { <<< "x,y,z: g", x, y, z, ":", g.gain() >>>; } } class ChannelSphere { if (room_height != room_width || room_width != room_depth) { // <<< "error: room dimensions: variable radius" >>>; // me.exit(); } // this parameter will define the sum gain of all channels; set to zero if you don't want to normalize 1.0 => float normalize; // our radius for the sphere room_radius => float sphere_radius; num_channels => int num_points; new ChannelPoint[num_points] @=> ChannelPoint points[]; public void SetNormalizeValue(float n) { if (n < 0) { <<< "Normalize value negative", n >>>; return; } n => normalize; } public void SetChannelRadius(float cr) { for (0 => int i; i < num_points; i++) { points[i].SetChannelRadius(cr); } } public void SetChannelRadius(float cr, int index) { if (index < 0 || index > num_points) { <<< "SetChannelRadius error: index out of bounds", index >>>; return; } points[index].SetChannelRadius(cr); } // connect our sound source 'in' to our class and sound output 'out' public void c_connect(float gn, UGen in, NChannelWave out) { // build matrix of channels (see above) // matrix gives distances of any channel from origin (0, 0, 0) // in the event you want a different origin, modify these values 0.0 => float o_x => float o_y => float o_z; 0 => int c; for (0 => int k; points_per_plane[k]; k++) { z_angle[k] => float phi; points_per_plane[k] => int n; for (0 => int i; i < n; i++) { ((i * 1.0) / n) * (2.0 * pi) + xy_angle_phase => float theta; o_x + (sphere_radius * Math.cos(theta) * Math.sin(phi)) => float x; o_y + (sphere_radius * Math.sin(theta) * Math.sin(phi)) => float y; o_z + (sphere_radius * Math.cos(phi)) => float z; points[c].connect(x, y, z, gn, in, out, c); ++c; } } } public void c_connect(UGen in, NChannelWave out) { // default to gain of .1 c_connect(0.1, in, out); } // set the sound source to a specific location in the room (plane) as defined above public void PanXYZ(float dx /* -1 = left, 1 = right */, float dy /* -1 = front, 1 = back */, float dz /* -1 = bottom, 1 = top */) { if (dx > 1.0 || dx < -1.0 || dy > 1.0 || dy < -1.0 || dz > 1.0 || dz < -1.0) { <<< "panxyz error: ", dx, dy, dz >>>; return; } // update gain of all channels based on inverse distance from panning point (dx, dy, dz) 0.0 => float sum; for (0 => int i; i < num_points; i++) { points[i].gain_at_point(dx, dy, dz) => float d; sum + d => sum; points[i].Set(d); } // normalize all values, allowing sum of gain of all channels to equal 'normalize' if (normalize > 0.0 && sum > 0.0) { normalize / sum => float ratio; for (0 => int i; i < num_points; i++) { points[i].Amplify(ratio); } } } // set each channel to a specific value public void Set(float gn) { for (0 => int i; i < num_points; i++) { points[i].Set(gn); } } // set left/right (stereo); note that this would over-ride fb/bt public void PanLR(float pan) { PanXYZ(pan, 0, 0); } // set front/back (fade); note that this would over-ride lr/bt public void PanFB(float pan) { PanXYZ(0, pan, 0); } // set bottom/top; note that this over-rides fb/lr, etc. public void PanBT(float pan) { PanXYZ(0, 0, pan); } public void PanXY(float x, float y) { PanXYZ(x, y, 0); } // pan channels from position x1,y1,z1 to x2,y2,z2: -1 <= x,y <= 1 public void Pan(float x1, float y1, float z1, float x2, float y2, float z2, float time_amount) { Math.fabs(x2 - x1) => float dx; Math.fabs(y2 - y1) => float dy; Math.fabs(z2 - z1) => float dz; distance(dx, dy, dz) => float delta; (delta * 25) $ int => int iterations; if (iterations <= 0) { time_amount::ms => now; return; } (x2 - x1) / iterations => dx; (y2 - y1) / iterations => dy; (z2 - z1) / iterations => dz; time_amount / iterations => float time_chunk; for (; iterations > 0; iterations--) { PanXYZ(x1, y1, z1); x1 + dx => x1; y1 + dy => y1; z1 + dz => z1; time_chunk::ms => now; time_amount - time_chunk => time_amount; } PanXYZ(x2, y2, z2); time_amount::ms => now; } public void debug_gains() { for (0 => int i; i < num_points; i++) { points[i].debug_gains(); } } } class ASndBuf extends ChannelSphere { SndBuf my_buf; Gain g; SetChannelRadius(.5); SetNormalizeValue(1.0); public void init(float gain, string file_name, NChannelWave out) { file_name => my_buf.read; gain => my_buf.gain; my_buf => g; gain => g.gain; c_connect(g, out); } public void SetGain(float gain) { gain => g.gain; } public void play() { 0 => my_buf.pos; } public void end() { my_buf.samples() => my_buf.pos; } } // // this class sits on top of a sound buffer to help facilitate my drone-buffering // defined below // class MySndData { SndBuf my_buf; public void open(string file_name) { file_name => my_buf.read; end(); } public void end() { my_buf.samples() => my_buf.pos; } public int at_end() { return my_buf.pos() >= my_buf.samples(); } public void connect( UGen ugen, float gain ) { my_buf => ugen; set_gain(gain); } public void play( int buf_pos ) { buf_pos => my_buf.pos; } public void play() { play(0); } public void playfromcurrentpos() { play(my_buf.pos()); } public void set_gain(float gain) { gain => my_buf.gain; } } // class PitchData // // this class builds a drone on top of a sound buffer. A drone simply keeps playing // from a given position for 'meter' duration // class DroneData extends MySndData { 0::ms => dur drone_meter; 0 => int buf_pos; 10 => int first_sample; Shred @the_drone; public void connect( UGen ugen, float gain ) { my_buf => ugen; SetGain(gain); } public void connect( UGen ugen, float gain, int nsample ) { connect(ugen, gain); nsample => first_sample; } public void DronePlay(int buf_pos) { // if play event would go past end of buffer, move to buffer beginning if (drone_meter > 0::ms && buf_pos > 0) { my_buf.samples() => float f; buf_pos / f => f; f * my_buf.length() => dur used_length; my_buf.length() - used_length => dur remaining_length; if (drone_meter > remaining_length) { 0 => buf_pos; } } play(buf_pos); } public void SetGain( float gain ) { set_gain(gain); } public float GetGain() { return my_buf.gain(); } public void SetMeter(dur meter_time) { if (meter_time < 3::ms ) { <<< "meter time too short for envelope" >>> ; return; } meter_time => drone_meter; } public void Drone(dur meter_time) { SetMeter(meter_time); while (true) { DronePlay(first_sample); drone_meter => now; } } } // class DroneData 0 => int overtone_type_fundamental; 1 => int overtone_type_standard; 2 => int overtone_type_odd; 3 => int overtone_type_offset; 4 => int overtone_type_additive; 5 => int overtone_type_variable; 6 => int overtone_type_M7; 7 => int overtone_type_m7; public float scale_pitch_to_freq2(float pitch, float tuning, int scale) { (tuning * scale) - scale => tuning; (pitch + tuning) / (scale * 1.0) => float f; return Math.pow(2, f); } public float scale_pitch_to_freq(float pitch, int scale) { return scale_pitch_to_freq2(pitch, 1.0, scale); } class OverTones { // must correspond to # of drones in multi-drone class below float v[num_overtones]; // standard partial series public void SetStdPartials(float ot) { ot => float dt; for (0 => int i; i < v.cap(); i++) { ot => v[i]; ot + dt => ot; } } // odd partial series public void SetOddPartials(float ot) { ot => float dt; for (0 => int i; i < v.cap(); i++) { ot => v[i]; ot + (dt * 2) => ot; } } // offset partial series public void SetOffsetPartials(float ot, float offset) { ot => float dt; for (0 => int i; i < v.cap(); i++) { ot => v[i]; ot + offset => ot; } } // additive partial series public void SetAdditivePartials(float ot, float additive) { ot => float dt; for (0 => int i; i < v.cap(); i++) { ot => v[i]; ot + ot * additive => ot; } } // variable partial series public void SetVariablePartials(float ot, float dx) { for (0 => int i; i < v.cap(); i++) { ot => v[i]; ot * dx => ot; } } // fundamental for all pitches public void SetFundamentals(float ot) { for (0 => int i; i < v.cap(); i++) { ot => v[i]; } } // Major 7th Chord public void SetMajor7th(float ot, int scale) { ot => v[0]; ot + scale_pitch_to_freq(M3, scale) => v[1]; ot + scale_pitch_to_freq(P5, scale) => v[2]; ot + scale_pitch_to_freq(M7, scale) => v[3]; } // Minor 7th Chord public void SetMinor7th(float ot, int scale) { ot => v[0]; ot + scale_pitch_to_freq(m3, scale) => v[1]; ot + scale_pitch_to_freq(P5, scale) => v[2]; ot + scale_pitch_to_freq(m7, scale) => v[3]; } public void DebugOut() { // <<< v[0], v[1], v[2], v[3], v[4] >>>; <<< v[0], v[1], v[2], v[3] >>>; } public void Set(OverTones new_ot) { for (0 => int i; i < new_ot.v.cap(); i++) { new_ot.v[i] => v[i]; } } } 0 => int timbre_type_linear; 1 => int timbre_type_poly; 2 => int timbre_type_inv_poly; Math.pow(2, 31) - 1 => float decay_max; decay_max $ int => int timbre_max_decay; class Timbre { int attack_dur; int attack_type; int decay_dur; int decay_type; int overtone_type; OverTones ovt; SetDefaults(0, diatonic_scale); // these could of course be parameterized also to customize... private float OffsetFunction(int pitch) { return (pitch + 1.0) / 1.666666667; } private float VariableFunction(int pitch) { return (pitch + 0.33) / 3.33333333; } public void Set(Timbre new_timbre) { new_timbre.attack_dur => attack_dur; new_timbre.attack_type => attack_type; new_timbre.decay_dur => decay_dur; new_timbre.decay_type => decay_type; new_timbre.overtone_type => overtone_type; ovt.Set(new_timbre.ovt); } public void SetDefaults(int pitch, int scale) { 100 => attack_dur; timbre_type_linear => attack_type; timbre_max_decay => decay_dur; timbre_type_linear => decay_type; scale_pitch_to_freq(pitch, scale) => float ot; ovt.SetFundamentals(ot); overtone_type_fundamental => overtone_type; } public void SetPitch(float tuning, int pitch, int scale) { scale_pitch_to_freq2(pitch, tuning, scale) => float ot; if (overtone_type == overtone_type_fundamental) { ovt.SetFundamentals(ot); } else if (overtone_type == overtone_type_standard) { ovt.SetStdPartials(ot); } else if (overtone_type == overtone_type_odd) { ovt.SetOddPartials(ot); } else if (overtone_type == overtone_type_offset) { ovt.SetOffsetPartials(ot, OffsetFunction(pitch)); } else if (overtone_type == overtone_type_additive) { ovt.SetAdditivePartials(ot, .333333); } else if (overtone_type == overtone_type_variable) { ovt.SetVariablePartials(ot, VariableFunction(pitch)); } else if (overtone_type == overtone_type_M7) { ovt.SetMajor7th(ot, scale); } else if (overtone_type == overtone_type_m7) { ovt.SetMinor7th(ot, scale); } else { <<< "Timbre:SetPitch:: Error: Unknown type" >>>; } } public float FundamentalFrequency() { return ovt.v[0]; } } class Stack { 0 => int top; float data[4]; public float TOS() { if (top < 0) <<< "TOS: error" >>>; return data[top]; } public void Set(float d) { if (top < 0) <<< "TOS: error" >>>; d => data[top]; } public void Push() { if (top++ >= data.cap()) <<< "Push: error" >>>; if (top > 1) <<< "Push: top:", top >>>; } public void Pop() { if (--top < 0) <<< "Pop: error" >>>; } } // // this class builds a more complex drone by grouping N buffer drones above, // and playing the N drones together with separate yet congruent meters. The // meters converge at the given 'meter' in the UpdateMeter function. // // class FooBar { public void set(dur i, dur j, float k, dur l) { } public void keyOn() { } public void keyOff() { } } class MultiWarpDrone extends ChannelSphere { // one drone for each partial in the overtone series DroneData drones[num_overtones]; Stack d_radius; d_radius.Set(1.0); Stack d_gain; d_gain.Set(0.0); TwoPole z; 1 => z.norm; 1.0 => z.gain; 1.0 => z.radius; // the drones create a nasty dc offset -- use a polezero to adjust PoleZero pz; .99 => pz.blockZero; Echo ec; 0.6 => ec.gain; // let's adjust this! JCRev jcr; .3 => jcr.mix; 1 => int echo_meter; Timbre timbre; 1.0 => float xdrone_tuning; 0.0 => float xdrone_tuning_constant; ADSR evlp; evlp.set(10::ms, 10::ms, .5, 500::ms); public void connect(float gain, int e_meter, int first_sample, NChannelWave out) { z => jcr => pz => ec => evlp; c_connect(evlp, out); // max radius on channels (channel overlap) SetChannelRadius(1.0); SetNormalizeValue(1.0); 0::ms => ec.max => ec.delay; gain => ec.gain; for (0 => int i; i < drones.cap(); i++ ) { drones[i].connect(z, gain, first_sample); } SetEcho(e_meter); SetRadius(1.0); SetGain(gain); SetPitch(0); } public void connect(float gain, int e_meter, NChannelWave out) { connect(gain, e_meter, 10, out); } public void open(string path) { for (0 => int i; i < drones.cap(); i++) { drones[i].open(path); } } // e-meter is a sub-meter as a function of our drone meter // OR (if > 10), is a separate sub-meter independent of drone meter private int echo_is_constant() { return echo_meter > 10; } public void SetOvertones(OverTones ot) { if (drones.cap() != ot.v.cap()) { <<< "error: SetOvertones: dronescap != ot.cap" >>>; me.exit(); } timbre.ovt.Set(ot); UpdateOvertones(); } public void UpdateOvertones() { // timbre.ovt.DebugOut(); // cut every partial by half so that we don't run out of buffer (adjust default register down an octave) for (0 => int i; i < drones.cap(); i++) { timbre.ovt.v[i] / 2.0 => drones[i].my_buf.rate; } } public void SetTimbre(int pitch, int scale) { timbre.SetDefaults(pitch, scale); SetOvertones(timbre.ovt); } public void SetTimbre(Timbre new_timbre) { timbre.Set(new_timbre); SetOvertones(timbre.ovt); } public void SetEcho(int e_meter) { e_meter => echo_meter; if (echo_is_constant()) { e_meter::ms => ec.max => ec.delay; } else { // echo meter will be set when we UpdateMeter for drone } } public void SetMeter(dur meter, int slice) { // check to see if we update echo meter too (as a function of meter/slice) if (!echo_is_constant()) { (meter / slice) / echo_meter => ec.max => ec.delay; } for (0 => int i; i < drones.cap(); i++) { meter / (slice - i) => dur current; drones[i].SetMeter(current); } } public void SetPitch(int pitch, int scale) { if (pitch < 0) { <<< "error set pitch", pitch >>>; 0 => pitch; } timbre.SetPitch(xdrone_tuning + xdrone_tuning_constant, pitch, scale); UpdateOvertones(); } // default: diatonic public void SetPitch(int pitch) { SetPitch(pitch, diatonic_scale); } public void TuneToFreq(float new_tuning) { new_tuning => xdrone_tuning; } public void TuneToPitch(int pitch, int scale) { TuneToFreq(scale_pitch_to_freq(pitch, scale)); } // default is The_Scale (typically diatonic) public void TuneToPitch(int pitch) { TuneToPitch(pitch, diatonic_scale); } public void SetEnvelope(int attack_dur, int decay_dur, float sustain_level, int attack_type, int decay_type) { attack_dur => timbre.attack_dur; attack_type => timbre.attack_type; decay_dur => timbre.decay_dur; decay_type => timbre.decay_type; decay_dur => int release; if (decay_type == timbre_type_poly) { release / 5 => release; } else if (decay_type == timbre_type_linear) { } else if (decay_type == timbre_type_inv_poly) { release * 3 => release; } else { <<< "error: SetEnvelope: unknown decay type", decay_type >>>; } evlp.set(attack_dur::ms, decay_dur::ms, sustain_level, release::ms); } public void SetRadius( float radius ) { if (radius < 0.0 || radius > 1.0) { <<< "radius error ", radius >>>; return; } // radius / .4 => jcr.mix; radius => z.radius; d_radius.Set(radius); } public float GetRadius() { return d_radius.TOS(); } public void PushRadius(float r) { d_radius.Push(); SetRadius(r); } public void PopRadius() { d_radius.Pop(); SetRadius(d_radius.TOS()); } public void SetGain(float gain) { gain => ec.gain; d_gain.Set(gain); for (0 => int i; i < drones.cap(); i++) { drones[i].SetGain(gain); // this 1.2 parameter is important -- each sequential drone in the array has a lower gain gain / 1.2 => gain; } } public float GetGain() { return d_gain.TOS(); } public void PushGain(float gain) { d_gain.Push(); SetGain(gain); } public void PopGain() { d_gain.Pop(); SetGain(d_gain.TOS()); } public void Drone(dur meter, int slice) { SetMeter(meter, slice); // spoke the drones, each with a slice-ly reduced meter for (0 => int i; i < drones.cap(); i++) { meter / (slice - (1 * i)) => dur current; spork ~drones[i].Drone(current) @=> drones[i].the_drone; } } public void Drone(dur meter, int echo, int slice) { SetEcho(echo); Drone(meter, slice); } public void KillDrone() { for (0 => int i; i < drones.cap(); i++) { drones[i].end(); Machine.remove(drones[i].the_drone.id()); } } // use radius on two pole to simulate attack and decay public void AttackLinear(int attack_dur) { spork ~evlp.keyOn(); for (attack_dur - 1 => int i; i > 0; i--) { SetRadius((i * 1.0) / attack_dur); 1::ms => now; } 1::ms => now; } public void DecayLinear(int decay_dur) { for (1 => int i; i < decay_dur; i++) { SetRadius((i * 1.0) / decay_dur); 1::ms => now; } spork ~evlp.keyOff(); 1::ms => now; } public void AttackPoly(int attack_dur) { spork ~evlp.keyOn(); Math.pow(attack_dur, 2) => float ad; for (attack_dur - 1 => int i; i > 0; i--) { SetRadius(Math.pow(i, 2) / ad); 1::ms => now; } 1::ms => now; } public void DecayPoly(int decay_dur) { Math.pow(decay_dur, 2) => float dd; for (1 => int i; i < decay_dur; i++) { SetRadius(Math.pow(i, 2) / dd); 1::ms => now; } spork ~evlp.keyOff(); 1::ms => now; } public void AttackInvPoly(int attack_dur) { spork ~evlp.keyOn(); Math.pow(attack_dur, 1.0 / 2.0) => float ad; for (attack_dur - 1 => int i; i > 0; i--) { SetRadius(Math.pow(i, 1.0 / 2.0) / ad); 1::ms => now; } 1::ms => now; } public void DecayInvPoly(int decay_dur) { Math.pow(decay_dur, 1.0 / 2.0) => float dd; for (1 => int i; i < decay_dur; i++) { SetRadius(Math.pow(i, 1.0 / 2.0) / dd); 1::ms => now; } spork ~evlp.keyOff(); 1::ms => now; } // default is to do nothing, but derived classes can override public void DynamicOvertone(int decay_dur) { } // this utility function varies the gains across the overtone series using a sin curve public void SinusoidalDynamicOvertone(int decay_dur) { // i'd do dynamic allocation but w/o garbage collect what's the point...? drones.cap() => int count; float save_gains[10]; if (count > save_gains.cap()) { <<< "error: dynamicovertone: array too small" >>>; me.exit(); } drones[0].GetGain() / 2 => float default_gain; for (0 => int k; k < count; k++) { drones[k].GetGain() => save_gains[k]; } 0 => int j; for (0 => int d; d < decay_dur / 10; d++) { for (0 => int i; i < count; i++) { Math.sin(((i + 1.0) / (count)) * pi) => float f; (f * default_gain) + default_gain => f; drones[(i + j) % count].SetGain(f); } // <<< drones[0].GetGain(), drones[1].GetGain(), drones[2].GetGain(), drones[3].GetGain(), drones[4].GetGain(), drones[5].GetGain() >>>; (j + 1) % count => j; 10::ms => now; } // just for cleanliness sake (decay_dur % 10)::ms => now; for (0 => int k; k < count; k++) { drones[k].SetGain(save_gains[k]); } } public void Play(int pitch, int scale, int length) { Play(pitch, scale, length, false); } public void PlaySustain(int pitch, int scale, int length) { Play(pitch, scale, length, true); } // default play function public void Play(int pitch, int scale, int length, int sustain) { PushRadius(.99); SetPitch(pitch, scale); min(timbre.attack_dur, length - 10) => int attack_dur; length - attack_dur => int decay_dur; if (!sustain) { min(timbre.decay_dur, length - attack_dur) => decay_dur; } now => time a_time; if (timbre.attack_type == timbre_type_linear) { AttackLinear(attack_dur); } else if (timbre.attack_type == timbre_type_poly) { AttackPoly(attack_dur); } else if (timbre.attack_type == timbre_type_inv_poly) { AttackInvPoly(attack_dur); } else { <<< "MultiWarpDrone:Play:: attack type error", timbre.attack_type >>>; } spork ~DynamicOvertone(decay_dur); if (timbre.decay_type == timbre_type_linear) { DecayLinear(decay_dur); } else if (timbre.decay_type == timbre_type_poly) { DecayPoly(decay_dur); } else if (timbre.decay_type == timbre_type_inv_poly) { DecayInvPoly(decay_dur); } else { <<< "MultiWarpDrone:Play:: decay type error", timbre.decay_type >>>; } (length - (attack_dur + decay_dur))::ms => now; PopRadius(); } } // MultiWarpDrone // PanWarpDrone understands how to 'warp' the drone -- an iterative linear transformation // of the sound buffers, in effect moving the gain and z-radius of a two-pole filter. The // class also understands how to Pan the location of a sound source. For example, PanRWarp // will 'play' the drone in a circle at origin (x,y) and radius (r) defined w/i the panning // plan of (-1, -1, 1, 1) -> (left, front, right, back) class PanWarpDrone extends MultiWarpDrone { // pan across radius r starting a phase p w/ origin x,y for time_amount public void PanRWarp(float o_x, float o_y, float z1, float r, float p, int counter_clockwise, float time_amount) { (time_amount / 50) $ int => int iterations; if (iterations <= 0) { time_amount::ms => now; return; } time_amount / iterations => float time_chunk; p => float theta; 2 * pi / (iterations * 1.0) => float theta_chunk; if (!counter_clockwise) theta_chunk * -1 => theta_chunk; for (0 => int i; i < iterations; i++) { o_x + r * Math.cos(theta) => float x1; o_y + r * Math.sin(theta) => float y1; PanXYZ(x1, y1, z1); time_chunk::ms => now; time_amount - time_chunk => time_amount; theta + theta_chunk => theta; } time_amount::ms => now; } // move from start to stop radius over duration time_chunk (in ms) public void RadiusWarp(float stop, float time_amount) { GetRadius() => float start; stop - start => float delta; // compute number of times to iterate loop (Math.fabs(delta) * 100.0) $ int => int iterations; // test edge case if (iterations <= 0) { SetRadius(stop); time_amount::ms => now; return; } time_amount / iterations => float time_chunk; delta / iterations => delta; for (; iterations > 0; iterations--) { SetRadius(start); start + delta => start; time_chunk::ms => now; time_amount - time_chunk => time_amount; } SetRadius(stop); time_amount::ms => now; } // move gain & radius in parallel to 'stop' value public void Warp(float stop, float time_amount) { GetGain() => float gain_start; stop - gain_start => float g_delta; // radius has an inverse relationship to gain GetRadius() => float radius_start; (1 - stop) - radius_start => float r_delta; // compute number of times to iterate loop (Math.min(Math.fabs(g_delta), Math.fabs(r_delta)) * 100.0) $ int => int iterations; // test edge case if (iterations <= 0) { // UpdateRadius(1 - stop); // UpdateGain(stop); time_amount::ms => now; return; } time_amount / iterations => float time_chunk; g_delta / iterations => g_delta; r_delta / iterations => r_delta; for (; iterations > 1; iterations--) { SetRadius(radius_start); radius_start + r_delta => radius_start; SetGain(gain_start); gain_start + g_delta => gain_start; time_chunk::ms => now; time_amount - time_chunk => time_amount; } SetRadius(1 - stop); SetGain(stop); time_amount::ms => now; } // max-out radius (ring), but iterate across gain for time_amount public void Ring(float stop, float time_amount) { GetGain() => float gain_start; stop - gain_start => float g_delta; // compute number of times to iterate loop (Math.fabs(g_delta) * 100.0) $ int => int iterations; // ring the drone SetRadius(1.0); // test edge case if (iterations <= 0) { SetGain(stop); time_amount::ms => now; return; } time_amount / iterations => float time_chunk; g_delta / iterations => g_delta; for (; iterations > 1; iterations--) { SetGain(gain_start); gain_start + g_delta => gain_start; time_chunk::ms => now; time_amount - time_chunk => time_amount; } SetGain(stop); time_amount::ms => now; } public void PanOscillate(float x1, float y1, float z1, float x2, float y2, float z2, float time_amount, float delay, int oscillations) { while (oscillations > 0) { delay::ms => now; Pan(x1, y1, z1, x2, y2, z2, time_amount / 2); Pan(x2, y2, z2, x1, y1, z1, time_amount / 2); --oscillations; } } public void Oscillate(float start, float stop, float time_amount, float delay, int oscillations) { while (oscillations > 0) { delay::ms => now; Warp(start, time_amount / 2); Warp(stop, time_amount / 2); --oscillations; } } public void CircleX(float z1, float radius, float theta, int clock_wise, float cur_bar, float total_time) { while (total_time > 0) { spork ~PanRWarp( 0.0, 0.0, z1, radius, theta, clock_wise, cur_bar); cur_bar::ms => now; total_time - cur_bar => total_time; } } public void CircleWarp(float z1, float mg, float theta, int clock_wise, float cur_bar, float total_time) { while (total_time > 0) { spork ~PanRWarp( 0.0, 0.0, z1, room_radius, theta, clock_wise, cur_bar); spork ~Oscillate( 0.1, mg, cur_bar, 0, 1); cur_bar::ms => now; total_time - cur_bar => total_time; } } public void PanRand(float cur_bar, int num_pans) { while (num_pans > 0) { Std.rand2f(room_left, room_right) => float x; Std.rand2f(room_front, room_back) => float y; Std.rand2f(room_bottom, room_top) => float z; PanXYZ(x, y, z); cur_bar::ms => now; --num_pans; } } // helix public void PanHelix(float time_chunk, int iterations, int panning_up) { // set up circle origin and radius 1.0 => float r; // we'll have each sound source follow by phase pi/2 pi => float phase_1; float phi; phi / (iterations + 1.0) => float phi_delta; if (panning_up) { pi => phi; } else { 0.0 => phi; phi_delta * -1.0 => phi_delta; } for (0 => int i; i <= iterations; i++) { ((i * 1.0) / iterations) * 2 * pi => float t; r * Math.cos(t + phase_1) => float x; r * Math.sin(t + phase_1) => float y; r * Math.cos(phi) => float z; phi - phi_delta => phi; PanXYZ(x, y, z); time_chunk::ms => now; } } // ascending helix public void PanHelixWarp(float time_chunk, float mg, int iterations) { spork ~Warp(mg, time_chunk * iterations); PanHelix(time_chunk, iterations, true); } } // PanWarp class LawnMowerDrone extends PanWarpDrone { [ 8,12, 6,5, 8,16, 8,4, 9,18, 5,17 ] @=> int pairs[]; 10 => int j; public void set_timbre() { timbre.SetDefaults(0, diatonic_scale); SetEnvelope(255, 1355, .5, timbre_type_linear, timbre_type_poly); overtone_type_offset => timbre.overtone_type; SetPitch(0, diatonic_scale); } public void set_drone_timbre() { timbre.SetDefaults(0, diatonic_scale); SetEnvelope(500, timbre_max_decay, .8, timbre_type_linear, timbre_type_inv_poly); overtone_type_fundamental => timbre.overtone_type; SetPitch(0, diatonic_scale); } public void init(float bar_length, NChannelWave ncw) { open("data/lawnmowerz.wav"); // a4+ 0.453 - 0.122462 => xdrone_tuning_constant; connect(0.00, 5, ncw); SetNormalizeValue(1.0); Rotate(bar_length); set_timbre(); } public void Rotate(float bar_length) { SetEcho(pairs[j]); Drone(bar_length::ms , pairs[j+1]); (j + 2) % pairs.cap() => j; } } class WashingMachineDrone extends PanWarpDrone { [ 300,12, 90,8, 5,17 ] @=> int pairs[]; // 300,7, // 50,13, // 90,8, // 9,18, pairs.cap() - 2 => int wmc_j; public void set_timbre() { timbre.SetDefaults(0, diatonic_scale); SetEnvelope(500, timbre_max_decay, .8, timbre_type_linear, timbre_type_inv_poly); overtone_type_fundamental => timbre.overtone_type; SetPitch(0, diatonic_scale); } public void init(float bar_length, NChannelWave ncw) { init(bar_length, ncw, wmc_j); } public void init(float bar_length, NChannelWave ncw, int rotation) { rotation => wmc_j; open("data/washingmachinez.wav"); // M2 0.122462 - 0.122462 => xdrone_tuning_constant; connect(0.00, 300, ncw); SetNormalizeValue(1.50); set_timbre(); Rotate(bar_length); } public void Rotate(float bar_length) { SetEcho(pairs[wmc_j]); Drone(bar_length::ms , pairs[wmc_j+1]); (wmc_j + 2) % pairs.cap() => wmc_j; } } class XBoxDrone extends PanWarpDrone { public void set_timbre() { timbre.SetDefaults(0, diatonic_scale); SetEnvelope(87, 615 , .4, timbre_type_inv_poly, timbre_type_inv_poly); overtone_type_variable => timbre.overtone_type; SetPitch(0, diatonic_scale); } public void init(float bar_length, NChannelWave ncw) { open("data/xbox360z.wav"); // m7+ 0.80 - 0.122462 => xdrone_tuning_constant; connect(0.00, 60, ncw); Drone(bar_length::ms, primes[4]); SetNormalizeValue(1.0); set_timbre(); } } class MixerDrone extends PanWarpDrone { public void set_timbre() { timbre.SetDefaults(0, diatonic_scale); SetEnvelope(85, 2555 , .4, timbre_type_poly, timbre_type_inv_poly); overtone_type_additive => timbre.overtone_type; SetPitch(0, diatonic_scale); } // mixer uses a sinusoidal relationship across overtone series public void DynamicOvertone(int decay_dur) { SinusoidalDynamicOvertone(decay_dur); } public void init(float bar_length, NChannelWave ncw) { open("data/mixmaster2z.wav"); // m3+ 0.227462 - 0.122462 => xdrone_tuning_constant; connect(0.00, 90, ncw); Drone(bar_length::ms, 7); SetNormalizeValue(1.0); set_timbre(); } } class TrimmerDrone extends PanWarpDrone { public void set_timbre() { timbre.SetDefaults(0, diatonic_scale); SetEnvelope(65, 365 , .2, timbre_type_poly, timbre_type_linear); overtone_type_odd => timbre.overtone_type; SetPitch(0, diatonic_scale); } public void init(float bar_length, NChannelWave ncw) { open("data/hedgetrimmerz.wav"); // m3 0.259921 - 0.122462 => xdrone_tuning_constant; connect(0.00, 15, 20, ncw); Drone(bar_length::ms, primes[4]); SetNormalizeValue(1.0); set_timbre(); } } class Timer { 0.0 => float tm; public float Set(float t) { t => tm; return t; } public float Get() { return tm; } public float Count() { tm::ms => now; 0 => tm; return tm; } public float Count(float t) { if (t > tm) { <<< "counting error", tm - t >>>; return 0.0; } tm - t => tm; t::ms => now; return tm; } } public void MotifA(PanWarpDrone dr, int quarter_note, int pitch, int scale) { dr.Play(pitch, scale, quarter_note); dr.Play(pitch + P4, scale, quarter_note); dr.Play(pitch + M3, scale, quarter_note * 2); dr.Play(pitch + m3, scale, quarter_note / 2); dr.Play(pitch + P4, scale, quarter_note / 2); dr.Play(pitch + M2, scale, quarter_note / 2); dr.Play(pitch, scale, quarter_note * 3 / 2); } public void A1Alto(PanWarpDrone dr, int pitch, int quarter_note, int scale) { spork ~dr.PlaySustain(pitch + P8 + m3, scale, quarter_note*4 - 1); (quarter_note*4)::ms => now; spork ~dr.PlaySustain(pitch + P8 + M2, scale, quarter_note*3 + 2); (quarter_note*4)::ms => now; spork ~dr.PlaySustain(pitch + P8 + P4, scale, quarter_note*4 - 1); (quarter_note*4)::ms => now; spork ~dr.PlaySustain(pitch + P8 + P5, scale, quarter_note*4 - 1); (quarter_note*4)::ms => now; spork ~dr.PlaySustain(pitch + m3, scale, quarter_note*1 + 2); (quarter_note*4)::ms => now; // -1 => work around miniaudicle bug on time! spork ~dr.PlaySustain(pitch + m6, scale, quarter_note*21/5 - 1); (quarter_note*21/5)::ms => now; spork ~dr.PlaySustain(pitch + P5, scale, quarter_note*8 - 1); (quarter_note*8)::ms => now; } public void A1Tenor1(PanWarpDrone dr, int pitch, int quarter_note, int scale) { dr.Play(pitch + P8, scale, quarter_note*8 + 1); dr.Play(pitch + m6, scale, quarter_note*8 - 1); dr.Play(pitch + P5, scale, quarter_note*8 + 1); dr.Play(pitch + m7, scale, quarter_note*5 - 1); quarter_note::ms => now; } public void A1Tenor2(PanWarpDrone dr, int pitch, int quarter_note, int scale) { dr.Play(pitch + P8 + P5, scale, quarter_note*8 - 1); dr.Play(pitch + m6, scale, quarter_note*8 + 1); dr.Play(pitch + P5, scale, quarter_note*4 - 1); dr.Play(pitch + P8, scale, quarter_note*26/5 + 1); dr.Play(pitch + m7, scale, quarter_note*7 - 1); quarter_note::ms => now; } public void A1Bass(PanWarpDrone dr, int pitch, int quarter_note, int scale) { pitch + P8 => pitch; // rest (quarter_note*4)::ms => now; dr.Play(pitch + P4, scale, quarter_note*4 + 1); dr.Play(pitch + m3, scale, quarter_note*8 - 1); dr.Play(pitch, scale, quarter_note*4 + 2); dr.Play(pitch + P5, scale, quarter_note*24/5 - 1); dr.Play(pitch + P5, scale, quarter_note*7 - 1); } public void A1(PanWarpDrone alto, PanWarpDrone tenor1, PanWarpDrone tenor2, PanWarpDrone bass, int key, int quarter, int scale) { alto.TuneToPitch(key + P8); tenor1.TuneToPitch(key + P8); tenor2.TuneToPitch(key + P8); bass.TuneToPitch(key); spork ~A1Alto(alto, key, quarter, scale); spork ~A1Tenor1(tenor1, key, quarter, scale); spork ~A1Tenor2(tenor2, key, quarter, scale); A1Bass(bass, key, quarter, scale); (quarter * 2)::ms => now; } public void MajorTriad(PanWarpDrone alto, PanWarpDrone tenor1, PanWarpDrone bass, int key, int quarter, int scale) { spork ~bass.Play(key + 0, scale, quarter); spork ~tenor1.Play(key + M3, scale, quarter); spork ~alto.Play(key + P5, scale, quarter); quarter::ms => now; } public void MajorTriadPanDown(PanWarpDrone alto, PanWarpDrone tenor1, PanWarpDrone bass, int key, int quarter, int scale) { spork ~alto.Pan(room_left / 2, room_front, room_top, room_left / 2, room_front, room_bottom, quarter); spork ~tenor1.Pan(room_right / 2, room_front, room_top, room_right / 2, room_front, room_bottom, quarter); spork ~bass.Pan(0, room_back, room_top, 0, room_back, room_bottom, quarter); MajorTriad(alto, tenor1, bass, key, quarter, scale); } public void MajorTriadPanUp(PanWarpDrone alto, PanWarpDrone tenor1, PanWarpDrone bass, int key, int quarter, int scale) { spork ~alto.Pan(room_left / 2, room_front, room_bottom, room_left / 2, room_front, room_top, quarter); spork ~tenor1.Pan(room_right / 2, room_front, room_bottom, room_right / 2, room_front, room_top, quarter); spork ~bass.Pan(0, room_back, room_bottom, 0, room_back, room_top, quarter); MajorTriad(alto, tenor1, bass, key, quarter, scale); } /* public void MinorTriad(PanWarpDrone alto, PanWarpDrone tenor1, PanWarpDrone bass, int key, int quarter, int scale) { spork ~bass.Play(key + 0, scale, quarter); spork ~tenor1.Play(key + P5, scale, quarter); spork ~alto.Play(key + P8 + m3, scale, quarter); quarter::ms => now; } public void SchumannA1(PanWarpDrone alto, PanWarpDrone tenor, int key, int quarter, int scale) { alto.TuneToPitch(key + P8); tenor.TuneToPitch(key); spork ~alto.Play(P8 + M3, scale, quarter); spork ~tenor.Play(P1, scale, quarter); (quarter + 1)::ms => now; spork ~alto.PlaySustain(P8 + M2, scale, quarter * 3 - 3); spork ~tenor.Play(P4, scale, quarter * 3 - 3); (quarter*3 - 1)::ms => now; spork ~alto.Play(P8 + M3, scale, quarter); spork ~tenor.Play(P1, scale, quarter); (quarter + 1)::ms => now; spork ~alto.PlaySustain(P8 + M2, scale, quarter * 3 - 3); spork ~tenor.Play(P4, scale, quarter * 3 - 3); (quarter*3 - 1)::ms => now; spork ~alto.Play(P8 + M3, scale, quarter); spork ~tenor.Play(P1, scale, quarter); (quarter)::ms => now; spork ~alto.Play(P8 + M2, scale, quarter); spork ~tenor.Play(P4, scale, quarter); (quarter*1)::ms => now; spork ~alto.Play(P8 + M3, scale, quarter); spork ~tenor.Play(M3, scale, quarter); (quarter*1)::ms => now; spork ~alto.Play(P8 + P4, scale, quarter); spork ~tenor.Play(M2, scale, quarter * 3); (quarter*1)::ms => now; spork ~alto.Play(P8 + M2, scale, quarter); (quarter*1)::ms => now; spork ~alto.Play(M6, scale, quarter); (quarter*1)::ms => now; spork ~alto.Play(P8 + M2, scale, quarter); spork ~tenor.Play(P4, scale, quarter); (quarter)::ms => now; spork ~alto.Play(P8 + P1, scale, quarter); spork ~tenor.Play(M3, scale, quarter); (quarter)::ms => now; } public void SchumannA2(PanWarpDrone alto, PanWarpDrone tenor, int key, int quarter, int scale) { alto.TuneToPitch(key + P8); tenor.TuneToPitch(key); spork ~alto.Play(P8, scale, quarter); spork ~tenor.Play(M6, scale, quarter); (quarter + 1)::ms => now; spork ~alto.PlaySustain(M7, scale, quarter * 3 - 3); spork ~tenor.Play(P8 + M2, scale, quarter * 3 - 3); (quarter*3 - 1)::ms => now; spork ~alto.Play(P8, scale, quarter); spork ~tenor.Play(M6, scale, quarter); (quarter + 1)::ms => now; spork ~alto.PlaySustain(M7, scale, quarter * 3 - 3); spork ~tenor.Play(P8 + M2, scale, quarter * 3 - 3); (quarter*3 - 1)::ms => now; spork ~alto.Play(P8, scale, quarter); spork ~tenor.Play(M6, scale, quarter); (quarter)::ms => now; spork ~alto.Play(M7, scale, quarter); spork ~tenor.Play(P8 + M2, scale, quarter); (quarter*1)::ms => now; spork ~alto.Play(P8, scale, quarter); spork ~tenor.Play(P8, scale, quarter); (quarter*1)::ms => now; spork ~alto.Play(P8 + M2, scale, quarter); spork ~tenor.Play(M7, scale, quarter * 3); (quarter*1)::ms => now; spork ~alto.Play(M7, scale, quarter); (quarter*1)::ms => now; spork ~alto.Play(m6, scale, quarter); (quarter*1)::ms => now; spork ~alto.Play(M7, scale, quarter); spork ~tenor.Play(P8 + M2, scale, quarter); (quarter)::ms => now; spork ~alto.Play(M6, scale, quarter); spork ~tenor.Play(P8, scale, quarter); (quarter)::ms => now; } public void Twinkle(PanWarpDrone dr, int key, int scale, int quarter_note) { dr.Play(key + P1,scale, quarter_note); dr.Play(key + P1, scale, quarter_note); dr.Play(key + P5, scale, quarter_note); dr.Play(key + P5, scale, quarter_note); dr.Play(key + M6, scale, quarter_note); dr.Play(key + M6, scale, quarter_note); dr.Play(key + P5, scale, quarter_note * 2); dr.Play(key + P4, scale, quarter_note); dr.Play(key + P4, scale, quarter_note); dr.Play(key + M3, scale, quarter_note); dr.Play(key + M3, scale, quarter_note); dr.Play(key + M2, scale, quarter_note); dr.Play(key + M2, scale, quarter_note); dr.Play(key + P1, scale, quarter_note * 2); } public void Major(PanWarpDrone dr, int key, int quarter_note) { dr.Play(key + P1, 12, quarter_note); dr.Play(key + M2, 12, quarter_note); dr.Play(key + M3, 12, quarter_note); dr.Play(key + P4, 12, quarter_note); dr.Play(key + P5, 12, quarter_note); dr.Play(key + M6, 12, quarter_note); dr.Play(key + M7, 12, quarter_note); dr.Play(key + P8, 12, quarter_note); } public void NaturalMinor(PanWarpDrone dr, int key, int quarter_note) { dr.Play(key + P1, 12, quarter_note); dr.Play(key + M2, 12, quarter_note); dr.Play(key + m3, 12, quarter_note); dr.Play(key + P4, 12, quarter_note); dr.Play(key + P5, 12, quarter_note); dr.Play(key + m6, 12, quarter_note); dr.Play(key + m7, 12, quarter_note); dr.Play(key + P8, 12, quarter_note); } public void OceanA1(PanWarpDrone s, PanWarpDrone a, PanWarpDrone t, PanWarpDrone b, int key, int quarter, int scale) { s.TuneToPitch(key + P8); a.TuneToPitch(key + P8); t.TuneToPitch(key + P8); b.TuneToPitch(key); spork ~s.Play(m3, scale, quarter * 3); spork ~a.Play(P5, scale, quarter * 3); spork ~t.Play(m3, scale, quarter * 3); spork ~b.Play(P8, scale, quarter * 3); (quarter * 2)::ms => now; spork ~s.Play(M2, scale, quarter * 3); spork ~a.Play(m6, scale, quarter * 3); spork ~t.Play(P4, scale, quarter * 3); spork ~b.Play(P8, scale, quarter * 3); (quarter * 2)::ms => now; spork ~s.Play(P4, scale, quarter * 3); spork ~a.Play(m6, scale, quarter * 3); spork ~t.Play(P4, scale, quarter * 3); spork ~b.Play(P8, scale, quarter * 3); (quarter * 2)::ms => now; spork ~s.Play(m3, scale, quarter * 3); spork ~a.Play(P5, scale, quarter * 3); spork ~t.Play(m3, scale, quarter * 3); spork ~b.Play(P8, scale, quarter * 3); (quarter * 2)::ms => now; spork ~s.Play(M2, scale, quarter * 3); spork ~a.Play(m6, scale, quarter * 3); spork ~t.Play(P4, scale, quarter * 3); spork ~b.Play(P8, scale, quarter * 3); (quarter * 2)::ms => now; spork ~s.Play(m3, scale, quarter * 3); spork ~a.Play(P4, scale, quarter * 3); spork ~t.Play(M2, scale, quarter * 3); spork ~b.Play(M7, scale, quarter * 3); (quarter * 2)::ms => now; spork ~s.Play(P1, scale, quarter * 4); spork ~a.Play(P5, scale, quarter * 4); spork ~t.Play(m3, scale, quarter * 4); spork ~b.Play(P8, scale, quarter * 4); (quarter * 4)::ms => now; } public void OceanA2(PanWarpDrone s, PanWarpDrone a, PanWarpDrone t, PanWarpDrone b, int key, int quarter, int scale) { s.TuneToPitch(key + P8); a.TuneToPitch(key + P8); t.TuneToPitch(key + P8); b.TuneToPitch(key); spork ~s.Play(m3, scale, quarter * 2); spork ~a.Play(P5, scale, quarter * 2); spork ~t.Play(m3, scale, quarter * 2); spork ~b.Play(P8, scale, quarter * 2); (quarter * 2)::ms => now; spork ~s.Play(M2, scale, quarter * 2); spork ~a.Play(m6, scale, quarter * 2); spork ~t.Play(P4, scale, quarter * 2); spork ~b.Play(P8, scale, quarter * 2); (quarter * 2)::ms => now; spork ~s.Play(P4, scale, quarter * 2); spork ~a.Play(m6, scale, quarter * 2); spork ~t.Play(P4, scale, quarter * 2); spork ~b.Play(P8, scale, quarter * 2); (quarter * 2)::ms => now; spork ~s.Play(M3, scale, quarter * 2); spork ~a.Play(P5, scale, quarter * 2); spork ~t.Play(M3, scale, quarter * 2); spork ~b.Play(P8, scale, quarter * 2); (quarter * 2)::ms => now; spork ~s.Play(m6, scale, quarter * 2); spork ~a.Play(m6, scale, quarter * 2); spork ~t.Play(P4, scale, quarter * 2); spork ~b.Play(P8, scale, quarter * 2); (quarter * 2)::ms => now; spork ~s.Play(P5, scale, quarter * 2); spork ~a.Play(M7, scale, quarter * 2); spork ~t.Play(M2, scale, quarter * 2); spork ~b.Play(P5, scale, quarter * 2); (quarter * 2)::ms => now; spork ~s.Play(M3, scale, quarter * 8); spork ~a.Play(P5, scale, quarter * 8); spork ~t.Play(M3, scale, quarter * 8); spork ~b.Play(P8, scale, quarter * 8); (quarter * 8)::ms => now; } public void Compare(NChannelWave ncw, int quarter) { XBoxDrone xmd; xmd.init(bar, ncw); xmd.SetGain(0.4); Major(xmd, P1, quarter); xmd.KillDrone(); WashingMachineDrone wmcx; wmcx.init(bar, ncw); wmcx.SetGain(.6); Major(wmcx, P8 * 3, quarter); wmcx.KillDrone(); LawnMowerDrone mower1; mower1.init(bar, ncw); mower1.SetGain(dyn_mp); Major(mower1, P8 * 2, quarter); mower1.KillDrone(); MixerDrone mxd; mxd.init(bar, ncw); mxd.SetGain(.4); Major(mxd, P1, quarter); mxd.KillDrone(); TrimmerDrone txd; txd.init(bar, ncw); txd.SetGain(.5); Major(txd, P1, quarter); txd.KillDrone(); } public void Test(NChannelWave ncw, int abar) { M2 => int key; WashingMachineDrone wmc[3]; for (0 => int i; i < 3; i++) { wmc[i].init(abar, ncw); wmc[i].SetGain(dyn_mf); } LawnMowerDrone mower1; mower1.init(abar, ncw); mower1.SetGain(dyn_mp); // Major(wmc[0], P8 * 2, abar / 4); // Major(mower1, 0, abar / 4); A1(mower1, wmc[0], wmc[1], wmc[2], M2, abar / 2, 10); // OceanA1(mower1, wmc[0], wmc[1], wmc[2], M2 + P8, abar / 2, 12); // OceanA2(mower1, wmc[0], wmc[1], wmc[2], M2 + P8,abar / 2, 12); } public void Test2(NChannelWave ncw, int abar) { 0 => int key; MixerDrone wmc; wmc.init(abar, ncw); wmc.SetGain(dyn_mp); WashingMachineDrone wmx; wmx.init(abar, ncw); wmx.SetGain(dyn_mp); SchumannA1(wmx, wmc, key, abar / 4, 12); SchumannA1(wmx, wmc, key, abar / 4, 12); SchumannA2(wmx, wmc, key, abar / 4, 12); SchumannA1(wmx, wmc, key, abar / 4, 12); me.exit(); } */ // main body of shred -- The Score <<< "here we go!" >>>; NChannelWave ncw; ncw.init(true); // Test2(ncw, bar); Timer tm; bar * 1.5 $int => int cur_bar; WashingMachineDrone wmc; wmc.init(cur_bar, ncw); tm.Set(cur_bar * 18); true => int clock_wise; spork ~wmc.CircleWarp(room_bottom, .12, 0, clock_wise, tm.Get() / 2, tm.Get()); tm.Count(1000); ASndBuf kids; kids.init(.2, "data/charlie singing x.wav", ncw); kids.PanXYZ(room_left, room_back, room_top); kids.play(); tm.Count(1000); tm.Count(2500); kids.SetGain(.15); tm.Count(1000); kids.SetGain(.12); tm.Count(1200); for (1 => int k; k <= 50; k++) { tm.Count(3000 / 50); kids.SetGain(.1001 - k/500.0); } kids.end(); tm.Count(4.0 * tm.Get() / 5 - 7000); // bring trimmer in from back right to middle of room (and then send back) TrimmerDrone trimmer; trimmer.init(bar, ncw); trimmer.SetGain(dyn_pp); spork ~trimmer.Pan(room_right, room_front, room_bottom, 0, 0, 0, cur_bar / 5.0); spork ~trimmer.Warp(.275, cur_bar / 5.0); tm.Count(cur_bar / 5.0); spork ~trimmer.Pan(0, 0, 0, room_right, room_front, room_bottom, cur_bar / 5.0); spork ~trimmer.Warp(0.01, cur_bar / 5.0); tm.Count(cur_bar / 5.0); tm.Count(cur_bar * 3); <<< "wmc2" >>>; WashingMachineDrone wmc2; bar * 1.3333 $ int => cur_bar; wmc2.init(bar * 1.2, ncw); spork ~wmc2.CircleWarp(room_bottom, .2, pi / 4, clock_wise, cur_bar * 2, cur_bar * 14); tm.Set(cur_bar * 14 - tm.Count()); spork ~wmc.Warp(0.01, cur_bar); tm.Count(8.1 * cur_bar); <<< "tm2" >>>; spork ~trimmer.Pan(room_right, room_front, room_bottom, 0, 0, 0, cur_bar / 6.0); spork ~trimmer.Warp(.35, cur_bar / 6.0); tm.Count(cur_bar / 6.0); spork ~trimmer.Pan(0, 0, 0, room_right, room_front, room_bottom, cur_bar / 6.0); spork ~trimmer.Warp(0.05, cur_bar / 3.0); tm.Count(cur_bar / 3.0); tm.Count(cur_bar); <<< "mower1" >>>; // initiate transition phrase: mower LawnMowerDrone mower1; mower1.init(bar, ncw); mower1.set_drone_timbre(); now => time cur_time; spork ~mower1.PanHelixWarp(bar / 8, dyn_pp, 16 * 8); tm.Count(cur_bar / 3.0); <<< "wmc3" >>>; bar * 1.1 $ int => cur_bar; wmc.KillDrone(); wmc.init(cur_bar, ncw); spork ~wmc.CircleWarp(room_bottom, .3, pi / 2, clock_wise, cur_bar , cur_bar * 13); <<< "bar17", cur_bar * 13 - tm.Get() >>>; tm.Set(cur_bar * 13 - tm.Count()); tm.Count(cur_bar * 2); spork ~wmc2.Warp(0.01, cur_bar / 5.0); tm.Count(cur_bar * 3); wmc2.KillDrone(); <<< "trimmer3", tm.Get() >>>; spork ~trimmer.Pan(room_right, room_front, room_bottom, 0, 0, 0, cur_bar / 7.0); spork ~trimmer.Warp(.4, cur_bar / 7.0); tm.Count(cur_bar / 7.0); <<< "trimmer3a", tm.Get() >>>; spork ~trimmer.Pan(0, 0, 0, room_right, room_front, room_bottom, cur_bar / 7.0); spork ~trimmer.Warp(0.07, cur_bar / 2.0); tm.Count(cur_bar / 2.0); <<< "trimmer3b", tm.Get() >>>; spork ~trimmer.Pan(room_right, room_front, room_bottom, 0, 0, 0, cur_bar / 7.0); spork ~trimmer.Warp(.4, cur_bar ); tm.Count(cur_bar + cur_bar / 7.0); <<< "trimmer3c", tm.Get() >>>; spork ~trimmer.Pan(0, 0, 0, room_right, room_front, room_bottom, cur_bar / 7.0); spork ~trimmer.Warp(0.01, cur_bar); spork ~wmc.Warp(.45, tm.Get() + cur_bar * 2); tm.Count(); // line up with our new bar (the mower) <<< "now", (now - cur_time), (23 * bar)::ms >>>; (bar * 23)::ms - (now - cur_time) => now; spork ~wmc.Warp(0.01, cur_bar); <<< "kids2" >>>; kids.init(.3, "data/noah piano.wav", ncw); kids.PanXYZ(room_left, room_back, room_top); kids.play(); wmc.Warp(0.01, cur_bar); mower1.Warp(0.01, 4 * bar); XBoxDrone xbox; xbox.init(cur_bar, ncw); MixerDrone mixer; mixer.init(cur_bar, ncw); mixer.SetGain(dyn_pp); xbox.SetGain(dyn_pp); wmc.SetGain(dyn_pp); mower1.set_timbre(); mower1.SetGain(dyn_pp); (bar * 9.0 / 7.0) $int => cur_bar; mixer.PanXYZ(room_right, room_front, room_bottom); mower1.PanXYZ(room_left, room_front, room_bottom); wmc.PanXYZ(room_left, room_back, room_top); trimmer.PanXYZ(room_left, room_back, room_top); xbox.PanXYZ(room_right, room_back, room_top); mixer.SetGain(dyn_pp); mower1.SetGain(dyn_pp); wmc.SetGain(dyn_pp); xbox.SetGain(dyn_pp); <<< "A1" >>>; spork ~A1(xbox, mixer, mower1, wmc, P5, cur_bar / 4, diatonic_scale); kids.SetGain(0.25); (cur_bar/2)::ms => now; kids.SetGain(0.2); (cur_bar/2)::ms => now; kids.SetGain(0.15); (cur_bar/2)::ms => now; kids.SetGain(0.10); (cur_bar)::ms => now; kids.end(); (cur_bar * 6)::ms => now; <<< "fugue 1" >>>; (bar * 7.0 / 8.0) $int => cur_bar; xbox.SetGain(dyn_p); spork ~xbox.CircleX( room_top, room_radius, pi/4, clock_wise, cur_bar * 2, cur_bar * 8); spork ~A1Alto(xbox, m6, cur_bar / 5, diatonic_scale + 2); (cur_bar/2)::ms => now; spork ~mixer.CircleX( room_bottom, room_radius, 3*pi/4, clock_wise, cur_bar * 2, cur_bar * 8); spork ~A1Alto(mixer, m3, cur_bar * 10 / 52, diatonic_scale + 1); (cur_bar)::ms => now; spork ~wmc.CircleX( room_top, room_radius, pi/4, clock_wise, cur_bar * 2, cur_bar * 8); spork ~A1Alto(wmc, m2, cur_bar * 10 / 54, diatonic_scale); (cur_bar/2)::ms => now; spork ~mower1.CircleX( room_bottom, room_radius, 3*pi/4, clock_wise, cur_bar * 2, cur_bar * 8); spork ~A1Alto(mower1, m7, cur_bar * 10 / 56, diatonic_scale - 1); (6*cur_bar)::ms => now; (bar * 3.0 / 4.0) $int => cur_bar; spork ~wmc.Warp(0.01, cur_bar * 5); (cur_bar * 3)::ms => now; spork ~trimmer.Pan(room_right, room_front, room_bottom, 0, 0, 0, cur_bar / 7.0); spork ~trimmer.Warp(.4, cur_bar / 2.0 ); (cur_bar / 2.0)::ms => now; // new bar tm.Set(bar * 39); // trimmer finally exists (doesn't return to right/front/bottom spork ~trimmer.Pan(0, 0, 0, room_left, room_back, room_top, cur_bar / 3.0); spork ~trimmer.Warp(0.07, cur_bar / 2.0); tm.Count(cur_bar / 2.0); tm.Count(1 * cur_bar); // this is the only drone to be aligned with the real bar WashingMachineDrone wmcx; wmcx.init(bar, ncw, 2); wmcx.SetGain(dyn_p); <<< "wmc returns on bar" >>>; // counter-clockwise this time spork ~wmcx.CircleWarp(0, .3, pi / 2.0, !clock_wise, bar * 2, tm.Get()); tm.Count(cur_bar / 2.0); spork ~mower1.Warp(0.01, cur_bar * 2); tm.Count(cur_bar * 2); mower1.KillDrone(); tm.Count(cur_bar * 2); wmc.SetGain(dyn_p); <<< "fugue 2" >>>; spork ~wmc.Warp(0.01, cur_bar * 7); spork ~xbox.PanHelix(cur_bar / 3, 10 * 3, true); spork ~A1Tenor1(xbox, P5 + m6, cur_bar / 3, 10); tm.Count(cur_bar/2); spork ~mixer.PanHelix(cur_bar * 12 / 33, 9 * 33 / 12, false); spork ~A1Tenor2(mixer, P4 + m3, cur_bar * 12 / 33, 9); tm.Count(cur_bar * 3); wmc.SetGain(dyn_pp); tm.Count(cur_bar * 2); wmc.SetGain(dyn_ppp); tm.Count(cur_bar * 2); wmc.KillDrone(); wmc.init(cur_bar, ncw); wmc2.init(cur_bar, ncw); WashingMachineDrone wmc4; wmc4.init(cur_bar, ncw); wmc.SetGain(dyn_ppp); wmc2.SetGain(dyn_ppp); wmc4.SetGain(dyn_ppp); m7 => int key; spork ~MajorTriadPanDown(wmc2, wmc, wmc4, key + P1, 1550 * 2, diatonic_scale); tm.Count(1550 * 2); spork ~MajorTriadPanUp(wmc2, wmc, wmc4, key + P4, 1550 * 2, diatonic_scale); tm.Count(1550 * 2); spork ~MajorTriadPanDown(wmc2, wmc, wmc4, key + P1, 1550 * 2, diatonic_scale); tm.Count(1550 * 2); spork ~MajorTriadPanUp(wmc2, wmc, wmc4, key + P4, 1550 * 2, diatonic_scale); tm.Count(1550 * 2); tm.Count(1000); spork ~MajorTriadPanDown(wmc2, wmc, wmc4, key + P1, 1550 * 2, diatonic_scale); tm.Count(1550 * 2); spork ~MajorTriadPanUp(wmc2, wmc, wmc4, key + P4, 1550 * 2, diatonic_scale); tm.Count(1550 * 2); spork ~MajorTriadPanDown(wmc2, wmc, wmc4, key + P1, 1550 * 2, diatonic_scale); tm.Count(1550 * 2); spork ~MajorTriadPanUp(wmc2, wmc, wmc4, key + P4, 1550 * 2, diatonic_scale); tm.Count(1550 * 2); spork ~xbox.Warp(0.01, cur_bar); spork ~mixer.Warp(0.01, cur_bar); spork ~trimmer.Warp(0.01, cur_bar); tm.Count(cur_bar * 1); mixer.KillDrone(); trimmer.KillDrone(); xbox.KillDrone(); <<< "Major Triad">>>; wmc.SetGain(dyn_p); wmc2.SetGain(dyn_p); wmc4.SetGain(dyn_p); spork ~MajorTriadPanDown(wmc2, wmc, wmc4, key + P1, 1550 * 2, diatonic_scale); tm.Count(1550 * 2); spork ~MajorTriadPanUp(wmc2, wmc, wmc4, key + P4, 1550 * 2, diatonic_scale); tm.Count(1550 * 2); wmc.SetGain(dyn_mp); wmc2.SetGain(dyn_mp); wmc4.SetGain(dyn_mp); spork ~MajorTriadPanDown(wmc2, wmc, wmc4, key + P1, 1550 * 2, diatonic_scale); tm.Count(1550 * 2); spork ~MajorTriadPanUp(wmc2, wmc, wmc4, key + P4, 1400 * 2, diatonic_scale); tm.Count(1400 * 2); spork ~MajorTriadPanDown(wmc2, wmc, wmc4, key + P1, 1480 * 2, diatonic_scale); tm.Count(1480 * 2); wmc2.SetGain(dyn_mf); wmc.SetGain(dyn_mf); wmc4.SetGain(dyn_mf); spork ~MajorTriadPanUp(wmc2, wmc, wmc4, key + P4, 1400 * 1, diatonic_scale); tm.Count(1400 * 1); // our gigantic dominant! spork ~MajorTriadPanDown(wmc2, wmc, wmc4, key + P5, 1450 * 15, diatonic_scale); tm.Count(1450 * 11); <<< "time remaining" , tm.Get() >>>; for (10 => int i; i > 0; i--) { wmc2.SetGain( (i / 10.0) * dyn_mp ); wmc.SetGain( (i / 10.0) * dyn_mp ); wmc4.SetGain( (i / 10.0) * dyn_mp ); tm.Count(tm.Get() / 3); } <<< "time remaining" , tm.Get() >>>; tm.Count(); <<< "warp to zero">>>; tm.Set(bar * 8); spork ~wmcx.Pan(0, 0, 0, room_right, room_front, room_bottom, tm.Get()); spork ~wmcx.Warp(0.01, tm.Get()); tm.Count(tm.Get()/2); spork ~wmc.Warp(0.01, bar); spork ~wmc4.Warp(0.01, bar); spork ~wmc2.Warp(0.01, bar); tm.Count(bar); wmc2.KillDrone(); wmc.KillDrone(); wmc4.KillDrone(); tm.Count(); ncw.done(); wmcx.KillDrone(); <<< "done" >>>;