Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 55 additions & 10 deletions src/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,9 @@ export function calculate(state: Game, from: number | Color | undefined | null,
}

const audioCache: { [key: string]: HTMLAudioElement } = {};
const audioBufferCache: { [key: string]: Promise<AudioBuffer> } = {};
let audioContext: AudioContext | null = null;
type WindowWithWebkitAudioContext = Window & { webkitAudioContext?: typeof AudioContext };

const checkerSounds = [
'capture.mp3',
Expand All @@ -319,17 +322,60 @@ const checkerSounds = [
'promote.mp3',
];

checkerSounds.forEach(file => {
const audio = new Audio();
audio.preload = 'auto';
audio.src = file;
audioCache[file] = audio;
});
const getAudioElement = (source: string) => {
if (!audioCache[source]) {
const audio = new Audio();
audio.preload = 'auto';
audio.src = source;
audioCache[source] = audio;
}
return audioCache[source];
};

const getAudioContext = () => {
if (audioContext) return audioContext;
const AudioContextConstructor = window.AudioContext || (window as WindowWithWebkitAudioContext).webkitAudioContext;
if (!AudioContextConstructor) return null;
audioContext = new AudioContextConstructor();
return audioContext;
};

const getAudioSource = (source: string | HTMLAudioElement) => {
if (typeof source === "string") return source;
return source.currentSrc || source.src;
};

export const playAudio = (audio: HTMLAudioElement) => {
audio.currentTime = 0;
const getAudioBuffer = async (source: string, context: AudioContext) => {
if (!audioBufferCache[source]) {
audioBufferCache[source] = fetch(source)
.then(response => response.arrayBuffer())
.then(arrayBuffer => context.decodeAudioData(arrayBuffer))
.catch(error => {
delete audioBufferCache[source];
throw error;
});
}
return audioBufferCache[source];
};

export const playAudio = (source: string | HTMLAudioElement) => {
const audioSource = getAudioSource(source);
(async () => {
try {
const context = getAudioContext();
if (context && audioSource) {
if (context.state === "suspended") {
await context.resume();
}
const buffer = await getAudioBuffer(audioSource, context);
const player = context.createBufferSource();
player.buffer = buffer;
player.connect(context.destination);
player.start(0);
return;
}
const audio = typeof source === "string" ? getAudioElement(source) : source;
audio.currentTime = 0;
await audio.play();
} catch (e) {
if (e instanceof DOMException && e.name === "NotAllowedError") {
Expand All @@ -344,8 +390,7 @@ export const playAudio = (audio: HTMLAudioElement) => {
export const playCheckerSound = () => {
const randomIndex = Math.floor(Math.random() * checkerSounds.length);
const randomMp3 = checkerSounds[randomIndex];
const audio = audioCache[randomMp3];
playAudio(audio);
playAudio(randomMp3);
};

const parseMoveLabel = (label: string, color: Color, ghosts: { [point: number]: number }, moved: { [point: number]: number }, ghostHit: { [point: number]: number }) => {
Expand Down
2 changes: 1 addition & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
</StrictMode>
);

const diceSound = new Audio('./shake-and-roll-dice-soundbible.mp3');
const diceSound = './shake-and-roll-dice-soundbible.mp3';

export function App() {
const { t } = useTranslation();
Expand Down