supports old documented way of loading wavetables:
This commit is contained in:
parent
a9957b45e5
commit
626a99ba5b
3 changed files with 40 additions and 42 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
import { noteToMidi, valueToMidi, getSoundIndex } from './util.mjs';
|
import { noteToMidi, valueToMidi, getSoundIndex, getCommonSampleInfo } from './util.mjs';
|
||||||
import { getAudioContext, registerSound, registerWaveTable } from './index.mjs';
|
import { getAudioContext, registerSound, registerWaveTable } from './index.mjs';
|
||||||
import { getADSRValues, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs';
|
import { getADSRValues, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs';
|
||||||
import { logger } from './logger.mjs';
|
import { logger } from './logger.mjs';
|
||||||
|
|
@ -22,39 +22,16 @@ function humanFileSize(bytes, si) {
|
||||||
return bytes.toFixed(1) + ' ' + units[u];
|
return bytes.toFixed(1) + ' ' + units[u];
|
||||||
}
|
}
|
||||||
|
|
||||||
// deduces relevant info for sample loading from hap.value and sample definition
|
|
||||||
// it encapsulates the core sampler logic into a pure and synchronous function
|
|
||||||
// hapValue: Hap.value, bank: sample bank definition for sound "s" (values in strudel.json format)
|
|
||||||
export function getSampleInfo(hapValue, bank) {
|
export function getSampleInfo(hapValue, bank) {
|
||||||
const { s, n = 0, speed = 1.0 } = hapValue;
|
const { speed = 1.0 } = hapValue;
|
||||||
let midi = valueToMidi(hapValue, 36);
|
const {transpose, url, index, midi, label} = getCommonSampleInfo(hapValue, bank)
|
||||||
let transpose = midi - 36; // C3 is middle C;
|
|
||||||
let sampleUrl;
|
|
||||||
let index = 0;
|
|
||||||
if (Array.isArray(bank)) {
|
|
||||||
index = getSoundIndex(n, bank.length);
|
|
||||||
sampleUrl = bank[index];
|
|
||||||
} else {
|
|
||||||
const midiDiff = (noteA) => noteToMidi(noteA) - midi;
|
|
||||||
// object format will expect keys as notes
|
|
||||||
const closest = Object.keys(bank)
|
|
||||||
.filter((k) => !k.startsWith('_'))
|
|
||||||
.reduce(
|
|
||||||
(closest, key, j) => (!closest || Math.abs(midiDiff(key)) < Math.abs(midiDiff(closest)) ? key : closest),
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
transpose = -midiDiff(closest); // semitones to repitch
|
|
||||||
index = getSoundIndex(n, bank[closest].length);
|
|
||||||
sampleUrl = bank[closest][index];
|
|
||||||
}
|
|
||||||
const label = `${s}:${index}`;
|
|
||||||
let playbackRate = Math.abs(speed) * Math.pow(2, transpose / 12);
|
let playbackRate = Math.abs(speed) * Math.pow(2, transpose / 12);
|
||||||
return { transpose, sampleUrl, index, midi, label, playbackRate };
|
return { transpose, url, index, midi, label, playbackRate };
|
||||||
}
|
}
|
||||||
|
|
||||||
// takes hapValue and returns buffer + playbackRate.
|
// takes hapValue and returns buffer + playbackRate.
|
||||||
export const getSampleBuffer = async (hapValue, bank, resolveUrl) => {
|
export const getSampleBuffer = async (hapValue, bank, resolveUrl) => {
|
||||||
let { sampleUrl, label, playbackRate } = getSampleInfo(hapValue, bank);
|
let { url: sampleUrl, label, playbackRate } = getSampleInfo(hapValue, bank);
|
||||||
if (resolveUrl) {
|
if (resolveUrl) {
|
||||||
sampleUrl = await resolveUrl(sampleUrl);
|
sampleUrl = await resolveUrl(sampleUrl);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,4 +76,31 @@ export function cycleToSeconds(cycle, cps) {
|
||||||
export function secondsToCycle(t, cps) {
|
export function secondsToCycle(t, cps) {
|
||||||
return t * cps;
|
return t * cps;
|
||||||
}
|
}
|
||||||
|
// deduces relevant info for sample loading from hap.value and sample definition
|
||||||
|
// it encapsulates the core sampler logic into a pure and synchronous function
|
||||||
|
// hapValue: Hap.value, bank: sample bank definition for sound "s" (values in strudel.json format)
|
||||||
|
export function getCommonSampleInfo(hapValue, bank) {
|
||||||
|
const { s, n = 0 } = hapValue;
|
||||||
|
let midi = valueToMidi(hapValue, 36);
|
||||||
|
let transpose = midi - 36; // C3 is middle C;
|
||||||
|
let url;
|
||||||
|
let index = 0;
|
||||||
|
if (Array.isArray(bank)) {
|
||||||
|
index = getSoundIndex(n, bank.length);
|
||||||
|
url = bank[index];
|
||||||
|
} else {
|
||||||
|
const midiDiff = (noteA) => noteToMidi(noteA) - midi;
|
||||||
|
// object format will expect keys as notes
|
||||||
|
const closest = Object.keys(bank)
|
||||||
|
.filter((k) => !k.startsWith('_'))
|
||||||
|
.reduce(
|
||||||
|
(closest, key, j) => (!closest || Math.abs(midiDiff(key)) < Math.abs(midiDiff(closest)) ? key : closest),
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
transpose = -midiDiff(closest); // semitones to repitch
|
||||||
|
index = getSoundIndex(n, bank[closest].length);
|
||||||
|
url = bank[closest][index];
|
||||||
|
}
|
||||||
|
const label = `${s}:${index}`;
|
||||||
|
return { transpose, url, index, midi, label };
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { getAudioContext, registerSound } from './index.mjs';
|
import { getAudioContext, registerSound } from './index.mjs';
|
||||||
import { getSoundIndex, valueToMidi } from './util.mjs';
|
import { getCommonSampleInfo, getSoundIndex, valueToMidi } from './util.mjs';
|
||||||
import {
|
import {
|
||||||
destroyAudioWorkletNode,
|
destroyAudioWorkletNode,
|
||||||
getADSRValues,
|
getADSRValues,
|
||||||
|
|
@ -38,12 +38,12 @@ export const WarpMode = Object.freeze({
|
||||||
FLIP: 21,
|
FLIP: 21,
|
||||||
});
|
});
|
||||||
|
|
||||||
async function loadWavetableFrames(url, label, frameLen = 256) {
|
async function loadWavetableFrames(url, label, frameLen = 2048) {
|
||||||
const ac = getAudioContext();
|
const ac = getAudioContext();
|
||||||
const buf = await loadBuffer(url, ac, label);
|
const buf = await loadBuffer(url, ac, label);
|
||||||
const ch0 = buf.getChannelData(0);
|
const ch0 = buf.getChannelData(0);
|
||||||
const total = ch0.length;
|
const total = ch0.length;
|
||||||
const numFrames = Math.floor(total / frameLen);
|
const numFrames = Math.max(1,Math.floor(total / frameLen));
|
||||||
const frames = new Array(numFrames);
|
const frames = new Array(numFrames);
|
||||||
for (let i = 0; i < numFrames; i++) {
|
for (let i = 0; i < numFrames; i++) {
|
||||||
const start = i * frameLen;
|
const start = i * frameLen;
|
||||||
|
|
@ -86,14 +86,8 @@ function humanFileSize(bytes, si) {
|
||||||
return bytes.toFixed(1) + ' ' + units[u];
|
return bytes.toFixed(1) + ' ' + units[u];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTableInfo(hapValue, tableUrls) {
|
export function getTableInfo(hapValue, urls) {
|
||||||
const { s, n = 0 } = hapValue;
|
return getCommonSampleInfo(hapValue,urls)
|
||||||
let midi = valueToMidi(hapValue, 36);
|
|
||||||
let transpose = midi - 36; // C3 is middle C;
|
|
||||||
const index = getSoundIndex(n, tableUrls.length);
|
|
||||||
const tableUrl = tableUrls[index];
|
|
||||||
const label = `${s}:${index}`;
|
|
||||||
return { transpose, tableUrl, index, midi, label };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadBuffer = (url, ac, label) => {
|
const loadBuffer = (url, ac, label) => {
|
||||||
|
|
@ -191,8 +185,8 @@ export async function onTriggerSynth(t, value, onended, bank, frameLen) {
|
||||||
wtWarpMode = WarpMode[wtWarpMode.toUpperCase()] ?? WarpMode.NONE;
|
wtWarpMode = WarpMode[wtWarpMode.toUpperCase()] ?? WarpMode.NONE;
|
||||||
}
|
}
|
||||||
const frequency = getFrequencyFromValue(value);
|
const frequency = getFrequencyFromValue(value);
|
||||||
const { tableUrl, label } = getTableInfo(value, bank);
|
const { url, label } = getTableInfo(value, bank);
|
||||||
const payload = await loadWavetableFrames(tableUrl, label, frameLen);
|
const payload = await loadWavetableFrames(url, label, frameLen);
|
||||||
const holdEnd = t + duration;
|
const holdEnd = t + duration;
|
||||||
const envEnd = holdEnd + release + 0.01;
|
const envEnd = holdEnd + release + 0.01;
|
||||||
const source = getWorklet(
|
const source = getWorklet(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue