Skip to content

Commit

Permalink
Improve audio handling
Browse files Browse the repository at this point in the history
  • Loading branch information
wojciechpolak committed Oct 2, 2024
1 parent 94b14be commit 2f9e83c
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 6 deletions.
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: "3"

services:
otd:
yact:
image: ${APP_IMAGE:-ghcr.io/wojciechpolak/yact:latest}
restart: unless-stopped
build: .
Expand Down
78 changes: 73 additions & 5 deletions src/components/CountdownTimer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

'use client';

import { useState, useEffect } from 'react';
import { useState, useEffect, useRef } from 'react';
import { FaPlus, FaMinus } from 'react-icons/fa';
import { useSettings } from '@/context/SettingsContext';

Expand Down Expand Up @@ -88,6 +88,20 @@ const CountdownTimer: React.FC<CountdownTimerProps> = ({
}
};

const audioContext = useRef<AudioContext | null>(null);
const audioBuffers = useRef<{[key: string]: AudioBuffer}>({});

useEffect(() => {
initializeAudioContext();
if (audioContext.current) {
unlockAudioContext(audioContext.current);
preloadSounds([
'/audio/end.mp3',
'/audio/tick.mp3'
]);
}
}, []);

// Update targetTimeState when timer starts or resets
useEffect(() => {
if (isActive) {
Expand Down Expand Up @@ -149,8 +163,7 @@ const CountdownTimer: React.FC<CountdownTimerProps> = ({
if (newTimeLeft <= 0) {
if (newTimeLeft === 0) {
if (playEndSound) {
const audio = new Audio('/audio/end.mp3');
audio.play();
playSound('/audio/end.mp3');
}
if (showNotifications) {
sendNotification();
Expand All @@ -174,8 +187,7 @@ const CountdownTimer: React.FC<CountdownTimerProps> = ({
}
else {
if (playLastTenSecondsSound && newTimeLeft <= 10 && newTimeLeft > 0) {
const audio = new Audio('/audio/tick.mp3');
audio.play();
playSound('/audio/tick.mp3');
}
}

Expand Down Expand Up @@ -258,6 +270,62 @@ const CountdownTimer: React.FC<CountdownTimerProps> = ({
}
};

const initializeAudioContext = () => {
if (!audioContext.current) {
if (window.AudioContext) {
audioContext.current = new window.AudioContext();
}
}
};

const unlockAudioContext = (audioCtx: AudioContext) => {
if (audioCtx.state !== 'suspended') {
return;
}

const resume = () => {
audioCtx.resume();
document.body.removeEventListener('touchstart', resume, false);
document.body.removeEventListener('touchend', resume, false);
document.body.removeEventListener('click', resume, false);
};

document.body.addEventListener('touchstart', resume, false);
document.body.addEventListener('touchend', resume, false);
document.body.addEventListener('click', resume, false);
};

const preloadSounds = (urls: string[]) => {
if (!audioContext.current) {
return;
}

urls.forEach((url) => {
fetch(url)
.then((response) => response.arrayBuffer())
.then((arrayBuffer) => audioContext.current!.decodeAudioData(arrayBuffer))
.then((audioBuffer) => {
audioBuffers.current[url] = audioBuffer;
})
.catch((error) => console.error('Error preloading sound:', error));
});
};

const playSound = (url: string) => {
if (!audioContext.current) {
return;
}
const audioBuffer = audioBuffers.current[url];
if (!audioBuffer) {
console.error('Audio buffer not found for url:', url);
return;
}
const source = audioContext.current.createBufferSource();
source.buffer = audioBuffer;
source.connect(audioContext.current.destination);
source.start(0);
};

return (
<div className="flex flex-col items-center">
{/* Timer Display */}
Expand Down

0 comments on commit 2f9e83c

Please sign in to comment.