mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-22 11:08:35 +00:00
Merge pull request #301 from tidalcycles/support-freq-in-sampler
add freq support to sampler
This commit is contained in:
commit
f437d976f8
@ -10,6 +10,7 @@ import {
|
|||||||
tokenizeNote,
|
tokenizeNote,
|
||||||
toMidi,
|
toMidi,
|
||||||
fromMidi,
|
fromMidi,
|
||||||
|
freqToMidi,
|
||||||
_mod,
|
_mod,
|
||||||
compose,
|
compose,
|
||||||
getFrequency,
|
getFrequency,
|
||||||
@ -94,6 +95,12 @@ describe('fromMidi', () => {
|
|||||||
expect(fromMidi(57)).toEqual(220);
|
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', () => {
|
describe('getFrequency', () => {
|
||||||
const happify = (val, context = {}) => pure(val).firstCycle()[0].setContext(context);
|
const happify = (val, context = {}) => pure(val).firstCycle()[0].setContext(context);
|
||||||
it('should turn note into frequency', () => {
|
it('should turn note into frequency', () => {
|
||||||
|
|||||||
@ -31,6 +31,30 @@ export const fromMidi = (n) => {
|
|||||||
return Math.pow(2, (n - 69) / 12) * 440;
|
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
|
* @deprecated does not appear to be referenced or invoked anywhere in the codebase
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { logger, toMidi } from '@strudel.cycles/core';
|
import { logger, toMidi, valueToMidi } from '@strudel.cycles/core';
|
||||||
import { getAudioContext } from './index.mjs';
|
import { getAudioContext } from './index.mjs';
|
||||||
|
|
||||||
const bufferCache = {}; // string: Promise<ArrayBuffer>
|
const bufferCache = {}; // string: Promise<ArrayBuffer>
|
||||||
@ -20,9 +20,12 @@ function humanFileSize(bytes, si) {
|
|||||||
return bytes.toFixed(1) + ' ' + units[u];
|
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 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
|
transpose = midi - 36; // C3 is middle C
|
||||||
|
|
||||||
const ac = getAudioContext();
|
const ac = getAudioContext();
|
||||||
|
|||||||
@ -288,10 +288,10 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => {
|
|||||||
|
|
||||||
if (soundfont) {
|
if (soundfont) {
|
||||||
// is soundfont
|
// is soundfont
|
||||||
bufferSource = await globalThis.getFontBufferSource(soundfont, note || n, ac);
|
bufferSource = await globalThis.getFontBufferSource(soundfont, note || n, ac, freq);
|
||||||
} else {
|
} else {
|
||||||
// is sample from loaded samples(..)
|
// 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?
|
// asny stuff above took too long?
|
||||||
if (ac.currentTime > t) {
|
if (ac.currentTime > t) {
|
||||||
|
|||||||
@ -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';
|
import { samples } from '@strudel.cycles/webaudio';
|
||||||
|
|
||||||
export async function prebake({ isMock = false, baseDir = '.' } = {}) {
|
export async function prebake({ isMock = false, baseDir = '.' } = {}) {
|
||||||
@ -25,9 +25,9 @@ Pattern.prototype.piano = function () {
|
|||||||
.s('piano')
|
.s('piano')
|
||||||
.release(0.1)
|
.release(0.1)
|
||||||
.fmap((value) => {
|
.fmap((value) => {
|
||||||
const midi = typeof value.note === 'string' ? toMidi(value.note) : value.note;
|
const midi = valueToMidi(value);
|
||||||
// pan by pitch
|
// 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 };
|
return { ...value, pan: (value.pan || 1) * pan };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user