Polish title screen presentation
This commit is contained in:
120
src/game/audio/SoundDirector.ts
Normal file
120
src/game/audio/SoundDirector.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
class SoundDirector {
|
||||
private context?: AudioContext;
|
||||
private master?: GainNode;
|
||||
private musicGain?: GainNode;
|
||||
private started = false;
|
||||
private sequenceTimer?: number;
|
||||
|
||||
start() {
|
||||
if (this.started) {
|
||||
return;
|
||||
}
|
||||
|
||||
const AudioContextCtor = window.AudioContext ?? window.webkitAudioContext;
|
||||
if (!AudioContextCtor) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.context = new AudioContextCtor();
|
||||
this.master = this.context.createGain();
|
||||
this.master.gain.value = 0.34;
|
||||
this.master.connect(this.context.destination);
|
||||
|
||||
this.musicGain = this.context.createGain();
|
||||
this.musicGain.gain.value = 0.0;
|
||||
this.musicGain.connect(this.master);
|
||||
this.started = true;
|
||||
|
||||
this.createAmbientBed();
|
||||
this.schedulePlucks();
|
||||
this.musicGain.gain.linearRampToValueAtTime(0.72, this.context.currentTime + 2.4);
|
||||
}
|
||||
|
||||
resume() {
|
||||
void this.context?.resume();
|
||||
}
|
||||
|
||||
playHover() {
|
||||
this.playTone(660, 0.035, 0.04, 'sine');
|
||||
}
|
||||
|
||||
playSelect() {
|
||||
this.playTone(330, 0.08, 0.12, 'triangle');
|
||||
window.setTimeout(() => this.playTone(495, 0.1, 0.08, 'triangle'), 65);
|
||||
}
|
||||
|
||||
private createAmbientBed() {
|
||||
if (!this.context || !this.musicGain) {
|
||||
return;
|
||||
}
|
||||
|
||||
const base = this.context.createOscillator();
|
||||
const fifth = this.context.createOscillator();
|
||||
const filter = this.context.createBiquadFilter();
|
||||
const bedGain = this.context.createGain();
|
||||
|
||||
base.type = 'sine';
|
||||
fifth.type = 'sine';
|
||||
base.frequency.value = 110;
|
||||
fifth.frequency.value = 165;
|
||||
filter.type = 'lowpass';
|
||||
filter.frequency.value = 520;
|
||||
bedGain.gain.value = 0.08;
|
||||
|
||||
base.connect(filter);
|
||||
fifth.connect(filter);
|
||||
filter.connect(bedGain);
|
||||
bedGain.connect(this.musicGain);
|
||||
base.start();
|
||||
fifth.start();
|
||||
}
|
||||
|
||||
private schedulePlucks() {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
|
||||
const notes = [220, 247, 294, 330, 392, 330, 294, 247];
|
||||
let index = 0;
|
||||
this.sequenceTimer = window.setInterval(() => {
|
||||
this.playTone(notes[index % notes.length], 0.18, 0.09, 'triangle', true);
|
||||
index += 1;
|
||||
}, 1800);
|
||||
}
|
||||
|
||||
private playTone(
|
||||
frequency: number,
|
||||
duration: number,
|
||||
gainValue: number,
|
||||
type: OscillatorType,
|
||||
routeToMusic = false
|
||||
) {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
|
||||
const oscillator = this.context.createOscillator();
|
||||
const gain = this.context.createGain();
|
||||
const now = this.context.currentTime;
|
||||
const destination = routeToMusic && this.musicGain ? this.musicGain : this.master;
|
||||
|
||||
oscillator.type = type;
|
||||
oscillator.frequency.value = frequency;
|
||||
gain.gain.setValueAtTime(0, now);
|
||||
gain.gain.linearRampToValueAtTime(gainValue, now + 0.012);
|
||||
gain.gain.exponentialRampToValueAtTime(0.0001, now + duration);
|
||||
|
||||
oscillator.connect(gain);
|
||||
gain.connect(destination ?? this.context.destination);
|
||||
oscillator.start(now);
|
||||
oscillator.stop(now + duration + 0.04);
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
webkitAudioContext?: typeof AudioContext;
|
||||
}
|
||||
}
|
||||
|
||||
export const soundDirector = new SoundDirector();
|
||||
Reference in New Issue
Block a user