museaikit
Version:
A powerful music-focused AI toolkit
138 lines • 5.22 kB
JavaScript
import * as tf from '@tensorflow/tfjs';
import { Chord, Note } from 'tonal';
import * as constants from './constants';
const CHORD_QUALITY_INTERVALS = [
['1P', '3M', '5P'],
['1P', '3m', '5P'],
['1P', '3M', '5A'],
['1P', '3m', '5d'],
];
export var ChordQuality;
(function (ChordQuality) {
ChordQuality[ChordQuality["Major"] = 0] = "Major";
ChordQuality[ChordQuality["Minor"] = 1] = "Minor";
ChordQuality[ChordQuality["Augmented"] = 2] = "Augmented";
ChordQuality[ChordQuality["Diminished"] = 3] = "Diminished";
ChordQuality[ChordQuality["Other"] = 4] = "Other";
})(ChordQuality || (ChordQuality = {}));
export class ChordSymbolException extends Error {
constructor(message) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
}
}
export class ChordEncodingException extends Error {
constructor(message) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
}
}
export class ChordSymbols {
static pitches(chord) {
const root = Chord.tokenize(chord)[0];
if (!root || !Chord.exists(chord)) {
throw new ChordSymbolException(`Unrecognized chord symbol: ${chord}`);
}
const notes = Chord.notes(chord);
return notes.map(Note.chroma);
}
static root(chord) {
const root = Chord.tokenize(chord)[0];
if (!root) {
throw new ChordSymbolException(`Chord symbol has unknown root: ${chord}`);
}
return Note.chroma(root);
}
static quality(chord) {
if (!Chord.exists(chord)) {
throw new ChordSymbolException(`Unrecognized chord symbol: ${chord}`);
}
const intervals = Chord.intervals(chord);
const qualities = CHORD_QUALITY_INTERVALS.map(cqis => cqis.every(cqi => intervals.includes(cqi)));
const i = qualities.indexOf(true);
const j = qualities.lastIndexOf(true);
if (i >= 0 && i === j) {
return i;
}
else {
return ChordQuality.Other;
}
}
}
export class ChordEncoder {
encodeProgression(chords, numSteps) {
const encodedChords = chords.map(chord => this.encode(chord));
const indices = Array.from(Array(numSteps).keys())
.map(step => Math.floor(step * encodedChords.length / numSteps));
return tf.stack(indices.map(i => encodedChords[i]));
}
}
export function chordEncoderFromType(type) {
switch (type) {
case 'MajorMinorChordEncoder':
return new MajorMinorChordEncoder();
case 'TriadChordEncoder':
return new TriadChordEncoder();
case 'PitchChordEncoder':
return new PitchChordEncoder();
default:
throw new Error(`Unknown chord encoder type: ${type}`);
}
}
export class MajorMinorChordEncoder extends ChordEncoder {
depth = 1 + 2 * constants.NUM_PITCH_CLASSES;
index(chord) {
if (chord === constants.NO_CHORD) {
return 0;
}
const root = ChordSymbols.root(chord);
const quality = ChordSymbols.quality(chord);
const index = 1 + quality * constants.NUM_PITCH_CLASSES + root;
if (index >= this.depth) {
throw new ChordEncodingException(`Chord is neither major nor minor: ${chord}`);
}
return index;
}
encode(chord) {
return tf.tidy(() => tf.oneHot(tf.tensor1d([this.index(chord)], 'int32'), this.depth)
.as1D());
}
}
export class TriadChordEncoder extends ChordEncoder {
depth = 1 + 4 * constants.NUM_PITCH_CLASSES;
index(chord) {
if (chord === constants.NO_CHORD) {
return 0;
}
const root = ChordSymbols.root(chord);
const quality = ChordSymbols.quality(chord);
const index = 1 + quality * constants.NUM_PITCH_CLASSES + root;
if (index >= this.depth) {
throw new ChordEncodingException(`Chord is not a standard triad: ${chord}`);
}
return index;
}
encode(chord) {
return tf.tidy(() => tf.oneHot(tf.tensor1d([this.index(chord)], 'int32'), this.depth)
.as1D());
}
}
export class PitchChordEncoder extends ChordEncoder {
depth = 1 + 3 * constants.NUM_PITCH_CLASSES;
encode(chord) {
return tf.tidy(() => {
if (chord === constants.NO_CHORD) {
return tf.oneHot(tf.tensor1d([0], 'int32'), this.depth).as1D();
}
const root = ChordSymbols.root(chord);
const rootEncoding = tf.oneHot(tf.tensor1d([root], 'int32'), constants.NUM_PITCH_CLASSES)
.as1D();
const pitchBuffer = tf.buffer([constants.NUM_PITCH_CLASSES]);
ChordSymbols.pitches(chord).forEach(pitch => pitchBuffer.set(1.0, pitch));
const pitchEncoding = pitchBuffer.toTensor().as1D();
const bassEncoding = rootEncoding;
return tf.concat1d([tf.tensor1d([0.0]), rootEncoding, pitchEncoding, bassEncoding]);
});
}
}
//# sourceMappingURL=chords.js.map