added _asNumber + interprete numbers as midi

This commit is contained in:
Felix Roos 2022-02-27 21:24:23 +01:00
parent 190729df73
commit 8a7e49780a
3 changed files with 44 additions and 6 deletions

View File

@ -1,5 +1,6 @@
import { useCallback, useState, useMemo } from 'react';
import { isNote } from 'tone';
import { fromMidi } from '../../util.mjs';
import { evaluate } from './evaluate';
import type { Pattern } from './types';
import useCycle from './useCycle';
@ -63,8 +64,12 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw }: any)
onEvent?.(event);
const { onTrigger } = event.context;
if (!onTrigger) {
const note = event.value;
if (!isNote(note)) {
let note = event.value;
// if value is number => interpret as midi number...
// TODO: what if value is meant as frequency? => check context
if (typeof event.value === 'number') {
note = fromMidi(event.value);
} else if (!isNote(note)) {
throw new Error('not a note: ' + note);
}
if (defaultSynth) {

View File

@ -1,5 +1,6 @@
import Fraction from 'fraction.js'
import { compose } from 'ramda'; // will remove this as soon as compose is implemented here
import { isNote, toMidi } from './util.mjs';
// Removes 'None' values from given list
const removeUndefineds = xs => xs.filter(x => x != undefined)
@ -466,20 +467,41 @@ class Pattern {
return this.fmap(func).appLeft(reify(other))
}
_asNumber() {
return this._withEvent(event => {
const asNumber = Number(event.value)
if(!isNaN(asNumber)) {
return asNumber;
}
const specialValue = {
e: Math.E,
pi: Math.PI
}[event.value];
if(typeof specialValue !== 'undefined') {
return specialValue;
}
if(isNote(event.value)) {
// set context type to midi to let the player know its meant as midi number and not as frequency
return new Hap(event.whole, event.part, toMidi(event.value), { ...event.context, type: 'midi' });
}
throw new Error('cannot parse as number: "' + event.value + '"');
})
}
add(other) {
return this._opleft(other, a => b => a + b)
return this._asNumber()._opleft(other, a => b => a + b)
}
sub(other) {
return this._opleft(other, a => b => a - b)
return this._asNumber()._opleft(other, a => b => a - b)
}
mul(other) {
return this._opleft(other, a => b => a * b)
return this._asNumber()._opleft(other, a => b => a * b)
}
div(other) {
return this._opleft(other, a => b => a / b)
return this._asNumber()._opleft(other, a => b => a / b)
}
union(other) {

11
util.mjs Normal file
View File

@ -0,0 +1,11 @@
export const isNote = (name) => /^[a-gA-G][#b]?[0-9]$/.test(name);
export const tokenizeNote = (note) => note.match(/^([a-gA-G])([#b])?([0-9])?$/).slice(1);
export const toMidi = (note) => {
const [pc, acc, oct] = tokenizeNote(note);
const chroma = { c: 0, d: 2, e: 4, f: 5, g: 7, a: 9, b: 11 }[pc];
const offset = acc?.split('').reduce((o, char) => o + { '#': 1, b: -1 }[char], 0) || 0;
return (Number(oct) + 1) * 12 + chroma + offset;
};
export const fromMidi = (n) => {
return Math.pow(2, (n - 69) / 12) * 440;
};