Modedular.ck

From CSWiki
Revision as of 09:30, 5 August 2008 by Steve (talk | contribs) (Reverted edit of Blogsdna, changed back to last version by Kijjaz)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

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", "" >>>;