// Digitar Digital Guitar Program // Karplus-Strong String Synthesis // Copyright 2009 Les Hall // This software is protected by the GNU General Public License // parameters "Digitar.wav" => string filename; false => int recording; true => int play_notes_or_chords; [82.407, 110.00, 146.83, 196.00, 246.94, 329.63] @=> float open_freqs[]; 24 => int num_frets; 0.85 => float sustain; 4.0 => float harmonics; [[0, 2, 2, 1, 0, 0], [-1, 0, 2, 2, 2, 0], [-1, -1, 0, 2, 3, 2], [3, 2, 0, 0, 0, 3], [-1, 3, 2, 0, 1, 0], [-1, -1, 3, 2, 1, 1], [0, 2, 2, 0, 0, 0], [-1, 0, 2, 2, 1, 0], [-1, -1, 0, 2, 3, 1], [-1, 0, 2, 0, 2, 0], [0, 2, 0, 1, 0, 0], [-1, 3, 2, 3, 1, 0], [-1, -1, 0, 2, 1, 2], [-1, 2, 1, 2, 0, 2], [3, 2, 0, 0, 0, 1]] @=> int chords[][]; // chords ["E", "A", "D", "G", "C", "F", "Em", "Am", "Dm", "A7", "E7", "C7", "D7", "B7", "G7"] @=> string chord_names[]; // names of the chords 0.3 => float distortion_level; 1.0 => float distortion_mix; 0.2 => float reverb_mix; [2, 3, 4, 6, 8] @=> int timings[]; 2 => int timing; // variables int counter; 10 => int n; 16 => int k; Math.pow(2, n) $ int => int m; int logic_base; int logic_table[k]; int logic_sum; int logic_string; int logic_fret; open_freqs.cap() => int num_strings; chords.cap() => int num_chords; int chord; int barre; 1 => int direction; // patches karplus_strong guitar[num_strings]; distortion dist_pedal; reverb rev_pedal; Gain jack; for (int s; s jack; guitar[s].set(open_freqs[s], harmonics * open_freqs[s], sustain); } jack => dist_pedal.input; dist_pedal.output => rev_pedal.input; rev_pedal.output => dac; dist_pedal.set(distortion_level, distortion_mix); rev_pedal.set(reverb_mix); adc => Gain microphone => dac; 0.0 => microphone.gain; dac => WvOut wvout => blackhole; // class declarations keyboard controller; // initialize things print(); // time loop while (true) { // increment the counter (counter + 1) % m => counter; // add up the logic table expression outputs 0 => logic_sum; for (int i; i0) { if ((logic_table[i] & counter) == logic_table[i]) { 1 +=> logic_sum; } } } // find the note to be played num_frets => logic_fret; for (int i; i0) { if ((logic_table[i] & counter) == logic_table[i]) { i => logic_fret; } } } num_strings => logic_string; if (logic_fret int j; j>=0; j--) { if ((Math.pow(2.0, j) $ int) & logic_table[logic_fret] & counter) { j => logic_string; } } } // play the guitar (1.0 / timings[timing])::second => dur tempo; if (play_notes_or_chords) { if ((logic_fret now; } else { tempo => now; } } else { if (logic_sum > 0) { //(logic_sum - 1) % num_chords => chord; logic_fret % num_chords => chord; play_chord(chords[chord], barre, direction, 9::ms); tempo - (60::ms) => now; } else { tempo => now; } -1 *=> direction; } } // classes // string synthesis class class karplus_strong { Noise n => Gain sum => DelayA dly => LPF lpf => sum; // kijjaz edit for testing: Impulse i => HPF plucktone => sum; // trying to add more tone to the start of each plucking .5 => float PickNoiseDur; sum => Gain output; 0.0 => n.gain; fun void set (float min_freq, float cutoff, float sustain) { second / min_freq => dly.max; // kijjaz edit for testing: // cutoff => lpf.freq; 1.04 => lpf.Q; // this make the harmonic sustains 40 => plucktone.Q; sustain => lpf.gain; } fun void pluck (float frequency, float strength) { 0.0 => output.gain; 1::ms => dly.delay; 10::ms => now; second / frequency => dly.delay; frequency => plucktone.freq; // kijjaz edit for testing: frequency * 3 => lpf.freq; // make up to 3rd harmonic sustain more strength => n.gain; strength => i.next; // kijjaz edit for testing: ((second/samp)/frequency)::samp * PickNoiseDur => now; // adjust noise duration according to frequency 0.0 => n.gain; 1.0 => output.gain; } } // play one chord fun void play_chord(int chord[], int barre, int direction, dur delta_t) { if (direction>0) { for (int s; s=0) { guitar[s].pluck(open_freqs[s] * Math.pow(2.0, Math.min(barre + chord[s], num_frets - 1) / 12.0), 10.0); } delta_t => now; } } else { for (num_strings - 1 => int s; s>=0; s--) { if (chord[s]>=0) { guitar[s].pluck(open_freqs[s] * Math.pow(2.0, Math.min(barre + chord[s], num_frets - 1) / 12.0), 10.0); } delta_t => now; } } } // distortion effects pedal class distortion { Gain input => Gain dry => Gain output; input => Gain div => Gain wet => output; input => FullRect fwr => Gain add => div; Step step => add; 4 => div.op; fun void set (float level, float mix) { 1.0 / (50.0 * level) => step.next; mix => wet.gain; 1.0 - mix => dry.gain; 0.5 => output.gain; } } // reverb effects pedal class reverb { Gain input => Gain dry => Gain output; input => JCRev jcrev => Gain wet => output; fun void set (float mix) { mix => jcrev.mix; if (mix<0.05) { 1.0 => dry.gain; 0.0 => wet.gain; } else { 0.0 => dry.gain; 1.0 => wet.gain; } 0.5 => output.gain; } } // keyboard interface class keyboard { // variables int index; [[0, 512, 49], [0, 256, 50], [0, 128, 51], [0, 64, 52], [0, 32, 53], [0, 16, 54], [0, 8, 55], [0, 4, 56], [0, 2, 57], [0, 1, 48], [1, 512, 81], [1, 256, 87], [1, 128, 69], [1, 64, 82], [1, 32, 84], [1, 16, 89], [1, 8, 85], [1, 4, 73], [1, 2, 79], [1, 1, 80], [2, 512, 65], [2, 256, 83], [2, 128, 68], [2, 64, 70], [2, 32, 71], [2, 16, 72], [2, 8, 74], [2, 4, 75], [2, 2, 76], [2, 1, 59], [3, 512, 90], [3, 256, 88], [3, 128, 67], [3, 64, 86], [3, 32, 66], [3, 16, 78], [3, 8, 77], [3, 4, 44], [3, 2, 46], [3, 1, 47]] @=> int keys[][]; string mode; // human interface device Hid hi; HidMsg msg; 0 => int device; // which keyboard // open keyboard (get device number from command line) if (!hi.openKeyboard(device)) { <<<"error: keyboard not found, exiting program", "">>>; me.exit(); } <<<"keyboard '" + hi.name() + "' ready", "">>>; // launch time loop shred spork ~ time_loop(); // infinite event loop fun void time_loop() { while( true ) { // wait on event hi => now; // get one or more messages while( hi.recv( msg ) ) { // check for action type if( msg.isButtonDown() ) { //<<< "down:", msg.which, "(code)", msg.key, "(usb key)", msg.ascii, "(ascii)" >>>; if (msg.key==58) { // F1 key 0 => logic_base; print(); } if (msg.key==59) { // F2 key 4 => logic_base; print(); } if (msg.key==60) { // F3 key 8 => logic_base; print(); } if (msg.key==61) { // F4 key 12 => logic_base; print(); } for (int key; key index; if (msg.ascii==keys[key][2]) { // monome keys if (keys[key][1] & logic_table[index]) { keys[key][1] ^ logic_table[index] => logic_table[index]; } else { keys[key][1] | logic_table[index] => logic_table[index]; } print(); } } if (msg.ascii==8) { // del key for (int i; i logic_table[i]; // clear logic table } print(); } if (msg.ascii==45) { // minus key if (mode=="distortion level") { 0.05 -=> distortion_level; if (distortion_level<0.05) { 0.05 => distortion_level; } dist_pedal.set(distortion_level, distortion_mix); } if (mode=="distortion mix") { 0.05 -=> distortion_mix; if (distortion_mix<0.0) { 0.0 => distortion_mix; } dist_pedal.set(distortion_level, distortion_mix); } if (mode=="barre") { 1 -=> barre; if (barre<0) { num_frets - 1 => barre; } } if (mode=="reverb mix") { 0.05 -=> reverb_mix; if (reverb_mix<0.0) { 0.0 => reverb_mix; } rev_pedal.set(reverb_mix); } print(); } if (msg.ascii==61) { // plus key if (mode=="distortion level") { 0.05 +=> distortion_level; if (distortion_level>1.0) { 1.0 => distortion_level; } dist_pedal.set(distortion_level, distortion_mix); } if (mode=="distortion mix") { 0.05 +=> distortion_mix; if (distortion_mix>1.0) { 1.0 => distortion_mix; } dist_pedal.set(distortion_level, distortion_mix); } if (mode=="barre") { 1 +=> barre; if (barre>=num_frets) { 0 => barre; } } if (mode=="reverb mix") { 0.05 +=> reverb_mix; if (reverb_mix>1.0) { 1.0 => reverb_mix; } rev_pedal.set(reverb_mix); } print(); } if (msg.key==62) { // F5 key "distortion level" => mode; print(); } if (msg.key==63) { // F6 key "distortion mix" => mode; print(); } if (msg.key==64) { // F7 key !play_notes_or_chords => play_notes_or_chords; print(); } if (msg.key==65) { // F8 key (timing + 1) % timings.cap() => timing; print(); } if (msg.key==66) { // F9 key "barre" => mode; print(); } if (msg.key==67) { // F10 key "reverb mix" => mode; print(); } if (msg.key==68) { // F11 key Math.fabs(microphone.gain() - 1.0) => microphone.gain; print(); } if (msg.key==69) { // F12 key !recording => recording; if (recording) { filename => wvout.wavFilename; } else { filename => wvout.closeFile; } print(); } } } } } } // print to the console monitor fun void print() { string s; for (int i; i<10; i++) { <<<"", "">>>; } <<<"Logic Table: use alpha-numeric matrix to toggle bits", "">>>; for (int i; i s; if ((i<(logic_base + 4)) & (i>=logic_base)) { "[] " + s => s; } else { " " + s => s; } for (int j; j>j)&1) { "@ " + s => s; } else { ". " + s => s; } } if (i==0) { s + "distortion level (F5) = " + distortion_level + " " => s; } if (i==1) { s + "distortion mix (F6) = " + distortion_mix + " " => s; } if (i==2) { if (play_notes_or_chords) { s + "playing notes (F7) " => s; } else { s + "playing chords (F7) " => s; } } if (i==3) { s + "tempo (F8) = " + timings[timing] + "/4 " => s; } if (i==4) { s + "barre (F9) = " + barre + " " => s; } if (i==5) { s + "reverb (F10) = " + reverb_mix + " " => s; } if (i==6) { if (microphone.gain() == 0.0) { s + "microphone (F11) = OFF " => s; } else { s + "microphone (F11) = -=<{ON}>=- " => s; } } if (i==7) { if (!recording) { s + "recording (F12) = OFF " => s; } else { s + "recording (F12) = -=<{ON}>=- " => s; } } <<>>; } }