Spaces:
Running
Running
File size: 6,807 Bytes
a94fc8f 1dcc163 a94fc8f 1dcc163 a94fc8f 1dcc163 a94fc8f 1dcc163 a94fc8f 1dcc163 7606f52 1dcc163 a94fc8f 1dcc163 7606f52 1dcc163 a94fc8f 1dcc163 a94fc8f 1dcc163 7606f52 1dcc163 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# file: battlewords/sounds.py
import os
import tempfile
import base64
import requests
import time
from io import BytesIO
from pathlib import Path
# Load environment variables from .env file
def _load_env():
"""Load .env file from project root"""
env_path = Path(__file__).parent.parent / ".env"
if env_path.exists():
with open(env_path) as f:
for line in f:
line = line.strip()
if line and not line.startswith("#") and "=" in line:
key, value = line.split("=", 1)
os.environ.setdefault(key.strip(), value.strip())
_load_env()
# Predefined prompts for sound effects: key: {"prompt": text, "duration": seconds}
EFFECT_PROMPTS = {
"correct_guess": {"prompt": "A short, sharp ding sound for a correct guess", "duration": 2},
"incorrect_guess": {"prompt": "A low buzz sound for an incorrect guess", "duration": 2},
"miss": {"prompt": "A soft thud sound for a miss", "duration": 1},
"hit": {"prompt": "A bright chime sound for a hit", "duration": 1},
"congratulations": {"prompt": "A triumphant fanfare sound for congratulations", "duration": 3}
}
_sound_cache = {}
# Hugging Face Inference API configuration
# Loaded from .env file or environment variable: HF_API_TOKEN
HF_API_TOKEN = os.environ.get("HF_API_TOKEN", None)
if HF_API_TOKEN:
print(f"Using HF_API_TOKEN: {HF_API_TOKEN[:10]}...")
HF_API_URL = "https://api-inference.huggingface.co/models/facebook/audiogen-medium"
def generate_sound_effect(effect: str, save_to_assets: bool = False, use_api: str = "huggingface") -> str:
"""
Generate a sound effect using external API based on the effect string.
Returns the path to the generated audio file.
Args:
effect: Name of the effect (must be in EFFECT_PROMPTS)
save_to_assets: If True, save to battlewords/assets/audio/effects/ instead of temp directory
use_api: API to use - "huggingface" (default) or "replicate"
"""
if effect not in EFFECT_PROMPTS:
raise ValueError(f"Unknown effect: {effect}. Available effects: {list(EFFECT_PROMPTS.keys())}")
# Check cache first (only for temp files)
if effect in _sound_cache and not save_to_assets:
if os.path.exists(_sound_cache[effect]):
return _sound_cache[effect]
effect_config = EFFECT_PROMPTS[effect]
prompt = effect_config["prompt"]
duration = effect_config["duration"]
print(f"Generating sound effect: {effect}")
print(f" Prompt: {prompt}")
print(f" Duration: {duration}s")
print(f" Using API: {use_api}")
audio_bytes = None
if use_api == "huggingface":
audio_bytes = _generate_via_huggingface(prompt, duration)
else:
raise ValueError(f"Unknown API: {use_api}")
if audio_bytes is None:
raise RuntimeError(f"Failed to generate sound effect: {effect}")
# Determine save location
if save_to_assets:
# Save to effects assets directory (preferred by audio.py)
assets_dir = os.path.join(os.path.dirname(__file__), "assets", "audio", "effects")
os.makedirs(assets_dir, exist_ok=True)
filename = f"{effect}.wav"
path = os.path.join(assets_dir, filename)
with open(path, "wb") as f:
f.write(audio_bytes)
print(f" Saved to: {path}")
else:
# Save to temporary file
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmpfile:
tmpfile.write(audio_bytes)
path = tmpfile.name
print(f" Saved to: {path}")
# Cache the path
_sound_cache[effect] = path
return path
def _generate_via_huggingface(prompt: str, duration: float, max_retries: int = 3) -> bytes:
"""
Generate audio using Hugging Face Inference API.
Uses facebook/audiogen-medium model for sound effects.
"""
headers = {}
if HF_API_TOKEN:
headers["Authorization"] = f"Bearer {HF_API_TOKEN}"
payload = {
"inputs": prompt,
"parameters": {
"duration": duration
}
}
for attempt in range(max_retries):
try:
print(f" Calling Hugging Face API (attempt {attempt + 1}/{max_retries})...")
response = requests.post(HF_API_URL, headers=headers, json=payload, timeout=60)
if response.status_code == 503:
# Model is loading, wait and retry
print(f" Model loading, waiting 10 seconds...")
time.sleep(10)
continue
if response.status_code == 200:
print(f" Success! Received {len(response.content)} bytes")
return response.content
else:
print(f" Error {response.status_code}: {response.text}")
if attempt < max_retries - 1:
time.sleep(5)
continue
else:
raise RuntimeError(f"API request failed: {response.status_code} - {response.text}")
except requests.exceptions.Timeout:
print(f" Request timed out")
if attempt < max_retries - 1:
time.sleep(5)
continue
else:
raise RuntimeError("API request timed out after multiple attempts")
except Exception as e:
print(f" Error: {e}")
if attempt < max_retries - 1:
time.sleep(5)
continue
else:
raise
return None
def get_sound_effect_path(effect: str) -> str:
"""
Get the path to a sound effect, generating it if necessary.
"""
return generate_sound_effect(effect)
def get_sound_effect_data_url(effect: str) -> str:
"""
Get a data URL for the sound effect, suitable for embedding in HTML.
"""
path = generate_sound_effect(effect)
with open(path, "rb") as f:
data = f.read()
encoded = base64.b64encode(data).decode()
return f"data:audio/wav;base64,{encoded}"
def generate_all_effects(save_to_assets: bool = True):
"""
Generate all sound effects defined in EFFECT_PROMPTS.
Args:
save_to_assets: If True, save to battlewords/assets/audio/effects/ directory
"""
print(f"\nGenerating {len(EFFECT_PROMPTS)} sound effects...")
print("=" * 60)
for effect_name in EFFECT_PROMPTS.keys():
try:
generate_sound_effect(effect_name, save_to_assets=save_to_assets)
print()
except Exception as e:
print(f"ERROR generating {effect_name}: {e}")
print()
print("=" * 60)
print("Sound effect generation complete!")
if __name__ == "__main__":
# Generate all sound effects when run as a script
generate_all_effects(save_to_assets=True) |