Skip to content

Instantly share code, notes, and snippets.

@hanshoglund
Created October 17, 2012 20:41
Show Gist options
  • Save hanshoglund/3908031 to your computer and use it in GitHub Desktop.
Save hanshoglund/3908031 to your computer and use it in GitHub Desktop.
Old JS music notation sketches
/*
* @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