UNPKG

maia-markov

Version:

Markov analysis and generation functions supporting various applications by Music Artificial Intelligence Algorithms, Inc.

270 lines (249 loc) 8.02 kB
// Imports // import fs const fs = require('fs') // const { Midi } = require('@tonejs/midi') // Constructor for KernImport object export default function KernImport(_fpath){ // Workaround for JS context peculiarities. // var self = this; this.fpath = _fpath this.data = this.get_data() this.lines = this.data.split("\n") // console.log("this.lines.slice(0, 50):", this.lines.slice(0, 50)) // Possible to return something. // return sth; } // Methods for KernImport object KernImport.prototype = { constructor: KernImport, get_anacrusis: function(){ const i = this.get_first_duration_index() const barAndI = this.get_first_numbered_bar_and_index() // if (barAndI[0] == 1 && barAndI[1] < i){ // This kind of situation, where the first bar is labeled and the first // line to contain a duration occurs after the first bar. // =1 =1 =1 =1 // 8GG 8r 8r 8B 8g // Result is no anacrusis. return 0 } else if (barAndI[0] == 1 && barAndI[1] > i){ // First bar is labeled and the first line to contain a duration occurs // before the first bar. // 8GG 8r 8r 8B 8g // =1 =1 =1 =1 // Result is anacrusis that needs counting. Sometimes the anacrusis adds // up to a whole bar's worth of music, in which case null may still be // returned. const dur = this.get_duration_between_lines(i, barAndI[1]) console.log("dur:", dur) const ts = this.get_first_time_signature() const crotchetsPerBar = ts[0]*4/ts[1] if (dur == crotchetsPerBar){ return 0 } else if (dur < crotchetsPerBar){ return dur } else { console.log("Anacrusis appears to be longer than one bar!!") return dur } } else if (barAndI[0] == 2){ // First bar is unlabeled and assumed complete. // 2.r 2.r 2dd 2ff // . . 4cc 8ee- // . . . 16dd // . . . 16ee- // =2 =2 =2 =2 // 2.r 2f 2b- 2dd return 0 } }, get_data: function(){ return fs.readFileSync(this.fpath, "utf8") }, get_duration_between_lines: function(idxBgn = 0, idxEnd = this.lines.length){ let i = idxBgn let dur = 0 while(i < idxEnd){ let line = this.lines[i].split("\t") // We only ever look at the first spine, because there must always be at // least one spine, and they can't swap so it remains legitimate. // Clean token to derive duration. let cleanToken = line[0] .split(" ")[0] // Gets rid of chordal content, which disrupts dot processing. .replace(/\[/g, "").replace(/\(/g, "") // Gets rid of stem and phrase info, which disrupts integer parsing. .replace(/&/g, "") const intgr = parseInt(cleanToken) if (!isNaN(intgr)){ let val = 4/intgr val = 4/intgr let dotVal = val/2 // Handle dots, double dots, etc. while (cleanToken.length > 1 && cleanToken.indexOf(".") >= 0){ val += dotVal dotVal /= 2 cleanToken = cleanToken.replace(".", "") } dur += val } i++ } return dur }, get_first_duration_index: function(){ let i = 0 let line while(i < this.lines.length){ let line = this.lines[i].split("\t") const cleanToken = line[0].replace(/\[/g, "").replace(/\(/g, "") const intgr = parseInt(cleanToken) if (!isNaN(intgr)){ // console.log("line:", line) return i } i++ } }, get_first_numbered_bar_and_index: function(){ let i = 0 let line while(i < this.lines.length){ let line = this.lines[i].split("\t") if (line[0].slice(0, 2) == "=1"){ // console.log("line:", line) return [1, i] } else if (line[0].slice(0, 2) == "=2"){ // console.log("line:", line) return [2, i] } i++ } }, get_first_time_signature: function(){ let i = 0 while(i < this.lines.length){ if (this.lines[i].slice(0, 2) == "*M"){ const justOneTS = this.lines[i].split("\t")[0] return justOneTS.replace("*M", "").split("/") } i++ } }, get_midi_data: function(aPath){ return fs.readFileSync(this.fpath, "utf8") }, get_phrase_boundary_ontimes: function(anacrusis = 0){ let i = 0 let kernIdx let line while (i < this.lines.length && kernIdx == undefined){ if (this.lines[i].indexOf("**kern") == 0){ line = this.lines[i].split("**kern") kernIdx = i i = this.lines.length - 1 } i++ } console.log("line:", line) if (line == undefined){ console.log("COULD NOT FIND START OF KERN SPINES. RETURNING EARLY!") return } // Keep track of incrementing time. const nosStaves = line.length - 1 let timeIncrArr = new Array(nosStaves) let spineIdxArr = new Array(nosStaves) line = this.lines[kernIdx].split("\t") i = 0 for (let k = 0; k < line.length; k++){ if (line[k] == "**kern"){ timeIncrArr[i] = [0] spineIdxArr[i] = k i++ } } // console.log("timeIncrArr:", timeIncrArr) console.log("spineIdxArr:", spineIdxArr) let phraseBgnOntimes = [], phraseEndOntimes = [] i = kernIdx while(i < this.lines.length){ // while(i < 100){ // console.log("i:", i) // if (this.lines[i][0] == "="){ // console.log("timeIncrArr:", timeIncrArr) // console.log("this.lines[i]:", this.lines[i]) // } if ( this.lines[i][0] !== "=" && this.lines[i].indexOf("!!!") == -1 && this.lines[i] !== "" ){ line = this.lines[i].split("\t") // console.log("line:", line) // Check for the presence of a spine-splitting command. let spineSplit = line.indexOf("*^") if (spineSplit >= 0){ console.log("line:", line) console.log("spineSplit:", spineSplit) } timeIncrArr = timeIncrArr.map(function(arr, j){ return arr.map(function(el){ const token = line[spineIdxArr[j]] // console.log("token:", token) // Check for phrase beginning and ending. const elRnd = Math.round(10000*(el - anacrusis))/10000 if ( token.indexOf("(") >= 0 && token.indexOf("q") == -1 && phraseBgnOntimes.indexOf(elRnd) == -1 ){ phraseBgnOntimes.push(elRnd) } if ( token.indexOf(")") >= 0 && token.indexOf("q") == -1 && phraseEndOntimes.indexOf(elRnd) == -1 ){ phraseEndOntimes.push(elRnd) } // Clean token to derive duration. let cleanToken = token .split(" ")[0] // Gets rid of chordal content, which disrupts dot processing. .replace(/\[/g, "").replace(/\(/g, "") // Gets rid of stem and phrase info, which disrupts integer parsing. .replace(/&/g, "") const intgr = parseInt(cleanToken) let val = 0 if (!isNaN(intgr)){ val = 4/intgr let dotVal = val/2 // Handle dots, double dots, etc. while (cleanToken.length > 1 && cleanToken.indexOf(".") >= 0){ val += dotVal // console.log("token:", token) // console.log("val:", val) dotVal /= 2 cleanToken = cleanToken.replace(".", "") } } // console.log("val:", val) return el + val }) }) } i++ } console.log("timeIncrArr:", timeIncrArr) // console.log("phraseBgnOntimes:", phraseBgnOntimes) // console.log("phraseEndOntimes:", phraseEndOntimes) return { "timeIncrArr": timeIncrArr, "phraseBgnOntimes": phraseBgnOntimes, "phraseEndOntimes": phraseEndOntimes } } }