Difference between revisions of "Modedular.ck"
From CSWiki
Line 408: | Line 408: | ||
<<< "thanks for staying with us. comments and bug reports are very welcome. and thanks for testing", "" >>>; | <<< "thanks for staying with us. comments and bug reports are very welcome. and thanks for testing", "" >>>; | ||
+ | [http://www.blogsdna.com Blogsdna] |
Revision as of 08:17, 5 August 2008
Update: This is the code for the version 0.3 testing
// Modedular version 0.3 testing // by Kijjasak Triyanond (kijjaz) kijjaz@yahoo.com // licence: Attribution-Share Alike 3.0 // You are free: // * to Share — to copy, distribute and transmit the work // * to Remix — to adapt the work // Under the following conditions: // * Attribution. You must attribute the work in the manner specified by the author or licensor // (but not in any way that suggests that they endorse you or your use of the work). // * Share Alike. If you alter, transform, or build upon this work, // you may distribute the resulting work only under the same, similar or a compatible license. // Brief Manual: // * Modes are kept as an int array of intervals --> intervals[] // * Octave Size is the sum of all the intervals, // Octave Size is automatically calculated when a mode is set. // * .update() recalculates Octave Size from the interval array // (important: if you change the value in intervals[] from outside, .update() should be executed after that) // * .set(int interval[]) copies the given array into its interval array // * .set(string preset) can set the mode to some preset modes, see below // * .get(int result[]) copies the interval array to an outside array // * .note(int pitch) returns interger note number (in semitone), pitch starts from 1 // (for example, if we have church mode (7 notes per octave), 1 2 3 4 5 6 7 8 is do re mi fa sol la ti do) // * .note(int pitch, int octave) also applies octave shift (can be negative) to the result. // * .chord(int root, int degree[], int result[]) // creates notes from the supplies chord degrees and copy to result // and .chord(int root, int octave, int degree[], int result[]) with chord offset // * .rotate(int x) moves the root position x times (can be negative) // * .setRotate(int x) moves the root position to position x (can be negative) // * .rotateApply() applies current rotation to the interval array // * .rotateApply(int x) moves the root position to position x then performs rotateApply() // Mode Operations: // * .retrograde() does the retrograde // * .invert() invert all intervals (multiply by -1) // * .trim(int begin, int end) trim the interval array to the selected area (from begin to end) // * addLeft(int input[]) and addRight(int input[]) // insert more interval values in front of or behind the current interval arrays // * .addValue(int input[]), .subtractValue(int input[]), multiplyValue(int input[]), divideValue(int input[]) // add or subtract or multiply or divide each cell of the interval array with the input array // or use (int input[], int offset) to set offset for the operations
class Modedular { [0] @=> int intervals[]; // array of intervals in the mode 0 => int octaveSize; // octave size in semitones 0 => int rootPosition; // for easy mode rotation // - - - Initialization fun int update() { // use this to octave Octave Size 0 => octaveSize; for(int i; i < intervals.cap(); i++) intervals[i] +=> octaveSize; return octaveSize; } fun void set(int input[]) { // use this to copy intervals from the input array new int[input.cap()] @=> intervals; for(int i; i < input.cap(); i++) input[i] => intervals[i]; update(); } fun void set(string input) { // use this to set the mode to a preset value by a string if (input == "lydian") set([2,2,2,1,2,2,1]); if (input == "ionian") set([2,2,1,2,2,2,1]); if (input == "mixolydian") set([2,2,1,2,2,1,2]); if (input == "dorian") set([2,1,2,2,2,1,2]); if (input == "aeolian") set([2,1,2,2,1,2,2]); if (input == "phrygian") set([1,2,2,2,1,2,2]); if (input == "locrian") set([1,2,2,1,2,2,2]); if (input == "harmonic minor") set([2,1,2,2,1,3,1]); if (input == "melodic minor") set([2,1,2,2,2,2,1]); if (input == "major pentatonic") set([2, 2, 3, 2, 3]); if (input == "minor pentatonic") set([3, 2, 2, 3, 2]); if (input == "wholetone") set([2,2,2,2,2,2]); if (input == "whole-half") set([2,1]); if (input == "half-whole") set([1,2]); // maybe this is a joke or something, but theoretically, yes it is this.. if (input == "chromatic") set([1]); update(); } fun void get(int input[]) { // use this to copy to an outside array for(int i; i < input.cap() && i < intervals.cap(); i++) intervals[i] => input[i]; } // - - - Acquiring note fun int note(int pitch) { // use this to acquire note (calculated in semitones) from the mode // without octave input pitch--; // so user can start the first pitch from 1 instead of 0 0 => int octave; // but we still have to use octave if pitch is negative // calculate pitch and octave for use the intervals array // by limiting pitch in rang 0..intervals.cap()-1 and adjust octave number if (pitch < 0) octave--; pitch / intervals.cap() +=> octave; (pitch - (pitch / intervals.cap() - 1) * intervals.cap()) % intervals.cap() => pitch; 0 => int sum; // calculate semitones for the pitch // with rootPosition for easy mode rotation for(int i; i < pitch; i++) intervals[(i + rootPosition) % intervals.cap()] +=> sum; octave * octaveSize +=> sum; // select desired octave return sum; // and we'll have the result in semitone } fun int note(int pitch, int octave) { // note, with octave number also return note(pitch) + octave * octaveSize; } // - - - RotationZ fun void rotate(int x) { // rotate the mode x times x +=> rootPosition; (rootPosition - (rootPosition / intervals.cap() - 1) * intervals.cap()) % intervals.cap() => rootPosition; } fun void setRotate(int x) { // reset rotation point to x x => rootPosition; (rootPosition - (rootPosition / intervals.cap() - 1) * intervals.cap()) % intervals.cap() => rootPosition; } fun void rotateApply() { // update current rotation into the interval array int dummy[intervals.cap()]; (rootPosition - (rootPosition / intervals.cap() - 1) * intervals.cap()) % intervals.cap() => rootPosition; for(int i; i < intervals.cap(); i++) intervals[(i + rootPosition) % intervals.cap()] => dummy[i]; for(int i; i < intervals.cap(); i++) dummy[i] => intervals[i]; 0 => rootPosition; // and clear the rootPosition } fun void rotateApply(int x) { // move root position to x and update current rotation into the interval array right away setRotate(x); rotateApply(); } // - - - Chord Formation fun void chord(int root, int degree[], int result[]) { // make a chord from position list (chord degrees) for(int i; i < degree.cap() && i < result.cap(); i++) { note(root-1 + degree[i]) => result[i]; } } fun void chord(int root, int octave, int degree[], int result[]) { // make a chord from position list, with octave for(int i; i < degree.cap() && i < result.cap(); i++) { note(root-1 + degree[i]) + octave * octaveSize => result[i]; } } // - - - Operation fun void retrograde() { int dummy; // swap all the left part with all the right part: an easy way to retrograde for(int i; i < intervals.cap()/2; i++) { intervals[i] => dummy; intervals[intervals.cap()-1 - i] => intervals[i]; dummy => intervals[intervals.cap()-1 - i]; } } fun void invert() { // this is one thing we should have: invertion of the intervals for(int i; i < intervals.cap(); i++) -1 *=> intervals[i]; update(); } fun void trim(int begin, int end) { // this can trim the interval array by selecting a section. // but we should prevent array-out-of-bound end++; if (begin < 0) 0 => begin; if (end >= intervals.cap()) intervals.cap()-1 => end; if (begin > end) 0 => begin => end; int dummy[end-begin]; // start trimming for(int i; i < end-begin; i++) intervals[begin + i] => dummy[i]; set(dummy); // and it's all done } fun void addLeft(int input[]) { // add a new set of intervals in front of the current array int dummy[intervals.cap() + input.cap()]; for(int i; i < input.cap(); i++) input[i] => dummy[i]; for(int i; i < intervals.cap(); i++) intervals[i] => dummy[input.cap() + i]; set(dummy); // and it's all done <<< intervals.cap() >>>; } fun void addRight(int input[]) { // add a new set of intervals in front of the current array int dummy[intervals.cap() + input.cap()]; for(int i; i < intervals.cap(); i++) intervals[i] => dummy[i]; for(int i; i < input.cap(); i++) input[i] => dummy[intervals.cap() + i]; set(dummy); // and it's all done <<< intervals.cap() >>>; } fun void addValue(int input[]) { // add a set of values to the original intervals for(int i; i < intervals.cap() && i < input.cap(); i++) input[i] +=> intervals[i]; update(); } fun void addValue(int input[], int offset) { // addValue with offset (offset - (offset / intervals.cap() - 1) * intervals.cap()) % intervals.cap() => offset; for(int i; i < intervals.cap() && i < input.cap(); i++) input[i] +=> intervals[(i + offset) % intervals.cap()]; update(); } fun void subtractValue(int input[]) { // subtract a set of values to the original intervals for(int i; i < intervals.cap() && i < input.cap(); i++) input[i] -=> intervals[i]; update(); } fun void subtractValue(int input[], int offset) { // subtractValue with offset (offset - (offset / intervals.cap() - 1) * intervals.cap()) % intervals.cap() => offset; for(int i; i < intervals.cap() && i < input.cap(); i++) input[i] -=> intervals[(i + offset) % intervals.cap()]; update(); } fun void multiplyValue(int input[]) { // multiply a set of values to the original intervals for(int i; i < intervals.cap() && i < input.cap(); i++) input[i] *=> intervals[i]; update(); } fun void multiplyValue(int input[], int offset) { // multiplyValue with offset (offset - (offset / intervals.cap() - 1) * intervals.cap()) % intervals.cap() => offset; for(int i; i < intervals.cap() && i < input.cap(); i++) input[i] *=> intervals[(i + offset) % intervals.cap()]; update(); } fun void divideValue(int input[]) { // divide a set of values from the original intervals // division by zero will result in no change for(int i; i < intervals.cap() && i < input.cap(); i++) if (input[i] != 0) input[i] /=> intervals[i]; update(); } fun void divideValue(int input[], int offset) { // divideValue with offset // division by zero will result in no change (offset - (offset / intervals.cap() - 1) * intervals.cap()) % intervals.cap() => offset; for(int i; i < intervals.cap() && i < input.cap(); i++) if (input[i] != 0) input[i] /=> intervals[(i + offset) % intervals.cap()]; update(); } }
A a new brief user manual is integrated in the comment in the code. A more detailed one will be posted soon!
And this is the test code for testing the class. It's also a tutorial/introduction.
// - - - test code: to test all the features Modedular A, B, C; A.set("harmonic minor"); // prepare A as a harmonic minor mode B.set([2, 2, 2, 1, 2, 1, 2]); // prepare B as lydian mixolydian (dominant lydian) C.set([3, 1, 2, 1, 3, 2]); // prepare C as something.. hmm kinda like a major blues, with 6 intervals Flute s1 => NRev rev => dac; s1.gain(.5); rev.mix(.15); Flute s2[6]; for(int i; i < 6; i++) { s2[i] => rev; s2[i].gain(.2); } 60 => int baseNote; // functions for easy playing fun void PlayScale(StkInstrument S, Modedular M) { for(1 => int i; i <= 15; i++) { M.note(i) + baseNote => Std.mtof => s1.freq; S.noteOn(.5); 200::ms => now; S.noteOff(.5); 50::ms => now; } second => now; // a little pause } fun void PlayChord(int notes[]) { for(int i; i < notes.cap(); i++) { notes[i] + baseNote => Std.mtof => s2[i].freq; s2[i].noteOn(.7); } second => now; for(int i; i < notes.cap(); i++) s2[i].noteOff(.7); 50::ms => now; } // let's perform each feature <<< "play scale A","" >>>; PlayScale(s1, A); <<< "play scale B","" >>>; PlayScale(s1, B); <<< "play scale C","" >>>; PlayScale(s1, C); int mode1[A.intervals.cap()]; <<< "get scale from A","" >>>; A.get(mode1); <<< "display the acquired mode intervals:","" >>>; for(int i; i < mode1.cap(); i++) <<< "interval ", i, ": ", mode1[i], " semitones." >>>; <<< "make some chords.. first, use [1,3,5] form on scale A","" >>>; int chord[3]; // chord degree = note number .. like 1 3 5 is a triad .. 1 3 5 7 a seventh chord for(1 => int i; i <= 8; i++) { A.chord(i, [1, 3, 5], chord); // this will create notes (in semitone) and put in the 'chord' array PlayChord(chord); } second => now; <<< "let's try chord with more voices.. let's say.. form [1,4,7,9,11]. Jazzy!","" >>>; new int[5] @=> chord; for(1 => int i; i <= 8; i++) { A.chord(i, [1, 4, 7, 9, 11], chord); PlayChord(chord); } second => now; <<< "let's try that with a different scale: scale B.. woohoo.","" >>>; for(1 => int i; i <= 8; i++) { B.chord(i, [1, 4, 7, 9, 11], chord); PlayChord(chord); } second => now; <<< "let's do some rotation: play scale A original:","" >>>; PlayScale(s1, A); <<< "we'll rotate it so that the mode will start from the next (second) root position instead.","" >>>; A.rotate(1); PlayScale(s1, A); <<< "rotate back","" >>>; A.rotate(-1); PlayScale(s1, A); <<< "rotate back another 2 times","" >>>; A.rotate(-2); PlayScale(s1, A); <<< "Apply the rotation to the interval array:","" >>>; A.rotateApply(); <<< "and let's see the content of the interval array now:","" >>>; for(int i; i < A.intervals.cap(); i++) <<< "interval ", i, ": ", A.intervals[i], " semitones." >>>; second => now; // - - - - - <<< "alright! let's do some operations on the modes. this time we'll play with mode B","" >>>; <<< "original:","" >>>; PlayScale(s1, B); <<< "retrograde!","" >>>; B.retrograde(); PlayScale(s1, B); <<< "inverse!","" >>>; 24 +=> baseNote; B.invert(); PlayScale(s1, B); <<< "retrograde again!","" >>>; B.retrograde(); PlayScale(s1, B); <<< "and inverse! (so this time it'd be back to the original)","" >>>; 24 -=> baseNote; B.invert(); PlayScale(s1, B); // - - - - - <<< "add more intervals behind mode B. let's say.. an ionian. so it'd be a 24-semitone mode!","" >>>; B.addRight([2, 2, 1, 2, 2, 2, 1]); for(int i; i < B.intervals.cap(); i++) <<< "interval ", i, ": ", B.intervals[i], " semitones." >>>; PlayScale(s1, B); <<< "trim it by selecting interval 5 to 11 (we'll get a 7-note mode)","" >>>; B.trim(5, 11); for(int i; i < B.intervals.cap(); i++) <<< "interval ", i, ": ", B.intervals[i], " semitones." >>>; PlayScale(s1, B); <<< "add 1 semitone to all the intervals!","" >>>; B.addValue([1,1,1,1,1,1,1]); PlayScale(s1, B); <<< "subtract 1 semitone from all the intervals!","" >>>; B.subtractValue([1,1,1,1,1,1,1]); PlayScale(s1, B); <<< "multiply 2 to all the intervals! (make it a 7-notes 24-semitone mode)","" >>>; B.multiplyValue([2,2,2,2,2,2,2]); PlayScale(s1, B); <<< "divide 2 from all the intervals!","" >>>; B.divideValue([2,2,2,2,2,2,2]); PlayScale(s1, B); <<< "thanks for staying with us. comments and bug reports are very welcome. and thanks for testing", "" >>>;