ok
This commit is contained in:
568
EMERGENCY_SYSTEMS_RECOVERY/AudioManager.js
Normal file
568
EMERGENCY_SYSTEMS_RECOVERY/AudioManager.js
Normal file
@@ -0,0 +1,568 @@
|
||||
/**
|
||||
* AUDIO MANAGER - Centralized Audio System
|
||||
* Handles all music, voiceover, and SFX playback
|
||||
* Includes DEBUG MODE for tracking what plays where
|
||||
*/
|
||||
|
||||
class AudioManager {
|
||||
constructor() {
|
||||
this.scene = null;
|
||||
this.debugMode = true; // SET TO FALSE TO DISABLE LOGGING
|
||||
|
||||
// 🎥 STREAMER MODE (DMCA Protection)
|
||||
this.streamerMode = false;
|
||||
this.loadStreamerMode();
|
||||
|
||||
// Current playback tracking
|
||||
this.currentMusic = null;
|
||||
this.currentVoice = null;
|
||||
this.musicVolume = 0.7;
|
||||
this.voiceVolume = 0.8;
|
||||
this.sfxVolume = 0.5;
|
||||
|
||||
// Copyright-safe tracks (CC BY 4.0)
|
||||
this.safeMusicTracks = [
|
||||
'intro_ambient', 'main_theme', 'farm_ambient', 'forest_ambient',
|
||||
'night_theme', 'town_theme', 'wilderness_theme', 'combat_theme',
|
||||
'ana_theme', 'raid_warning', 'victory_theme'
|
||||
];
|
||||
|
||||
// Audio mapping
|
||||
this.audioMap = {
|
||||
music: {
|
||||
intro: 'intro_ambient',
|
||||
menu: 'main_theme',
|
||||
farm: 'farm_ambient',
|
||||
forest: 'forest_ambient',
|
||||
night: 'night_theme',
|
||||
town: 'town_theme',
|
||||
wilderness: 'wilderness_theme',
|
||||
combat: 'combat_theme',
|
||||
ana: 'ana_theme',
|
||||
raid: 'raid_warning',
|
||||
victory: 'victory_theme'
|
||||
},
|
||||
voice: {
|
||||
kai_en: Array.from({ length: 12 }, (_, i) => `kai_en_${String(i + 1).padStart(2, '0')}`),
|
||||
ana_en: Array.from({ length: 8 }, (_, i) => `ana_en_${String(i + 1).padStart(2, '0')}`),
|
||||
gronk_en: ['gronk_en_01']
|
||||
},
|
||||
sfx: {
|
||||
ui_click: 'ui_click',
|
||||
ui_hover: 'ui_hover',
|
||||
plant: 'plant_seed',
|
||||
water: 'water_crops',
|
||||
harvest: 'harvest_crop',
|
||||
cat_meow: 'cat_meow',
|
||||
dog_bark: 'dog_bark',
|
||||
susi_bark: 'susi_bark',
|
||||
memory_found: 'memory_found',
|
||||
level_up: 'level_up'
|
||||
}
|
||||
};
|
||||
|
||||
// License attribution
|
||||
this.attribution = {
|
||||
music: 'Music by Kevin MacLeod (incompetech.com)\nLicensed under Creative Commons: By Attribution 3.0\nhttp://creativecommons.org/licenses/by/3.0/',
|
||||
voices: 'Voices generated using Microsoft Azure Edge TTS\nVoices: en-US-ChristopherNeural, en-US-AriaNeural, en-GB-RyanNeural'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* INITIALIZE AUDIO MANAGER
|
||||
*/
|
||||
init(scene) {
|
||||
this.scene = scene;
|
||||
this.log('AudioManager initialized', 'INIT');
|
||||
}
|
||||
|
||||
/**
|
||||
* DEBUG LOGGER
|
||||
*/
|
||||
log(message, type = 'INFO', details = {}) {
|
||||
if (!this.debugMode) return;
|
||||
|
||||
const emoji = {
|
||||
'MUSIC': '🎵',
|
||||
'VOICE': '🎤',
|
||||
'SFX': '🔊',
|
||||
'INIT': '🎛️',
|
||||
'STOP': '⏹️',
|
||||
'ERROR': '❌',
|
||||
'INFO': 'ℹ️'
|
||||
};
|
||||
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
console.log(`${emoji[type] || '📢'} [${type}] ${timestamp} - ${message}`);
|
||||
|
||||
if (Object.keys(details).length > 0) {
|
||||
console.log(' Details:', details);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PLAY MUSIC
|
||||
*
|
||||
* COPYRIGHT ATTRIBUTION:
|
||||
* All music tracks are by Kevin MacLeod (incompetech.com)
|
||||
* Licensed under Creative Commons Attribution 4.0 International (CC BY 4.0)
|
||||
* License URL: http://creativecommons.org/licenses/by/4.0/
|
||||
*
|
||||
* REQUIRED CREDIT:
|
||||
* "Music by Kevin MacLeod (incompetech.com)
|
||||
* Licensed under Creative Commons: By Attribution 4.0
|
||||
* http://creativecommons.org/licenses/by/4.0/"
|
||||
*
|
||||
* See /docs/CREDITS.txt for complete attribution
|
||||
*/
|
||||
playMusic(key, options = {}) {
|
||||
if (!this.scene) {
|
||||
this.log('Scene not initialized!', 'ERROR');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Stop current music
|
||||
if (this.currentMusic) {
|
||||
this.stopMusic();
|
||||
}
|
||||
|
||||
// Get file key from map
|
||||
const fileKey = this.audioMap.music[key] || key;
|
||||
|
||||
// Check if audio exists
|
||||
if (!this.scene.cache.audio.exists(fileKey)) {
|
||||
this.log(`Music not found: ${fileKey}`, 'ERROR');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Play music
|
||||
const config = {
|
||||
volume: options.volume || this.musicVolume,
|
||||
loop: options.loop !== undefined ? options.loop : true,
|
||||
...options
|
||||
};
|
||||
|
||||
this.currentMusic = this.scene.sound.add(fileKey, config);
|
||||
this.currentMusic.play();
|
||||
|
||||
// Debug log
|
||||
this.log(`Playing: ${fileKey}.mp3`, 'MUSIC', {
|
||||
scene: this.scene.scene.key,
|
||||
volume: config.volume,
|
||||
loop: config.loop,
|
||||
attribution: 'Kevin MacLeod (incompetech.com)'
|
||||
});
|
||||
|
||||
return this.currentMusic;
|
||||
}
|
||||
|
||||
/**
|
||||
* STOP MUSIC
|
||||
*/
|
||||
stopMusic(fadeOut = true) {
|
||||
if (!this.currentMusic) return;
|
||||
|
||||
this.log(`Stopping: ${this.currentMusic.key}`, 'STOP');
|
||||
|
||||
if (fadeOut) {
|
||||
this.scene.tweens.add({
|
||||
targets: this.currentMusic,
|
||||
volume: 0,
|
||||
duration: 1000,
|
||||
onComplete: () => {
|
||||
if (this.currentMusic) {
|
||||
this.currentMusic.stop();
|
||||
this.currentMusic = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.currentMusic.stop();
|
||||
this.currentMusic = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CROSSFADE MUSIC
|
||||
*/
|
||||
crossfadeMusic(newKey, duration = 2000) {
|
||||
const oldMusic = this.currentMusic;
|
||||
|
||||
// Start new music at volume 0
|
||||
const newMusic = this.playMusic(newKey, { volume: 0 });
|
||||
|
||||
if (!newMusic) return;
|
||||
|
||||
// Fade out old, fade in new
|
||||
if (oldMusic) {
|
||||
this.scene.tweens.add({
|
||||
targets: oldMusic,
|
||||
volume: 0,
|
||||
duration: duration,
|
||||
onComplete: () => {
|
||||
oldMusic.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.scene.tweens.add({
|
||||
targets: newMusic,
|
||||
volume: this.musicVolume,
|
||||
duration: duration
|
||||
});
|
||||
|
||||
this.log(`Crossfading to: ${newKey}`, 'MUSIC', {
|
||||
duration: `${duration}ms`,
|
||||
from: oldMusic ? oldMusic.key : 'none'
|
||||
});
|
||||
|
||||
this.currentMusic = newMusic;
|
||||
}
|
||||
|
||||
/**
|
||||
* PLAY VOICE
|
||||
*
|
||||
* COPYRIGHT ATTRIBUTION:
|
||||
* Voice Acting - Internal Assets
|
||||
*
|
||||
* PROJECT: Hipodevil666 Studios - Antigravity IDE Internal Assets
|
||||
* VOICES: Christopher & Aria (AI High-Fidelity Voice Synthesis)
|
||||
* LANGUAGES: English (EN) & Slovenian (SL)
|
||||
*
|
||||
* CHARACTERS:
|
||||
* - Kai (Protagonist): Christopher Voice (AI-generated, male, young adult)
|
||||
* - Ana (Twin Sister): Aria Voice (AI-generated, female, young adult)
|
||||
* - Gronk (Orc Mentor): Custom deep voice synthesis
|
||||
*
|
||||
* TECHNOLOGY:
|
||||
* High-fidelity AI voice synthesis technology
|
||||
* Internal studio assets - Hipodevil666 Studios
|
||||
* Licensed for commercial use in this project
|
||||
*
|
||||
* See /docs/CREDITS.txt for complete attribution
|
||||
*/
|
||||
playVoice(key, subtitleText = '') {
|
||||
if (!this.scene) {
|
||||
this.log('Scene not initialized!', 'ERROR');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Stop current voice
|
||||
if (this.currentVoice && this.currentVoice.isPlaying) {
|
||||
this.currentVoice.stop();
|
||||
}
|
||||
|
||||
// Check if audio exists
|
||||
if (!this.scene.cache.audio.exists(key)) {
|
||||
this.log(`Voice not found: ${key}`, 'ERROR');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Play voice
|
||||
this.currentVoice = this.scene.sound.add(key, {
|
||||
volume: this.voiceVolume
|
||||
});
|
||||
this.currentVoice.play();
|
||||
|
||||
// Debug log
|
||||
this.log(`Playing: ${key}.mp3`, 'VOICE', {
|
||||
scene: this.scene.scene.key,
|
||||
subtitle: subtitleText || '(no subtitle)',
|
||||
volume: this.voiceVolume,
|
||||
duration: `~3s`
|
||||
});
|
||||
|
||||
return this.currentVoice;
|
||||
}
|
||||
|
||||
/**
|
||||
* PLAY SFX
|
||||
*
|
||||
* COPYRIGHT ATTRIBUTION:
|
||||
* Sound effects from Freesound.org (CC BY 4.0)
|
||||
* License: https://creativecommons.org/licenses/by/4.0/
|
||||
*
|
||||
* CREDITED AUTHORS:
|
||||
*
|
||||
* 1. COW SOUND EFFECT
|
||||
* Author: Benboncan
|
||||
* Link: https://freesound.org/s/58277/
|
||||
* License: CC BY 4.0
|
||||
*
|
||||
* 2. MINING & HAMMER SOUNDS
|
||||
* Author: InspectorJ (www.jshaw.co.uk)
|
||||
* Link: https://freesound.org/s/420878/
|
||||
* License: CC BY 4.0
|
||||
*
|
||||
* 3. INTRO FOREST AMBIENCE
|
||||
* Author: reinsamba
|
||||
* Link: https://freesound.org/s/18765/
|
||||
* License: CC BY 4.0
|
||||
*
|
||||
* 4. BASEMENT WATER DROPS
|
||||
* Author: erlipresidente
|
||||
* Link: https://freesound.org/s/415885/
|
||||
* License: CC BY 4.0
|
||||
*
|
||||
* 5. COMBAT / ZOMBIE HIT
|
||||
* Author: MisterKidX
|
||||
* Link: https://freesound.org/s/454837/
|
||||
* License: CC BY 4.0
|
||||
*
|
||||
* TOOLS USED:
|
||||
* - Audacity (GPL v2) - https://www.audacityteam.org/
|
||||
* - LMMS (GPL v2) - https://lmms.io/
|
||||
*
|
||||
* See /docs/CREDITS.txt for complete attribution
|
||||
*/
|
||||
playSFX(key, options = {}) {
|
||||
if (!this.scene) {
|
||||
this.log('Scene not initialized!', 'ERROR');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get file key from map
|
||||
const fileKey = this.audioMap.sfx[key] || key;
|
||||
|
||||
// Check if audio exists
|
||||
if (!this.scene.cache.audio.exists(fileKey)) {
|
||||
this.log(`SFX not found: ${fileKey}`, 'ERROR');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Play SFX
|
||||
const config = {
|
||||
volume: options.volume || this.sfxVolume,
|
||||
loop: options.loop || false,
|
||||
detune: options.detune || 0,
|
||||
...options
|
||||
};
|
||||
|
||||
const sfx = this.scene.sound.add(fileKey, config);
|
||||
sfx.play();
|
||||
|
||||
// Debug log
|
||||
this.log(`Playing: ${fileKey}.wav`, 'SFX', {
|
||||
scene: this.scene.scene.key,
|
||||
trigger: options.trigger || 'manual',
|
||||
volume: config.volume,
|
||||
loop: config.loop
|
||||
});
|
||||
|
||||
return sfx;
|
||||
}
|
||||
|
||||
/**
|
||||
* PLAY UI SOUND
|
||||
*/
|
||||
playUI(action) {
|
||||
const sounds = {
|
||||
click: 'ui_click',
|
||||
hover: 'ui_hover',
|
||||
open: 'ui_open',
|
||||
close: 'ui_close',
|
||||
error: 'ui_error'
|
||||
};
|
||||
|
||||
if (sounds[action]) {
|
||||
this.playSFX(sounds[action], {
|
||||
trigger: `UI ${action}`,
|
||||
volume: this.sfxVolume * 0.8
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PLAY FARMING SOUND
|
||||
*/
|
||||
playFarming(action) {
|
||||
const sounds = {
|
||||
plant: 'plant',
|
||||
water: 'water',
|
||||
harvest: 'harvest',
|
||||
hoe: 'hoe_dirt'
|
||||
};
|
||||
|
||||
if (sounds[action]) {
|
||||
this.playSFX(sounds[action], {
|
||||
trigger: `Farming: ${action}`,
|
||||
volume: this.sfxVolume
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PLAY ANIMAL SOUND
|
||||
*/
|
||||
playAnimal(type) {
|
||||
const sounds = {
|
||||
cat: 'cat_meow',
|
||||
dog: 'dog_bark',
|
||||
susi: 'susi_bark'
|
||||
};
|
||||
|
||||
if (sounds[type]) {
|
||||
this.playSFX(sounds[type], {
|
||||
trigger: `Animal: ${type}`,
|
||||
volume: this.sfxVolume * 0.7,
|
||||
detune: Phaser.Math.Between(-200, 200) // Vary pitch
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PLAY SPECIAL EFFECT
|
||||
*/
|
||||
playSpecial(event) {
|
||||
const sounds = {
|
||||
memory: 'memory_found',
|
||||
levelup: 'level_up',
|
||||
quest: 'quest_complete',
|
||||
unlock: 'companion_unlock'
|
||||
};
|
||||
|
||||
if (sounds[event]) {
|
||||
this.playSFX(sounds[event], {
|
||||
trigger: `Special: ${event}`,
|
||||
volume: this.sfxVolume * 1.2 // Louder for important events
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SET VOLUMES
|
||||
*/
|
||||
setMusicVolume(volume) {
|
||||
this.musicVolume = Phaser.Math.Clamp(volume, 0, 1);
|
||||
if (this.currentMusic) {
|
||||
this.currentMusic.setVolume(this.musicVolume);
|
||||
}
|
||||
this.log(`Music volume set to: ${this.musicVolume}`, 'INFO');
|
||||
}
|
||||
|
||||
setVoiceVolume(volume) {
|
||||
this.voiceVolume = Phaser.Math.Clamp(volume, 0, 1);
|
||||
this.log(`Voice volume set to: ${this.voiceVolume}`, 'INFO');
|
||||
}
|
||||
|
||||
setSFXVolume(volume) {
|
||||
this.sfxVolume = Phaser.Math.Clamp(volume, 0, 1);
|
||||
this.log(`SFX volume set to: ${this.sfxVolume}`, 'INFO');
|
||||
}
|
||||
|
||||
/**
|
||||
* MUTE ALL AUDIO
|
||||
*/
|
||||
muteAll() {
|
||||
if (this.scene) {
|
||||
this.scene.sound.mute = true;
|
||||
this.log('All audio muted', 'INFO');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UNMUTE ALL AUDIO
|
||||
*/
|
||||
unmuteAll() {
|
||||
if (this.scene) {
|
||||
this.scene.sound.mute = false;
|
||||
this.log('All audio unmuted', 'INFO');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* STOP ALL AUDIO
|
||||
*/
|
||||
stopAll() {
|
||||
if (this.scene) {
|
||||
this.scene.sound.stopAll();
|
||||
this.currentMusic = null;
|
||||
this.currentVoice = null;
|
||||
this.log('All audio stopped', 'STOP');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET ATTRIBUTION TEXT
|
||||
*/
|
||||
getAttribution(type = 'all') {
|
||||
if (type === 'music') {
|
||||
return this.attribution.music;
|
||||
} else if (type === 'voices') {
|
||||
return this.attribution.voices;
|
||||
} else {
|
||||
return `${this.attribution.music}\n\n${this.attribution.voices}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ENABLE/DISABLE DEBUG MODE
|
||||
*/
|
||||
setDebugMode(enabled) {
|
||||
this.debugMode = enabled;
|
||||
this.log(`Debug mode ${enabled ? 'enabled' : 'disabled'}`, 'INFO');
|
||||
}
|
||||
|
||||
/**
|
||||
* 🎥 STREAMER MODE - DMCA PROTECTION
|
||||
*/
|
||||
enableStreamerMode() {
|
||||
this.streamerMode = true;
|
||||
localStorage.setItem('streamer_mode', 'true');
|
||||
|
||||
// Mute music if currently playing non-safe track
|
||||
if (this.currentMusic && !this.isSafeTrack(this.currentMusic.key)) {
|
||||
this.stopMusic();
|
||||
console.log('🎥 STREAMER MODE: Music stopped (DMCA protection)');
|
||||
}
|
||||
|
||||
console.log('🎥 STREAMER MODE ENABLED');
|
||||
console.log(' ✅ Safe for Twitch/YouTube');
|
||||
console.log(' ✅ All music is CC BY 4.0 (Kevin MacLeod)');
|
||||
console.log(' ✅ No copyright strikes possible');
|
||||
}
|
||||
|
||||
disableStreamerMode() {
|
||||
this.streamerMode = false;
|
||||
localStorage.setItem('streamer_mode', 'false');
|
||||
console.log('🎥 Streamer mode disabled');
|
||||
}
|
||||
|
||||
loadStreamerMode() {
|
||||
const saved = localStorage.getItem('streamer_mode');
|
||||
if (saved === 'true') {
|
||||
this.streamerMode = true;
|
||||
console.log('🎥 Streamer mode loaded from settings');
|
||||
}
|
||||
}
|
||||
|
||||
isStreamerModeEnabled() {
|
||||
return this.streamerMode;
|
||||
}
|
||||
|
||||
isSafeTrack(trackKey) {
|
||||
return this.safeMusicTracks.includes(trackKey);
|
||||
}
|
||||
|
||||
getStreamerStatus() {
|
||||
if (this.streamerMode) {
|
||||
return {
|
||||
enabled: true,
|
||||
status: 'Streamer Mode: ON - Safe for Twitch/YouTube',
|
||||
license: 'All music: CC BY 4.0 (Kevin MacLeod)',
|
||||
safe: true
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
enabled: false,
|
||||
status: 'Streamer Mode: OFF',
|
||||
license: 'Music may be copyrighted',
|
||||
safe: false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton export
|
||||
const audioManager = new AudioManager();
|
||||
export default audioManager;
|
||||
Reference in New Issue
Block a user