Modedular.ck

From CSWiki
Revision as of 03:05, 26 February 2008 by Kijjaz (talk | contribs)

Jump to: navigation, search

This is the code for the version 0.2 testing

// Modedular version 0.2 testing
// by Kijjasak Triyanond (kijjaz) kijjaz@yahoo.com 

// [note] this is still quite experimental, so some functions might have problems
// some more features will be added soon.
// [fixes]
// * now the note function's code had been cleaned up
//   and is greatly reduced (in size) and optimized (generalized); the output is the same.
// * i've decided that the normal rotation should not rotate the whole mode data,
//   but instead set the offset of the readhead while using note function
//   the rotation can by 'Applied' so that it really rotate the interval array.
// * now chord is fully functional, check the test code to see chord in action           

// 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.

class Modedular
{
    [0] @=> int intervals[]; // array of intervals in the mode
    0 => int octaveSize; // octave size in semitones
    0 => int rotationOffset; // for easy mode rotation
       
    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]);
        update();
    }
    fun void get(int input[])
    {
        // use this to copy to an outside array
        new int[input.cap()] @=> input;
        for(int i; i < input.cap(); i++) intervals[i] => input[i];
    }
   
    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 + rotationOffset) % 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;
    }   
   
    fun void rotate(int x)
    {
        // rotate the mode x times        
        x +=> rotationOffset;
        (rotationOffset - (rotationOffset / intervals.cap() - 1) * intervals.cap()) % intervals.cap() => rotationOffset;
    }
    fun void setRotate(int x)
    {
        // reset rotation point to x
        x => rotationOffset;
        (rotationOffset - (rotationOffset / intervals.cap() - 1) * intervals.cap()) % intervals.cap() => rotationOffset;
    }
    fun void rotateApply()
    {
        // use current rotation offset to really rotate the interval array.
        // then reset the rotation offset.
        int dummy[intervals.cap()];
        for(int i; i < intervals.cap(); i++) intervals[(i + rotationOffset) % intervals.cap()] => dummy[i];
        for(int i; i < intervals.cap(); i++) dummy[i] => intervals[i];
        0 => rotationOffset;
    }
    fun void rotateApply(int x)
    {
        // use in supplied number as the rotation offset, then really rotate the interval array.
        // then reset the rotation offset, also.        
        setRotate(x);
        rotateApply();
    }
   
    fun void chord(int root, int positions[], int result[])
    {
        // make a chord from position list (chord degrees)
        for(int i; i < positions.cap() && i < result.cap(); i++)
        {
            note(root + positions[i] - 1) => result[i];
        }
    }
    fun void chord(int root, int octave, int positions[], int result[])
    {
        // make a chord from position list, with octave
        for(int i; i < positions.cap() && i < result.cap(); i++)
        {
            note(root + positions[i] - 1) + octave * octaveSize => result[i];
        }
    }   
}

User Manual: (draft)

  • Use .set to set a new mode. The mode's data is formed by a serie of intervals (in semitones).

For example, .set( [2, 2, 1, 2, 2, 2, 1] ) will create Ionian mode or Major Scale. Other kinds and length of a scale can be created also for example, .set( [2, 1] ) creates a Whole-Half diminished scale.

  • .set can be used to set the mode to a preset by passing a string with the mode's name.

For example, .set("harmonic minor") would make the mode a harmonic minor instantly. Current mode names are: lydian, ionian, mixolydian, dorian, aeolian, phrygian, locrian, harmonic minor, melodic minor

  • Use .get to copy the interval array.
int result[];
A.get(result);

would create a new array and assign to result with the value copied from the mode's interval array

  • Function .note(int pitch) would return semitones (in integer) that can be added to a midi note value. The result will be the note in the mode in the key of the midi note value that we add.[br]

Note: the pitch value starts from 1, not 0. for example, 1 3 5 7 in ionian mode forms a major 7th chord.

  • .note can also accept octave adjustment by using .note(int pitch, int octave)
  • .rotate(int x) will rotate the mode x times and the result can be read by .note and .chord (the interval array would stay the same, the rotation does not change the original array, it changes only the "Root Position" value)
  • .setRotate(int x) set the "Root Position" directly
  • .rotateApply() will update the current rotation to the interval array itself, thus changing the interval array
  • .rotateApply(int x) will set the "Root Position" at the x position then update to the interval array right away.
  • .chord(int root, int ChordDegrees[], int result[]) - specify root as the root of the chord. ChordDegrees contains a serie of chord degrees to create, the result (notes of the chord) would be copied into result.[br]

For example, if we have .set("ionian"), and use .chord with root = 1, ChordDegree = [1, 3, 5, 7], the result would be a major 7th chord on the tonic.

  • .chord(int root, int octave, int ChordDegrees[], int result[]) is also possible. use the octave parameter to adjust octave.

Have fun playing with modes. Some improvements will be added soon!

An example code for the usage of the class (with some focus on rotation and chord):

60 => int baseNote; // use middle C as base note

Flute s1 => NRev rev => dac; // prepare flute and reverb for performance

Clarinet s2[4]; // prepare clarinet orchestra
for(int i; i < 4; i++)
{
    s2[i] => rev;
    s2[i].gain(.1);
}

s1.gain(.5);
rev.mix(.15);

Modedular A;

fun void PlayScale()
{
    for(1 => int i; i <= 15; i++)
    {
        A.note(i) + baseNote => Std.mtof => s1.freq;
        s1.noteOn(.2);
        200::ms => now;
        s1.noteOff(.2);
        50::ms => now;
    }
}

<<< "play notes from C ionain mode.", "" >>>;
A.set("ionian");
PlayScale();
second => now;

<<< "rotate mode by +1.", "" >>>;
A.rotate(1);
PlayScale();
<<< "oh, correct. That was dorian, so the rotation works fine", "" >>>;
second => now;

<<< "now rotate it by -3 and let's see.", "" >>>;
A.rotate(-3);
PlayScale();
<<< "that is aeolian. Okay the rotation works correctly", "" >>>;
second => now;

<<< "take a look at the interval array: ", "" >>>;
for(int i; i < A.intervals.cap(); i++) <<< "interval ", i, " : ", A.intervals[i] >>>;
<<< "ah! after rotating it around, it still stays as an ionian the same,", "" >>>;
<<< "let's try applying the rotation and see that the change is update in the interval array.", "" >>>;
A.rotateApply();
for(int i; i < A.intervals.cap(); i++) <<< "interval ", i, " : ", A.intervals[i] >>>;
PlayScale();
second => now;

// - - - chord testing
// although using Note is simple enough to use as chord creator, but I'm introducing a chord idea:

fun void PlayChord(int semitones[])
{
    for(int i; i < 4; i++)
    {
        semitones[i] + baseNote => Std.mtof => s2[i].freq;
        s2[i].noteOn(.8);
    }
    1200::ms => now;
    for(int i; i < 4; i++) s2[i].noteOff(.8);
    200::ms => now;
    
}

<<< "make 7th chord form: 1 3 5 7", "" >>>;
int ChordResult[4]; // will be used to keep notes produced from Modedular's chord function
int ChordDegree[]; // will be used to specify chord form

[1, 3, 5, 7] @=> ChordDegree;

for(1 => int i; i <= 8; i++)
{
    A.chord(i, ChordDegree, ChordResult);
    PlayChord(ChordResult);
}
second => now;

<<< "try changing into another mode, and play with a different set of chord degrees", "" >>>;
A.set([1, 2, 1, 2, 2, 2, 2]); // set A to "Super Locrian" mode to play some jazzy tension chords
[1, 4, 5, 7 + 7] @=> ChordDegree; // change voicing form, notice the +7, this simply means up an octave
// (because this time, we have 7 tones per octave)

for(1 => int i; i <= 8; i++)
{
    A.chord(i, ChordDegree, ChordResult);
    PlayChord(ChordResult);
}
second => now;