доработка звука по уровням, разный тон

This commit is contained in:
Maxim
2026-06-17 11:43:58 +03:00
parent 5938d94a8f
commit 5d2d37fc7b

View File

@@ -6,9 +6,14 @@ export class AudioEngine {
private gainNode: GainNode | null = null;
private isPlaying: boolean = false;
private currentLevel: number = 0;
private currentFrequency: number = 800;
private fadeTimeout: number | null = null;
private readonly FREQUENCY: number = 800; // 800 Гц
private readonly FADE_TIME: number = 0.05; // 50 мс для плавного старта/остановки
private readonly FREQUENCY_MAP: Record<number, number> = {
1: 800, // Уровень 1 → 800 Гц
2: 1000, // Уровень 2 → 1000 Гц
3: 1200 // Уровень 3 → 1200 Гц
};
constructor() {
// Инициализация при первом использовании
@@ -34,19 +39,51 @@ export class AudioEngine {
}
}
/**
* Получение частоты для уровня
*/
private getFrequencyForLevel(level: number): number {
return this.FREQUENCY_MAP[level] || 800;
}
/**
* Обновление частоты осциллятора
*/
private updateFrequency(frequency: number): void {
if (!this.oscillator) return;
try {
// Плавное изменение частоты за 50 мс
const currentTime = this.audioContext?.currentTime || 0;
this.oscillator.frequency.exponentialRampToValueAtTime(
frequency,
currentTime + this.FADE_TIME
);
this.currentFrequency = frequency;
console.log(`[Audio] Частота: ${frequency} Гц`);
} catch (error) {
// Если не удалось плавно, устанавливаем сразу
this.oscillator.frequency.value = frequency;
this.currentFrequency = frequency;
}
}
/**
* Создание звукового графа
*/
private createSoundGraph(): void {
private createSoundGraph(level: number): void {
if (!this.audioContext) return;
// Останавливаем старый звук
this.stopSound();
const frequency = this.getFrequencyForLevel(level);
this.currentFrequency = frequency;
// Создаём осциллятор
this.oscillator = this.audioContext.createOscillator();
this.oscillator.type = 'sine';
this.oscillator.frequency.value = this.FREQUENCY;
this.oscillator.frequency.value = frequency;
// Создаём усилитель
this.gainNode = this.audioContext.createGain();
@@ -60,7 +97,7 @@ export class AudioEngine {
this.oscillator.start();
this.isPlaying = true;
console.log('[Audio] Звук создан, частота:', this.FREQUENCY, 'Гц');
console.log(`[Audio] Звук создан, частота: ${frequency} Гц, уровень: ${level}`);
}
/**
@@ -89,11 +126,18 @@ export class AudioEngine {
return;
}
const oldLevel = this.currentLevel;
this.currentLevel = newLevel;
// Получаем частоту для нового уровня
const newFrequency = this.getFrequencyForLevel(newLevel);
// Если уровень > 0 и звук не создан — создаём
if (newLevel > 0 && !this.oscillator) {
this.createSoundGraph();
this.createSoundGraph(newLevel);
// Устанавливаем громкость после создания
this.setVolume(newLevel);
return;
}
// Если уровень 0 — выключаем звук
@@ -102,36 +146,55 @@ export class AudioEngine {
return;
}
// Если звук не играет — включаем
// Если звук не играет — включаем заново
if (!this.isPlaying || !this.oscillator) {
this.createSoundGraph();
this.createSoundGraph(newLevel);
this.setVolume(newLevel);
return;
}
// Расчёт громкости: уровень 1 = 15%, 2 = 30%, 3 = 50%
// Изменяем частоту, если она изменилась
if (newFrequency !== this.currentFrequency) {
this.updateFrequency(newFrequency);
}
// Обновляем громкость
this.setVolume(newLevel);
console.log(`[Audio] Уровень: ${oldLevel}${newLevel}, Частота: ${newFrequency} Гц`);
}
/**
* Установка громкости для уровня
*/
private setVolume(level: number): void {
if (!this.gainNode || !this.audioContext) return;
// Расчёт громкости: уровень 1 = 10%, 2 = 25%, 3 = 45%
const volumeMap: Record<number, number> = {
1: 0.10,
2: 0.25,
3: 0.45
};
const targetVolume = volumeMap[newLevel] || 0;
const targetVolume = volumeMap[level] || 0;
// Плавно меняем громкость
if (this.gainNode) {
try {
const currentGain = this.gainNode.gain.value;
// Если звук выключен или очень тихий, включаем сразу
if (currentGain < 0.01) {
this.gainNode.gain.setValueAtTime(targetVolume, this.audioContext!.currentTime);
this.gainNode.gain.setValueAtTime(targetVolume, this.audioContext.currentTime);
} else {
// Плавное изменение за 50 мс
this.gainNode.gain.exponentialRampToValueAtTime(
Math.max(targetVolume, 0.001),
this.audioContext!.currentTime + this.FADE_TIME
this.audioContext.currentTime + this.FADE_TIME
);
}
} catch (error) {
// fallback
this.gainNode.gain.value = targetVolume;
}
console.log(`[Audio] Уровень: ${newLevel}, Громкость: ${(targetVolume * 100).toFixed(0)}%`);
}
/**
@@ -146,9 +209,14 @@ export class AudioEngine {
this.fadeTimeout = null;
}
// Плавно убавляем громкость за 100 мс
const currentTime = this.audioContext.currentTime;
this.gainNode.gain.exponentialRampToValueAtTime(0.001, currentTime + this.FADE_TIME * 2);
try {
// Плавно убавляем громкость за 100 мс
const currentTime = this.audioContext.currentTime;
this.gainNode.gain.exponentialRampToValueAtTime(0.001, currentTime + this.FADE_TIME * 2);
} catch (error) {
// fallback
this.gainNode.gain.value = 0;
}
// Останавливаем звук после затухания
this.fadeTimeout = window.setTimeout(() => {
@@ -199,6 +267,20 @@ export class AudioEngine {
return this.isPlaying && this.currentLevel > 0;
}
/**
* Получение текущей частоты
*/
public getCurrentFrequency(): number {
return this.currentFrequency;
}
/**
* Получение текущего уровня
*/
public getCurrentLevel(): number {
return this.currentLevel;
}
/**
* Полная остановка и освобождение ресурсов
*/
@@ -213,6 +295,7 @@ export class AudioEngine {
this.audioContext = null;
}
this.currentLevel = 0;
this.currentFrequency = 800;
console.log('[Audio] Ресурсы освобождены');
}
}