UNPKG

maia-markov

Version:

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

292 lines (268 loc) 9.79 kB
// Imports const mu = require('maia-util') import Analyzer from './Analyzer' // import { state2string, string2state } from './Analyzer' import Generator from './Generator' // import get_points_from_states from './Generator' // Constructor for PatternGenerator object export default function PatternGenerator(_onBgn, _onEnd, _midiBgn, _trans){ // Workaround for JS context peculiarities. // var self = this; this.onBgn = _onBgn // Ontime of first segment of first occurrence. this.onEnd = _onEnd // Ontime of last segment of first occurrence. this.midiBgn = _midiBgn // MIDI note of lowest note in first segment. this.trans = _trans // Translation vectors for all occurrences of the pattern. this.an = new Analyzer this.gn = new Generator // Possible to return something. // return sth; } // Methods for PatternGenerator object PatternGenerator.prototype = { constructor: PatternGenerator, generate_with_shortest_path: function(nCand, param){ let self = this // Shorten a few parameters names. const stateType = param.stateType const g = param.graph const idxOn = param.indices.ontime // Iterate until we have enough candidates. let stateSequences = new Array(nCand) let iCand = 0, nosAttempts = 1 // Set initial and final states. const initialObj = this.get_initial("initial", param) param.randCount = initialObj.randCount const initialScPair = initialObj.stateContextPair const finalObj = this.get_initial("final", param) param.randCount = finalObj.randCount const finalScPair = finalObj.stateContextPair // Does a (shortest) path exist between initial and final? const initialStr = self.an.state2string(initialScPair[stateType]) // console.log("initialStr:", initialStr) const finalStr = self.an.state2string(finalScPair[stateType]) // console.log("finalStr:", finalStr) const shortPath = g.print_shortest_path(initialStr, finalStr) if (shortPath !== undefined){ stateSequences[iCand] = shortPath iCand++ } // To get up to the number of candidates we need, allow freedom in new // "final" state and "initial" state partway through the requested passage. while(iCand < nCand){ // Set the "free" initial and final states. const initialFreeObj = this.get_initial("initial", param) param.randCount = initialFreeObj.randCount const initialFreeScPair = initialFreeObj.stateContextPair const finalFreeObj = this.get_initial("final", param) param.randCount = finalFreeObj.randCount const finalFreeScPair = finalFreeObj.stateContextPair // Do shortest paths exist? const initialFreeStr = self.an.state2string(initialFreeScPair[stateType]) // console.log("initialFreeStr:", initialFreeStr) const finalFreeStr = self.an.state2string(finalFreeScPair[stateType]) // console.log("finalFreeStr:", finalFreeStr) const shortPathA = g.print_shortest_path(initialStr, finalFreeStr) // console.log("shortPathA:", shortPathA) const shortPathB = g.print_shortest_path(initialFreeStr, finalStr) // console.log("shortPathB:", shortPathB) if (shortPathA !== undefined && shortPathB !== undefined){ stateSequences[iCand] = shortPathA.concat(shortPathB) iCand++ } nosAttempts++ } // console.log("stateSequences:", stateSequences) // Convert to state-context pairs. let scPairSequences = stateSequences.map(function(ss){ return self.state_sequence2state_context_pairs(ss, param) }) let pointSets = scPairSequences.map(function(scPairInfo){ return self.gn.get_points_from_states(scPairInfo.stateContextPairs, param) }) // let metrics = self.get_metrics(pointSets) // let estStylisticSuccess = self.estimate_stylistic_success(pointSets, metrics) let psMetrics = pointSets.map(function(ps, idx){ return { "pointSet": ps, "stateCtxPairs": scPairSequences[idx].stateContextPairs, // "metrics": metrics[idx], // "estStylisticSuccess": estStylisticSuccess[idx] } }) // .sort(function(a, b){ // return a.estStylisticSuccess - b.estStylisticSuccess // }) return { "randCount": param.randCount, 'nosAttempts': nosAttempts, "psMetrics": psMetrics } }, exampleDiscPatt: [ { "label": "A", "otherProperties": "here", "translators": [[0, 0], [12, 0], [28, 0], [40, 0]], "occurrences": [ { "label": "A0", "ontimeBgn": 0, "ontimeEnd": 8, "subsetScore": 1 }, { "label": "A1", "ontimeBgn": 12, "ontimeEnd": 20, "subsetScore": 1 }, { "label": "A2", "ontimeBgn": 28, "ontimeEnd": 36, "subsetScore": 1 }, { "label": "A3", "ontimeBgn": 40, "ontimeEnd": 48, "subsetScore": 1 } ] }, { "label": "B", "otherProperties": "here", "translators": [[0, 0], [28, 0]], "occurrences": [ { "label": "B0", "ontimeBgn": 0, "ontimeEnd": 24, "subsetScore": 0 }, { "label": "B1", "ontimeBgn": 28, "ontimeEnd": 52, "subsetScore": 0 } ] } ], generate_with_patterns: function(discoveredPatterns, param){ const self = this const ontimeWindow = [0, 64] let winsAddressed = [] // Calculate max subset scores. discoveredPatterns.forEach(function(dp){ dp.maxArgmaxSubsetScore = mu.max_argmax( dp.occurrences.map(function(o){ return o.subsetScore }) ) }) // Sort by max subset score. discoveredPatterns = discoveredPatterns.sort(function(x, y){ // DOUBLE-CHECK THIS! return x.maxArgmaxSubsetScore[0] - y.maxArgmaxSubsetScore[0] }) // Go through each occurrence of each pattern and see if we can address it. discoveredPatterns.forEach(function(dp){ // Address the occurrence that received the maximum subset score. let occ = dp.occurrences[dp.maxArgmaxSubsetScore[1]] // If we take the example of B0, when we come to it, winsAddressed will // already look like this: // [[0, 8], [12, 20], [28, 36], [40, 48]] // and we'll want the output of generate_time_windows() to be // [[8, 12], [20, 28]], // acknwoledging that these are the time windows belonging to B0 that // still need to be addressed. const winsToAddress = self.generate_time_windows( o.ontimeBgn, o.ontimeEnd, winsAddressed ) // Generate content for these time windows. // Paste new content to time windows corresponding to translations of this // pattern (other occurrences), }) // Generate for time windows that remain unaddressed because they do not // feature in any pattern occurrences. }, get_initial: function(strRequest, aParam){ console.log("strRequest:", strRequest) const stateType = aParam.stateType let randCount = aParam.randCount let stateCtxPair if (aParam[strRequest] !== null){ // It's an initial provided state or an initial distribution. if (aParam[strRequest][stateType] !== undefined){ // It's an initial provided state. stateCtxPair = aParam[strRequest][stateType] } else { // It's an initial distribution. stateCtxPair = mu.choose_one(aParam[strRequest]) randCount++ } } else { // Choose an initial state from beat 1 of the stm. stateCtxPair = mu.choose_one( aParam.stm.filter(function(sc){ return sc[stateType][0] == 1 }) ) randCount++ } return { "randCount": randCount, "stateContextPair": stateCtxPair } }, state_sequence2state_context_pairs: function(stateSeq, aParam){ const self = this const stateType = aParam.stateType let randCount = aParam.randCount let stateCtxPairs = stateSeq.map(function(stateStr, idx){ console.log("idx:", idx) const state = self.an.string2state(stateStr) // Locate the state. if (idx == 0){ // Edge case let relIdx = mu.array_object_index_of_array( aParam.initial, state, stateType ) return aParam.initial[relIdx] } else if (idx == stateSeq.length - 1){ // Edge case let relIdx = mu.array_object_index_of_array( aParam.final, state, stateType ) return aParam.final[relIdx] } else { // Usual case // Locate previous state in stm. Then choose from among potentially many // continuations with the appropriate state. let relIdx = mu.array_object_index_of_array( aParam.stm, self.an.string2state(stateSeq[idx - 1]), stateType ) console.log("relIdx:", relIdx) let candCont = aParam.stm[relIdx].continuations.filter(function(cont){ // console.log("cont[stateType]:", cont[stateType]) return cont[stateType].equals(state) }) randCount++ return mu.choose_one(candCont) } }) // When allowing freedom in concatenating two shortest paths, one ending in // state A and the other beginning in state B, it is unlikely that B is // among the continuations of A, so an undefined value will have crept in, // which is removed here. .filter(function(scp){ return scp !== undefined }) // console.log("stateCtxPairs from state_sequence2state_context_pairs():", stateCtxPairs) return { "randCount": randCount, "stateContextPairs": stateCtxPairs } } }