This commit is contained in:
Jade (Rose) Rowland
2026-02-08 20:32:59 -05:00
parent feab3f5c86
commit 4f05674cb3
3 changed files with 41 additions and 49 deletions
+9 -19
View File
@@ -1,4 +1,4 @@
import { getBaseURL, getCommonSampleInfo, noteToFreq } from './util.mjs';
import { BASE_MIDI_NOTE, getBaseURL, getCommonSampleInfo, noteToFreq, noteToMidi } from './util.mjs';
import { registerSound, registerWaveTable } from './index.mjs';
import { getAudioContext } from './audioContext.mjs';
import {
@@ -34,10 +34,8 @@ function humanFileSize(bytes, si) {
function getSampleInfo(hapValue, bank) {
const { speed = 1.0 } = hapValue;
const { transpose, url, index, midi, label, baseFrequency } = getCommonSampleInfo(hapValue, bank);
const relativeBaseFreq = noteToFreq('C');
const frequencyAdjustment = relativeBaseFreq / baseFrequency;
const playbackRate = (Math.abs(speed) * Math.pow(2, transpose / 12)) * frequencyAdjustment;
const playbackRate = Math.abs(speed) * Math.pow(2, transpose / 12);
return { transpose, url, index, midi, label, playbackRate };
}
@@ -160,7 +158,7 @@ export const processSampleMap = (sampleMap, fn, baseUrl = sampleMap._base || '')
if (baseUrl.startsWith('github:')) {
baseUrl = githubPath(baseUrl, '');
}
const fullUrl = (v) => baseUrl + v;
const fullUrl = (v) => ({ url: baseUrl + v, midi: extractMidiNoteFromString(v) });
if (Array.isArray(value)) {
//return [key, value.map(replaceUrl)];
value = value.map(fullUrl);
@@ -364,7 +362,6 @@ function registerSample(key, bank, params) {
}
export function registerSampleSource(key, bank, params) {
const isWavetable = key.startsWith('wt_');
if (isWavetable) {
registerWaveTable(key, bank, params);
@@ -373,22 +370,15 @@ export function registerSampleSource(key, bank, params) {
}
}
function extractNoteFromString(str) {
export function extractMidiNoteFromString(str) {
const regex = /_([a-gA-G])([#b])?([1-9])?\b/;
const match = str.match(regex);
if (match == null) {
return { note: "C", base: "C", accidental: undefined, octave: 3 }
return BASE_MIDI_NOTE;
}
const base = match[1].toUpperCase();
const accidental = match[2] ?? ""
const octave_str = match[3] ?? "";
const octave = Number(octave_str)
return { note: base + accidental + octave_str, base, accidental, octave }
const accidental = match[2] ?? '';
const octave_str = match[3] ?? '';
const parsedVal = base + accidental + octave_str;
return noteToMidi(parsedVal);
}
export function extractBaseFrequencyFromString(str) {
const { note } = extractNoteFromString(str);
return noteToFreq(note)
}
+28 -26
View File
@@ -3,10 +3,10 @@ import { logger } from './logger.mjs';
// currently duplicate with core util.mjs to skip dependency
// TODO: add separate util module?
/**
*
* @typedef {Object} SampleMetaData
* @property {string} url
* @property {baseFrequency} number
*
* @typedef {Object} SampleMetaData
* @property {string} url
* @property {baseFrequency} number
*/
export const tokenizeNote = (note) => {
if (typeof note !== 'string') {
@@ -25,9 +25,9 @@ export const getAccidentalsOffset = (accidentals) => {
return accidentals?.split('').reduce((o, char) => o + accs[char], 0) || 0;
};
/**
*
* @param {string} note
* @param {number} defaultOctave
*
* @param {string} note
* @param {number} defaultOctave
* @returns {number}
*/
export const noteToMidi = (note, defaultOctave = 3) => {
@@ -40,8 +40,8 @@ export const noteToMidi = (note, defaultOctave = 3) => {
return (Number(oct) + 1) * 12 + chroma + offset;
};
/**
*
* @param {number} n
*
* @param {number} n
* @returns {number}
*/
export const midiToFreq = (n) => {
@@ -49,24 +49,22 @@ export const midiToFreq = (n) => {
};
export const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
export const freqToMidi = (freq) => {
return (12 * Math.log(freq / 440)) / Math.LN2 + 69;
};
/**
*
* @param {string} note
* @param {number} defaultOctave
*
* @param {string} note
* @param {number} defaultOctave
* @returns {number}
*/
export const noteToFreq = (note, defaultOctave = 3) => {
return midiToFreq(noteToMidi(note, defaultOctave))
}
return midiToFreq(noteToMidi(note, defaultOctave));
};
function __valueToMidi(value) {
if (typeof value !== 'object') {
if (typeof value !== 'object') {
throw new Error('valueToMidi: expected object value');
}
let { freq, note } = value;
@@ -79,7 +77,7 @@ function __valueToMidi(value) {
if (typeof note === 'number') {
return note;
}
return
return;
}
export const valueToMidi = (value, fallbackValue) => {
@@ -87,7 +85,7 @@ export const valueToMidi = (value, fallbackValue) => {
if (parsedValue == null) {
throw new Error('valueToMidi: expected freq or note to be set');
}
return parsedValue
return parsedValue;
};
export function nanFallback(value, fallback = 0, silent) {
@@ -116,15 +114,18 @@ export function secondsToCycle(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 const BASE_MIDI_NOTE = 36;
export function getCommonSampleInfo(hapValue, bank) {
const { s, n = 0 } = hapValue;
let midi = valueToMidi(hapValue, 36);
let transpose = midi - 36; // C3 is middle C;
const maybeMidiNote = __valueToMidi(hapValue);
const midi = maybeMidiNote ?? BASE_MIDI_NOTE;
let transpose = midi - BASE_MIDI_NOTE; // C3 is middle C;
let index = 0;
let samplemeta;
if (Array.isArray(bank)) {
index = getSoundIndex(n, bank.length);
samplemeta = bank[index]
samplemeta = bank[index];
} else {
const midiDiff = (noteA) => noteToMidi(noteA) - midi;
// object format will expect keys as notes
@@ -136,13 +137,14 @@ export function getCommonSampleInfo(hapValue, bank) {
);
transpose = -midiDiff(closest); // semitones to repitch
index = getSoundIndex(n, bank[closest].length);
samplemeta = bank[closest][index];
samplemeta = bank[closest][index];
}
const label = `${s}:${index}`;
if (maybeMidiNote != null) {
transpose = transpose + (BASE_MIDI_NOTE - samplemeta.midi);
}
return { transpose, index, midi, label, ...samplemeta };
return { transpose, index, midi, label, url: samplemeta.url };
}
/** Selects entries from `source` and renames them via `map`
+4 -4
View File
@@ -1,4 +1,4 @@
import { extractBaseFrequencyFromString, registerSampleSource } from '@strudel/webaudio';
import { extractMidiNoteFromString, registerSampleSource } from '@strudel/webaudio';
import { isAudioFile } from './files.mjs';
import { logger } from '@strudel/core';
@@ -28,7 +28,7 @@ export function clearIDB(dbName) {
}
// queries the DB, and registers the sounds so they can be played
export function registerSamplesFromDB(config = userSamplesDBConfig, onComplete = () => { }) {
export function registerSamplesFromDB(config = userSamplesDBConfig, onComplete = () => {}) {
openDB(config, (objectStore) => {
const query = objectStore.getAll();
query.onerror = (e) => {
@@ -60,8 +60,8 @@ export function registerSamplesFromDB(config = userSamplesDBConfig, onComplete =
return blobToDataUrl(blob).then((path) => {
const sampleInfoMap = sounds.get(parentDirectory) ?? new Map();
const baseFrequency = extractBaseFrequencyFromString(title)
sampleInfoMap.set(title, { url: path, baseFrequency });
const midi = extractMidiNoteFromString(title);
sampleInfoMap.set(title, { url: path, midi });
sounds.set(parentDirectory, sampleInfoMap);
return;