Modedular.ck
From CSWiki
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", "" >>>;
