Skip to main content

Audio System

Система звуковых эффектов для TMA. Singleton AudioManager с предзагрузкой и интеграцией в settingsStore.

Цель: Мгновенное воспроизведение звуков без задержек, глобальное управление через настройки.


1. Summary

Goal: Обеспечить тактильную обратную связь через звуковые эффекты при ключевых действиях пользователя (получение наград, открытие кейсов, повышение уровня).

Architecture:

  • AudioManager — Singleton, предзагружает все звуки при первом взаимодействии
  • useAudio — React hook для удобного использования в компонентах
  • settingsStore — глобальный toggle звука (синхронизируется с backend)

2. Sound Library

ИмяФайлКогда использоватьДлительность
scrapscrap.mp3Получение Scrap~2 сек
xpxp.mp3Получение XP~2 сек
lvluplvlup.mp3Повышение уровня~2 сек
opencaseopencase.mp3Открытие кейса (анимация)~9 сек
prizecaseprizecase.mp3Выигрыш из кейса / рулетки~1.5 сек
takerewardtakereward.mp3Клик при сборе награды~0.2 сек
spinspin.mp3Вращение колеса удачи~3 сек

Расположение файлов: frontend/public/sounds/

Требования к звукам:

  • Формат: MP3 128kbps
  • Длительность: 0.2-9 сек (зависит от use case)
  • Размер: < 50 KB (короткие эффекты)

3. Usage

Через хук (рекомендуется для компонентов)

import { useAudio } from '../hooks/useAudio';

const MyComponent = () => {
const { play } = useAudio();

const handleClaim = () => {
play('scrap');
};

return <button onClick={handleClaim}>Забрать</button>;
};

Напрямую через AudioManager (для non-React кода)

import { audioManager } from '../utils/AudioManager';

audioManager.play('opencase');
audioManager.play('prizecase', { volume: 0.7 });
Когда какой вариант
  • useAudio — внутри React компонентов (HomeScreen, RaffleModal и т.д.)
  • audioManager — в анимациях, таймерах, callback-ах вне React lifecycle (CaseOpening, DailySpinScreen)

4. API

useAudio Hook

const { play, toggleMute, setVolume, status } = useAudio();
МетодПараметрыОписание
play(name, config?)name: SoundName, config?: { volume, loop }Воспроизвести звук
toggleMute()Переключить звук вкл/выкл (через settingsStore)
setVolume(volume)volume: number (0-1)Установить глобальную громкость
Поле statusТипОписание
isEnabledbooleanЗвук включён (из settingsStore)
isMutedbooleanЗвук выключен (инверсия isEnabled)
volumenumberТекущая глобальная громкость (0-1)
loadedSoundsnumberКоличество загруженных звуков

AudioManager (Singleton)

МетодВозвратОписание
play(name, config?)HTMLAudioElement | nullВоспроизвести (null если звук выключен)
stopAll()voidОстановить все звуки
setVolume(volume)voidГлобальная громкость (0-1)
getDuration(name)numberДлительность в ms (из метаданных файла)
getStatus()object{ isEnabled, isMuted, volume, loadedSounds, initialized }
destroy()voidCleanup (отписка от store, очистка ресурсов)
toggleMute() deprecated

audioManager.toggleMute() помечен как deprecated. Используй settingsStore.toggleSound() или useAudio().toggleMute().


5. Architecture

Инициализация

User Interaction (click/touch)

AudioManager.init()

┌─────────────────────────────┐
│ 1. Subscribe to settingsStore│
│ 2. Preload all 7 sounds │
│ 3. Save durations from metadata│
└─────────────────────────────┘

AudioManager инициализируется lazy — при первом click или touchstart пользователя. Это обходит ограничения браузеров на autoplay.

Воспроизведение

play('scrap')

┌─────────────────────────┐
│ 1. Check settingsStore │──→ sound=false → return null
│ 2. Find preloaded sound │──→ not found → return null
│ 3. Stop previous (same) │
│ 4. Clone HTMLAudioElement│
│ 5. Set volume + loop │
│ 6. Play clone │
│ 7. Auto-cleanup on end │
└─────────────────────────┘

Клонирование: Каждое воспроизведение создаёт clone от оригинала. Это позволяет:

  • Воспроизводить один звук несколько раз подряд
  • Не ждать окончания предыдущего
  • Автоматически удалять clone после окончания

Интеграция с settingsStore

settingsStore.toggleSound()

Zustand state: sound = false

┌──────────────────────────┐
│ settingsStore subscriber │
│ → audioManager.setEnabled(false)│
│ → audioManager.stopAll() │
└──────────────────────────┘

Debounced sync to Backend (500ms)

Zustand persist to localStorage

6. Добавление нового звука

1. Добавь файл

Положи MP3 в frontend/public/sounds/my-sound.mp3

2. Зарегистрируй в AudioManager

В frontend/src/utils/AudioManager.ts:

// 1. Добавь в SoundName type
type SoundName =
| 'scrap'
// ...
| 'my-sound'; // Новый звук

// 2. Добавь путь в soundPaths
private soundPaths: Record<SoundName, string> = {
// ...
'my-sound': '/sounds/my-sound.mp3',
};

3. Используй в компоненте

const { play } = useAudio();
play('my-sound');

4. Обнови эту документацию

Добавь новый звук в таблицу Sound Library (секция 2).


7. Файлы системы

ФайлНазначение
frontend/public/sounds/*.mp3Звуковые файлы
frontend/src/utils/AudioManager.tsSingleton менеджер (preload, play, volume)
frontend/src/hooks/useAudio.tsReact hook для компонентов
frontend/src/stores/settingsStore.tsХранение toggle звука (sound: boolean)