// Guitar Riff Lab with MAUI Control Interface // copyright 2008 Les Hall // this software is protected by the GNU General Public License // control variables 2 => int n; // base of count 8 => int num_digits; // number of digits in base-n count 8 => int num_and_terms; // number of and terms float freq; // the frequency of the next note 0 => int play_note; // 1 to play this note 0 => int and_term; // equal to and_bits if play_note should be true 0 => int and_bits; // number of bits anded in a term int j[num_digits]; // the base-n count int plus_amount[num_digits]; // adding up mouse clicks, 1-9 int minus_amount[num_digits]; // adding up mouse clicks, 1-9 int and_values[num_and_terms][num_digits]; // holds current value of buttons MAUI_View control_view; MAUI_Button start_button, clear_button; MAUI_Button xor_button; MAUI_Button and_buttons[num_and_terms][num_digits]; MAUI_LED count[num_digits]; MAUI_Button plus_buttons[num_digits]; MAUI_Button minus_buttons[num_digits]; MAUI_Slider master_volume; MAUI_Slider base; MAUI_Slider freq_mult, freq_offset; MAUI_Slider reverb; MAUI_Slider notes_per_second; MAUI_Slider body_size; MAUI_Button Guitar, Flute; MAUI_Button Snare, Drum; MAUI_Button Mandolin; control_view.size (800, 600); control_view.name ("Boolean Sequencing Guitar Riff Lab"); start_button.toggleType (); start_button.size (120, 60); start_button.position (0, 0); start_button.name ("start/stop"); control_view.addElement (start_button); clear_button.pushType (); clear_button.size (120, 60); clear_button.position (150, 0); clear_button.name ("clear"); control_view.addElement (clear_button); xor_button.toggleType (); xor_button.size (120, 60); xor_button.position (300, 0); xor_button.name ("OR/XOR"); control_view.addElement (xor_button); for (0 => int a; a < num_and_terms; a++) { for (0 => int b; b < num_digits; b++) { 0 => and_values[a][b]; and_buttons[a][b].pushType (); and_buttons[a][b].size (75, 75); and_buttons[a][b].position (50*(num_digits-b-1), 75+50*a); and_buttons[a][b].name (""); control_view.addElement (and_buttons[a][b]); } } for (0 => int b; b < num_digits; b++) { count[b].color (count[b].red); count[b].size (50, 50); count[b].position (12+50*(num_digits-b-1), 50); control_view.addElement (count[b]); } for (0 => int b; b < num_digits; b++) { if (b%2 == 0) { 1 => plus_amount[b]; } plus_buttons[b].pushType (); plus_buttons[b].size (75, 75); plus_buttons[b].position (50*(num_digits-b-1), 75+50*num_and_terms); plus_buttons[b].name ("+"+plus_amount[b]); control_view.addElement (plus_buttons[b]); } for (0 => int b; b < num_digits; b++) { if (b%2 == 1) { 1 => minus_amount[b]; } minus_buttons[b].pushType (); minus_buttons[b].size (75, 75); minus_buttons[b].position (50*(num_digits-b-1), 75+50*(num_and_terms+1)); minus_buttons[b].name ("-"+minus_amount[b]); control_view.addElement (minus_buttons[b]); } "Volume" => master_volume.name; master_volume.range (0, 100); master_volume.value (20); master_volume.size (200, 75); master_volume.position (450, 0); master_volume.displayFormat (master_volume.integerFormat); control_view.addElement (master_volume); "base" => base.name; base.range (2, 10); base.value (2); base.size (200, 75); base.position (450, 50); base.displayFormat (base.integerFormat); control_view.addElement (base); "freq_multiplier" => freq_mult.name; freq_mult.range (1, 100); freq_mult.value (10); freq_mult.size (200, 75); freq_mult.position (450, 100); control_view.addElement (freq_mult); "freq_offset" => freq_offset.name; freq_offset.range (0, 20); freq_offset.value (5); freq_offset.size (200, 75); freq_offset.position (450, 150); control_view.addElement (freq_offset); "reverb" => reverb.name; reverb.range (0, 1); reverb.value (0.1); reverb.size (200, 75); reverb.position (450, 200); control_view.addElement (reverb); "Notes per Second" => notes_per_second.name; notes_per_second.range (1, 16); notes_per_second.value (8); notes_per_second.size (200, 75); notes_per_second.position (450, 250); notes_per_second.displayFormat (notes_per_second.integerFormat); control_view.addElement (notes_per_second); "Body Size" => body_size.name; body_size.range (0, 1); body_size.value (0.3); body_size.size (200, 75); body_size.position (450, 300); control_view.addElement (body_size); Guitar.toggleType (); Guitar.size (120, 60); Guitar.position (650, 0); Guitar.name ("Guitar"); Guitar.state (1); control_view.addElement (Guitar); Flute.toggleType (); Flute.size (120, 60); Flute.position (650, 50); Flute.name ("Flute"); control_view.addElement (Flute); Snare.toggleType (); Snare.size (120, 60); Snare.position (650, 100); Snare.name ("Snare"); control_view.addElement (Snare); Drum.toggleType (); Drum.size (120, 60); Drum.position (650, 150); Drum.name ("Drum"); control_view.addElement (Drum); Mandolin.toggleType (); Mandolin.size (120, 60); Mandolin.position (650, 200); Mandolin.name ("Mandolin"); control_view.addElement (Mandolin); control_view.display (); // many thanks to kijjaz for the mandolin-based Stratocaster guitar sound with overdrive: // Mandolin as the electric guitar test: by kijjaz (kijjaz@yahoo.com) // feel free to use, modify, publish class kjzGuitar101 { Mandolin str[3]; // create mandolin strings SinOsc overdrive => NRev rev => Gain output; // create overdrive to reverb to dac overdrive.sync(1); // make overdrive do Sine waveshaping rev.mix(0.02); // set reverb mix rev.gain(0.6); // set master gain output.gain(1.0); // set output gain // connect strings, set string damping for(int i; i < 3; i++) { str[i] => overdrive; .9 => str[i].stringDamping; 0.5 => str[i].bodySize; } } kjzGuitar101 A; // lead guitar A.output => Gain master => dac; 0 => master.op; master.gain (0.2); Flute B; B => master; // many thanks to kijjaz (kijjaz@yahoo.com) for the snare code examples, rock on kijjaz! // easy white noise snare class kjzSnare101 { // note: connect output to external sources to connect Noise s => Gain s_env => LPF s_f => Gain output; // white noise source Impulse i => Gain g => Gain g_fb => g => LPF g_f => s_env; 3 => s_env.op; // make s envelope a multiplier s_f.set(3000, 4); // set default drum filter g_fb.gain(1.0 - 1.0/3000); // set default drum decay g_f.set(200, 1); // set default drum attack fun void setFilter(float f, float Q) { s_f.set(f, Q); } fun void setDecay(float decay) { g_fb.gain(1.0 - 1.0 / decay); // decay unit: samples! } fun void setAttack(float attack) { g_f.freq(attack); // attack unit: Hz! } fun void hit(float velocity) { velocity => i.next; } } kjzSnare101 C; C.output => master; // simple analog-sounding bass drum with pitch and amp decay and sine overdrive class kjzBD101 { Impulse i; // the attack i => Gain g1 => Gain g1_fb => g1 => LPF g1_f => Gain BDFreq; // BD pitch envelope i => Gain g2 => Gain g2_fb => g2 => LPF g2_f; // BD amp envelope // drum sound oscillator to amp envelope to overdrive to LPF to output BDFreq => SinOsc s => Gain ampenv => SinOsc s_ws => LPF s_f => Gain output; g2_f => ampenv; // amp envelope of the drum sound 3 => ampenv.op; // set ampenv a multiplier 1 => s_ws.sync; // prepare the SinOsc to be used as a waveshaper for overdrive // set default 80.0 => BDFreq.gain; // BD initial pitch: 80 hz 1.0 - 1.0 / 2000 => g1_fb.gain; // BD pitch decay g1_f.set(100, 1); // set BD pitch attack 1.0 - 1.0 / 4000 => g2_fb.gain; // BD amp decay g2_f.set(50, 1); // set BD amp attack .75 => ampenv.gain; // overdrive gain s_f.set(600, 1); // set BD lowpass filter fun void hit(float v) { v => i.next; } fun void setFreq(float f) { f => BDFreq.gain; } fun void setPitchDecay(float f) { f => g1_fb.gain; } fun void setPitchAttack(float f) { f => g1_f.freq; } fun void setDecay(float f) { f => g2_fb.gain; } fun void setAttack(float f) { f => g2_f.freq; } fun void setDriveGain(float g) { g => ampenv.gain; } fun void setFilter(float f) { f => s_f.freq; } } kjzBD101 D; D.output => master; Mandolin E; E => master; E.bodySize(0.35); E.pluckPos(0.70); function void start () { while (true) { // wait for the start button to be pushed start_button.onChange () => now; !master.op () => master.op; } } function void clear () { while (true) { // wait for the start button to be pushed clear_button.onChange () => now; for (0 => int b; b < num_digits; b++) { 0 => j[b]; count[b].unlight (); } } } function void and_buttons_adj (int a, int b) { while (true) { and_buttons[a][b].onChange () => now; 1 +=> and_values[a][b]; if (and_values[a][b] == 0) { and_buttons[a][b].name (""); } if (and_values[a][b] == 1) { and_buttons[a][b].name ("&"); } if (and_values[a][b] == 2) { and_buttons[a][b].name ("!"); } if (and_values[a][b] > 2) { 0 => and_values[a][b]; and_buttons[a][b].name (""); } and_buttons[a][b].onChange () => now; } } function void plus_buttons_adj (int b) { while (true) { plus_buttons[b].onChange () => now; if (minus_amount[b] > 0) { 1 -=> minus_amount[b]; } else { 1 +=> plus_amount[b]; } plus_buttons[b].name ("+"+plus_amount[b]); minus_buttons[b].name ("-"+minus_amount[b]); plus_buttons[b].onChange () => now; } } function void minus_buttons_adj (int b) { while (true) { minus_buttons[b].onChange () => now; if (plus_amount[b] > 0) { 1 -=> plus_amount[b]; } else { 1 +=> minus_amount[b]; } minus_buttons[b].name ("-"+minus_amount[b]); plus_buttons[b].name ("+"+plus_amount[b]); minus_buttons[b].onChange () => now; } } function void volume_adj () { while (true) { // wait for the volume slider to be changed master_volume.onChange () => now; master_volume.value () / 100.0 => master.gain; } } function void base_adj () { while (true) { base.onChange () => now; base.value () $ int => n; } } function void reverb_adj () { while (true) { reverb.onChange () => now; reverb.value () => A.rev.mix; } } function void body_size_adj () { while (true) { body_size.onChange () => now; for(int i; i < 3; i++) { body_size.value () => A.str[i].bodySize; } } } function void flute_adj () { while (true) { Flute.onChange () => now; if (Flute.state ()) { B.jetDelay (0.96); B.jetReflection (0.50); B.endReflection (0.88); B.noiseGain (0.24); B.vibratoFreq (2.85); B.vibratoGain (0.36); B.pressure (0.98); B.startBlowing (1); B.gain (1); } else { B.stopBlowing (1); B.noteOff (0); B.gain (0); } } } spork ~ start (); spork ~ clear (); for (0 => int b; b < num_digits; b++) { spork ~ plus_buttons_adj (b); spork ~ minus_buttons_adj (b); } for (0 => int a; a < num_and_terms; a++) { for (0 => int b; b < num_digits; b++) { spork ~ and_buttons_adj (a, b); } } spork ~ volume_adj (); spork ~ base_adj (); spork ~ reverb_adj (); spork ~ body_size_adj (); spork ~ flute_adj (); while (true) { for (0 => j[7]; j[7] < n; j[7]++) { for (0 => j[6]; j[6] < n; j[6]++) { for (0 => j[5]; j[5] < n; j[5]++) { for (0 => j[4]; j[4] < n; j[4]++) { for (0 => j[3]; j[3] < n; j[3]++) { for (0 => j[2]; j[2] < n; j[2]++) { for (0 => j[1]; j[1] < n; j[1]++) { for (0 => j[0]; j[0] < n; j[0]++) { for (0 => int b; b < num_digits; b++) { if (j[b]) { count[b].light (); } else { count[b].unlight (); } } 0 => play_note; for (0 => int a; a < num_and_terms; a++) { 0 => and_term; 0 => and_bits; for (0 => int b; b < num_digits; b++) { if (and_values[a][b] == 1) { if (j[b]) { 1 +=> and_term; } 1 +=> and_bits; } if (and_values[a][b] == 2) { if (!j[b]) { 1 +=> and_term; } 1 +=> and_bits; } } if ( (and_term == and_bits) && (and_bits > 0) ) { if (xor_button.state ()) { play_note ^ 1 => play_note; } else { 1 => play_note; } } } if (play_note) { 0 => freq; for (0 => int b; b < num_digits; b++) { j[b]*plus_amount[b] +=> freq; j[b]*minus_amount[b] -=> freq; } freq_mult.value () * (freq_offset.value () + freq) => freq; if (Guitar.state ()) { for(int i; i < 3; i++) { 0.5 => A.str[i].stringDamping; } for (0 => int i; i < 3; i++) { freq * (i+1) => A.str[i].freq; } for (0 => int i; i < 3; i++) { 0.8 - i/4.0 => A.str[i].pluck; } } if (Flute.state ()) { B.freq (10*freq); B.noteOn (0.5); } if (Snare.state ()) { C.hit (0.8); } if (Drum.state ()) { D.hit (0.8); } if (Mandolin.state ()) { E.freq (5*freq); E.pluck (0.5); } } // advance time while (!start_button.state ()) { 1::second => now; } 1::second / notes_per_second.value () => now; } } } } } } } } }