Created
October 17, 2012 20:41
-
-
Save hanshoglund/3908031 to your computer and use it in GitHub Desktop.
Old JS music notation sketches
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* @nolint | |
* Test implementation of Time notator | |
* | |
* This code generates a collection of possible notations for a given duration, | |
* according to notations options defined in the environment. | |
* | |
* A notation is a simple structure consisting of a list of durations or nested | |
* notations of a relative length, determined by the multiplier property. | |
* | |
* The notation tree should be walked and compared to an actual sequence of events | |
* to determine its accuracy. | |
* | |
* @author Hans Höglund | |
*/ | |
// Configuration | |
this.tuplets = { | |
2 : [ ratio(3, 2) | |
/*ratio(5, 4), | |
ratio(6, 4), | |
ratio(7, 4), | |
ratio(9, 8), | |
ratio(11, 8), | |
ratio(13, 8)*/ | |
], | |
3 : [ ratio(2, 3) | |
/*ratio(4, 3), | |
ratio(5, 3), | |
ratio(7, 6), | |
ratio(11, 6)*/ | |
] | |
} | |
this.minDur = ratio(1, 8) | |
this.maxDur = ratio(2) | |
this.maxNesting = 0, | |
this.maxDots = 1 | |
this.cache = false | |
this.print = true | |
// Caching | |
var rationals = {} | |
/** | |
* Notates a bar of the given duration. Returns an array of notations. | |
* | |
* dur Duration of the bar to notate | |
* num Maximum number of events | |
*/ | |
function bar(dur, num) | |
{ | |
if (isNote(dur) || isDottedNote(dur)) | |
return simple(dur, num, 0); | |
else | |
return compound(dur, num, 0); | |
} | |
/** | |
* Notates a compound duration. Returns an array of notations. | |
* | |
* dur Duration to notate | |
* num Maximum number of events | |
* nest Level of tuplet nesting | |
*/ | |
function compound(dur, num, nest) | |
{ | |
var firstDur, | |
lastDur, | |
first, | |
last; | |
firstDur = noteValue(dur.divide(2)) | |
short = dur.subtract(firstDur) | |
first = simple(firstDur, num, nest); | |
if (isNote(lastDur) || isDottedNote(lastDur)) | |
last = simple(lastDur, num, nest); | |
else | |
last = compound(lastDur, num, nest); | |
return combine(first, last) | |
} | |
/** | |
* Notates a simple duration. Returns an array of notations. | |
* | |
* A simple duration is a duration such that there is exactly one note value | |
* or dotted note value representing it (i.e. 1/4, 3/8, 1/2). | |
* | |
* dur Duration to notate | |
* num Maximum number of events | |
* skip Skip one level of subdivision (used when applying a deeper-level | |
* division directly, as in the case of syncopation) | |
* nest Level of tuplet nesting | |
*/ | |
function simple(dur, num, skip, nest) | |
{ | |
if (Rational.compare(dur, this.minDur) < 0 || num < 1) | |
return empty(); | |
if (Rational.compare(dur, this.minDur) === 0 || num === 1) | |
return single(dur); | |
var variants; | |
// Full | |
if (nest === undefined || nest === 0) | |
variants = single(dur); | |
else | |
variants = []; | |
if (isNote(dur)) { | |
// Three-to-four | |
for (dots = 1; dots <= maxDots && num >= 2; dots++) { | |
var short = dur.divide(Math.pow(2, dots + 1)); | |
if (Rational.compare(short, this.minDur) < 0) | |
break; | |
variants = variants.concat( | |
combine( | |
single(dur.subtract(short)), | |
single(short))) | |
} | |
// Syncopation | |
if (Rational.compare(dur.divide(4), this.minDur) >= 0 && num >= 3) { | |
variants = variants.concat( | |
combineStrict( | |
simple(dur.divide(4), num - 2, false, nest), | |
simple(dur.divide(2), num - 2, true, nest), | |
simple(dur.divide(4), num - 2, false, nest))) | |
} | |
// Division | |
if (!skip) { | |
variants = variants.concat( | |
combineStrict( | |
simple(dur.divide(2), num - 1, false, nest), | |
simple(dur.divide(2), num - 1, false, nest))) | |
} | |
// Tuplets | |
if (nest == undefined || nest < this.minDur) { | |
for (var i in this.minDur[2]) { | |
var div = this.minDur[2][i]; | |
variants = variants.concat( | |
tuplet(div, dur.multiply(div), num, nest + 1)) | |
} | |
} | |
} else if (isDottedNote(dur)) { | |
// Two-to-three | |
variants = variants.concat( | |
combine( | |
simple(dur.multiply(ratio(2, 3)), num - 1, false, nest), | |
simple(dur.multiply(ratio(1, 3)), num - 1, false, nest))) | |
// TODO Tuplets | |
} else { | |
throw new Error("Invalid duration " + dur.toString()) | |
} | |
return variants | |
} | |
/** | |
* Notates the given tuplet. Returns an array of notations. | |
*/ | |
function tuplet(val, dur, num, nest) | |
{ | |
// var variants; | |
// | |
// if (isNote(dur) || isDottedNote(dur)) { | |
// variants = simple(dur, num, false, nest) | |
// } else { | |
// variants = compound(dur, num, nest) | |
// } | |
// return functions.map(function (notation) { | |
// return new Notation(val, [notation]) | |
// }, variants) | |
} | |
/** | |
* Notates the given duration with a single note. Returns an array containing | |
* exactly one element, which is this notation. | |
* | |
* If the given value is not a note value, the result is undefined. | |
*/ | |
function single(dur) | |
{ | |
return Array.box(new Notation(dur)); | |
} | |
/** | |
* Returns an empty array, representing no possible notations. | |
*/ | |
function empty() | |
{ | |
return []; | |
} | |
/** | |
* @class | |
* Represents a notation of a particular passage. Each notation contain | |
* either a duration or a sequence of child notations. | |
*/ | |
function Notation(duration, multiplier, children) { | |
this.duration = duration || 0; | |
this.multiplier = multiplier || ratio(1); | |
this.children = children || []; | |
} | |
/** | |
* Combines the given arrays of notations. | |
*/ | |
Notation.combine = function () { | |
// TODO | |
} | |
/** | |
* Combines the given notations, preserving ordering. Returns an array of | |
* notations. | |
* | |
* All arguments must be Notations or arrays of Notations to combine. | |
*/ | |
Notation.combineStrict = function () { | |
var input = util.parseArgs(arguments).map(function(arg) { | |
if (arg instanceof Notation) | |
return [ arg ]; | |
else if (arg instanceof Array) | |
return arg; | |
else | |
throw new Error("Arguments must be an Array or Notation"); | |
}); | |
return Array.combineAny(input, true).map(function(variant) { | |
return new Notation(null, null, variant); | |
}); | |
} | |
/* | |
* Returns a note value n so that n >= dur. | |
*/ | |
function noteValue(dur) | |
{ | |
var note, dotted; | |
note = ratio(1) | |
while (Rational.compare(note, dur) > 0) note = note.divide(2); | |
while (Rational.compare(note, dur) < 0) note = note.multiply(2); | |
dotted = note.multiply(ratio(3, 4)); | |
if (Rational.compare(dotted, dur) >= 0) return dotted; else return note; | |
} | |
/* | |
* Returns whether the given duration is a note value. | |
*/ | |
function isNote(dur) { | |
if (dur === 1) | |
return true; | |
else | |
if (dur.num < 1) | |
return isNote(dur.reciprocal()); | |
var note = log2(dur); | |
return (Math.floor(note) === note) | |
} | |
/* | |
* Returns whether the given duration is a dotted note value. | |
*/ | |
function isDottedNote(dur) { | |
return isNote(dur.multiply(ratio(2, 3))) | |
} | |
function log2(x) { | |
return Math.log(x) / Math.log(2) | |
} | |
/* | |
* Returns the given rational number. | |
*/ | |
var ratio = function (m, n) { | |
if (this.cache) { | |
if (arguments.length > 1) | |
return new Rational(m, n); | |
else | |
return new Rational(m); | |
} else { | |
var i = m.toString() + "/" + (n||1).toString(); | |
if (!rationals.hasOwnProperty(i)) { | |
if (arguments.length > 1) | |
rationals[i] = new Rational(m, n); | |
else | |
rationals[i] = new Rational(m); | |
} | |
return rationals[i]; | |
} | |
} | |
//-------------------------------------------------------------------- | |
// Debug | |
Notation.serialize = function () { | |
if (this.children.length > 1) | |
return this.duration.noteString(); | |
else | |
return this.children.reduce(function(red, child) { | |
return red.push(child.serialize()); | |
}, []); | |
} | |
Rational.prototype.noteString = function () { | |
if (isNote(this)) | |
return ["t", "x", "e", "q", "h", "w", "b", "l"][log2(this) + 5]; | |
else if (isDottedNote(this)) | |
return this.multiply(ratio(2, 3)).noteString() + "."; | |
else | |
return "NotANote"; | |
}; | |
function bang() | |
{ | |
// var fout = new File("/Users/hans/Desktop/fout.txt", "write"); | |
// fout.eof = 0 | |
// out = function(m) { fout.writestring(m) }; | |
var a = new Notation(ratio(1,8)); | |
var b = new Notation(ratio(1,4)); | |
var c = new Notation(null, null, [a, b]); | |
var d = new Notation(null, null, [b, a]); | |
var x = new Notation(null, null, [c, d]); | |
x.postln(true) | |
// fout.close() | |
// Notation.combine(c, d).postln(true); | |
/* | |
var result; | |
var time = function () { | |
result = bar(ratio(1), Number.MAX_VALUE) | |
}.benchmark() | |
var fout = new File("/Users/hans/Desktop/fout.txt", "write"); | |
fout.eof = 0 | |
util.trace(result, null, null, function(m) { | |
fout.writestring(m) | |
}) | |
fout.close() | |
postln("Generated in " + time + " ms") | |
postln(result.length.toString() + " results") | |
*/ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment