,
This commit is contained in:
parent
70423851ba
commit
1fe5015056
55 changed files with 2036 additions and 685 deletions
|
|
@ -1,8 +1,16 @@
|
|||
interface PianoSampleDefinition {
|
||||
import type { LoadedPianoSample } from './garden-audio-types';
|
||||
|
||||
export interface PianoSampleDefinition {
|
||||
midi: number;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface PianoSampleLoadProgress {
|
||||
loadedCount: number;
|
||||
totalCount: number;
|
||||
sample?: PianoSampleDefinition;
|
||||
}
|
||||
|
||||
const sampleBaseUrl = `${import.meta.env.BASE_URL}audio/piano/`;
|
||||
|
||||
const sampleFiles: Array<[fileName: string, midi: number]> = [
|
||||
|
|
@ -44,3 +52,94 @@ export const pianoSampleDefinitions: Array<PianoSampleDefinition> = sampleFiles
|
|||
url: `${sampleBaseUrl}${fileName}`,
|
||||
}))
|
||||
.sort((a, b) => a.midi - b.midi);
|
||||
|
||||
let loadedPianoSamples: Array<LoadedPianoSample> | null = null;
|
||||
let pianoSampleLoadPromise: Promise<Array<LoadedPianoSample>> | null = null;
|
||||
|
||||
export const preloadPianoSamples = (
|
||||
onProgress?: (progress: PianoSampleLoadProgress) => void
|
||||
): Promise<Array<LoadedPianoSample>> => {
|
||||
const OfflineAudioContextConstructor =
|
||||
globalThis.OfflineAudioContext ?? globalThis.webkitOfflineAudioContext;
|
||||
|
||||
if (!OfflineAudioContextConstructor) {
|
||||
return Promise.reject(
|
||||
new Error('OfflineAudioContext is required to preload piano samples.')
|
||||
);
|
||||
}
|
||||
|
||||
const decodeContext = new OfflineAudioContextConstructor(1, 1, 44_100);
|
||||
return loadPianoSamples(decodeContext, onProgress);
|
||||
};
|
||||
|
||||
export const loadPianoSamples = (
|
||||
decodeContext: BaseAudioContext,
|
||||
onProgress?: (progress: PianoSampleLoadProgress) => void
|
||||
): Promise<Array<LoadedPianoSample>> => {
|
||||
if (loadedPianoSamples) {
|
||||
onProgress?.({
|
||||
loadedCount: loadedPianoSamples.length,
|
||||
totalCount: pianoSampleDefinitions.length,
|
||||
});
|
||||
return Promise.resolve([...loadedPianoSamples]);
|
||||
}
|
||||
|
||||
if (pianoSampleLoadPromise) {
|
||||
return pianoSampleLoadPromise;
|
||||
}
|
||||
|
||||
let loadedCount = 0;
|
||||
const totalCount = pianoSampleDefinitions.length;
|
||||
onProgress?.({ loadedCount, totalCount });
|
||||
|
||||
pianoSampleLoadPromise = Promise.all(
|
||||
pianoSampleDefinitions.map(async (sample) => {
|
||||
const loadedSample = await loadPianoSample(decodeContext, sample);
|
||||
loadedCount += 1;
|
||||
onProgress?.({ loadedCount, totalCount, sample });
|
||||
return loadedSample;
|
||||
})
|
||||
).then(
|
||||
(samples) => {
|
||||
loadedPianoSamples = samples.slice().sort((a, b) => a.midi - b.midi);
|
||||
return [...loadedPianoSamples];
|
||||
},
|
||||
(error: unknown) => {
|
||||
pianoSampleLoadPromise = null;
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
|
||||
return pianoSampleLoadPromise;
|
||||
};
|
||||
|
||||
export const getLoadedPianoSamples = (): Array<LoadedPianoSample> | null =>
|
||||
loadedPianoSamples ? [...loadedPianoSamples] : null;
|
||||
|
||||
export const resetPianoSampleCacheForTest = (): void => {
|
||||
loadedPianoSamples = null;
|
||||
pianoSampleLoadPromise = null;
|
||||
};
|
||||
|
||||
const loadPianoSample = async (
|
||||
decodeContext: BaseAudioContext,
|
||||
sample: PianoSampleDefinition
|
||||
): Promise<LoadedPianoSample> => {
|
||||
const response = await fetch(sample.url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Unable to load piano sample ${sample.url}`);
|
||||
}
|
||||
|
||||
const audioData = await response.arrayBuffer();
|
||||
const buffer = await decodeAudioData(decodeContext, audioData);
|
||||
return { midi: sample.midi, buffer };
|
||||
};
|
||||
|
||||
const decodeAudioData = (
|
||||
decodeContext: BaseAudioContext,
|
||||
audioData: ArrayBuffer
|
||||
): Promise<AudioBuffer> =>
|
||||
new Promise((resolve, reject) => {
|
||||
const decodePromise = decodeContext.decodeAudioData(audioData, resolve, reject);
|
||||
decodePromise?.then(resolve, reject);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue