from math import floor,log from math_stuff import * from __future__ import generators from string import split from copy import deepcopy def cents_et(degree,division): """cents_et(degree,division) returns the cents value of a given pitch \ degree in a given octave divison.""" return (1200.0/division)*degree def note_and_bend(cents): """note_and_bend(cents) returns a tuple containing a standard 12-equal \ keyboard MIDI note number, plus a pitch bend value required to hit the \ target parameter's cents value""" note,remain=divmod(cents,100.0) if remain>=50: note=note+1 remain=remain-100 note=int(note) bend=8192+int(round(remain*40.96)) if bend==7562: ### fixes a korg x5dr bug bend=7561 ################### return note,bend def ji_space(numerator,denominator): """ji_space(numerator,denominator) returns an octave-reduced and \ simplified numerator, denominator, and cents value as a 3-tuple. \ The numerator and denominator themselves can be passed as arithmetic \ expressions to be evaluated, so that in effect, fractions can be \ multiplied, etc.""" dec_val=numerator/(denominator+0.0) if numerator == 0 or denominator == 0: raise ZeroDivisionError if dec_val < 1: new_dec_val=dec_val while new_dec_val < 1: numerator=numerator*2 new_dec_val=numerator/(denominator+0.0) elif dec_val > 2: new_dec_val=dec_val while new_dec_val > 2: denominator=denominator*2 new_dec_val=numerator/(denominator+0.0) factor=gcd(numerator,denominator) cents=log((numerator+0.0)/denominator)/log(2)*1200 return numerator/factor,denominator/factor,cents def make_offsets(scale_array): """make_offsets(scale_array) returns a cents-offset from 12-equal \ temperament array based on an inputted array of string representations \ of a Just Intonation scale as fractions, e.g. ['1/1','16/15', etc.]. Since \ this is designed to give 12-member sets, the input scale must be 12-members \ large.""" out=[scale_array[0]] step_of_12=0 for frac in scale_array[1:]: fraction=split(frac,'/') cents_value=ji_space(int(fraction[0]),int(fraction[1]))[2] out.append(int(round(cents_value-step_of_12))) step_of_12 += 100 return out def octave_of_fraction(numerator,denominator): """octave_of_fraction(numerator,denominator) returns the MIDI octave\ that a given just intonation ratio will lie in, with middle C equal to 5.""" dec_val = numerator/(denominator+0.0) return 5 + floor(log(dec_val)/log(2)) def make_meantone(three_two,length): three_two=int(round(three_two*1000)) values=[] indexes=[] for x in range(length): new=(x*three_two)%1200000 if new not in values: values.append(new) values.append(1200000) unsorted_values=values[:] values.sort() for item in unsorted_values: indexes.append(values.index(item)) return values, unsorted_values, indexes def meanfifth(frac): """return a meantone fifth size in octave, cents, and linear factor amount""" pure=log(1.5)/log(2) syn_comma=log(81/80.0)/log(2) factor=syn_comma*frac meantone_fifth=pure-factor return meantone_fifth, meantone_fifth*1200, pow(2, meantone_fifth) def ji2cents(scale_string): """ji2cents(scale_string) returns an array of cents values from a space seperated\ series of just intonation ratios, expressed as 'n/d'""" out_array=[] local_array=scale_string.split() for ratio in local_array: n,d=ratio.split('/') out_array.append(ji_space(int(n),int(d))[2]) return out_array def rotate(list): p=list.pop() list.reverse() list.append(p) list.reverse() return list def modulation_matrix(scale_array): """modulation_matrix(scale_array) returns 12 arrays of twelve MIDI pitch bends in each array\ so that a simple key picking algorithm can apply pitch bends to a given text representation\ of a MIDI file. 'scale_array' must be an array of precisely 12 pitches represented as cents values\ of the desired base scale, starting with 0.0 cents.""" out_array=[] equal_12=range(0,1200,100) base_scale=[int(round(scale_array[i]-equal_12[i])) for i in range(len(scale_array))] base_scale_copy=deepcopy(base_scale[:]) out_array.append(base_scale) for transposition in base_scale[1:]: transposed=[] base_scale_copy=rotate(base_scale_copy) for pitch in base_scale_copy: transposed.append(transposition+pitch) out_array.append(transposed) return out_array def is_mos(array): diffs=[] for x in range(1,len(array),1): diff=array[x]-array[x-1] if diff not in diffs: diffs.append(diff) if len(diffs)<=2: return 1 else: return 0 def find_mos_a(three_two,limit): mos_set=[] for x in range(5,limit,1): scale=make_meantone(three_two,x) if is_mos(scale[2]): mos_set.append(x) return mos_set def first(n,g): return [g.next() for x in range(n)] def find_mos(three_two, mustBeProper=0): ratio = three_two/12e2 i = int(ratio) mPrev, m = 0, 1 while abs(ratio-i)>1e-6: ratio = 1/(ratio-i) i = int(ratio) if mustBeProper: mPrev, m = m, m*i + mPrev yield m else: mPrev, m = m, mPrev for n in range(i): m += mPrev yield m def microtonal_chromatic_scale(division, octave_min, octave_max): notes=[] bends=[] for oct in range(octave_min, octave_max+1, 1): for step in range(division): note,bend=note_and_bend(cents_et(step,division)) notes.append((oct*12)+note) bends.append(bend) return notes,bends class microtonal: def __init__(self,division): # 3-limit intervals: self.three_two=int(round((ji_space(3,2)[2]*division)/1200)) self.four_three=division-self.three_two # 5-limit intervals: self.five_four=int(round((ji_space(5,4)[2]*division)/1200)) self.five_three=self.five_four+division-self.three_two self.six_five=self.three_two-self.five_four self.eight_five=division-self.five_four self.fifteen_eight=self.three_two+self.five_four self.sixteen_fifteen=self.four_three-self.three_two # 7-limit: self.seven_four=int(round((ji_space(7,4)[2]*division)/1200)) self.eight_seven=division-self.seven_four self.seven_six=self.seven_four-self.three_two self.seven_five=self.seven_four-self.five_four self.ten_seven=self.five_four+division-self.seven_four self.twelve_seven=self.three_two+division-self.seven_four # 9-limit: self.nine_eight=(self.three_two*2)-division self.sixteen_nine=division-self.nine_eight self.nine_five=self.nine_eight+division-self.five_four self.tritone=self.nine_eight+self.five_four self.nine_seven=self.nine_eight+division-self.seven_four # 11-limit: self.eleven_eight=int(round((ji_space(11,8)[2]*division)/1200)) # 13-limit: self.thirteen_eight=int(round((ji_space(13,8)[2]*division)/1200)) ############## scales: self.major=[0, self.nine_eight, self.five_four, self.four_three, self.three_two, self.five_three, self.fifteen_eight] self.minor=[0, self.nine_eight, self.six_five, self.four_three, self.three_two, self.eight_five, self.sixteen_nine] self.dorian=[0, self.nine_eight, self.six_five, self.four_three, self.three_two, self.five_three, self.sixteen_nine] self.phrygian=[0, self.sixteen_fifteen, self.six_five, self.four_three, self.three_two, self.eight_five, self.sixteen_nine] self.lydian=[0, self.nine_eight, self.five_four, self.tritone, self.three_two, self.five_three, self.fifteen_eight] self.mixolydian=[0, self.nine_eight, self.five_four, self.four_three, self.three_two, self.five_three, self.sixteen_nine] self.locrian=[0, self.sixteen_fifteen, self.six_five, self.four_three, self.tritone, self.eight_five, self.sixteen_nine] self.octatonic=[0, self.nine_eight, self.six_five, self.four_three, self.tritone, self.eight_five, self.five_three, self.fifteen_eight] self.whole_tone=[0, self.nine_eight, self.five_four, self.tritone, self.eight_five, self.sixteen_nine] self.pentatonic=[0, self.nine_eight, self.five_four, self.three_two, self.five_three] self.gypsy=[0, self.sixteen_fifteen, self.five_four, self.four_three, self.three_two, self.eight_five, self.fifteen_eight] self.chromatic=range(division) def microtonal_scale(division,type,oct_min,oct_max): scale=[] for octave in range(oct_min,oct_max): for note in eval("microtonal(%i).%s" % (division,type)): scale.append((octave*division)+note) return scale def create_scale_from_cents(list,oct_min,oct_max): notes=[] bends=[] for octave in range(oct_min,oct_max): for cents in list: note,bend=note_and_bend(cents) notes.append((octave*12)+note) bends.append(bend) note,bend=note_and_bend(list[0]) notes.append((oct_max*12)+note) bends.append(bend) return notes,bends def dudon(x,y): scale=[] out_scale=[] for a in range(1,x+1,1): if x%a==0: scale.append(a) for b in range(1,y+1,1): if (y%b==0) and (b not in scale): scale.append(b) scale.sort() print "scale is %s" % str(scale) max_note=max(scale) if max_note%2==0: out_scale.append(max_note/2) scaling_factor=1 else: scaling_factor=2 out_scale.append(max_note) max_note=max_note*2 for item in scale: factor=log(max_note/(item+0.0))/log(2) answer=item*pow(2,int(factor)) if answer not in out_scale: out_scale.append(answer) out_scale.sort() return out_scale