,
This commit is contained in:
parent
70423851ba
commit
1fe5015056
55 changed files with 2036 additions and 685 deletions
|
|
@ -3,7 +3,7 @@ import { clamp, clamp01 } from '../utils/clamp';
|
|||
import { GardenAudioConfig } from './garden-audio-config';
|
||||
import { GardenAudioGraph } from './garden-audio-graph';
|
||||
import { ActivePianoVoice, LoadedPianoSample, PianoNote } from './garden-audio-types';
|
||||
import { pianoSampleDefinitions } from './piano-samples';
|
||||
import { getLoadedPianoSamples, loadPianoSamples } from './piano-samples';
|
||||
|
||||
export class PianoSampler {
|
||||
private sampleLoadPromise: Promise<void> | null = null;
|
||||
|
|
@ -16,28 +16,20 @@ export class PianoSampler {
|
|||
private readonly graph: GardenAudioGraph
|
||||
) {}
|
||||
|
||||
public async load(context: AudioContext): Promise<void> {
|
||||
public load(context: BaseAudioContext): Promise<void> {
|
||||
const loadedSamples = getLoadedPianoSamples();
|
||||
if (loadedSamples) {
|
||||
this.setSamples(loadedSamples);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (this.sampleLoadPromise) {
|
||||
return this.sampleLoadPromise;
|
||||
}
|
||||
|
||||
this.sampleLoadPromise = Promise.all(
|
||||
pianoSampleDefinitions.map(async (sample) => {
|
||||
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 context.decodeAudioData(audioData);
|
||||
return { midi: sample.midi, buffer };
|
||||
})
|
||||
)
|
||||
.then((samples) => {
|
||||
this.samples = samples.sort((a, b) => a.midi - b.midi);
|
||||
})
|
||||
.catch(() => {
|
||||
this.samples = [];
|
||||
});
|
||||
this.sampleLoadPromise = loadPianoSamples(context).then((samples) => {
|
||||
this.setSamples(samples);
|
||||
});
|
||||
|
||||
return this.sampleLoadPromise;
|
||||
}
|
||||
|
|
@ -89,13 +81,9 @@ export class PianoSampler {
|
|||
this.trimActiveVoices(scheduledStart);
|
||||
while (this.activeVoices.length >= this.config.piano.maxVoices) {
|
||||
const oldest = this.activeVoices.shift();
|
||||
oldest?.gain.gain.cancelScheduledValues(scheduledStart);
|
||||
oldest?.gain.gain.setTargetAtTime(
|
||||
this.engineConfig.piano.minGain,
|
||||
scheduledStart,
|
||||
this.engineConfig.piano.voiceStealFadeSeconds
|
||||
);
|
||||
oldest?.source.stop(scheduledStart + this.engineConfig.piano.voiceStealStopSeconds);
|
||||
if (oldest) {
|
||||
this.stopVoice(oldest, scheduledStart);
|
||||
}
|
||||
}
|
||||
|
||||
source.buffer = sample.buffer;
|
||||
|
|
@ -162,6 +150,21 @@ export class PianoSampler {
|
|||
);
|
||||
}
|
||||
|
||||
public stopAll(): void {
|
||||
const context = this.graph.context;
|
||||
if (!context) {
|
||||
this.activeVoices = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const now = context.currentTime;
|
||||
|
||||
this.activeVoices.forEach((voice) => {
|
||||
this.stopVoice(voice, now);
|
||||
});
|
||||
this.activeVoices = [];
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
this.sampleLoadPromise = null;
|
||||
this.samples = [];
|
||||
|
|
@ -181,4 +184,25 @@ export class PianoSampler {
|
|||
private trimActiveVoices(now: number): void {
|
||||
this.activeVoices = this.activeVoices.filter((voice) => voice.stopAt > now);
|
||||
}
|
||||
|
||||
private stopVoice(voice: ActivePianoVoice, now: number): void {
|
||||
const stopAt = now + this.engineConfig.piano.voiceStealStopSeconds;
|
||||
|
||||
voice.gain.gain.cancelScheduledValues(now);
|
||||
voice.gain.gain.setTargetAtTime(
|
||||
this.engineConfig.piano.minGain,
|
||||
now,
|
||||
this.engineConfig.piano.voiceStealFadeSeconds
|
||||
);
|
||||
voice.stopAt = stopAt;
|
||||
try {
|
||||
voice.source.stop(stopAt);
|
||||
} catch {
|
||||
// The voice may already have ended; either way it is no longer part of the mix.
|
||||
}
|
||||
}
|
||||
|
||||
private setSamples(samples: Array<LoadedPianoSample>): void {
|
||||
this.samples = samples.slice().sort((a, b) => a.midi - b.midi);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue