//collection of musical scales, code and data public class Scale { //minor scales [0, 2, 3, 5, 7, 8, 10] @=> int min[]; //minor mode [0, 2, 3, 5, 7, 8, 11] @=> int har[]; //harmonic minor [0, 2, 3, 5, 7, 9, 11] @=> int asc[]; //ascending melodic minor [0, 1, 3, 5, 7, 8, 10] @=> int nea[]; //make 2nd degree neapolitain //other church modes [0, 2, 4, 5, 7, 9, 11] @=> int maj[]; //major scale [0, 2, 4, 5, 7, 8, 10] @=> int mixo[]; //church mixolydian [0, 2, 3, 5, 7, 9, 10] @=> int dor[]; //church dorian [0, 2, 4, 7, 9] @=> int pent[]; //major pentatonic [0, 1, 4, 5, 7, 8, 10] @=> int jewish[]; //phrygian dominant, jewish scale [0, 2, 3, 6, 7, 8, 11] @=> int gypsy[]; //hungarian or gypsy [0, 1, 4, 5, 7, 8, 11] @=> int arabic[]; //arabic scale [0, 2, 4, 6, 8, 10] @=> int whole_tone[]; //the whole tone scale [0, 2, 3, 5, 6, 8, 9, 11] @=> int dim[]; //diminished scale //convenience for minor keys fun int[] minor(int deg) { if (deg == 4) return asc; //ascending else return min; } fun int[] neap(int deg) { if (deg == 4) return asc; else if (deg == 1) return nea; else return min; } //two-sided scale degrees fun int scale(int note, int sc[]) { sc.cap() => int len; (note / len) => int oct; (note % len) => int off; while (off < 0) { len +=> off; 1 -=> oct; } return oct * 12 + sc[off]; } //do the octave and chrom and conversion stuff too fun float scaleFreq(int note, int sc[], int oct, int chrom) { return Std.mtof(oct*12+scale(note, sc)+chrom); } //Simple Arpeggiation fun int arp(int a) { a/3 => int o; return (a%3)*2+7*o; } //simple arpeggiate fun int arp(int a, int osteps, int deg[]) { a/deg.cap() => int o; a => int i; //wrap negative, two sided while( i<0 ) osteps +=> i; return deg[i%deg.cap()] + osteps*o; } //generalized arp //Bass-Independant Arpeggiation //preserve the general contour while staying in right scales //variables for avoiding doubling 100 => int ARP_STEPS; int arp_used[ARP_STEPS]; //reset the doubling tracking array fun void arp_reset() { for (0=>int i; i arp_used[i]; } //convenience versions of iarp fun int iarp(int a, int b, int osteps, int deg[]) { return iarp(a, b, osteps, deg, 0, 0); //no drag, doubles } //bass invariant arp fun int iarp(int a, int b, int deg[]) { //normal scales, no drag, doubles return iarp(a, b, 7, deg, 0, 0); } fun int iarp(int a, int b, int osteps, int deg[], float drag, int nd) { //n is the note in bass-deg-0 arp(a, osteps, deg) => int n; //drag the target note by some factor of bass (drag * b) $ int +=> n; //avoid scanning too much, jump down octaves 0 => int ojump; while( ojump + b + arp(0, osteps, deg) > n ) osteps -=> ojump; //go down an octave //find the closest one in a shifted scale //shifted scale is b+deg[i...], or b+arp(i) //scale must start out lower to compensate 0=>int cl; for (0=>int i; i cl; //cantidate if ( nd && arp_used[i] ) continue; //skip doubles if ( cl >= n ) { 1 => arp_used[i]; //mark used break; //found it } } return cl; } //bass invariant arp, with drag and nodouble //convenience versions of charp fun int charp(int a, int chrb, int deg[], int sc[]) { return charp(a, chrb, 7, deg, sc, 0, 0); } //chromatic bass-independent arpeggiation fun int charp(int a, int chrb, int osteps, int deg[], int sc[], float drag, int nd) { //render the note in chromatic bass 0 (C) //render it to a chromatic number arp(a, osteps, deg) => int n; //the scale will change, but maybe not a big deal scale(n, sc) => int nc; //drag, add it to the target (drag * chrb) $ int +=> nc; //to scan, make first cantidate less than the target 0 => int ojump; while( ojump+chrb+scale( arp( 0, osteps, deg ), sc ) > nc ) 12 -=> ojump; //go down chromatic octave 0=>int cl; for (0=>int i; i cl; //cantidate //skip if already used if ( nd && arp_used[i] ) continue; if ( cl >= n ) { //found it 1 => arp_used[i]; break; } } return cl; } //the NEW WAY OF doing stuff //the abstraction, wrap an array //([0,2,4],7) => [..., -7,-5,-3, 0,2,4, 7,9,11, ...] fun int wrap_array(int a, int ker[], int jump) { ker.cap() => int len; a/len => int j; //number of jumps a%len => int s; //steps within kernel if ( s<0 ) { //the negative border case s+len => s; //move s to positive range j-1 => j; //one more negative jump } return ker[s]+j*jump; //kernel scale + jumps } //end of wrap_array //fun int scale(int note, int sc[]) //{ return wrap_array(note, sc, 12) } //fun int arp(int a, int osteps, int deg[]) //swap inputs here, or change convention //{ return wrap_array(a, deg, osteps) } //strategy: compute the arp shift for note-0 in bass b //then jump up the scale depending on what you need //arguments are ordered by importance fun int iarpB(int a, int b, int deg[], float drag, int osteps) { deg.cap() => int len; //initial jump down the scale, in arp-steps ((b/osteps)+1)*-len => int s0; //arp deg //find the first translated one geq to zero-bass scale[0] until ( b+wrap_array(s0,deg,osteps) >= deg[0] ) { s0++; } //inc until found //the real shift is the zero-shift for this bass plus how far up //drag is applied by reversing the direction of s0 by multiplying the float ((s0 * drag) $ int) + a => int s; //whether this calls arp or wrap_array is a style point return b+wrap_array(s,deg,osteps); } //convenience versions of this fun int iarpB(int a, int b, int deg[], float drag) { return iarpB(a, b, deg, drag, 7); } fun int iarpB(int a, int b, int deg[]) { return iarpB(a, b, deg, 0, 7); } fun int iarpB(int a, int b) { return iarpB(a, b, [0,2,4], 0, 7); } fun int iarpB(int a) { return iarpB(a, 0, [0,2,4], 0, 7); } //globals, a hack static int gint; } //end class