Skip to content

Commit

Permalink
use AudioContext, make adding other types of sounds (for checks, capt…
Browse files Browse the repository at this point in the history
…ures, etc) easy
  • Loading branch information
0Xero7 committed Nov 27, 2024
1 parent 49adad4 commit 5ccd408
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 6 deletions.
4 changes: 2 additions & 2 deletions frontend/app.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AudioPlayer } from './audio';
import {AudioPlayer, AudioEventType} from './audio';
import {BoardArea, SideBoardVisualization} from './board_area';
import {GameSelection} from './game_selection';
import {MoveList} from './movelist';
Expand Down Expand Up @@ -126,7 +126,7 @@ export class App implements WebsocketObserver {

// Only play move sound when the next move is 1 ply ahead of the current position
if (position.ply === currentPly + 1) {
this.audioPlayer.playMoveAudio();
this.audioPlayer.playAudio(AudioEventType.MOVE);
}
}
this.boardArea.updatePosition(position);
Expand Down
58 changes: 54 additions & 4 deletions frontend/audio.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,63 @@
export enum AudioEventType {
MOVE,
}

class DebouncedAudioPlayer {
private audioContext : AudioContext;
private audio: AudioBuffer;
private cooldown: number;
private lastPlayed: number;

constructor(context: AudioContext, audio: AudioBuffer, cooldown: number = 0) {
this.audioContext = context;
this.audio = audio;
this.cooldown = cooldown;
this.lastPlayed = 0;
}

public play() {
const now = Date.now();
if (now - this.lastPlayed < this.cooldown) {
return;
}
this.lastPlayed = now;

this.audioContext.resume();
if (this.audioContext.state !== "running") {
return;
}

const source = this.audioContext.createBufferSource();
source.buffer = this.audio;
source.connect(this.audioContext.destination);
source.start(0);
}
}

export class AudioPlayer {
private move : HTMLAudioElement;
private sounds: Map<AudioEventType, DebouncedAudioPlayer>;

constructor() {
this.move = new Audio('static/public_sound_standard_Move.mp3');
this.sounds = new Map();
const context = new AudioContext();
this.addSound(context, AudioEventType.MOVE, 'static/public_sound_standard_Move.mp3');
}

private async addSound(context: AudioContext, eventType: AudioEventType, path: string): Promise<void> {
const moveSound = await this.loadSound(context, path);
this.sounds.set(eventType, new DebouncedAudioPlayer(context, moveSound, 50));
}

private async loadSound(context: AudioContext, url: string): Promise<AudioBuffer> {
const response = await fetch(url);
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await context.decodeAudioData(arrayBuffer);
return audioBuffer;
}

public playMoveAudio(): void {
public playAudio(eventType: AudioEventType): void {
try {
this.move.play();
this.sounds.get(eventType)?.play();
} catch (e) {
// Need user interaction to play audio
}
Expand Down

0 comments on commit 5ccd408

Please sign in to comment.