extend note { function withOctave(octave: int) { return Note(this.pitch, octave, this.duration.get("numerator"), this.duration.get("denominator")); } function withDuration(duration: int) { return Note(this.pitch, this.octave, duration, false); } function withDuration(numerator: int, denominator: int) { return Note(this.pitch, this.octave, numerator, denominator) } function transpose(value: int) { return noteFromInt(this.toInt() + value, this.duration.get("numerator"), this.duration.get("denominator")); } function toInt() { return this.octave * 12 + [ "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "H" ].indexOf(this.pitch); } } function noteFromInt(intPitch: int, numerator: int, denominator: int) { pitch = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "H"].get(mod(intPitch, 12)); octave = Int(intPitch / 12); return Note(pitch, octave, numerator, denominator); } function noteFromInt(intPitch: int, duration: int, dot: bool) { pitch = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "H"].get(mod(intPitch, 12)); octave = Int(intPitch / 12); return Note(pitch, octave, duration, dot); } function range(begin: note, end: note, filter: string = "all", duration: int = 4, dot: bool = false) { filters = { "all" -> ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "H"], "diatonic" -> ["C", "D", "E", "F", "G", "A", "H"], "chromatic" -> ["C#", "D#", "F#", "G#", "A#"] }; if(not filters.containsKey(filter)) { throw "Unknown filter '" + filter + "'"; } currentFilter = filters.get(filter); return (range(begin.toInt(), end.toInt()) as i ^ noteFromInt(i, duration, dot)) as n ^ n % currentFilter.contains(n.pitch); } function transpose(value: int, ...notes: ) { return transpose(value, notes); } function transpose(value: int, notes: list) { output = []; notes as item ^ { if(typeOf(item) == "note") { output = output + [item.transpose(value)]; } else { output = output + [item]; } } return output; } function triplet(a: note, b: note, c: note) { return tuplet(2, a, b, c); } function quintuplet(a: note, b: note, c: note, d: note, e: note) { return tuplet(4, a, b, c, d, e); } function tuplet(sub: int, ...notes: note) { return notes as note ^ note.withDuration(note.duration.get("numerator") * sub, note.duration.get("denominator") * notes.size); } function semitones(...notes: ) { return semitones(notes); } function semitones(staff: list) { notes = _filterNotes(staff); if(notes.size < 2) { return []; } return ((notes.size - 1) as i ^ [notes.get(i+1).toInt() - notes.get(i).toInt()]).flatten(); } function _filterNotes(staff: list) { return staff as item ^ item % typeOf(item) == "note"; } function transposeTo(target: note, ...notes: ) { return transposeTo(target, notes); } function transposeTo(target: note, staff: list) { notes = _filterNotes(staff); if(notes.size < 1) { return staff; } semitones = semitones(notes.get(0), target).get(0); return transpose(semitones, staff); }