Merge pull request #301 from tidalcycles/support-freq-in-sampler

add freq support to sampler
This commit is contained in:
Felix Roos 2022-12-13 21:21:43 +01:00 committed by GitHub
commit f437d976f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 42 additions and 8 deletions

View File

@ -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', () => {

View File

@ -31,6 +31,30 @@ 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('valueToMidi: expected object value');
}
let { freq, note } = value;
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('valueToMidi: expected freq or note to be set');
}
return fallbackValue;
};
/**
* @deprecated does not appear to be referenced or invoked anywhere in the codebase
*/

View File

@ -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<ArrayBuffer>
@ -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, note }, 36);
transpose = midi - 36; // C3 is middle C
const ac = getAudioContext();

View File

@ -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) {

View File

@ -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 };
});
};