diff --git a/src/components/mugicplayer/mugicplayer.ts b/src/components/mugicplayer/mugicplayer.ts
index 57050ee..e4c360f 100644
--- a/src/components/mugicplayer/mugicplayer.ts
+++ b/src/components/mugicplayer/mugicplayer.ts
@@ -1,43 +1,20 @@
-import Tone from 'tone';
+const Tone = require('tone');
// https://github.com/Tonejs/Tone.js/wiki/Time
-class Note {
+// https://github.com/Tonejs/Tone.js/wiki/Events
+export class Note {
time: number;
- value: {
- pitch: string;
- octave: number;
- }
+ pitch: string;
+ octave: number;
- constructor(time: number, value: any) {
+ constructor(time: number, value: {pitch: string, octave: number}) {
this.time = time;
- this.value = value;
+ this.pitch = value.pitch;
+ this.octave = value.octave;
}
get pair() {
- return [this.time * Tone.time("4"), this.value.pitch + this.value.octave.toString()];
- }
-
- toNumber (): number {
- return
- }
-}
-
-const letter_to_number = (letter: string): number => {
- switch (letter.charAt(0).toUpperCase()) {
- case "A":
- return 1;
- case "B":
- return 2;
- case "C":
- return 3;
- case "D":
- return 4;
- case "E":
- return 5;
- case "F":
- return 6;
- case "G":
- return 7;
+ return [this.time + "/4n", this.pitch + this.octave.toString()];
}
}
@@ -53,48 +30,176 @@ class MugicPlayer {
constructor() {
this.synth = new Tone.Synth().toMaster();
- Tone.Transport.bpm.value = 120;
+ Tone.Transport.bpm.value = 100;
}
-
+ // Example: Canon of Casuality
+ // 2Eb 2F 2D 2G 2Bb 1A 3D
+ // up down up up down up
+ /**
+ * Composes and plays a tune parsed from the Mugic entry
+ * @input the string of Mugic notes
+ */
play(input: string) {
- let pattern: Note[] = [];
- input.split(" ").forEach((digit) => {
+ let tune: Note[] = [];
+ input.split(" ").forEach((note) => {
// db notation uses duration (quarter notes) and pitch
- let [d, n] = digit.split(/[1-8]{1}/);
- pattern.push(new Note(parseInt(d), parseNote(n, pattern)));
+ let d = note.match(/(?:[1-8]{1})/)[0];
+ let n = note.split(/(?:[1-8]{1})/)[1];
+ tune.push(new Note(parseInt(d), parseNote(n, tune)));
});
-
- new Tone.Part(
- (time: any, pitch: any) => {
- this.synth.triggerAttackRelease(pitch, "4n", time);
- },
- pattern.map((note) => {return note.pair})
- ).start(0);
- Tone.Transport.start();
+ let output = tune.map((note) => {return note.pair});
+
+ console.log(output);
+
+ // new Tone.Part(
+ // (time: any, pitch: any) => {
+ // this.synth.triggerAttackRelease(pitch, "4n", time);
+ // },
+ // tune.map((note) => {return note.pair})
+ // ).start(0);
+
+ // Tone.Transport.start();
}
}
-// Example: Canon of Casuality
-// Eb 2F 2D 2G 2Bb 1A 3D
-// up down up up down up
+/**
+ * Tries to find the closer note to match of the previous notes octave
+ */
+/*
+ We have an array of previous notes; for the first to cases the octave is middle (4) easy.
+ Now for the third and further note, we'll need to look at the previous notes.
+ First check the "closness" to the last note. (if its within 2.5 notes lets say).
+ Go with the appropriate octave (an A above a G4 would be an A5)
+ But if its not within that closeness. Look at the trend.
+ Is the last note a step down from the note before?
+ If so consider that to be a higher weight. If going up, go up, if going down go down with repeated notes
+ Example, a note 4 away but on a downward trend would be prioritized over a 3 away in the other direction.
+ If there is no change. Pick the octive the previous notes used.
+*/
+export const parseNote = (note: string, seq: Note[]): {pitch: string, octave: number} => {
+ let octave: number = (() => {
+ // If its the first two notes
+ if (seq.length < 2) return 4;
-// If going up, go up, if going down go down with repeated notes
-// If the first note and last are the same, they share the same octive
-// If there is no trend (same note without previous two)
-// Try to find the closer note to match with
-const parseNote = (note: string, prev: Note[]): {pitch: string, octave: number} => {
- let octave: number;
- if (prev.length < 2) octave = 4;
- else {
- //TODO
- }
+ const l = seq.length;
+
+ let previous = letter_to_number(seq[l-1].pitch);
+ let current = letter_to_number(note);
+ let distance = compare(previous, current);
+
+ // If its within two pitches of the previous, use the closest note
+ if (distance < 3) {
+ if (distance === 0) return seq[l-1].octave;
+
+ if (previous > 5) {
+ if (current < 3) {
+ return seq[l-1].octave + 1;
+ }
+ else {
+ return seq[l-1].octave;
+ }
+ }
+ else if (previous < 3) {
+ if (current > 5) {
+ return seq[l-1].octave - 1;
+ }
+ else {
+ return seq[l-1].octave;
+ }
+ }
+ return seq[l-1].octave;
+ }
+
+ // If its slightly further away, look at the last two notes for a trend
+ let prev2 = letter_to_number(seq[l-2].pitch);
+ // Downward trend
+ if (prev2 > previous) {
+ if (previous < current) {
+ return seq[l-1].octave;
+ }
+ return seq[l-1].octave - 1;
+ }
+ // upward trend
+ else if (prev2 < previous) {
+ if (previous < current) {
+ return seq[l-1].octave;
+ }
+ return seq[l-1].octave + 1;
+ }
+ // same notes
+ else {
+
+ }
+
+ return 1; // debug shouldn't reach 1
+ })();
return {pitch: note, octave};
}
+/**
+ * Takes two pitches and returns the distance between them
+ */
+const compare = (one: number, two: number): number => {
+ let res = Math.abs(one - two);
+ if (res < 4) {
+ return res;
+ }
+ else if (res > 3.5) {
+ return res - 1;
+ }
+ else if (res > 4.5) {
+ return res - 2;
+ }
+ else if (res > 5.5) {
+ return res - 3;
+ }
+ else if (res > 6.5) {
+ return res - 4;
+ }
+
+ return res;
+}
+/**
+ * Converts a pitch to numerical value for calculations
+ */
+const letter_to_number = (pitch: string): number => {
+ let num: number;
+ switch (pitch.charAt(0).toUpperCase()) {
+ case "A":
+ num = 1;
+ break;
+ case "B":
+ num = 2;
+ break;
+ case "C":
+ num = 3;
+ break;
+ case "D":
+ num = 4;
+ break;
+ case "E":
+ num = 5;
+ break;
+ case "F":
+ num = 6;
+ break;
+ case "G":
+ num = 7;
+ break;
+ // In the case of incorrect input, coerce note to a C
+ default:
+ num = 3;
+ }
+ if (pitch.length > 1) {
+ if (pitch.charAt(1).toLowerCase() === "b") num -= .5;
+ else if (pitch.charAt(1) === "#") num += .5;
+ }
+ return num;
+}
export default MugicPlayer.getInstance();
diff --git a/src/components/mugicplayer/playbutton.js b/src/components/mugicplayer/playbutton.js
deleted file mode 100644
index e69de29..0000000
diff --git a/src/components/mugicplayer/playbutton.tsx b/src/components/mugicplayer/playbutton.tsx
new file mode 100644
index 0000000..e4f47f0
--- /dev/null
+++ b/src/components/mugicplayer/playbutton.tsx
@@ -0,0 +1,8 @@
+import React from 'react';
+import MugicPlayer from './mugicplayer';
+
+export default (props: any) => (
+
+ {MugicPlayer.play(props.notes)}} />
+
+);
diff --git a/tsconfig.json b/tsconfig.json
index e8b20e6..7542d18 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -30,7 +30,7 @@
"resolveJsonModule": true,
// Library files to be used in the project.
// Tells the compiler that "DOM-APIs" and new ECMAScript features are valid.
- "lib": ["dom", "es2018"],
+ "lib": ["dom", "ES2019"],
// Allows @decorators
"experimentalDecorators": true,
// Skips checking libraries
diff --git a/webpack.config.babel.js b/webpack.config.babel.js
index e47ae37..369c3f2 100644
--- a/webpack.config.babel.js
+++ b/webpack.config.babel.js
@@ -9,16 +9,16 @@ require('@babel/register');
const devMode = (process.env.NODE_ENV !== 'production' && process.argv.indexOf('-p') === -1);
const config = {
- entry: ['@babel/polyfill', `./src/components/index.js`],
+ entry: ['@babel/polyfill', './src/components/index.js'],
resolve: {
- extensions: ['.js', '.jsx'],
+ extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
devServer: {
host: '0.0.0.0',
historyApiFallback: {
- index: 'index.dev.html',
+ index: 'index.html',
},
},
@@ -78,16 +78,16 @@ const config = {
options: {
presets: [
'@babel/typescript',
- '@babel/preset-env',
+ "@babel/preset-env",
'@babel/preset-react',
'@babel/preset-flow',
],
plugins: [
'@babel/plugin-transform-runtime',
+ '@babel/plugin-proposal-object-rest-spread',
['@babel/plugin-proposal-decorators', {legacy: true}],
['@babel/plugin-proposal-class-properties', {loose: true}],
- '@babel/plugin-syntax-dynamic-import',
- '@babel/proposal-object-rest-spread',
+ ['@babel/plugin-transform-computed-properties', {loose: true}],
],
},
},