From df73ce8a60aa0e543e7506e5a4f893f1b521fd56 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 12 Dec 2022 20:41:09 +0100 Subject: [PATCH 1/3] add freq support to sampler --- packages/core/test/util.test.mjs | 7 +++++++ packages/core/util.mjs | 25 +++++++++++++++++++++++++ packages/webaudio/sampler.mjs | 9 ++++++--- packages/webaudio/webaudio.mjs | 4 ++-- repl/src/prebake.mjs | 6 +++--- 5 files changed, 43 insertions(+), 8 deletions(-) diff --git a/packages/core/test/util.test.mjs b/packages/core/test/util.test.mjs index 17dceb81..9ebccee3 100644 --- a/packages/core/test/util.test.mjs +++ b/packages/core/test/util.test.mjs @@ -10,6 +10,7 @@ import { tokenizeNote, toMidi, fromMidi, + freqToMidi, _mod, compose, getFrequency, @@ -94,6 +95,12 @@ describe('fromMidi', () => { expect(fromMidi(57)).toEqual(220); }); }); +describe('freqToMidi', () => { + it('should turn frequency into midi', () => { + expect(freqToMidi(440)).toEqual(69); + expect(freqToMidi(220)).toEqual(57); + }); +}); describe('getFrequency', () => { const happify = (val, context = {}) => pure(val).firstCycle()[0].setContext(context); it('should turn note into frequency', () => { diff --git a/packages/core/util.mjs b/packages/core/util.mjs index b1997977..d6eb9aec 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -31,6 +31,31 @@ export const fromMidi = (n) => { return Math.pow(2, (n - 69) / 12) * 440; }; +export const freqToMidi = (freq) => { + return (12 * Math.log(freq / 440)) / Math.LN2 + 69; +}; + +export const valueToMidi = (value, fallbackValue) => { + if (typeof value !== 'object') { + throw new Error('Hap.getMidi: expected object value'); + } + let { freq, note, n } = value; + note = note ?? n; + if (typeof freq === 'number') { + return freqToMidi(freq); + } + if (typeof note === 'string') { + return toMidi(note); + } + if (typeof note === 'number') { + return note; + } + if (!fallbackValue) { + throw new Error('Hap.getMidi: expected freq or n / note to be set'); + } + return fallbackValue; +}; + /** * @deprecated does not appear to be referenced or invoked anywhere in the codebase */ diff --git a/packages/webaudio/sampler.mjs b/packages/webaudio/sampler.mjs index 094af87f..82c5aad0 100644 --- a/packages/webaudio/sampler.mjs +++ b/packages/webaudio/sampler.mjs @@ -1,4 +1,4 @@ -import { logger, toMidi } from '@strudel.cycles/core'; +import { logger, toMidi, valueToMidi } from '@strudel.cycles/core'; import { getAudioContext } from './index.mjs'; const bufferCache = {}; // string: Promise @@ -20,9 +20,12 @@ function humanFileSize(bytes, si) { return bytes.toFixed(1) + ' ' + units[u]; } -export const getSampleBufferSource = async (s, n, note, speed) => { +export const getSampleBufferSource = async (s, n, note, speed, freq) => { let transpose = 0; - let midi = typeof note === 'string' ? toMidi(note) : note || 36; + if (freq !== undefined && note !== undefined) { + logger('[sampler] hap has note and freq. ignoring note', 'warning'); + } + let midi = valueToMidi({ freq, n, note }, 36); transpose = midi - 36; // C3 is middle C const ac = getAudioContext(); diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 532957fa..4cac9083 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -288,10 +288,10 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => { if (soundfont) { // is soundfont - bufferSource = await globalThis.getFontBufferSource(soundfont, note || n, ac); + bufferSource = await globalThis.getFontBufferSource(soundfont, note || n, ac, freq); } else { // is sample from loaded samples(..) - bufferSource = await getSampleBufferSource(s, n, note, speed); + bufferSource = await getSampleBufferSource(s, n, note, speed, freq); } // asny stuff above took too long? if (ac.currentTime > t) { diff --git a/repl/src/prebake.mjs b/repl/src/prebake.mjs index 97443dcf..a2e97062 100644 --- a/repl/src/prebake.mjs +++ b/repl/src/prebake.mjs @@ -1,4 +1,4 @@ -import { Pattern, toMidi } from '@strudel.cycles/core'; +import { Pattern, toMidi, valueToMidi } from '@strudel.cycles/core'; import { samples } from '@strudel.cycles/webaudio'; export async function prebake({ isMock = false, baseDir = '.' } = {}) { @@ -25,9 +25,9 @@ Pattern.prototype.piano = function () { .s('piano') .release(0.1) .fmap((value) => { - const midi = typeof value.note === 'string' ? toMidi(value.note) : value.note; + const midi = valueToMidi(value); // pan by pitch - const pan = panwidth(Math.min(midi / maxPan, 1), 0.5); + const pan = panwidth(Math.min(Math.round(midi) / maxPan, 1), 0.5); return { ...value, pan: (value.pan || 1) * pan }; }); }; From 09b06da403b576e62e13ed14a8193e4db226fd6e Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 12 Dec 2022 20:45:15 +0100 Subject: [PATCH 2/3] fix error message --- packages/core/util.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/util.mjs b/packages/core/util.mjs index d6eb9aec..6239945f 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -37,7 +37,7 @@ export const freqToMidi = (freq) => { export const valueToMidi = (value, fallbackValue) => { if (typeof value !== 'object') { - throw new Error('Hap.getMidi: expected object value'); + throw new Error('valueToMidi: expected object value'); } let { freq, note, n } = value; note = note ?? n; From c6d57f6a56a82ccf944ee20fb6d1e22f66b4194b Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 12 Dec 2022 20:53:11 +0100 Subject: [PATCH 3/3] fix non note samples --- packages/core/util.mjs | 5 ++--- packages/webaudio/sampler.mjs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/core/util.mjs b/packages/core/util.mjs index 6239945f..d8a7a233 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -39,8 +39,7 @@ export const valueToMidi = (value, fallbackValue) => { if (typeof value !== 'object') { throw new Error('valueToMidi: expected object value'); } - let { freq, note, n } = value; - note = note ?? n; + let { freq, note } = value; if (typeof freq === 'number') { return freqToMidi(freq); } @@ -51,7 +50,7 @@ export const valueToMidi = (value, fallbackValue) => { return note; } if (!fallbackValue) { - throw new Error('Hap.getMidi: expected freq or n / note to be set'); + throw new Error('valueToMidi: expected freq or note to be set'); } return fallbackValue; }; diff --git a/packages/webaudio/sampler.mjs b/packages/webaudio/sampler.mjs index 82c5aad0..7b437ae3 100644 --- a/packages/webaudio/sampler.mjs +++ b/packages/webaudio/sampler.mjs @@ -25,7 +25,7 @@ export const getSampleBufferSource = async (s, n, note, speed, freq) => { if (freq !== undefined && note !== undefined) { logger('[sampler] hap has note and freq. ignoring note', 'warning'); } - let midi = valueToMidi({ freq, n, note }, 36); + let midi = valueToMidi({ freq, note }, 36); transpose = midi - 36; // C3 is middle C const ac = getAudioContext();