57 lines
1.8 KiB
TypeScript
57 lines
1.8 KiB
TypeScript
import { AudioDocument } from "@suaveplan/audio-core";
|
|
|
|
/** Build a synthetic metronome audio document. */
|
|
export function makeMetronome(opts: {
|
|
bpm: number;
|
|
durationSeconds: number;
|
|
sampleRate?: number;
|
|
clickSamples?: number;
|
|
clickFreq?: number;
|
|
}): AudioDocument {
|
|
const sampleRate = opts.sampleRate ?? 8000;
|
|
const total = Math.floor(opts.durationSeconds * sampleRate);
|
|
const data = new Float32Array(total);
|
|
const beatPeriodSamples = Math.floor((60 / opts.bpm) * sampleRate);
|
|
const clickSamples = opts.clickSamples ?? 200;
|
|
const clickFreq = opts.clickFreq ?? 1000;
|
|
for (let beat = 0; beat * beatPeriodSamples < total; beat++) {
|
|
const start = beat * beatPeriodSamples;
|
|
for (let i = 0; i < clickSamples && start + i < total; i++) {
|
|
const env = Math.exp(-i / (clickSamples / 4));
|
|
data[start + i] =
|
|
env * Math.sin((2 * Math.PI * clickFreq * i) / sampleRate);
|
|
}
|
|
}
|
|
return new AudioDocument({
|
|
sampleRate,
|
|
channels: 1,
|
|
sourceLength: total,
|
|
pages: [{ channelData: [data], startSample: 0 }],
|
|
});
|
|
}
|
|
|
|
/** Build a silent audio document. */
|
|
export function makeSilence(opts: {
|
|
durationSeconds: number;
|
|
sampleRate?: number;
|
|
}): AudioDocument {
|
|
const sampleRate = opts.sampleRate ?? 8000;
|
|
const total = Math.floor(opts.durationSeconds * sampleRate);
|
|
return new AudioDocument({
|
|
sampleRate,
|
|
channels: 1,
|
|
sourceLength: total,
|
|
pages: [{ channelData: [new Float32Array(total)], startSample: 0 }],
|
|
});
|
|
}
|
|
|
|
/** Empty document. */
|
|
export function makeEmpty(): AudioDocument {
|
|
return new AudioDocument({
|
|
sampleRate: 8000,
|
|
channels: 1,
|
|
sourceLength: 0,
|
|
pages: [],
|
|
});
|
|
}
|