maia-markov
Version:
Markov analysis and generation functions supporting various applications by Music Artificial Intelligence Algorithms, Inc.
161 lines (150 loc) • 5.27 kB
JavaScript
// Copyright Tom Collins, 24.9.2020
// Analysing Composition object files to create state-transition matrix and
// initial distribution for the AI Music Generation Challenge 2020.
// Requires
const path = require("path")
const fs = require("fs")
const uu = require("uuid/v4")
const mu = require("maia-util")
const mm = require("./../dist/index")
// Individual user paths
const mainPaths = {
"tom": {
"inRoot": path.join("/Users", "tomthecollins", "Shizz", "Data", "Music", "1001"),
"inDirs": [ "compObj" ],
"outputDir": path.join(__dirname, "stm"),
"outputFileName": "aimgc2020_6_8"
},
"anotherUser": {
// ...
}
}
// Parameters
const param = {
"stateType": "beat_rel_MNN_state",
// "stateType": "beat_rel_sq_MNN_state",
"onAndOff": false,
// "squashRangeMidi": 12,
"nosConsecutives": 4,
// "phraseBoundaryPropName": "pbo",
"restDurationForPhraseBoundaries": 1,
"timeSignatureAllowed": [6, 8]
}
const fn = [0, 1/6, 1/4, 1/3, 1/2, 2/3, 3/4, 5/6, 1]
// Avoid these ids because they were used to select excerpts of human-composed
// material.
const idAvoid = ["0035"]
// Grab user name from command line to set path to data.
let nextU = false
let mainPath;
process.argv.forEach(function(arg, ind){
if (arg === "-u"){
nextU = true
}
else if (nextU){
mainPath = mainPaths[arg]
nextU = false
}
})
// fs.mkdir(outdir);
// Import and analyse the Composition object files.
let inDirsFilt = fs.readdirSync(mainPath["inRoot"]);
inDirsFilt = inDirsFilt.filter(function(inDir){
return mainPath["inDirs"].indexOf(inDir) >= 0
})
console.log("inDirsFilt:", inDirsFilt)
inDirsFilt.forEach(function(inDir, jDir){
console.log("Working on inDir:", inDir, "jDir:", jDir)
let pComps = [];
let pbos = [];
let pFiles = fs.readdirSync(path.join(mainPath["inRoot"], inDir))
pFiles = pFiles.filter(function(pFile){
return pFile.split(".")[1] == "json" &&
idAvoid.indexOf(pFile.split(".")[0]) == -1
})
console.log("pFiles.length:", pFiles.length);
pFiles.forEach(function(pFile, iFile){
// pFiles.slice(0, 500).forEach(function(pFile, iFile){
console.log("pFile:", pFile)
// if (iFile % 10 == 0){
console.log("!!! PFILE " + (iFile + 1) + " OF " + pFiles.length + " !!!")
// }
try {
const coData = fs.readFileSync(path.join(mainPath["inRoot"], inDir, pFile))
const co = JSON.parse(coData)
co.id = pFile.split(".")[0]
const points = mu.comp_obj2note_point_set(co)
console.log("points.slice(0, 3):", points.slice(0, 3))
const fsm = mu.fifth_steps_mode(points, mu.krumhansl_and_kessler_key_profiles, 1, 3)
console.log("fsm:", fsm)
co.keySignatures = [{
"fifthSteps": fsm[2],
"mode": fsm[3]
}]
// Phrase begin ontimes and phrase end ontimes
let pbo = [], peo = []
const segs = mu.segment(points, true, 0, 3)
segs.forEach(function(seg, idx){
if (seg.points.length == 0 && seg.offtime - seg.ontime >= param.restDurationForPhraseBoundaries){
pbo.push(seg.offtime)
peo.push(seg.ontime)
}
})
// Does this piece have the appropriate time signature?
console.log("co.timeSignatures[0]:", co.timeSignatures[0])
console.log("param.timeSignatureAllowed:", param.timeSignatureAllowed)
if (
co.timeSignatures.length == 1 &&
co.timeSignatures[0].topNo === param.timeSignatureAllowed[0] &&
co.timeSignatures[0].bottomNo === param.timeSignatureAllowed[1]
){
// Yes. Stick the phrase boundary ontimes on to the Composition object
// so they can be used by an.construct_initial below.
co.pbo = pbo
co.peo = peo
pComps.push(co)
}
}
catch (e) {
console.log(e)
}
});
console.log("pComps.length:", pComps.length)
const an = new mm.Analyzer()
// Construct STM.
let pStm = an.construct_stm(pComps, param)
console.log("pStm.length:", pStm.length)
// for (let k = 0; k < 10; k++){
// console.log("pStm[k]:", pStm[k])
// console.log("pStm[k].continuations:", pStm[k].continuations)
// }
pStm = an.prune_stm(pStm, param)
console.log("pStm.length after pruning:", pStm.length)
// console.log("pStm[0].beat_mnn_state:", pStm[0].beat_mnn_state);
// console.log("pStm.slice(0, 1):", pStm.slice(0, 1));
fs.writeFileSync(
path.join(mainPath["outputDir"], mainPath["outputFileName"] + ".js"),
// "var perc_" + kernDir + " = " +
JSON.stringify(pStm)//, null, 2)
// + ";"
)
// Construct inner and final distributions.
let pInitialDistbn = [], pFinalDistbn = []
pComps.forEach(function(co){
let scp = an.comp_obj2beat_rel_mnn_states(co, param.onAndOff)
if (scp.length > 0){
pInitialDistbn.push(scp[0])
pFinalDistbn.push(scp[scp.length - 1])
}
})
pInitialDistbn = an.prune_initial(pInitialDistbn, pStm, param)
fs.writeFileSync(
path.join(mainPath["outputDir"], mainPath["outputFileName"] + "_initial.js"),
JSON.stringify(pInitialDistbn)//, null, 2
)
pFinalDistbn = an.prune_initial(pFinalDistbn, pStm, param)
fs.writeFileSync(
path.join(mainPath["outputDir"], mainPath["outputFileName"] + "_final.js"),
JSON.stringify(pFinalDistbn)//, null, 2)
)
})