mml-optimizer
Advanced tools
Comparing version 0.0.5 to 0.0.6
399
index.js
@@ -1,2 +0,2 @@ | ||
var aStar = require('a-star'); | ||
var core = require('./optimizer-core'); | ||
var extend = require('extend'); | ||
@@ -36,366 +36,2 @@ | ||
function noteDurationToTicks(duration, options) { | ||
return noteDurationAndDotsToTicks( | ||
parseInt(duration, 10), | ||
/[^.]*([.]*)/.exec(duration)[1].length, | ||
options); | ||
} | ||
function noteDurationAndDotsToTicks(duration, dots, options) { | ||
var ticks = Math.floor(options.tpqn * 4 / duration); | ||
for (var i = 0; i < dots; ++i) | ||
ticks = Math.floor(ticks * 1.5); | ||
return ticks; | ||
} | ||
function ticksToAllNoteDurations(ticks, options) { | ||
var dots = ''; | ||
var upperBoundIncl; | ||
var lowerBoundExcl; | ||
var result = []; | ||
while (true) { | ||
// Bounds for the preimage of floor(1.5*floor(1.5*...floor(4*tpqn/ticks))) | ||
upperBoundIncl = Math.pow(1.5,dots.length)*4*options.tpqn / ticks; | ||
lowerBoundExcl = Math.pow(1.5,dots.length)*4*options.tpqn / (ticks + 3*Math.pow(1.5,dots.length) - 2); | ||
if (lowerBoundExcl >= options.minimumNoteDuration) | ||
break; | ||
for (var n = Math.floor(upperBoundIncl); n > lowerBoundExcl; --n) { | ||
if (noteDurationAndDotsToTicks(n, dots.length, options) === ticks) { | ||
result.push(n + dots); | ||
break; | ||
} | ||
} | ||
dots += '.'; | ||
} | ||
return result; | ||
} | ||
function relativeDuration(ticks, durationRelativeTo, options) { | ||
var candidates = []; | ||
return ticksToAllNoteDurations(ticks, options) | ||
.map(function (duration) { | ||
if (duration === durationRelativeTo) | ||
return ''; | ||
else if (parseInt(duration, 10) === parseInt(durationRelativeTo, 10) && | ||
duration.length > durationRelativeTo.length) | ||
return duration.slice(durationRelativeTo.length); | ||
return duration; | ||
}) | ||
.reduce(function (shortestDuration, duration) { | ||
if (shortestDuration.length > duration.length) | ||
return duration; | ||
return shortestDuration; | ||
}); | ||
} | ||
function noteNameToMidiPitch(note, octave) { | ||
var midiMap = { | ||
'c': 0, | ||
'd': 2, | ||
'e': 4, | ||
'f': 5, | ||
'g': 7, | ||
'a': 9, | ||
'b': 11 | ||
}; | ||
var accidentalMap = { | ||
'+': 1, | ||
'-': -1, | ||
'': 0 | ||
}; | ||
return 12*octave + midiMap[note[0]] + accidentalMap[note[1] || '']; | ||
} | ||
function validOctaves(pitch) { | ||
var octave = Math.floor(pitch / 12); | ||
if (pitch % 12 === 0 && octave > 0) | ||
return [octave - 1, octave]; | ||
if (pitch % 12 === 11) | ||
return [octave, octave + 1]; | ||
return [octave]; | ||
} | ||
function midiPitchToNoteName(pitch, octave) { | ||
var noteNameMap = { | ||
'-1': 'c-', | ||
'0': 'c', | ||
'1': 'c+', | ||
'2': 'd', | ||
'3': 'd+', | ||
'4': 'e', | ||
'5': 'f', | ||
'6': 'f+', | ||
'7': 'g', | ||
'8': 'g+', | ||
'9': 'a', | ||
'10': 'a+', | ||
'11': 'b', | ||
'12': 'b+' | ||
}; | ||
return noteNameMap[pitch - (octave * 12)]; | ||
} | ||
function parseMml(mmlString, options) { | ||
var state = extend({ time: 0 }, options.defaultState); | ||
var tokens = []; | ||
while (mmlString.length) { | ||
var result; | ||
var tokenLength; | ||
if (result = /^\/\*[^]*?\*\//.exec(mmlString)) { | ||
tokenLength = result[0].length; | ||
} else if (result = /^([A-Ga-g][+#-]?)([0-9]*[.]*)/.exec(mmlString)) { | ||
var noteName = result[1].toLowerCase().replace(/#/g,'+'); | ||
var duration = result[2]; | ||
if (!duration) | ||
duration = state.duration; | ||
else if (/^[.]+$/.test(duration)) { | ||
duration = state.duration + duration; | ||
} | ||
var pitch = noteNameToMidiPitch(noteName, state.octave) + options.transpose; | ||
var ticks = noteDurationToTicks(duration, options); | ||
tokens.push({ | ||
type: 'note', | ||
pitch: pitch, | ||
ticks: ticks, | ||
volume: state.volume, | ||
time: state.time | ||
}); | ||
state.time += ticks; | ||
tokenLength = result[0].length; | ||
} else if (result = /^[Rr]([0-9]*[.]*)/.exec(mmlString)) { | ||
var duration = result[1]; | ||
if (!duration) | ||
duration = state.duration; | ||
else if (/^[.]+$/.test(duration)) { | ||
duration = state.duration + duration; | ||
} | ||
var ticks = noteDurationToTicks(duration, options); | ||
tokens.push({ | ||
type: 'rest', | ||
ticks: ticks, | ||
time: state.time | ||
}); | ||
state.time += ticks; | ||
tokenLength = result[0].length; | ||
} else if (result = /^[Nn]([0-9]+)/.exec(mmlString)) { | ||
var pitch = parseInt(result[1], 10) + options.transpose; | ||
var ticks = noteDurationToTicks(state.duration, options); | ||
tokens.push({ | ||
type: 'note', | ||
pitch: pitch, | ||
ticks: ticks, | ||
volume: state.volume, | ||
time: state.time | ||
}); | ||
state.time += ticks; | ||
tokenLength = result[0].length; | ||
} else if (result = /^[Ll]([0-9]+[.]*)/.exec(mmlString)) { | ||
state.duration = result[1]; | ||
tokenLength = result[0].length; | ||
} else if (result = /^[Oo]([0-9]+)/.exec(mmlString)) { | ||
var octave = parseInt(result[1], 10) + options.octaveOffset; | ||
state.octave = octave; | ||
tokenLength = result[0].length; | ||
} else if (result = /^[Vv]([0-9]+)/.exec(mmlString)) { | ||
var volume = parseInt(result[1], 10) / options.maxVolume; | ||
state.volume = volume; | ||
tokens.push({ | ||
type: 'volume', | ||
volume: volume, | ||
time: state.time | ||
}); | ||
tokenLength = result[0].length; | ||
} else if (result = /^[Tt]([0-9]+)/.exec(mmlString)) { | ||
var tempo = parseInt(result[1], 10); | ||
state.tempo = tempo; | ||
tokens.push({ | ||
type: 'tempo', | ||
tempo: tempo, | ||
time: state.time | ||
}); | ||
tokenLength = result[0].length; | ||
} else if (mmlString[0] === '&') { | ||
tokens.push({ type: 'tie' }); | ||
tokenLength = 1; | ||
} else if (mmlString[0] === '<') { | ||
--state.octave; | ||
tokenLength = 1; | ||
} else if (mmlString[0] === '>') { | ||
++state.octave; | ||
tokenLength = 1; | ||
} else if (mmlString[0] === ',') { | ||
tokens.push({ type: 'nextVoice' }); | ||
if (!options.tracksShareState) | ||
state = extend({}, options.defaultState); | ||
state.time = 0; | ||
tokenLength = 1; | ||
} else { | ||
tokenLength = 1; | ||
} | ||
mmlString = mmlString.slice(tokenLength); | ||
} | ||
return tokens; | ||
} | ||
function tokenText(token, state, options) { | ||
switch (token.type) { | ||
case 'note': return noteText(token.pitch, token.ticks, state.octave, state.duration, options); | ||
case 'rest': return restText(token.ticks, state.duration, options); | ||
case 'duration': return durationText(token.duration); | ||
case 'octave': return octaveText(token.octave, state.octave, options); | ||
case 'volume': return volumeText(token.volume, options); | ||
case 'tempo': return tempoText(token.tempo); | ||
case 'tie': return '&'; | ||
case 'nextVoice': return ','; | ||
} | ||
throw new Error('Unexpected token type.'); | ||
} | ||
function noteText(pitch, ticks, currentOctave, currentDuration, options) { | ||
return midiPitchToNoteName(pitch, currentOctave) + relativeDuration(ticks, currentDuration, options); | ||
} | ||
function restText(ticks, currentDuration, options) { | ||
return 'r' + relativeDuration(ticks, currentDuration, options); | ||
} | ||
function durationText(duration) { | ||
return 'L' + duration; | ||
} | ||
function octaveText(octave, currentOctave, options) { | ||
if (currentOctave - octave === 1) | ||
return '<'; | ||
if (currentOctave - octave === -1) | ||
return '>'; | ||
return 'O' + (octave - options.octaveOffset); | ||
} | ||
function volumeText(volume, options) { | ||
return 'V' + Math.round(volume * options.maxVolume); | ||
} | ||
function tempoText(tempo) { | ||
return 'T' + tempo; | ||
} | ||
function tokenNeighbors(token, state, options) { | ||
var neighbors = []; | ||
switch (token.type) { | ||
case 'note': | ||
validOctaves(token.pitch).forEach(function (octave) { | ||
if (octave === state.octave) { | ||
neighbors.push(extend({}, state, { cursor: state.cursor + 1 })); | ||
if (token.ticks !== noteDurationToTicks(state.duration, options)) { | ||
ticksToAllNoteDurations(token.ticks, options) | ||
.forEach(function (duration) { | ||
neighbors.push(extend({}, state, { duration: duration })); | ||
while (duration[duration.length-1] === '.') { | ||
duration = duration.slice(0,-1); | ||
neighbors.push(extend({}, state, { duration: duration })); | ||
} | ||
}); | ||
} | ||
} else { | ||
neighbors.push(extend({}, state, { octave: octave })); | ||
} | ||
}); | ||
break; | ||
case 'rest': | ||
neighbors.push(extend({}, state, { cursor: state.cursor + 1 })); | ||
if (token.ticks !== noteDurationToTicks(state.duration, options)) { | ||
ticksToAllNoteDurations(token.ticks, options) | ||
.forEach(function (duration) { | ||
neighbors.push(extend({}, state, { duration: duration })); | ||
while (duration[duration.length-1] === '.') { | ||
duration = duration.slice(0,-1); | ||
neighbors.push(extend({}, state, { duration: duration })); | ||
} | ||
}); | ||
} | ||
case 'volume': | ||
case 'tempo': | ||
case 'tie': | ||
neighbors.push(extend({}, state, { cursor: state.cursor + 1 })); | ||
case 'nextVoice': | ||
if (options.tracksShareState) | ||
neighbors.push(extend({}, state, { cursor: state.cursor + 1 })); | ||
else | ||
neighbors.push(extend({}, options.defaultState, { cursor: state.cursor + 1 })); | ||
break; | ||
} | ||
return neighbors; | ||
} | ||
function findPath(mmlTokens, options) { | ||
var result = aStar({ | ||
start: extend({}, options.defaultState, { cursor: 0 }), | ||
isEnd: function (node) { | ||
return node.cursor === mmlTokens.length; | ||
}, | ||
neighbor: function (node) { | ||
return tokenNeighbors(mmlTokens[node.cursor], node, options); | ||
}, | ||
distance: function (nodeA, nodeB) { | ||
if (nodeA.cursor !== nodeB.cursor) | ||
return tokenText(mmlTokens[nodeA.cursor], nodeA, options).length; | ||
if (nodeA.duration !== nodeB.duration) | ||
return durationText(nodeB.duration).length; | ||
if (nodeA.octave !== nodeB.octave) | ||
return octaveText(nodeB.octave, nodeA.octave, options).length; | ||
throw new Error('Unexpected node transition.'); | ||
}, | ||
heuristic: function (node) { | ||
return mmlTokens.length - node.cursor; | ||
}, | ||
hash: function (node) { | ||
return JSON.stringify(node); | ||
} | ||
}); | ||
if (result.status === 'noPath') | ||
throw new Error('No optimized path found.'); | ||
return result.path; | ||
} | ||
function optimizeTokens(mmlTokens, options) { | ||
var path = findPath(mmlTokens, options); | ||
var optimizedTokens = []; | ||
for (var i = 1; i < path.length; ++i) { | ||
if (path[i].cursor !== path[i-1].cursor) | ||
optimizedTokens.push(mmlTokens[path[i-1].cursor]); | ||
else if (path[i].duration !== path[i-1].duration) | ||
optimizedTokens.push({ | ||
type: 'duration', | ||
duration: path[i].duration, | ||
time: mmlTokens[path[i].cursor].time | ||
}); | ||
else if (path[i].octave !== path[i-1].octave) | ||
optimizedTokens.push({ | ||
type: 'octave', | ||
octave: path[i].octave, | ||
time: mmlTokens[path[i].cursor].time | ||
}); | ||
else | ||
throw new Error('Unexpected node transition.'); | ||
} | ||
return optimizedTokens; | ||
} | ||
function generateMml(tokens, options) { | ||
return tokens.reduce(function (acc, token) { | ||
return extend(acc, { | ||
text: acc.text + tokenText(token, acc, options), | ||
octave: token.type === 'octave' ? token.octave : acc.octave, | ||
tempo: token.type === 'tempo' ? token.tempo : acc.tempo, | ||
volume: token.type === 'volume' ? token.volume : acc.volume, | ||
duration: token.type === 'duration' ? token.duration : acc.duration | ||
}); | ||
}, extend({}, options.defaultState, { text: '' })).text; | ||
} | ||
function getOptions(alias) { | ||
@@ -407,5 +43,4 @@ if (!alias) | ||
module.exports = function opt(input, options) { | ||
function parse(mml, options) { | ||
options = options || {}; | ||
var outputOptions = getOptions(options.output); | ||
var inputOptions = extend({}, getOptions(options.input), { | ||
@@ -416,18 +51,20 @@ tpqn: outputOptions.tpqn, | ||
}); | ||
var parsed = parseMml(input, inputOptions); | ||
var optimized = optimizeTokens(parsed, outputOptions); | ||
var generated = generateMml(optimized, outputOptions); | ||
var parsed = core.parseMml(mml, inputOptions); | ||
return parsed; | ||
} | ||
function generate(tokens, options) { | ||
options = options || {}; | ||
var outputOptions = getOptions(options.output); | ||
var optimized = core.optimizeTokens(tokens, outputOptions); | ||
var generated = core.generateMml(optimized, outputOptions); | ||
return generated; | ||
} | ||
module.exports = function opt(mml, options) { | ||
var parsed = parse(mml, options); | ||
var generated = generate(parsed, options); | ||
return generated; | ||
}; | ||
module.exports.noteDurationToTicks = noteDurationToTicks; | ||
module.exports.ticksToAllNoteDurations = ticksToAllNoteDurations; | ||
module.exports.relativeDuration = relativeDuration; | ||
module.exports.noteNameToMidiPitch = noteNameToMidiPitch; | ||
module.exports.validOctaves = validOctaves; | ||
module.exports.midiPitchToNoteName = midiPitchToNoteName; | ||
module.exports.parseMml = parseMml; | ||
module.exports.findPath = findPath; | ||
module.exports.optimizeTokens = optimizeTokens; | ||
module.exports.generateMml = generateMml; | ||
module.exports.parse = parse; | ||
module.exports.generate = generate; |
{ | ||
"name": "mml-optimizer", | ||
"version": "0.0.5", | ||
"version": "0.0.6", | ||
"description": "Optimizer for Music Macro Language (MML)", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
694
test.js
@@ -1,2 +0,4 @@ | ||
var opt = require('./index.js'); | ||
var opt = require('./index'); | ||
var core = require('./optimizer-core'); | ||
var assert = require('chai').assert; | ||
@@ -17,357 +19,359 @@ var extend = require('extend'); | ||
describe('noteDurationToTicks', function () { | ||
var options = { tpqn: 500 }; | ||
runCases('should convert {0} to {1}', opt.noteDurationToTicks, [ | ||
[['1', options], 2000], | ||
[['1.', options], 3000], | ||
[['1..', options], 4500], | ||
[['1...', options], 6750], | ||
[['3', options], 666], | ||
[['3.', options], 999], | ||
[['4', options], 500], | ||
[['4.', options], 750], | ||
[['5', options], 400], | ||
[['5.', options], 600], | ||
[['7', options], 285], | ||
[['7.', options], 427], | ||
[['64', options], 31], | ||
[['64.', options], 46] | ||
]); | ||
}); | ||
describe('optimizer-core', function () { | ||
describe('noteDurationToTicks', function () { | ||
var options = { tpqn: 500 }; | ||
runCases('should convert {0} to {1}', core.noteDurationToTicks, [ | ||
[['1', options], 2000], | ||
[['1.', options], 3000], | ||
[['1..', options], 4500], | ||
[['1...', options], 6750], | ||
[['3', options], 666], | ||
[['3.', options], 999], | ||
[['4', options], 500], | ||
[['4.', options], 750], | ||
[['5', options], 400], | ||
[['5.', options], 600], | ||
[['7', options], 285], | ||
[['7.', options], 427], | ||
[['64', options], 31], | ||
[['64.', options], 46] | ||
]); | ||
}); | ||
describe('ticksToAllNoteDurations', function () { | ||
var options = { tpqn: 500, minimumNoteDuration: 64 }; | ||
runCases('should convert {0} to {1}', opt.ticksToAllNoteDurations, [ | ||
[[31, options], ['64']], | ||
[[46, options], ['43', '64.']], | ||
[[61, options], ['48.']], | ||
[[111, options], ['18', '27.']], | ||
[[166, options], ['12', '18.', '27..']], | ||
[[285, options], ['7', '35....', '52.....']], | ||
[[333, options], ['6', '9.', '30....', '45.....']], | ||
[[400, options], ['5']], | ||
[[427, options], ['7.', '35.....', '52......']], | ||
[[500, options], ['4']], | ||
[[600, options], ['5.']], | ||
[[666, options], ['3']], | ||
[[750, options], ['4.']], | ||
[[999, options], ['3.']], | ||
[[2000, options], ['1']], | ||
[[3000, options], ['1.']], | ||
[[4500, options], ['1..']], | ||
[[6750, options], ['1...']] | ||
]); | ||
describe('ticksToAllNoteDurations', function () { | ||
var options = { tpqn: 500, minimumNoteDuration: 64 }; | ||
runCases('should convert {0} to {1}', core.ticksToAllNoteDurations, [ | ||
[[31, options], ['64']], | ||
[[46, options], ['43', '64.']], | ||
[[61, options], ['48.']], | ||
[[111, options], ['18', '27.']], | ||
[[166, options], ['12', '18.', '27..']], | ||
[[285, options], ['7', '35....', '52.....']], | ||
[[333, options], ['6', '9.', '30....', '45.....']], | ||
[[400, options], ['5']], | ||
[[427, options], ['7.', '35.....', '52......']], | ||
[[500, options], ['4']], | ||
[[600, options], ['5.']], | ||
[[666, options], ['3']], | ||
[[750, options], ['4.']], | ||
[[999, options], ['3.']], | ||
[[2000, options], ['1']], | ||
[[3000, options], ['1.']], | ||
[[4500, options], ['1..']], | ||
[[6750, options], ['1...']] | ||
]); | ||
it('should convert ticks to duration and back', function () { | ||
for (var i = 1; i <= 64; ++i) { | ||
for (var dots = ''; dots.length < 3; dots += '.') { | ||
var ticks = opt.noteDurationToTicks(i + dots, options); | ||
var durations = opt.ticksToAllNoteDurations(ticks, options); | ||
durations.forEach(function (duration) { | ||
assert.equal(opt.noteDurationToTicks(duration, options), | ||
ticks, | ||
'Testing ' + i + dots); | ||
}); | ||
it('should convert ticks to duration and back', function () { | ||
for (var i = 1; i <= 64; ++i) { | ||
for (var dots = ''; dots.length < 3; dots += '.') { | ||
var ticks = core.noteDurationToTicks(i + dots, options); | ||
var durations = core.ticksToAllNoteDurations(ticks, options); | ||
durations.forEach(function (duration) { | ||
assert.equal(core.noteDurationToTicks(duration, options), | ||
ticks, | ||
'Testing ' + i + dots); | ||
}); | ||
} | ||
} | ||
} | ||
}); | ||
}); | ||
}); | ||
describe('relativeDuration', function () { | ||
var options = { tpqn: 500, minimumNoteDuration: 64 }; | ||
runCases('should convert {0} to {1}', opt.relativeDuration, [ | ||
[[opt.noteDurationToTicks('4', options), '1', options], '4'], | ||
[[opt.noteDurationToTicks('4', options), '4', options], ''], | ||
[[opt.noteDurationToTicks('4', options), '4.', options], '4'], | ||
[[opt.noteDurationToTicks('4.', options), '4', options], '.'], | ||
[[opt.noteDurationToTicks('4..', options), '4.', options], '.'], | ||
[[opt.noteDurationToTicks('4...', options), '4.', options], '..'], | ||
[[opt.noteDurationToTicks('4....', options), '4.', options], '...'], | ||
[[opt.noteDurationToTicks('12', options), '12', options], ''], | ||
[[opt.noteDurationToTicks('12', options), '18', options], '.'], | ||
[[opt.noteDurationToTicks('12', options), '27', options], '12'] | ||
]); | ||
}); | ||
describe('relativeDuration', function () { | ||
var options = { tpqn: 500, minimumNoteDuration: 64 }; | ||
runCases('should convert {0} to {1}', core.relativeDuration, [ | ||
[[core.noteDurationToTicks('4', options), '1', options], '4'], | ||
[[core.noteDurationToTicks('4', options), '4', options], ''], | ||
[[core.noteDurationToTicks('4', options), '4.', options], '4'], | ||
[[core.noteDurationToTicks('4.', options), '4', options], '.'], | ||
[[core.noteDurationToTicks('4..', options), '4.', options], '.'], | ||
[[core.noteDurationToTicks('4...', options), '4.', options], '..'], | ||
[[core.noteDurationToTicks('4....', options), '4.', options], '...'], | ||
[[core.noteDurationToTicks('12', options), '12', options], ''], | ||
[[core.noteDurationToTicks('12', options), '18', options], '.'], | ||
[[core.noteDurationToTicks('12', options), '27', options], '12'] | ||
]); | ||
}); | ||
describe('noteNameToMidiPitch', function () { | ||
runCases('should convert {0} to {1}', opt.noteNameToMidiPitch, [ | ||
[['c', 5], 60], | ||
[['c+', 5], 61], | ||
[['c-', 5], 59], | ||
[['c', 4], 48], | ||
[['a', 5], 69] | ||
]); | ||
}); | ||
describe('noteNameToMidiPitch', function () { | ||
runCases('should convert {0} to {1}', core.noteNameToMidiPitch, [ | ||
[['c', 5], 60], | ||
[['c+', 5], 61], | ||
[['c-', 5], 59], | ||
[['c', 4], 48], | ||
[['a', 5], 69] | ||
]); | ||
}); | ||
describe('validOctaves', function () { | ||
runCases('should convert {0} to {1}', opt.validOctaves, [ | ||
[opt.noteNameToMidiPitch('g', 4), [4]], | ||
[opt.noteNameToMidiPitch('c', 4), [3, 4]], | ||
[opt.noteNameToMidiPitch('b', 4), [4, 5]], | ||
[opt.noteNameToMidiPitch('c', 0), [0]] | ||
]); | ||
}); | ||
describe('validOctaves', function () { | ||
runCases('should convert {0} to {1}', core.validOctaves, [ | ||
[core.noteNameToMidiPitch('g', 4), [4]], | ||
[core.noteNameToMidiPitch('c', 4), [3, 4]], | ||
[core.noteNameToMidiPitch('b', 4), [4, 5]], | ||
[core.noteNameToMidiPitch('c', 0), [0]] | ||
]); | ||
}); | ||
describe('midiPitchToNoteName', function () { | ||
runCases('should convert {0} to {1}', opt.midiPitchToNoteName, [ | ||
[[67, 5], 'g'], | ||
[[68, 5], 'g+'], | ||
[[60, 5], 'c'], | ||
[[59, 5], 'c-'], | ||
[[59, 4], 'b'], | ||
[[60, 4], 'b+'] | ||
]); | ||
}); | ||
describe('midiPitchToNoteName', function () { | ||
runCases('should convert {0} to {1}', core.midiPitchToNoteName, [ | ||
[[67, 5], 'g'], | ||
[[68, 5], 'g+'], | ||
[[60, 5], 'c'], | ||
[[59, 5], 'c-'], | ||
[[59, 4], 'b'], | ||
[[60, 4], 'b+'] | ||
]); | ||
}); | ||
describe('parseMml', function () { | ||
var options = { | ||
tpqn: 500, | ||
minimumNoteDuration: 64, | ||
defaultState: { | ||
octave: 4, | ||
tempo: 100, | ||
volume: 100/127, | ||
duration: '4' | ||
}, | ||
maxVolume: 127, | ||
tracksShareState: true, | ||
octaveOffset: -1, | ||
transpose: 1 | ||
}; | ||
runCases('should parse {0}', opt.parseMml, [ | ||
[['ccc', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 } | ||
]], | ||
[['cdefgabCDEFGAB', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 51, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 53, ticks: 500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 54, ticks: 500, volume: 100/127, time: 1500 }, | ||
{ type: 'note', pitch: 56, ticks: 500, volume: 100/127, time: 2000 }, | ||
{ type: 'note', pitch: 58, ticks: 500, volume: 100/127, time: 2500 }, | ||
{ type: 'note', pitch: 60, ticks: 500, volume: 100/127, time: 3000 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 3500 }, | ||
{ type: 'note', pitch: 51, ticks: 500, volume: 100/127, time: 4000 }, | ||
{ type: 'note', pitch: 53, ticks: 500, volume: 100/127, time: 4500 }, | ||
{ type: 'note', pitch: 54, ticks: 500, volume: 100/127, time: 5000 }, | ||
{ type: 'note', pitch: 56, ticks: 500, volume: 100/127, time: 5500 }, | ||
{ type: 'note', pitch: 58, ticks: 500, volume: 100/127, time: 6000 }, | ||
{ type: 'note', pitch: 60, ticks: 500, volume: 100/127, time: 6500 }, | ||
]], | ||
[['c#d#e#f#g#a#b#c+d+e+f+g+a+b+c-d-e-f-g-a-b-', options], [ | ||
{ type: 'note', pitch: 50, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 52, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 54, ticks: 500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 55, ticks: 500, volume: 100/127, time: 1500 }, | ||
{ type: 'note', pitch: 57, ticks: 500, volume: 100/127, time: 2000 }, | ||
{ type: 'note', pitch: 59, ticks: 500, volume: 100/127, time: 2500 }, | ||
{ type: 'note', pitch: 61, ticks: 500, volume: 100/127, time: 3000 }, | ||
{ type: 'note', pitch: 50, ticks: 500, volume: 100/127, time: 3500 }, | ||
{ type: 'note', pitch: 52, ticks: 500, volume: 100/127, time: 4000 }, | ||
{ type: 'note', pitch: 54, ticks: 500, volume: 100/127, time: 4500 }, | ||
{ type: 'note', pitch: 55, ticks: 500, volume: 100/127, time: 5000 }, | ||
{ type: 'note', pitch: 57, ticks: 500, volume: 100/127, time: 5500 }, | ||
{ type: 'note', pitch: 59, ticks: 500, volume: 100/127, time: 6000 }, | ||
{ type: 'note', pitch: 61, ticks: 500, volume: 100/127, time: 6500 }, | ||
{ type: 'note', pitch: 48, ticks: 500, volume: 100/127, time: 7000 }, | ||
{ type: 'note', pitch: 50, ticks: 500, volume: 100/127, time: 7500 }, | ||
{ type: 'note', pitch: 52, ticks: 500, volume: 100/127, time: 8000 }, | ||
{ type: 'note', pitch: 53, ticks: 500, volume: 100/127, time: 8500 }, | ||
{ type: 'note', pitch: 55, ticks: 500, volume: 100/127, time: 9000 }, | ||
{ type: 'note', pitch: 57, ticks: 500, volume: 100/127, time: 9500 }, | ||
{ type: 'note', pitch: 59, ticks: 500, volume: 100/127, time: 10000 } | ||
]], | ||
[['c2c4c8c', options], [ | ||
{ type: 'note', pitch: 49, ticks: 1000, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 49, ticks: 250, volume: 100/127, time: 1500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1750 } | ||
]], | ||
[['c2c2.c2..c2...', options], [ | ||
{ type: 'note', pitch: 49, ticks: 1000, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 1500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 49, ticks: 2250, volume: 100/127, time: 2500 }, | ||
{ type: 'note', pitch: 49, ticks: 3375, volume: 100/127, time: 4750 } | ||
]], | ||
[['cc.c..c...', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 750, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 1125, volume: 100/127, time: 1250 }, | ||
{ type: 'note', pitch: 49, ticks: 1687, volume: 100/127, time: 2375 } | ||
]], | ||
[['n60n30n90', options], [ | ||
{ type: 'note', pitch: 61, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 31, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 91, ticks: 500, volume: 100/127, time: 1000 } | ||
]], | ||
[['r2r4r8r', options], [ | ||
{ type: 'rest', ticks: 1000, time: 0 }, | ||
{ type: 'rest', ticks: 500, time: 1000 }, | ||
{ type: 'rest', ticks: 250, time: 1500 }, | ||
{ type: 'rest', ticks: 500, time: 1750 } | ||
]], | ||
[['r2r2.r2..r2...', options], [ | ||
{ type: 'rest', ticks: 1000, time: 0 }, | ||
{ type: 'rest', ticks: 1500, time: 1000 }, | ||
{ type: 'rest', ticks: 2250, time: 2500 }, | ||
{ type: 'rest', ticks: 3375, time: 4750 } | ||
]], | ||
[['rr.r..r...', options], [ | ||
{ type: 'rest', ticks: 500, time: 0 }, | ||
{ type: 'rest', ticks: 750, time: 500 }, | ||
{ type: 'rest', ticks: 1125, time: 1250 }, | ||
{ type: 'rest', ticks: 1687, time: 2375 } | ||
]], | ||
[['ccc /* this is a comment v64 */ ccc', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 2000 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 2500 } | ||
]], | ||
[['ccc /* this is a\nmulti line\r\ncomment v64 */ ccc', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 2000 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 2500 } | ||
]], | ||
[['ccL8cc', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 250, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 49, ticks: 250, volume: 100/127, time: 1250 } | ||
]], | ||
[['rrL8rr', options], [ | ||
{ type: 'rest', ticks: 500, time: 0 }, | ||
{ type: 'rest', ticks: 500, time: 500 }, | ||
{ type: 'rest', ticks: 250, time: 1000 }, | ||
{ type: 'rest', ticks: 250, time: 1250 } | ||
]], | ||
[['L2cc.c..c...', options], [ | ||
{ type: 'note', pitch: 49, ticks: 1000, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 1500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 49, ticks: 2250, volume: 100/127, time: 2500 }, | ||
{ type: 'note', pitch: 49, ticks: 3375, volume: 100/127, time: 4750 } | ||
]], | ||
[['L4.cc.c..c...', options], [ | ||
{ type: 'note', pitch: 49, ticks: 750, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 1125, volume: 100/127, time: 750 }, | ||
{ type: 'note', pitch: 49, ticks: 1687, volume: 100/127, time: 1875 }, | ||
{ type: 'note', pitch: 49, ticks: 2530, volume: 100/127, time: 3562 } | ||
]], | ||
[['o3c>c>c<c<c', options], [ | ||
{ type: 'note', pitch: 25, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 37, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 37, ticks: 500, volume: 100/127, time: 1500 }, | ||
{ type: 'note', pitch: 25, ticks: 500, volume: 100/127, time: 2000 } | ||
]], | ||
[['t180ccc', options], [ | ||
{ type: 'tempo', tempo: 180, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 } | ||
]], | ||
[['v64ccc', options], [ | ||
{ type: 'volume', volume: 64/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 64/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 64/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 64/127, time: 1000 } | ||
]], | ||
[['c&c&c', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'tie' }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'tie' }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 } | ||
]], | ||
[['L8V64O2c,c,c', options], [ | ||
{ type: 'volume', volume: 64/127, time: 0 }, | ||
{ type: 'note', pitch: 13, ticks: 250, volume: 64/127, time: 0 }, | ||
{ type: 'nextVoice' }, | ||
{ type: 'note', pitch: 13, ticks: 250, volume: 64/127, time: 0 }, | ||
{ type: 'nextVoice' }, | ||
{ type: 'note', pitch: 13, ticks: 250, volume: 64/127, time: 0 } | ||
]], | ||
[['L8V64O2c,c,c', extend({}, options, { tracksShareState: false })], [ | ||
{ type: 'volume', volume: 64/127, time: 0 }, | ||
{ type: 'note', pitch: 13, ticks: 250, volume: 64/127, time: 0 }, | ||
{ type: 'nextVoice' }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'nextVoice' }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 } | ||
]], | ||
[['@#$ ccc !|/', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 } | ||
]] | ||
]); | ||
}); | ||
describe('parseMml', function () { | ||
var options = { | ||
tpqn: 500, | ||
minimumNoteDuration: 64, | ||
defaultState: { | ||
octave: 4, | ||
tempo: 100, | ||
volume: 100/127, | ||
duration: '4' | ||
}, | ||
maxVolume: 127, | ||
tracksShareState: true, | ||
octaveOffset: -1, | ||
transpose: 1 | ||
}; | ||
runCases('should parse {0}', core.parseMml, [ | ||
[['ccc', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 } | ||
]], | ||
[['cdefgabCDEFGAB', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 51, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 53, ticks: 500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 54, ticks: 500, volume: 100/127, time: 1500 }, | ||
{ type: 'note', pitch: 56, ticks: 500, volume: 100/127, time: 2000 }, | ||
{ type: 'note', pitch: 58, ticks: 500, volume: 100/127, time: 2500 }, | ||
{ type: 'note', pitch: 60, ticks: 500, volume: 100/127, time: 3000 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 3500 }, | ||
{ type: 'note', pitch: 51, ticks: 500, volume: 100/127, time: 4000 }, | ||
{ type: 'note', pitch: 53, ticks: 500, volume: 100/127, time: 4500 }, | ||
{ type: 'note', pitch: 54, ticks: 500, volume: 100/127, time: 5000 }, | ||
{ type: 'note', pitch: 56, ticks: 500, volume: 100/127, time: 5500 }, | ||
{ type: 'note', pitch: 58, ticks: 500, volume: 100/127, time: 6000 }, | ||
{ type: 'note', pitch: 60, ticks: 500, volume: 100/127, time: 6500 }, | ||
]], | ||
[['c#d#e#f#g#a#b#c+d+e+f+g+a+b+c-d-e-f-g-a-b-', options], [ | ||
{ type: 'note', pitch: 50, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 52, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 54, ticks: 500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 55, ticks: 500, volume: 100/127, time: 1500 }, | ||
{ type: 'note', pitch: 57, ticks: 500, volume: 100/127, time: 2000 }, | ||
{ type: 'note', pitch: 59, ticks: 500, volume: 100/127, time: 2500 }, | ||
{ type: 'note', pitch: 61, ticks: 500, volume: 100/127, time: 3000 }, | ||
{ type: 'note', pitch: 50, ticks: 500, volume: 100/127, time: 3500 }, | ||
{ type: 'note', pitch: 52, ticks: 500, volume: 100/127, time: 4000 }, | ||
{ type: 'note', pitch: 54, ticks: 500, volume: 100/127, time: 4500 }, | ||
{ type: 'note', pitch: 55, ticks: 500, volume: 100/127, time: 5000 }, | ||
{ type: 'note', pitch: 57, ticks: 500, volume: 100/127, time: 5500 }, | ||
{ type: 'note', pitch: 59, ticks: 500, volume: 100/127, time: 6000 }, | ||
{ type: 'note', pitch: 61, ticks: 500, volume: 100/127, time: 6500 }, | ||
{ type: 'note', pitch: 48, ticks: 500, volume: 100/127, time: 7000 }, | ||
{ type: 'note', pitch: 50, ticks: 500, volume: 100/127, time: 7500 }, | ||
{ type: 'note', pitch: 52, ticks: 500, volume: 100/127, time: 8000 }, | ||
{ type: 'note', pitch: 53, ticks: 500, volume: 100/127, time: 8500 }, | ||
{ type: 'note', pitch: 55, ticks: 500, volume: 100/127, time: 9000 }, | ||
{ type: 'note', pitch: 57, ticks: 500, volume: 100/127, time: 9500 }, | ||
{ type: 'note', pitch: 59, ticks: 500, volume: 100/127, time: 10000 } | ||
]], | ||
[['c2c4c8c', options], [ | ||
{ type: 'note', pitch: 49, ticks: 1000, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 49, ticks: 250, volume: 100/127, time: 1500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1750 } | ||
]], | ||
[['c2c2.c2..c2...', options], [ | ||
{ type: 'note', pitch: 49, ticks: 1000, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 1500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 49, ticks: 2250, volume: 100/127, time: 2500 }, | ||
{ type: 'note', pitch: 49, ticks: 3375, volume: 100/127, time: 4750 } | ||
]], | ||
[['cc.c..c...', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 750, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 1125, volume: 100/127, time: 1250 }, | ||
{ type: 'note', pitch: 49, ticks: 1687, volume: 100/127, time: 2375 } | ||
]], | ||
[['n60n30n90', options], [ | ||
{ type: 'note', pitch: 61, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 31, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 91, ticks: 500, volume: 100/127, time: 1000 } | ||
]], | ||
[['r2r4r8r', options], [ | ||
{ type: 'rest', ticks: 1000, time: 0 }, | ||
{ type: 'rest', ticks: 500, time: 1000 }, | ||
{ type: 'rest', ticks: 250, time: 1500 }, | ||
{ type: 'rest', ticks: 500, time: 1750 } | ||
]], | ||
[['r2r2.r2..r2...', options], [ | ||
{ type: 'rest', ticks: 1000, time: 0 }, | ||
{ type: 'rest', ticks: 1500, time: 1000 }, | ||
{ type: 'rest', ticks: 2250, time: 2500 }, | ||
{ type: 'rest', ticks: 3375, time: 4750 } | ||
]], | ||
[['rr.r..r...', options], [ | ||
{ type: 'rest', ticks: 500, time: 0 }, | ||
{ type: 'rest', ticks: 750, time: 500 }, | ||
{ type: 'rest', ticks: 1125, time: 1250 }, | ||
{ type: 'rest', ticks: 1687, time: 2375 } | ||
]], | ||
[['ccc /* this is a comment v64 */ ccc', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 2000 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 2500 } | ||
]], | ||
[['ccc /* this is a\nmulti line\r\ncomment v64 */ ccc', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 2000 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 2500 } | ||
]], | ||
[['ccL8cc', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 250, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 49, ticks: 250, volume: 100/127, time: 1250 } | ||
]], | ||
[['rrL8rr', options], [ | ||
{ type: 'rest', ticks: 500, time: 0 }, | ||
{ type: 'rest', ticks: 500, time: 500 }, | ||
{ type: 'rest', ticks: 250, time: 1000 }, | ||
{ type: 'rest', ticks: 250, time: 1250 } | ||
]], | ||
[['L2cc.c..c...', options], [ | ||
{ type: 'note', pitch: 49, ticks: 1000, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 1500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 49, ticks: 2250, volume: 100/127, time: 2500 }, | ||
{ type: 'note', pitch: 49, ticks: 3375, volume: 100/127, time: 4750 } | ||
]], | ||
[['L4.cc.c..c...', options], [ | ||
{ type: 'note', pitch: 49, ticks: 750, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 1125, volume: 100/127, time: 750 }, | ||
{ type: 'note', pitch: 49, ticks: 1687, volume: 100/127, time: 1875 }, | ||
{ type: 'note', pitch: 49, ticks: 2530, volume: 100/127, time: 3562 } | ||
]], | ||
[['o3c>c>c<c<c', options], [ | ||
{ type: 'note', pitch: 25, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 37, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 37, ticks: 500, volume: 100/127, time: 1500 }, | ||
{ type: 'note', pitch: 25, ticks: 500, volume: 100/127, time: 2000 } | ||
]], | ||
[['t180ccc', options], [ | ||
{ type: 'tempo', tempo: 180, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 } | ||
]], | ||
[['v64ccc', options], [ | ||
{ type: 'volume', volume: 64/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 64/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 64/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 64/127, time: 1000 } | ||
]], | ||
[['c&c&c', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'tie' }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'tie' }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 } | ||
]], | ||
[['L8V64O2c,c,c', options], [ | ||
{ type: 'volume', volume: 64/127, time: 0 }, | ||
{ type: 'note', pitch: 13, ticks: 250, volume: 64/127, time: 0 }, | ||
{ type: 'nextVoice' }, | ||
{ type: 'note', pitch: 13, ticks: 250, volume: 64/127, time: 0 }, | ||
{ type: 'nextVoice' }, | ||
{ type: 'note', pitch: 13, ticks: 250, volume: 64/127, time: 0 } | ||
]], | ||
[['L8V64O2c,c,c', extend({}, options, { tracksShareState: false })], [ | ||
{ type: 'volume', volume: 64/127, time: 0 }, | ||
{ type: 'note', pitch: 13, ticks: 250, volume: 64/127, time: 0 }, | ||
{ type: 'nextVoice' }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'nextVoice' }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 } | ||
]], | ||
[['@#$ ccc !|/', options], [ | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 49, ticks: 500, volume: 100/127, time: 1000 } | ||
]] | ||
]); | ||
}); | ||
describe('findPath', function () { | ||
var options = { | ||
tpqn: 500, | ||
minimumNoteDuration: 64, | ||
defaultState: { | ||
octave: 4, | ||
tempo: 120, | ||
volume: 100/127, | ||
duration: '4' | ||
}, | ||
transpose: 0 | ||
}; | ||
runCases('should find a path given an input token set', opt.findPath, [ | ||
[[opt.parseMml('cdef', options), options], [ | ||
{ cursor: 0, octave: 4, tempo: 120, volume: 100/127, duration: '4' }, | ||
{ cursor: 1, octave: 4, tempo: 120, volume: 100/127, duration: '4' }, | ||
{ cursor: 2, octave: 4, tempo: 120, volume: 100/127, duration: '4' }, | ||
{ cursor: 3, octave: 4, tempo: 120, volume: 100/127, duration: '4' }, | ||
{ cursor: 4, octave: 4, tempo: 120, volume: 100/127, duration: '4' } | ||
]], | ||
[[opt.parseMml('c16d16e32f32', options), options], [ | ||
{ cursor: 0, octave: 4, tempo: 120, volume: 100/127, duration: '4' }, | ||
{ cursor: 0, octave: 4, tempo: 120, volume: 100/127, duration: '16' }, | ||
{ cursor: 1, octave: 4, tempo: 120, volume: 100/127, duration: '16' }, | ||
{ cursor: 2, octave: 4, tempo: 120, volume: 100/127, duration: '16' }, | ||
{ cursor: 2, octave: 4, tempo: 120, volume: 100/127, duration: '32' }, | ||
{ cursor: 3, octave: 4, tempo: 120, volume: 100/127, duration: '32' }, | ||
{ cursor: 4, octave: 4, tempo: 120, volume: 100/127, duration: '32' } | ||
]] | ||
]); | ||
}); | ||
describe('findPath', function () { | ||
var options = { | ||
tpqn: 500, | ||
minimumNoteDuration: 64, | ||
defaultState: { | ||
octave: 4, | ||
tempo: 120, | ||
volume: 100/127, | ||
duration: '4' | ||
}, | ||
transpose: 0 | ||
}; | ||
runCases('should find a path given an input token set', core.findPath, [ | ||
[[core.parseMml('cdef', options), options], [ | ||
{ cursor: 0, octave: 4, tempo: 120, volume: 100/127, duration: '4' }, | ||
{ cursor: 1, octave: 4, tempo: 120, volume: 100/127, duration: '4' }, | ||
{ cursor: 2, octave: 4, tempo: 120, volume: 100/127, duration: '4' }, | ||
{ cursor: 3, octave: 4, tempo: 120, volume: 100/127, duration: '4' }, | ||
{ cursor: 4, octave: 4, tempo: 120, volume: 100/127, duration: '4' } | ||
]], | ||
[[core.parseMml('c16d16e32f32', options), options], [ | ||
{ cursor: 0, octave: 4, tempo: 120, volume: 100/127, duration: '4' }, | ||
{ cursor: 0, octave: 4, tempo: 120, volume: 100/127, duration: '16' }, | ||
{ cursor: 1, octave: 4, tempo: 120, volume: 100/127, duration: '16' }, | ||
{ cursor: 2, octave: 4, tempo: 120, volume: 100/127, duration: '16' }, | ||
{ cursor: 2, octave: 4, tempo: 120, volume: 100/127, duration: '32' }, | ||
{ cursor: 3, octave: 4, tempo: 120, volume: 100/127, duration: '32' }, | ||
{ cursor: 4, octave: 4, tempo: 120, volume: 100/127, duration: '32' } | ||
]] | ||
]); | ||
}); | ||
describe('optimizeTokens', function () { | ||
var options = { | ||
tpqn: 500, | ||
minimumNoteDuration: 64, | ||
defaultState: { | ||
octave: 4, | ||
tempo: 120, | ||
volume: 100/127, | ||
duration: '4' | ||
}, | ||
transpose: 0 | ||
}; | ||
runCases('should return an optimized token set', opt.optimizeTokens, [ | ||
[[opt.parseMml('cdef', options), options], [ | ||
{ type: 'note', pitch: 48, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 50, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 52, ticks: 500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 53, ticks: 500, volume: 100/127, time: 1500 } | ||
]], | ||
[[opt.parseMml('c16d16e32f32', options), options], [ | ||
{ type: 'duration', duration: '16', time: 0 }, | ||
{ type: 'note', pitch: 48, ticks: 125, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 50, ticks: 125, volume: 100/127, time: 125 }, | ||
{ type: 'duration', duration: '32', time: 250 }, | ||
{ type: 'note', pitch: 52, ticks: 62, volume: 100/127, time: 250 }, | ||
{ type: 'note', pitch: 53, ticks: 62, volume: 100/127, time: 312 } | ||
]] | ||
]); | ||
describe('optimizeTokens', function () { | ||
var options = { | ||
tpqn: 500, | ||
minimumNoteDuration: 64, | ||
defaultState: { | ||
octave: 4, | ||
tempo: 120, | ||
volume: 100/127, | ||
duration: '4' | ||
}, | ||
transpose: 0 | ||
}; | ||
runCases('should return an optimized token set', core.optimizeTokens, [ | ||
[[core.parseMml('cdef', options), options], [ | ||
{ type: 'note', pitch: 48, ticks: 500, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 50, ticks: 500, volume: 100/127, time: 500 }, | ||
{ type: 'note', pitch: 52, ticks: 500, volume: 100/127, time: 1000 }, | ||
{ type: 'note', pitch: 53, ticks: 500, volume: 100/127, time: 1500 } | ||
]], | ||
[[core.parseMml('c16d16e32f32', options), options], [ | ||
{ type: 'duration', duration: '16', time: 0 }, | ||
{ type: 'note', pitch: 48, ticks: 125, volume: 100/127, time: 0 }, | ||
{ type: 'note', pitch: 50, ticks: 125, volume: 100/127, time: 125 }, | ||
{ type: 'duration', duration: '32', time: 250 }, | ||
{ type: 'note', pitch: 52, ticks: 62, volume: 100/127, time: 250 }, | ||
{ type: 'note', pitch: 53, ticks: 62, volume: 100/127, time: 312 } | ||
]] | ||
]); | ||
}); | ||
}); | ||
@@ -374,0 +378,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
34793
8
903