```julia using MIDI ``` # Types ```julia type KBMode modename::String #Name of Mode (eg Minor) keys::Array #Array of 12 KBKey objects end ``` ```julia type KBKey keyname::String #Name of key (eg F) dist::Array #1x12 vector representing probability distribution of notes for this key end ``` ```julia type KBChord tonic::String #(eg A) mode::String #(mM7) rawname::String #(eg Am, C7) rep::Array #Notes of given chord end ``` ```julia type VectorNote keyname::String vector::Array end ``` ```julia type inputChord name::String vector::Array label::String end ``` ```julia type inputChordList chords::Array{inputChord} end ``` ```julia type inputChordProgression chords::inputChordList startTimes::Array{Any} endTimes::Array{Any} end ``` ```julia type chordDist targetchord::inputChord distance::Float64 end ``` ```julia type chordDistances name::inputChord distances::Array{chordDist} end ``` ```julia type chordDistanceList list::Array{chordDistances} end ``` ```julia type chordSubProbs chord::inputChord probabilities::Array end ``` ```julia type chordSubProbList probabilities::Array{chordSubProbs} chordlist::inputChordList end ``` ```julia type cpkbEntry chordprior::Array localmetric::Int last::Int uniqueID::String dist::Array end ``` # Knowledge Base ## Major Mode ```julia KBMode_Major = KBMode("Major",Array{KBKey}(12)); KBKey_CM = KBKey("C=",[0.32, 0.01, 0.03, 0.01, 0.22, 0.04, 0.01, 0.225, 0.01, 0.03, 0.07, 0.025]); KBKey_CsM = KBKey("C#",[0.025, 0.32, 0.01, 0.03, 0.01, 0.22, 0.04, 0.01, 0.225, 0.01, 0.03, 0.07]); KBKey_DM = KBKey("D=",[0.07, 0.025, 0.32, 0.01, 0.03, 0.01, 0.22, 0.04, 0.01, 0.225, 0.01, 0.03]); KBKey_DsM = KBKey("D#",[0.03, 0.07, 0.025, 0.32, 0.01, 0.03, 0.01, 0.22, 0.04, 0.01, 0.225, 0.01]); KBKey_EM = KBKey("E=",[0.01, 0.03, 0.07, 0.025, 0.32, 0.01, 0.03, 0.01, 0.22, 0.04, 0.01, 0.225]); KBKey_FM = KBKey("F=",[0.225, 0.01, 0.03, 0.07, 0.025, 0.32, 0.01, 0.03, 0.01, 0.22, 0.04, 0.01]); KBKey_FsM = KBKey("F#",[0.01, 0.225, 0.01, 0.03, 0.07, 0.025, 0.32, 0.01, 0.03, 0.01, 0.22, 0.04]); KBKey_GM = KBKey("G=",[0.04, 0.01, 0.225, 0.01, 0.03, 0.07, 0.025, 0.32, 0.01, 0.03, 0.01, 0.22]); KBKey_GsM = KBKey("G#",[0.22, 0.04, 0.01, 0.225, 0.01, 0.03, 0.07, 0.025, 0.32, 0.01, 0.03, 0.01]); KBKey_AM = KBKey("A=",[0.01, 0.22, 0.04, 0.01, 0.225, 0.01, 0.03, 0.07, 0.025, 0.32, 0.01, 0.03]); KBKey_AsM = KBKey("A#",[0.03, 0.01, 0.22, 0.04, 0.01, 0.225, 0.01, 0.03, 0.07, 0.025, 0.32, 0.01]); KBKey_BM = KBKey("B=",[0.01, 0.03, 0.01, 0.22, 0.04, 0.01, 0.225, 0.01, 0.03, 0.07, 0.025, 0.32]); KBMode_Major.keys=[KBKey_CM,KBKey_CsM,KBKey_DM,KBKey_DsM,KBKey_EM,KBKey_FM,KBKey_FsM,KBKey_GM,KBKey_GsM,KBKey_AM,KBKey_AsM,KBKey_BM]; ``` ## Minor Mode ```julia KBMode_Minor = KBMode("Minor",Array{KBKey}(12)); KBKey_Cm = KBKey("C=",[0.17, 0.01, 0.13, 0.17, 0.01, 0.12, 0.01, 0.16, 0.1, 0.02, 0.03, 0.07]); KBKey_Csm = KBKey("C#",[0.07, 0.17, 0.01, 0.13, 0.17, 0.01, 0.12, 0.01, 0.16, 0.1, 0.02, 0.03]); KBKey_Dm = KBKey("D=",[0.03, 0.07, 0.17, 0.01, 0.13, 0.17, 0.01, 0.12, 0.01, 0.16, 0.1, 0.02]); KBKey_Dsm = KBKey("D#",[0.02, 0.03, 0.07, 0.17, 0.01, 0.13, 0.17, 0.01, 0.12, 0.01, 0.16, 0.1]); KBKey_Em = KBKey("E=",[0.1, 0.02, 0.03, 0.07, 0.17, 0.01, 0.13, 0.17, 0.01, 0.12, 0.01, 0.16]); KBKey_Fm = KBKey("F=",[0.16, 0.1, 0.02, 0.03, 0.07, 0.17, 0.01, 0.13, 0.17, 0.01, 0.12, 0.01]); KBKey_Fsm = KBKey("F#",[0.01, 0.16, 0.1, 0.02, 0.03, 0.07, 0.17, 0.01, 0.13, 0.17, 0.01, 0.12]); KBKey_Gm = KBKey("G=",[0.12, 0.01, 0.16, 0.1, 0.02, 0.03, 0.07, 0.17, 0.01, 0.13, 0.17, 0.01]); KBKey_Gsm = KBKey("G#",[0.01, 0.12, 0.01, 0.16, 0.1, 0.02, 0.03, 0.07, 0.17, 0.01, 0.13, 0.17]); KBKey_Am = KBKey("A=",[0.17, 0.01, 0.12, 0.01, 0.16, 0.1, 0.02, 0.03, 0.07, 0.17, 0.01, 0.13]); KBKey_Asm = KBKey("A#",[0.13, 0.17, 0.01, 0.12, 0.01, 0.16, 0.1, 0.02, 0.03, 0.07, 0.17, 0.01]); KBKey_Bm = KBKey("B=",[0.01, 0.13, 0.17, 0.01, 0.12, 0.01, 0.16, 0.1, 0.02, 0.03, 0.07, 0.17]); KBMode_Minor.keys=[KBKey_Cm,KBKey_Csm,KBKey_Dm,KBKey_Dsm,KBKey_Em,KBKey_Fm,KBKey_Fsm,KBKey_Gm,KBKey_Gsm,KBKey_Am,KBKey_Asm,KBKey_Bm]; ``` ## Major Chords ```julia KBChord_CM = KBChord("C","M","CM",[1,0,0,0,1,0,0,1,0,0,0,0]); KBChord_CsM = KBChord("C#","M","C#M",[0,1,0,0,0,1,0,0,1,0,0,0]); KBChord_DM = KBChord("D","M","DM",[0,0,1,0,0,0,1,0,0,1,0,0]); KBChord_DsM = KBChord("D#","M","D#M",[0,0,0,1,0,0,0,1,0,0,1,0]); KBChord_EM = KBChord("E","M","EM",[0,0,0,0,1,0,0,0,1,0,0,1]); KBChord_FM = KBChord("F","M","FM",[1,0,0,0,0,1,0,0,0,1,0,0]); KBChord_FsM = KBChord("F#","M","F#M",[0,1,0,0,0,0,1,0,0,0,1,0]); KBChord_GM = KBChord("G","M","GM",[0,0,1,0,0,0,0,1,0,0,0,1]); KBChord_GsM = KBChord("G","M","G#M",[1,0,0,1,0,0,0,0,1,0,0,0]); KBChord_AM = KBChord("A","M","AM",[0,1,0,0,1,0,0,0,0,1,0,0]); KBChord_AsM = KBChord("A#","M","A#M",[0,0,1,0,0,1,0,0,0,0,1,0]); KBChord_BM = KBChord("B","M","BM",[0,0,0,1,0,0,1,0,0,0,0,1]); ``` ```julia xKBChord_CM = KBChord("C","M","CM",[1,0,0,0,1,0,0,1,0,0,0,1]); xKBChord_CsM = KBChord("C#","M","C#M",[1,1,0,0,0,1,0,0,1,0,0,0]); xKBChord_DM = KBChord("D","M","DM",[0,1,1,0,0,0,1,0,0,1,0,0]); xKBChord_DsM = KBChord("D#","M","D#M",[0,0,1,1,0,0,0,1,0,0,1,0]); xKBChord_EM = KBChord("E","M","EM",[0,0,0,1,1,0,0,0,1,0,0,1]); xKBChord_FM = KBChord("F","M","FM",[1,0,0,0,1,1,0,0,0,1,0,0]); xKBChord_FsM = KBChord("F#","M","F#M",[0,1,0,0,0,1,1,0,0,0,1,0]); xKBChord_GM = KBChord("G","M","GM",[0,0,1,0,0,0,1,1,0,0,0,1]); xKBChord_GsM = KBChord("G","M","G#M",[1,0,0,1,0,0,0,1,1,0,0,0]); xKBChord_AM = KBChord("A","M","AM",[0,1,0,0,1,0,0,0,1,1,0,0]); xKBChord_AsM = KBChord("A#","M","A#M",[0,0,1,0,0,1,0,0,0,1,1,0]); xKBChord_BM = KBChord("B","M","BM",[0,0,0,1,0,0,1,0,0,0,1,1]); ``` ## Minor Chords ```julia KBChord_Cm = KBChord("C","m","Cm",[1,0,0,1,0,0,0,1,0,0,0,0]); KBChord_Csm = KBChord("C#","m","C#m",[0,1,0,0,1,0,0,0,1,0,0,0]); KBChord_Dm = KBChord("D","m","Dm",[0,0,1,0,0,1,0,0,0,1,0,0]); KBChord_Dsm = KBChord("D#","m","D#m",[0,0,0,1,0,0,1,0,0,0,1,0]); KBChord_Em = KBChord("E","m","Em",[0,0,0,0,1,0,0,1,0,0,0,1]); KBChord_Fm = KBChord("F","m","Fm",[1,0,0,0,0,1,0,0,1,0,0,0]); KBChord_Fsm = KBChord("F#","m","F#m",[0,1,0,0,0,0,1,0,0,1,0,0]); KBChord_Gm = KBChord("G","m","Gm",[0,0,1,0,0,0,0,1,0,0,1,0]); KBChord_Gsm = KBChord("G","m","G#m",[0,0,0,1,0,0,0,0,1,0,0,1]); KBChord_Am = KBChord("A","m","Am",[1,0,0,0,1,0,0,0,0,1,0,0]); KBChord_Asm = KBChord("A#","m","A#m",[0,1,0,0,0,1,0,0,0,0,1,0]); KBChord_Bm = KBChord("B","m","Bm",[0,0,1,0,0,0,1,0,0,0,0,1]); ``` ```julia xKBChord_Cm = KBChord("C","m","Cm",[1,0,0,1,0,0,0,1,0,0,1,0]); xKBChord_Csm = KBChord("C#","m","C#m",[0,1,0,0,1,0,0,0,1,0,0,1]); xKBChord_Dm = KBChord("D","m","Dm",[1,0,1,0,0,1,0,0,0,1,0,0]); xKBChord_Dsm = KBChord("D#","m","D#m",[0,1,0,1,0,0,1,0,0,0,1,0]); xKBChord_Em = KBChord("E","m","Em",[0,0,1,0,1,0,0,1,0,0,0,1]); xKBChord_Fm = KBChord("F","m","Fm",[1,0,0,1,0,1,0,0,1,0,0,0]); xKBChord_Fsm = KBChord("F#","m","F#m",[0,1,0,0,1,0,1,0,0,1,0,0]); xKBChord_Gm = KBChord("G","m","Gm",[0,0,1,0,0,1,0,1,0,0,1,0]); xKBChord_Gsm = KBChord("G","m","G#m",[0,0,0,1,0,0,1,0,1,0,0,1]); xKBChord_Am = KBChord("A","m","Am",[1,0,0,0,1,0,0,1,0,1,0,0]); xKBChord_Asm = KBChord("A#","m","A#m",[0,1,0,0,0,1,0,0,1,0,1,0]); xKBChord_Bm = KBChord("B","m","Bm",[0,0,1,0,0,0,1,0,0,1,0,1]); ``` ## Dominant7 Chords ```julia KBChord_C7 = KBChord("C","7","C7",[1,0,0,0,1,0,0,1,0,0,1,0]); KBChord_Cs7 = KBChord("C#","7","C#7",[0,1,0,0,0,1,0,0,1,0,0,1]); KBChord_D7 = KBChord("D","7","D7",[1,0,1,0,0,0,1,0,0,1,0,0]); KBChord_Ds7 = KBChord("D#","7","D#7",[0,1,0,1,0,0,0,1,0,0,1,0]); KBChord_E7 = KBChord("E","7","E7",[0,0,1,0,1,0,0,0,1,0,0,1]); KBChord_F7 = KBChord("F","7","F7",[1,0,0,1,0,1,0,0,0,1,0,0]); KBChord_Fs7 = KBChord("F#","7","F#7",[0,1,0,0,1,0,1,0,0,0,1,0]); KBChord_G7 = KBChord("G","7","G7",[0,0,1,0,0,1,0,1,0,0,0,1]); KBChord_Gs7 = KBChord("G","7","G#7",[1,0,0,1,0,0,1,0,1,0,0,0]); KBChord_A7 = KBChord("A","7","A7",[0,1,0,0,1,0,0,1,0,1,0,0]); KBChord_As7 = KBChord("A#","7","A#7",[0,0,1,0,0,1,0,0,1,0,1,0]); KBChord_B7 = KBChord("B","7","B7",[0,0,0,1,0,0,1,0,0,1,0,1]); ``` ## Chord Label Array ```julia KBLabels = [KBChord_CM, KBChord_CsM, KBChord_DM, KBChord_DsM, KBChord_EM, KBChord_FM, KBChord_FsM, KBChord_GM, KBChord_GsM, KBChord_AM, KBChord_AsM, KBChord_BM, KBChord_Cm, KBChord_Csm, KBChord_Dm, KBChord_Dsm, KBChord_Em, KBChord_Fm, KBChord_Fsm, KBChord_Gm, KBChord_Gsm, KBChord_Am, KBChord_Asm, KBChord_Bm, KBChord_C7, KBChord_Cs7, KBChord_D7, KBChord_Ds7, KBChord_E7, KBChord_F7, KBChord_Fs7, KBChord_G7, KBChord_Gs7, KBChord_A7, KBChord_As7, KBChord_B7]; ``` ```julia xKBLabels = [xKBChord_CM, xKBChord_CsM, xKBChord_DM, xKBChord_DsM, xKBChord_EM, xKBChord_FM, xKBChord_FsM, xKBChord_GM, xKBChord_GsM, xKBChord_AM, xKBChord_AsM, xKBChord_BM, xKBChord_Cm, xKBChord_Csm, xKBChord_Dm, xKBChord_Dsm, xKBChord_Em, xKBChord_Fm, xKBChord_Fsm, xKBChord_Gm, xKBChord_Gsm, xKBChord_Am, xKBChord_Asm, xKBChord_Bm, KBChord_C7, KBChord_Cs7, KBChord_D7, KBChord_Ds7, KBChord_E7, KBChord_F7, KBChord_Fs7, KBChord_G7, KBChord_Gs7, KBChord_A7, KBChord_As7, KBChord_B7]; ``` ## Parameters ```julia rho = 0.99; # Constant for calculating loudness lambda = 5; # Constant for calculating substitution probability chordsize = 5; # maximum number of notes to consider for labeling chord minchordsize = 3; # if the chord has fewer notes than this, we'll consider it to be repeating the preceding chord again metaoctave = 5; # tells the chordlist what octave to read the 60 defined chords in threshold = 1.2; # Maximum chord distance cutoff w_pspr1 = 1; # Weight factor for PSPR1 in scoring melody phrase segmentation w_pspr2 = 1; # Weight factor for PSPR2 in scoring melody phrase segmentation w_pspr3 = 1; # Weight factor for PSPR3 in scoring melody phrase segmentation ``` # Functions ## Preprocessing ```julia function getNoteName(y) x = mod(y,12)+1; ret = ""; if x == 1 ret = "C=" elseif x == 2 ret = "C#" elseif x == 3 ret = "D=" elseif x == 4 ret = "D#" elseif x == 5 ret = "E=" elseif x == 6 ret = "F=" elseif x == 7 ret = "F#" elseif x == 8 ret = "G=" elseif x == 9 ret = "G#" elseif x == 10 ret = "A=" elseif x == 11 ret = "A#" elseif x == 12 ret = "B=" else ret = "ERROR" end end ``` ```julia function getVectorNote(letter) ret = zeros(12); if letter == "C=" ret = [1,0,0,0,0,0,0,0,0,0,0,0] elseif letter == "C#" ret = [0,1,0,0,0,0,0,0,0,0,0,0] elseif letter == "D=" ret = [0,0,1,0,0,0,0,0,0,0,0,0] elseif letter == "D#" ret = [0,0,0,1,0,0,0,0,0,0,0,0] elseif letter == "E=" ret = [0,0,0,0,1,0,0,0,0,0,0,0] elseif letter == "F=" ret = [0,0,0,0,0,1,0,0,0,0,0,0] elseif letter == "F#" ret = [0,0,0,0,0,0,1,0,0,0,0,0] elseif letter == "G=" ret = [0,0,0,0,0,0,0,1,0,0,0,0] elseif letter == "G#" ret = [0,0,0,0,0,0,0,0,1,0,0,0] elseif letter == "A=" ret = [0,0,0,0,0,0,0,0,0,1,0,0] elseif letter == "A#" ret = [0,0,0,0,0,0,0,0,0,0,1,0] elseif letter == "B=" ret = [0,0,0,0,0,0,0,0,0,0,0,1] end return ret; end ``` ```julia function keyDistance(key1::Array, key2::Array) sum = 0; distance = 0; for i in 1:12 sum += (key1[i] - key2[i])^2; end distance = sqrt(sum); return distance; end ``` ```julia function getFirstMeasure(notes::Array{MIDI.Note}) subnotes = filter(x->x.position < 96*4, notes); return subnotes; end ``` ```julia function makePieceVector(notes::Array{MIDI.Note}) piecevector = zeros(12); l = length(notes); for i in 1:l rawnote = notes[i]; note = getVectorNote(getNoteName(rawnote.value)); piecevector += (1/l)*note; end return piecevector; end ``` ```julia function determineKey(piecevector::Array, magicnumber::Int) mindist = 1000; key = "Z#Mojnr"; index = 0; for i in 1:12 candidate = KBMode_Major.keys[i]; distance = keyDistance(piecevector, candidate.dist); if magicnumber == i distance -= .2; end if distance < mindist mindist = distance; key = string(candidate.keyname, "Major"); index = i; end end for i in 1:12 candidate = KBMode_Minor.keys[i]; distance = keyDistance(piecevector, candidate.dist); if magicnumber == i+12 distance -= .2; end if distance < mindist mindist = distance; key = string(candidate.keyname, "Minor"); index = i+12; end end return [key,index]; end ``` ```julia function analyzeKey(notes::Array{MIDI.Note}) piecevector = makePieceVector(notes); m1vector = makePieceVector(getFirstMeasure(notes)); magicnumber = determineKey(m1vector, 0)[2]; key = determineKey(piecevector, magicnumber)[1]; return key; end ``` ```julia function calculateOffset(keyname::String) letter = ""; offset = 0; letter = keyname[1:2]; if letter == "C=" offset = 0; elseif letter == "C#" offset = -1; elseif letter == "D=" offset = -2; elseif letter == "D#" offset = -3; elseif letter == "E=" offset = -4; elseif letter == "F=" offset = -5; elseif letter == "F#" offset = -6; elseif letter == "G=" offset = 5; elseif letter == "G#" offset = 4; elseif letter == "A=" offset = 3; elseif letter == "A#" offset = 2; elseif letter == "B=" offset = 1; end return offset; end ``` ```julia function transposePiece(notes::Array{MIDI.Note},track1::Array{MIDI.Note},track2::Array{MIDI.Note}) keyname = analyzeKey(notes); offset = calculateOffset(keyname); retnotes = Array{MIDI.Note}(length(notes)); rett1 = Array{MIDI.Note}(length(track1)); rett2 = Array{MIDI.Note}(length(track2)); for i in 1:length(notes) note = notes[i]; newval = note.value + offset; note.value = newval; retnotes[i] = note; end for i in 1:length(track1) note = track1[i]; newval = note.value + offset; note.value = newval; rett1[i] = note; end for i in 1:length(track2) note = track2[i]; newval = note.value + offset; note.value = newval; rett2[i] = note; end return (retnotes,rett1,rett2); end ``` ```julia function processSong(filename::String) MIDIfile = readMIDIfile(filename); MIDIfile.tpq = 96; #print(BPM(MIDIfile)); #print("\n"); #print(ms_per_tick(MIDIfile)); #print("\n"); notes = getnotes(MIDIfile.tracks[2]).notes; if length(MIDIfile.tracks) > 2 for i in 3:length(MIDIfile.tracks) append!(notes, getnotes(MIDIfile.tracks[i]).notes) end end track1 = getnotes(MIDIfile.tracks[2]).notes; track2 = getnotes(MIDIfile.tracks[3]).notes; notes = transposePiece(notes,track1,track2); key = analyzeKey(notes[1]); mode = key[3:7]; print(key); outfile = MIDI.MIDIFile(); track1 = MIDI.MIDITrack(); track2 = MIDI.MIDITrack(); MIDI.addnotes!(track1,notes[2]); MIDI.addnotes!(track2,notes[3]); push!(outfile.tracks, track1); push!(outfile.tracks, track2); outname = mode*"_"*filename[1:length(filename)-4]; MIDI.writeMIDIfile(outname, outfile); end ``` ```julia function processAll(filenames::Array{String}) len = length(filenames); for i in 1:len processSong(filenames[i]); end end ``` ## Chords ```julia function chordDistance(chord1::inputChord, chord2::inputChord) v1 = chord1.vector; v2 = chord2.vector; intermediateSum = 0; for i in 1:12 intermediateSum += (v1[i] - v2[i])^2 end distance = sqrt(intermediateSum) return distance end ``` ```julia function labelChord(vchord::Array) label = ""; mindist = 10000; for i in 1:length(KBLabels) delta = chordDistance(inputChord("",vchord,""), inputChord("",KBLabels[i].rep,"")); if delta < mindist mindist = delta; label = KBLabels[i].rawname; end end return label; end ``` ```julia function calculateLoudness(chord::Array{MIDI.Note}) loudness = zeros(length(chord)); for i in 1:length(chord) n = chord[i]; loudness[i] = rho^(div(n.value,12) + mod(n.value,12)+1); end return loudness; end ``` ```julia function vectorizeChord(chord::Array{MIDI.Note}) returnVector = zeros(12); loudnessVector = calculateLoudness(chord); for i in 1:12 for j in 1:length(chord) n = chord[j]; if mod(n.value,12)+1 == i returnVector[i] += loudnessVector[j]; end end end return returnVector; end ``` ```julia function calculateReductionWeight(chord::Array{MIDI.Note}) weights = zeros(length(chord)); label = labelChord(vectorizeChord(chord)); for i in 1:length(chord) n = chord[i]; dur = div(n.duration,96); weights[i] = (rho^(div(n.value,12) + mod(n.value,12)+1))*dur; end return weights; end ``` ```julia function reduceChord(chord::Array{MIDI.Note}, label::String) labelchord = filter(x -> x.rawname == label, KBLabels)[1]; newchord = filter(x -> labelchord.rep[mod(x.value,12)+1] == 1, chord); returnchord = Array{MIDI.Note}; quit = 0; i = 1; while quit == 0 if length(newchord) >= minchordsize returnchord = newchord; quit = 1; else if labelchord.rep[i] == 1 if !(in(MIDI.Note(60+i-1,96,0,0), newchord)) push!(newchord, MIDI.Note(60+i-1,96,0,0)); end end end i = i+1; if i > 12 i = i-12; end end return returnchord; end ``` ```julia function chordCombine(entry::inputChord, index, chordlist::inputChordList) oldChord = chordlist.chords[index]; oldVector = oldChord.vector; newVector = entry.vector; newChord = inputChord(oldChord.name, zeros(12), oldChord.label) for i in 1:12 newChord.vector[i] = (oldVector[i] + newVector[i])/2 end return newChord; end ``` ```julia function getChordNames(chordlist::inputChordList) namelist = []; for i in 1:length(chordlist.chords) push!(namelist,(chordlist.chords[i]).name); end return namelist; end ``` ```julia function makeChordName(chord::Array{MIDI.Note}) notes = Array{String}(0); name = ""; if length(chord) > 0 for i in 1:length(chord) n = chord[i]; letter = getNoteName(n.value); octave = dec(div(n.value,12)); notename = letter * octave; if length(notes) == 0 push!(notes,notename); elseif !(notename in notes) push!(notes,notename); end end sort!(notes,by=last); for i in 1:length(notes) name = name * notes[i]; end end return name end ``` ```julia function kbChordToInputChord(kbchord::KBChord, octave::Int) vector = kbchord.rep; label = kbchord.rawname; chord = Array{MIDI.Note}(0); for i in 1:12 if vector[i] == 1 value = octave*12 + (i-1); note = MIDI.Note(value, 96, 0, 0); push!(chord,note); end end name = makeChordName(chord); v = vectorizeChord(chord); returnchord = inputChord(name,v,label); return returnchord; end ``` ```julia function readChord(chord::Array{MIDI.Note}, chordlist::inputChordList, progression::inputChordProgression, startTime::Int, endTime::Int) v = vectorizeChord(chord); label = labelChord(v); rchord = reduceChord(chord, label); if length(rchord) >= minchordsize name = makeChordName(rchord); entry = inputChord(name,v,label); else entry = chordlist.chords[length(chordlist.chords)]; name = entry.name; end push!(progression.chords.chords, entry); push!(progression.startTimes, startTime); push!(progression.endTimes, endTime); if length(chordlist.chords) == 0 push!(chordlist.chords,entry) else namelist = getChordNames(chordlist); index = findfirst(namelist.==name); if index == 0 push!(chordlist.chords,entry) else chordlist.chords[index] = chordCombine(entry,index,chordlist); end end end ``` ```julia function findLastPosition(notes::Array) positionList = [] for i in 1:length(notes) push!(positionList,notes[i].position) end return maximum(positionList); end ``` ```julia function addKBChords(chordlist::inputChordList) for i in 1:length(KBLabels) newchord = kbChordToInputChord(KBLabels[i], metaoctave); push!(chordlist.chords, newchord); end return chordlist; end ``` ```julia function processChords(notes::Vector{Note}, division::Int) chordlist = inputChordList([]); progression = inputChordProgression(inputChordList([]), [], []); lastPosition = findLastPosition(notes); i = 0; while i <= lastPosition chord = filter(x->x.position >= i && x.position < i + division, notes); readChord(chord, chordlist, progression, i, i+division); i += division; end chordlist = addKBChords(chordlist); retarray=[chordlist, progression]; return retarray; end ``` ```julia function calculateChordDistances(chordlist::inputChordList) distancelist = chordDistanceList([]); for i in 1:length(chordlist.chords) chord = chordlist.chords[i]; distances = chordDistances(chord,[]); dlist = []; for j in 1:length(chordlist.chords) dist = chordDist(chordlist.chords[j], chordDistance(chord, chordlist.chords[j])); push!(dlist,dist); end distances.distances = dlist; push!(distancelist.list, distances); end return distancelist; end ``` ```julia function calculateTheta(chord1::inputChord, chord2::inputChord) delta = chordDistance(chord1, chord2); theta = exp(-1*lambda*delta); return theta; end ``` ```julia function thetaSum(chord::inputChord, chordlist::inputChordList) sum = 0; for i in 1:length(chordlist.chords) sum += calculateTheta(chord, chordlist.chords[i]); end return sum; end ``` ```julia function probSubstitution(chord1::inputChord, chord2::inputChord, chordlist::inputChordList) delta = chordDistance(chord1, chord2); if delta < threshold theta = calculateTheta(chord1, chord2); sum = thetaSum(chord1, chordlist); prob = theta/sum; return prob; else return -1; end end ``` ```julia function chordSubProbVector(chord::inputChord, chordlist::inputChordList) l = length(chordlist.chords); v = zeros(l); j = 0; for i in 1:l v[i-j] = probSubstitution(chord, chordlist.chords[i], chordlist); if v[i-j] < 0 deleteat!(v,i-j); j = j + 1; end end return v; end ``` ```julia function generateChordSubProbList(chordlist::inputChordList) l = length(chordlist.chords); CSPL = chordSubProbList(Array{chordSubProbs}(l), chordlist); for i in 1:l entry = chordSubProbs(chordlist.chords[i], chordSubProbVector(chordlist.chords[i], chordlist)); CSPL.probabilities[i] = entry; end return CSPL; end ``` ```julia function cpkbFreqToProb(cpkb::Array) length1 = length(cpkb); for i in 1:length1 dist = cpkb[i].dist; length2 = length(dist); freqsum = 0; for j in 1:length2 freqsum += dist[j][2]; end for j in 1:length2 dist[j][2] = (dist[j][2])/freqsum; end end return cpkb end ``` ```julia function arraykbFreqToProb(cpkb::Array) length1 = length(cpkb); for i in 1:length1 dist = cpkb[i][2]; length2 = length(dist); freqsum = 0; for j in 1:length2 freqsum += dist[j][2]; end for j in 1:length2 dist[j][2] = (dist[j][2])/freqsum; end end return cpkb end ``` ```julia function learnCP(cps::Array) cpkb = []; length1 = length(cps); for i in 1:length1 cp = cps[i].chords.chords; length2 = length(cp); for j in 1:length2 thischord = cp[j].name; localmetric = mod(j,4); if localmetric == 0 localmetric = 4; end if (length2-j) < 4 last = 1; else last = 0; end if j == 1 chordprior = ["Start"]; cpstring = chordprior[1]; elseif j == 2 chordprior = [cp[1].name]; cpstring = chordprior[1] else chordprior = String[]; push!(chordprior, cp[j-2].name); push!(chordprior, cp[j-1].name); cpstring = chordprior[1]*"_"*chordprior[2]; end uniqueID = cpstring*"_"*string(localmetric)*"_"*string(last); index = findfirst(x -> x.uniqueID == uniqueID, cpkb); if index == 0 dist = [[thischord, 1]]; entry = cpkbEntry(chordprior, localmetric, last, uniqueID, dist); push!(cpkb,entry); else index2 = findfirst(x -> x[1] == thischord, cpkb[index].dist) if index2 == 0 push!(cpkb[index].dist, [thischord, 1]); else cpkb[index].dist[index2][2] += 1; end end end end cpkb = cpkbFreqToProb(cpkb); return cpkb; end ``` ## Accompaniment Rhythm ```julia function buildRhythmForm(chord::Vector{Note}, metricunit::Int, division::Int) rhythmform = [0,0,0,0,0,0,0,0]; for i in 1:length(chord) note = chord[i]; pos = trunc(Int,(mod(note.position, division))/metricunit + 1); rhythmform[pos] = 1; end indices = []; for i in 1:8 if rhythmform[i] == 1 push!(indices,i); end end push!(indices,9); if length(indices) > 1 for i in 1:(length(indices) - 1) a = indices[i]; diff = indices[i+1] - indices[i]; rhythmform[a] = diff; end end return rhythmform; end ``` ```julia function learnRhythmForms(notes::Vector{Note}, division::Int) rhythmforms = []; metricunit = 48; lastPosition = findLastPosition(notes); i = 0; while i <= lastPosition chord = filter(x->x.position >= i && x.position < i + division, notes); rhythmform = buildRhythmForm(chord,metricunit,division); push!(rhythmforms,rhythmform); i += division; end return rhythmforms; end ``` ```julia function rhythmFormToString(rhythmform::Array) retstring = ""; for i in 1:8 retstring = retstring*string(rhythmform[i]); end return retstring; end ``` ```julia function learnRhythmCP(rhythmform_proglist::Array) rpkb = []; length1 = length(rhythmform_proglist); for i in 1:length1 rp = rhythmform_proglist[i]; length2 = length(rp); for j in 1:length2 rhythmform = rp[j]; localmetric = mod(j,4); if localmetric == 0 localmetric = 4; end if (length2-j) < 4 last = 1; else last = 0; end if j == 1 rhythmform_prior = ["Start"]; rpstring = rhythmform_prior[1]; else rhythmform_prior = rp[j-1]; rpstring = rhythmFormToString(rhythmform_prior); end uniqueID = rpstring*"_"*string(localmetric)*"_"*string(last); index = findfirst(x -> x.uniqueID == uniqueID, rpkb); if index == 0 dist = [[rhythmform, 1]]; entry = cpkbEntry(rhythmform_prior, localmetric, last, uniqueID, dist); push!(rpkb,entry); else index2 = findfirst(x -> x[1] == rhythmform, rpkb[index].dist) if index2 == 0 push!(rpkb[index].dist, [rhythmform, 1]); else rpkb[index].dist[index2][2] += 1; end end end end rpkb = cpkbFreqToProb(rpkb); return rpkb; end ``` ## Learning and Generation Assumption: preprocessing gives us files transposed to C, with a time division of 96 ticks, with all melody notes in track 1 and all accompaniment notes in track 2. ```julia function readAllChords(filenames::Array{String}) l = length(filenames); chordlist = []; proglist_m = []; proglist_M = []; melody_proglist_m = []; melody_proglist_M = []; rhythmform_proglist = []; for i in 1:l filename = filenames[i]; MIDIfile = readMIDIfile(filename); melody = getnotes(MIDIfile.tracks[1]).notes; notes = getnotes(MIDIfile.tracks[1]).notes; append!(notes,getnotes(MIDIfile.tracks[2]).notes); chord_division = 96*4; mode = filename[1:5]; results = processChords(notes, chord_division); chords = results[1].chords; prog = results[2]; plength = max(melody[length(melody)].position + melody[length(melody)].duration, notes[length(notes)].position + notes[length(notes)].duration); plength = trunc(Int, plength); melody_prog = makeMelodyPhraseProgression(melody, plength); rhythmform_prog = learnRhythmForms(notes, chord_division); push!(rhythmform_proglist, rhythmform_prog); for j in 1:length(chords) if chords[j] in chordlist ; else push!(chordlist,chords[j]); end end if mode == "Major" push!(proglist_M, prog); push!(melody_proglist_M, melody_prog); elseif mode == "Minor" push!(proglist_m, prog); push!(melody_proglist_m, melody_prog); else print("YOU HECKED UP, SOME FILE ISN'T PREPROCESSED\n"); end print(i); print("\n"); end cpkb_M = learnCP(proglist_M); cpkb_m = learnCP(proglist_m); substitution_probs = generateChordSubProbList(inputChordList(chordlist)); rhythm_probs = learnRhythmCP(rhythmform_proglist); melody_cpkb_M = learnPhraseCPs(melody_proglist_M, proglist_M); melody_cpkb_m = learnPhraseCPs(melody_proglist_m, proglist_m); retarray = [cpkb_M, cpkb_m, substitution_probs, rhythm_probs, melody_cpkb_M, melody_cpkb_m]; return retarray; end ``` ```julia function markovNextChord(cpkb::Array, prior::Array, localmetric::Int, last::Int) index = findfirst(x -> (x.chordprior == prior && x.localmetric == localmetric && x.last == last), cpkb) if index == 0 index = findfirst(x -> (x.chordprior == prior && x.localmetric == localmetric), cpkb); end if index == 0 index = findfirst(x -> (x.chordprior == prior), cpkb); end if index == 0 index = findfirst(x -> (x.localmetric == localmetric), cpkb); end entry = cpkb[index]; dist = entry.dist; n = rand(); runningsum = 0; next = ""; for i in 1:length(dist) runningsum += dist[i][2]; if n < runningsum next = dist[i][1]; break; end end return(next); end ``` Assumption: plength is greater than or equal to 8 ```julia function markovSelectChords(cpkb::Array, plength::Int) prog = Array{String}(plength); last = 0; prog[1] = markovNextChord(cpkb, ["Start"], 1, last); prog[2] = markovNextChord(cpkb, [prog[1]], 2, last); for i in 3:plength if (plength - i) < 4 last = 1; end localmetric = mod(i,4); if localmetric == 0 localmetric = 4; end priors = [prog[i-2], prog[i-1]]; prog[i] = markovNextChord(cpkb, priors, localmetric, last); end return prog; end ``` ```julia function markovSelectRhythms(rpkb::Array, plength::Int) prog = Array{Array}(plength); last = 0; prog[1] = markovNextChord(rpkb, ["Start"], 1, last); prog[2] = markovNextChord(rpkb, [prog[1]], 2, last); for i in 3:plength if (plength - i) < 4 last = 1; end localmetric = mod(i,4); if localmetric == 0 localmetric = 4; end priors = [prog[i-2], prog[i-1]]; prog[i] = markovNextChord(rpkb, priors, localmetric, last); end return prog; end ``` ```julia function pickSubChord(dist::Array) n = rand(); runningsum = 0; index = 1; for i in 1:length(dist) runningsum += dist[i]; if n < runningsum index = i; break; end end return index; end ``` ```julia function substituteChords(prog::Array, subprobs::chordSubProbList) outprog = Array{String}(length(prog)); probs = subprobs.probabilities; chord = ""; for i in 1:length(prog) name = prog[i]; index = findfirst(x -> x.chord.name == name, probs); if index == 0 index = 1; end entry = probs[index]; dist = entry.probabilities; index = pickSubChord(dist); chord = probs[index].chord.name; outprog[i] = chord; end return outprog; end ``` ```julia function generateProgression(cpkb_M::Array, cpkb_m::Array, substitution_probs::chordSubProbList, mode::String, plength::Int) if mode == "Major" cpkb = cpkb_M; elseif mode == "Minor" cpkb = cpkb_m; else print("Error! Bad mode argument!\n"); end prog = markovSelectChords(cpkb, plength); prog = substituteChords(prog, substitution_probs); return prog; end ``` ```julia function generateRhythmProg(rpkb::Array, plength::Int) rhythm_prog = markovSelectRhythms(rpkb, plength); return rhythm_prog; end ``` ```julia function getNoteValue(name::String) value = 2000; if name == "C=" value = 0; elseif name == "C#" value = 1; elseif name == "D=" value = 2; elseif name == "D#" value = 3; elseif name == "E=" value = 4; elseif name == "F=" value = 5; elseif name == "F#" value = 6; elseif name == "G=" value = 7; elseif name == "G#" value = 8; elseif name == "A=" value = 9; elseif name == "A#" value = 10; elseif name == "B=" value = 11; else print("Error in getNoteValue\n"); end return value; end ``` ```julia function generateWithRhythm(notelist::Array, rhythmform::Array, ordinality::Int) returnnotes = []; for i in 1:8 n = rand(); if rhythmform[i] != 0 dur = (96*rhythmform[i])/2 pos = (ordinality-1)*96*4 + (96*i)/2 if mod(i,8) == 1 push!(returnnotes, MIDI.Note(notelist[1], dur, pos, 0)) if n > 0.5 push!(returnnotes, MIDI.Note(notelist[2], dur, pos, 0)) end elseif mod(i,4) == 1 push!(returnnotes, MIDI.Note(notelist[1], dur, pos, 0)) push!(returnnotes, MIDI.Note(notelist[2], dur, pos, 0)) if (n > 0.5 && length(notelist) > 2) push!(returnnotes, MIDI.Note(notelist[3], dur, pos, 0)) end elseif mod(i,2) == 1 for j in 3:length(notelist) push!(returnnotes, MIDI.Note(notelist[j], dur, pos, 0)) end else if n < 0.33 push!(returnnotes, MIDI.Note(notelist[1], dur, pos, 0)) elseif n < 0.66 push!(returnnotes, MIDI.Note(notelist[2], dur, pos, 0)) else push!(returnnotes, MIDI.Note(notelist[length(notelist)], dur, pos, 0)) end end end end return returnnotes; end ``` ```julia function chordNameToNotes(name::String, rhythmform::Array, offset::Int, ordinality::Int) l = length(name)/3; divlength = 96*2; notelist = []; returnnotes = []; for i in 1:l index = Int(3*(i-1) + 1); notename = name[index:(index+2)]; note = getNoteValue(notename[1:2]); oct = parse(Int, notename[3]); note = (note + oct*12) + offset; push!(notelist,note) end returnnotes = generateWithRhythm(notelist, rhythmform, ordinality); return returnnotes; end ``` ```julia function makeChordLabel(name::String) l = trunc(Int,length(name)/3); notelist = Array{MIDI.Note}(l); for i in 1:l index = Int(3*(i-1) + 1); notename = name[index:(index+2)]; note = getNoteValue(notename[1:2]); oct = parse(Int, notename[3]); note = MIDI.Note((note + oct*12) + offset,96,0,0); notelist[i] = note; end label = labelChord(vectorizeChord(notelist)); return(label); end ``` ```julia function outputProgression(prog::Array, rhythms::Array, melody::Array, offset::Int, outname::String) outfile = MIDI.MIDIFile(); track1 = MIDI.MIDITrack(); notes1 = MIDI.Note[]; notes2 = MIDI.Note[]; l = length(prog); for i in 1:length(melody) note = melody[i]; note.value = note.value + offset; push!(notes1, note); end for i in 1:l chordnotes = chordNameToNotes(prog[i], rhythms[i], offset, i); for j in 1:length(chordnotes) push!(notes2,chordnotes[j]); end end last = notes2[length(notes2)].position + notes2[length(notes2)].duration; buffernote = MIDI.Note(0, 96*2, last, 0, 0); push!(notes2,buffernote); MIDI.addnotes!(track1, notes1); MIDI.addnotes!(track1, notes2); push!(outfile.tracks, track1); MIDI.writeMIDIfile(outname, outfile); print(outname*" written and ready to be listened to!\n"); end ``` ## Melody ```julia function IOI(note1::Note, note2::Note) return (note2.position - note1.position); end ``` ```julia function OOI(note1::Note, note2::Note) return (note2.position - (note1.position + note1.duration)); end ``` ```julia function getmeanIOI(notes::Vector{Note}) IOIs = []; for i in 1:(length(notes)-1) push!(IOIs, IOI(notes[i+1],notes[i])); end meanIOI = mean(IOIs); return trunc(Int,meanIOI); end ``` ```julia function getPSPR1(note1::Note, note2::Note, meanIOI::Int) return w_pspr1*((IOI(note2,note1) + OOI(note2,note1))/meanIOI); end ``` ```julia function getPSPR2(n::Int) return w_pspr2*(-abs(log2(n) - 3)); end ``` ```julia function getPSPR3(note::Note) return w_pspr3*(-log2((mod(note.position,96*4)*16/(96*4))+0.99)); end ``` ```julia function getPSPRscore(phrase1::Vector{Note}, phrase2::Vector{Note}, meanIOI::Int) PSPR1 = getPSPR1(phrase1[length(phrase1)], phrase2[1], meanIOI); PSPR2 = getPSPR2(phrase2); PSPR3 = getPSPR3(phrase2[1]); return PSPR1 + PSPR2 + PSPR3; end ``` ```julia function makePSPR1vector(notes::Vector{Note}) PSPR1vector = []; meanIOI = getmeanIOI(notes); for i in 1:(length(notes)-1) note1 = notes[i]; note2 = notes[i+1]; push!(PSPR1vector, getPSPR1(note1, note2, meanIOI)); end return PSPR1vector; end ``` ```julia function makePSPR3vector(notes::Vector{Note}) PSPR3vector = []; push!(PSPR3vector,0); for i in 1:(length(notes)-1) note = notes[i+1]; push!(PSPR3vector, getPSPR3(note)); end return PSPR3vector; end ``` ```julia function findMPdivisions(scores::Array) MPdivisions = []; remainingScores = scores; quit = 0; index = 0; push!(MPdivisions,0); while quit == 0 iteratedScores = copy(remainingScores); for i in 1:length(remainingScores) iteratedScores[i] = remainingScores[i] * getPSPR2(i); end m = trunc(Int,findmax(iteratedScores)[2]); push!(MPdivisions,index + m); index = index + m; if m == length(remainingScores) quit = 1; else remainingScores = getindex(remainingScores, (m+1):length(remainingScores)); end end return(MPdivisions); end ``` ```julia function addSilentFinalNote(notes::Vector{Note}) lastnote = notes[length(notes)]; newpos = lastnote.position + lastnote.duration; diff = 96*4 - mod(newpos, 96*4); newpos = newpos + diff; newnote = MIDI.Note(0,0,newpos,0,0); push!(notes,newnote); return notes; end ``` ```julia function makeMelodyPhrases(notes::Vector{Note}) melodyPhrases = []; notes = addSilentFinalNote(notes); PSPR1vector = makePSPR1vector(notes); PSPR3vector = makePSPR3vector(notes); PSPR1_3vector = []; for i in 1:length(PSPR1vector) push!(PSPR1_3vector, PSPR1vector[i] + PSPR3vector[i]); end MPdivisions = findMPdivisions(PSPR1_3vector); for i in 1:(length(MPdivisions)-1) index1 = MPdivisions[i] + 1; index2 = MPdivisions[i+1]; push!(melodyPhrases, getindex(notes,index1:index2)); end return melodyPhrases; end ``` ```julia function constructPhraseProgression(phrases::Array, plength::Int) phraseProgression = []; firstOOI = phrases[1][1].position; if firstOOI > 0 restphrase = [MIDI.Note(0,firstOOI,0,0,0)]; push!(phraseProgression, restphrase); end push!(phraseProgression, phrases[1]); for i in 2:length(phrases) note1 = phrases[i-1][length(phrases[i-1])]; note2 = phrases[i][1]; ooi = OOI(note1, note2); if ooi > 0 restphrase = [MIDI.Note(0,ooi,note1.position + note1.duration,0,0)]; push!(phraseProgression, restphrase); end push!(phraseProgression, phrases[i]); end lastnote = phrases[length(phrases)][length(phrases[length(phrases)])]; lastend = lastnote.position + lastnote.duration; diff = plength - lastend; if diff > 0 restphrase = [MIDI.Note(0,diff,lastend,0,0)]; push!(phraseProgression, restphrase); end return phraseProgression; end ``` ```julia function makeMelodyPhraseProgression(notes::Vector{Note}, plength::Int) melodyphrases = makeMelodyPhrases(notes); phraseProgression = constructPhraseProgression(melodyphrases, plength); return phraseProgression; end ``` ```julia function makePhraseRhythmID(phrase::Array) id = ""; firstpos = phrase[1].position; for i in 1:(length(phrase)) note = phrase[i]; dur = string(note.duration); pos = string(note.position - firstpos); id = id*dur*";"*pos; if i < length(phrase) id = id*"|"; end end return(id); end ``` ```julia function learnPhraseRhythmCP(phrase_proglist::Array) prkb = []; length1 = length(phrase_proglist); for i in 1:length1 pr = phrase_proglist[i]; length2 = length(pr); for j in 1:length2 phrase = pr[j]; rest = 0; if phrase[1].velocity == 0 rest = 1; end if rest == 0 label = makePhraseRhythmID(phrase); prhythm = []; firstpos = phrase[1].position; for k in 1:(length(phrase)) note = phrase[k]; prentry = [note.duration, note.position - firstpos]; push!(prhythm, prentry); end else prhythm = ["REST"]; end if j == 1 prior = "Start"; else if pr[j-1][1].velocity == 0 prior = "REST"; else prior = makePhraseRhythmID(pr[j-1]); end end index = findfirst(x -> x[1] == prior, prkb); if index == 0 dist = [[prhythm, 1]]; entry = [prior,dist]; push!(prkb,entry); else index2 = findfirst(x -> x[1] == prhythm, prkb[index][2]) if index2 == 0 push!(prkb[index][2], [prhythm, 1]); else prkb[index][2][index2][2] += 1; end end end end prkb = arraykbFreqToProb(prkb); return prkb; end ``` ```julia function makeValueID(value::Int, chordname::String) id = string(value)*"_"*chordname; return id; end ``` ```julia function learnPhraseValueCP(phrase_proglist::Array, chord_proglist::Array) pvkb = []; length1 = length(phrase_proglist); chordlength = 96*4; for i in 1:length1 pv = phrase_proglist[i]; length2 = length(pv); prog = chord_proglist[i]; timing = 0; curchord = 0; chordname = "NOPE"; for j in 1:length2 phrase = pv[j]; rest = 0; if phrase[1].velocity == 0 rest = 1; timing = timing + phrase[1].duration; end if rest == 0 for k in 1:length(phrase) note = phrase[k]; value = note.value; curchord = min(length(prog.chords.chords), div(timing, chordlength) + 1); timing = timing + note.duration; chordname = prog.chords.chords[curchord].label; if k == 1 prior = "Start_"*chordname; else prior = makeValueID(trunc(Int,phrase[k-1].value), chordname); end index = findfirst(x -> x[1] == prior, pvkb); if index == 0 dist = [[value, 1.0]]; entry = [prior,dist]; push!(pvkb,entry); else index2 = findfirst(x -> x[1] == value, pvkb[index][2]) if index2 == 0 push!(pvkb[index][2], [value, 1.0]); else pvkb[index][2][index2][2] += 1; end end end end end end pvkb = arraykbFreqToProb(pvkb); return pvkb; end ``` ```julia function learnPhraseCPs(phrase_proglist::Array, chord_proglist::Array) phraseCPs = []; push!(phraseCPs, learnPhraseRhythmCP(phrase_proglist)); push!(phraseCPs, learnPhraseValueCP(phrase_proglist, chord_proglist::Array)); return phraseCPs; end ``` ```julia function generateMelody(cpkb_M::Array, cpkb_m::Array, mode::String, prog::Array) if mode == "Major" cpkb = cpkb_M; elseif mode == "Minor" cpkb = cpkb_m; else print("Error! Bad mode argument!\n"); end rhythmCP = cpkb[1]; valueCP = cpkb[2]; melody = markovMakeMelody(rhythmCP, valueCP, prog); return melody; end ``` ```julia function markovMakeMelody(rhythmCP::Array, valueCP::Array, prog::Array) chordlength = 96*4; plength = chordlength*length(prog); dpf_list = markovMakeDPFlist(rhythmCP, plength); values = markovMakeMelValues(valueCP, dpf_list, prog); melody = constructMelody(dpf_list, values); return melody; end ``` ```julia function markovMakeDPFlist(rhythmCP::Array, plength::Int) dpf_list = []; phrase_list = []; quit = 0; current_time = 0; i = 2; push!(phrase_list, markovNextMRPhrase(rhythmCP, ["Start"], current_time, plength)); current_time = current_time + phrase_list[1][2]; if current_time >= plength quit = 1; end while quit == 0 prior = phrase_list[i-1][1]; phrase = markovNextMRPhrase(rhythmCP, [prior], current_time, plength); push!(phrase_list, phrase); current_time = current_time + phrase_list[i][2]; if current_time >= plength quit = 1; end i += 1; end running_position = 0; for j in 1:length(phrase_list) phrase = phrase_list[j][1]; l = phrase_list[j][2]; if (phrase[1] != "REST" && phrase != "REST") for k in 1:length(phrase) d = phrase[k][1]; p = phrase[k][2] + running_position; f = 0; if k == 1 f = 1; end dpf = [d,p,f]; push!(dpf_list, dpf); end end running_position += l; end return dpf_list; end ``` ```julia function markovNextMRPhrase(rhythmCP::Array, prior::Array, current_time::Int, plength::Int) index = findfirst(x -> (x[1] == prior[1]), rhythmCP); if index == 0 index = rand(1:length(rhythmCP)); end entry = rhythmCP[index]; dist = entry[2]; n = rand(); runningsum = 0; next = ""; for i in 1:length(dist) runningsum += dist[i][2]; if n < runningsum next = dist[i][1]; break; end end if (next == "REST" || next[1] == "REST") l = mod(current_time,96*4); else l = trunc(Int,next[length(next)][1] + next[length(next)][2]); if l > (plength - current_time) next = ["REST"]; l = (plength - current_time); end end #DIAGNOSTIC JAZZ #for i in 1:length(next) #if next != ["REST"] #print(trunc(Int,next[i]/96)); #print("_"); #end #end #print("\n"); #print(l/96); #print("\n"); #print("\n"); ### return([next,l]); end ``` ```julia function getPhraseLength(phrase::String) length = 0; if phrase != "REST" phrase_vector = split(phrase,"|"); for i in 1:length(phrase_vector) dp_strings = split(phrase_vector[k],";"); d = parse(Int,dp_strings[1]); length += d; end end return length; end ``` ```julia function markovMakeMelValues(valueCP::Array, dpf_list::Array, prog::Array) values = []; for i in 1:length(dpf_list) dpf = dpf_list[i]; timing = dpf[1] + dpf[2]; chord_n = div(timing, 96*4) + 1; #if chord_n > length(prog) #break; #end chordname = makeChordLabel(prog[chord_n]); if dpf[3] == 1 prior = "Start_"*chordname; else if i == 1 print("ERROR in markovMakeMelValues: when i is 1 dpf[3] != 1\n"); end prior = string(trunc(Int,values[i-1]))*"_"*chordname; end value = markovNextValue(valueCP, prior); push!(values,value); end return(values); end ``` ```julia function markovNextValue(valueCP::Array, prior::String) index = findfirst(x -> (x[1] == prior), valueCP); if index == 0 index = findfirst(x -> (x[1][1:2] == prior[1:2]), valueCP); end entry = valueCP[index]; dist = entry[2]; n = rand(); runningsum = 0; next = 0; for i in 1:length(dist) runningsum += dist[i][2]; if n < runningsum next = dist[i][1]; break; end end return(next); end ``` ```julia function constructMelody(dpf_list::Array, values::Array) melody = []; l = length(dpf_list); for i in 1:l note = MIDI.Note(values[i], dpf_list[i][1], dpf_list[i][2], 0); push!(melody,note); end return(melody); end ``` ## Higher Order Writing/Reading ```julia function writeModel(filename::String,results::Array) cpkb_M = results[1]; cpkb_m = results[2]; sub_probs = results[3]; rpkb = results[4]; mel_M = results[5]; mel_m = results[6]; open(filename,"w") do f write(f, string(cpkb_M)); write(f, "\n"); write(f, string(cpkb_m)); write(f, "\n"); write(f, string(sub_probs)); write(f, "\n"); write(f, string(rpkb)); write(f, "\n"); write(f, string(mel_M)); write(f, "\n"); write(f, string(mel_m)); write(f, "\n"); end end ``` ```julia function readModel(filename::String) results = []; open(filename) do f lines = readlines(f); major = lines[1]; minor = lines[2]; subprobs = lines[3]; rpkb = lines[4]; mel_M = lines[5]; mel_m = lines[6]; push!(results, eval(parse(major))); push!(results, eval(parse(minor))); push!(results, eval(parse(subprobs))); push!(results, eval(parse(rpkb))); push!(results, eval(parse(mel_M))); push!(results, eval(parse(mel_m))); end return results; end ``` ```julia function finalPreProc(filenames::Array{String}) processAll(filenames); print("Preprocessing Complete!\n") end ``` ```julia function finalMakeModel(filenames::Array{String}, modelname::String) model = readAllChords(filenames); writeModel(modelname, model); print("Model Written!\n") end ``` ```julia function finalGenerate(filename::String, mode::String, offset::Int, plength::Int, outname::String) model = readModel(filename); cpkb_M = model[1]; cpkb_m = model[2]; sub_probs = model[3]; rpkb = model[4]; mel_M = model[5]; mel_m = model[6]; prog = generateProgression(cpkb_M, cpkb_m, sub_probs, mode, plength); rhythms = generateRhythmProg(rpkb, plength); melody = generateMelody(mel_M, mel_m, mode, prog); outputProgression(prog, rhythms, melody, offset, outname); end ``` # Execution ## Parameters Populate these parameters to set up final execution functions below ```julia # List of input file names for preprocessing, include ".mid" extension # MIDI files need to be in a particular format, where track2 contains melody notes, track3 contains accompaniment # Also currently only supports pieces in 4/4 time, sorry not sorry filenames_pre = ["Bach_13Am.mid", "Bach_EnglishSuite6InDMinorGavotte.mid", "Bach_LittlePreludeInF.mid", "Bach_Musette.mid", "Beethoven_EcossaiseInG.mid", "Blohm_FantasieAllegro.mid", "Blohm_Pastorella.mid", "Brahms_HungarianDanceNo5.mid", "Clarke_PrinceofDenmarksMarch.mid", "Faure_Pavane.mid", "Fischer_Melpomene.mid", "Mozart_Adagio.mid", "Pachelbel_CanonInD.mid", "Purcell_SuiteInGMajor.mid", "Rameau_LaJoyeuse.mid", "Tchaikovsky_ClassicalSolo.mid", "Telemann_Bourree.mid", "Uematsu_FF4Theme.mid", "Uematsu_ForeverRachel.mid", "Uematsu_TerrasTheme.mid"]; # List of preprocessed input file names -- note that this list will have to be populated after running preprocessing on the input files and observing the filenames of the output filenames = ["Minor_Bach_13Am.mid", "Minor_Bach_EnglishSuite6InDMinorGavotte.mid", "Major_Bach_LittlePreludeInF.mid", "Major_Bach_Musette.mid", "Major_Blohm_FantasieAllegro.mid", "Major_Blohm_Pastorella.mid", "Major_Clarke_PrinceofDenmarksMarch.mid", "Minor_Faure_Pavane.mid", "Minor_Fischer_Melpomene.mid", "Major_Mozart_Adagio.mid", "Major_Pachelbel_CanonInD.mid", "Major_Purcell_SuiteInGMajor.mid", "Minor_Telemann_Bourree.mid", "Minor_Uematsu_ForeverRachel.mid", "Minor_Uematsu_TerrasTheme.mid"]; # Name to give model text file, can be whatever, should have ".txt" extension modelname = "whatevername.txt"; # Mode for output ("Major" or "Minor") mode = "Major"; # Interval by which to transpose output, any reasonable (resulting notes will be in a register humans can hear) negative or nonnegative integer offset = -2; # Number of measures to generate, works best as a multiple of 4 plength = 8; # Name to give output MIDI file, can be whatever, don't include ".mid" extension outname = "anynameyouwant"; ``` ## Preprocessing Execution ```julia finalPreProc(filenames_pre); ``` ## Model Learning Execution ```julia finalMakeModel(filenames, modelname); ``` ## Generation Execution ```julia finalGenerate(modelname, mode, offset, plength, outname); ```