BattleWords / battlewords /generate_sounds.py
Surn's picture
v0.2.8 - add sound effects, incorrect guess list
1dcc163
#!/usr/bin/env python3
"""
Standalone script to generate sound effects using Hugging Face API.
Uses only built-in Python libraries (no external dependencies).
"""
import os
import json
import urllib.request
import time
from pathlib import Path
# Load environment variables from .env if present
env_path = Path(__file__).parent / ".env"
if env_path.exists():
with open(env_path) as f:
for line in f:
if line.strip() and not line.startswith("#"):
key, _, value = line.strip().partition("=")
os.environ[key] = value
# Get Hugging Face API token from environment variable
HF_API_TOKEN = os.environ.get("HF_API_TOKEN")
if not HF_API_TOKEN:
print("Warning: HF_API_TOKEN not set in environment or .env file.")
# Using your UnlimitedMusicGen Gradio Space
SPACE_URL = "https://surn-unlimitedmusicgen.hf.space"
GRADIO_API_URL = f"{SPACE_URL}/api/predict"
GRADIO_STATUS_URL = f"{SPACE_URL}/call/predict/{{event_id}}"
# Sound effects to generate
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}
}
def generate_sound_effect_gradio(effect_name: str, prompt: str, duration: float, output_dir: Path) -> bool:
"""Generate a single sound effect using Gradio API (async call)."""
print(f"\nGenerating: {effect_name}")
print(f" Prompt: {prompt}")
print(f" Duration: {duration}s")
# Step 1: Submit generation request
payload = json.dumps({
"data": [prompt, duration]
}).encode('utf-8')
headers = {
"Content-Type": "application/json"
}
try:
print(f" Submitting request to Gradio API...")
# Submit the job
req = urllib.request.Request(GRADIO_API_URL, data=payload, headers=headers, method='POST')
with urllib.request.urlopen(req, timeout=30) as response:
if response.status == 200:
result = json.loads(response.read().decode())
event_id = result.get("event_id")
if not event_id:
print(f" βœ— No event_id returned")
return False
print(f" Job submitted, event_id: {event_id}")
# Step 2: Poll for results
status_url = GRADIO_STATUS_URL.format(event_id=event_id)
for poll_attempt in range(30): # Poll for up to 5 minutes
time.sleep(10)
print(f" Polling for results (attempt {poll_attempt + 1}/30)...")
status_req = urllib.request.Request(status_url, headers=headers)
try:
with urllib.request.urlopen(status_req, timeout=30) as status_response:
# Gradio returns streaming events, read until we get the result
for line in status_response:
line = line.decode('utf-8').strip()
if line.startswith('data: '):
event_data = json.loads(line[6:]) # Remove 'data: ' prefix
if event_data.get('msg') == 'process_completed':
# Get the audio file URL
output_data = event_data.get('output', {}).get('data', [])
if output_data and len(output_data) > 0:
audio_url = output_data[0].get('url')
if audio_url:
# Download the audio file
full_audio_url = f"https://surn-unlimitedmusicgen.hf.space{audio_url}"
print(f" Downloading from: {full_audio_url}")
audio_req = urllib.request.Request(full_audio_url)
with urllib.request.urlopen(audio_req, timeout=30) as audio_response:
audio_data = audio_response.read()
# Save to file
output_path = output_dir / f"{effect_name}.wav"
with open(output_path, "wb") as f:
f.write(audio_data)
print(f" βœ“ Success! Saved to: {output_path}")
print(f" File size: {len(audio_data)} bytes")
return True
elif event_data.get('msg') == 'process_error':
print(f" βœ— Generation error: {event_data.get('output')}")
return False
except Exception as poll_error:
print(f" Polling error: {poll_error}")
continue
print(f" βœ— Timeout waiting for generation")
return False
else:
print(f" βœ— Error {response.status}: {response.read().decode()}")
return False
except Exception as e:
print(f" βœ— Error: {e}")
return False
def main():
"""Generate all sound effects."""
print("=" * 70)
print("Sound Effects Generator for BattleWords")
print("=" * 70)
print(f"Using UnlimitedMusicGen Gradio API")
print(f"API URL: {GRADIO_API_URL}")
print(f"\nGenerating {len(EFFECT_PROMPTS)} sound effects...\n")
# Create output directory
output_dir = Path(__file__).parent / "assets" / "audio"
output_dir.mkdir(parents=True, exist_ok=True)
print(f"Output directory: {output_dir}\n")
# Generate each effect
success_count = 0
for effect_name, config in EFFECT_PROMPTS.items():
if generate_sound_effect_gradio(
effect_name,
config["prompt"],
config["duration"],
output_dir
):
success_count += 1
# Small delay between requests
if effect_name != list(EFFECT_PROMPTS.keys())[-1]:
print(" Waiting 5 seconds before next request...")
time.sleep(5)
print("\n" + "=" * 70)
print(f"Generation complete! {success_count}/{len(EFFECT_PROMPTS)} successful")
print("=" * 70)
if success_count == len(EFFECT_PROMPTS):
print("\nβœ“ All sound effects generated successfully!")
else:
print(f"\n⚠ {len(EFFECT_PROMPTS) - success_count} sound effects failed to generate")
if __name__ == "__main__":
main()