elevenlabs stuff

This commit is contained in:
Yahya Kousa
2025-10-19 00:21:43 -05:00
parent 3c961efaff
commit 710b4bad57
6 changed files with 128 additions and 1 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.env

View File

@@ -1,3 +1,8 @@
fastapi==0.115.2 fastapi==0.115.2
uvicorn==0.31.1 uvicorn==0.31.1
httpx==0.27.2 httpx==0.27.2
elevenlabs==2.18.0
python-dotenv==1.1.1
fastf1==3.6.1
pandas==2.3.3
requests==2.32.5

Binary file not shown.

2
scripts/fetch_race_data.py Normal file → Executable file
View File

@@ -175,7 +175,7 @@ def prepare_telemetry_stream(telemetry: pd.DataFrame, sample_rate_hz: float = 10
# Resample to target rate if needed # Resample to target rate if needed
telemetry = telemetry.copy() telemetry = telemetry.copy()
telemetry['Time'] = pd.to_timedelta(telemetry['Time']) telemetry['Time'] = pd.to_timedelta(telemetry['Time'])
telemetry = telemetry.sort_values('Time') telemetry = telemetry.sort_values(['LapNumber', 'Time'])
# Convert to milliseconds for easier time tracking # Convert to milliseconds for easier time tracking
telemetry['TimeMs'] = (telemetry['Time'].dt.total_seconds() * 1000).astype(int) telemetry['TimeMs'] = (telemetry['Time'].dt.total_seconds() * 1000).astype(int)

View File

@@ -0,0 +1,31 @@
"""
Example: Integrate voice feedback with AI strategy decisions
"""
from voice_service import RaceEngineerVoice
from pathlib import Path
def announce_strategy_decision(decision: dict):
"""
Convert AI strategy decision to voice announcement.
Args:
decision: Dict with keys like 'action', 'tire_compound', 'lap'
"""
engineer = RaceEngineerVoice()
# Generate appropriate message
if decision['action'] == 'pit':
text = f"Box this lap for {decision['tire_compound']}. In, in, in!"
elif decision['action'] == 'stay_out':
text = "Stay out. These tires are still competitive"
elif decision['action'] == 'push':
text = f"Push mode. We need {decision.get('gap_target', 3)} seconds"
else:
text = decision.get('message', 'Copy that')
# Synthesize and save
audio_path = Path(f"data/audio/lap_{decision.get('lap', 0)}_command.mp3")
engineer.synthesize_strategy_message(text, audio_path)
return audio_path

90
scripts/voice_service.py Normal file
View File

@@ -0,0 +1,90 @@
"""
ElevenLabs Voice Service for F1 Race Engineer
Converts AI strategy recommendations to natural speech.
"""
import os
from pathlib import Path
from elevenlabs.client import ElevenLabs
from elevenlabs import save
from dotenv import load_dotenv
load_dotenv()
class RaceEngineerVoice:
def __init__(self, voice_id: str = "mbBupyLcEivjpxh8Brkf"): # Default: Rachel
"""
Initialize ElevenLabs voice service.
Args:
voice_id: ElevenLabs voice ID (Rachel is default, professional female voice)
"""
self.client = ElevenLabs(api_key=os.getenv("ELEVENLABS_API_KEY"))
self.voice_id = voice_id
def synthesize_strategy_message(
self,
text: str,
output_path: Path,
stability: float = 0.4,
similarity_boost: float = 0.95
) -> Path:
"""
Convert strategy text to speech.
Args:
text: Message to synthesize (e.g., "Box this lap, box this lap")
output_path: Where to save audio file
stability: Voice stability (0-1)
similarity_boost: Voice similarity (0-1)
Returns:
Path to generated audio file
"""
audio = self.client.text_to_speech.convert(
voice_id=self.voice_id,
text=text,
model_id="eleven_multilingual_v2", # Fast, low-latency model
voice_settings={
"stability": stability,
"similarity_boost": similarity_boost,
"style": 0.7,
"use_speaker_boost": True
}
)
# Save audio
output_path = Path(output_path)
output_path.parent.mkdir(parents=True, exist_ok=True)
# Write the audio generator to file
save(audio, str(output_path))
return output_path
def race_engineer_commands(self) -> dict:
"""Common F1 race engineer commands"""
return {
"box_now": "Box this lap, box this lap",
"stay_out": "Stay out, stay out. We're looking good on these tires",
"push": "Push now, push. We need to build a gap",
"save_tires": "Manage those tires. Lift and coast into the corners",
"traffic_ahead": "Traffic ahead. Blue flags expected",
"safety_car": "Safety car, safety car. We're checking strategy",
"undercut_threat": "Undercut threat from behind. We may need to respond",
"fastest_lap": "You're on for fastest lap. Push this lap",
}
# Example usage
if __name__ == "__main__":
engineer = RaceEngineerVoice()
# Generate pit call
audio_file = engineer.synthesize_strategy_message(
text="Mama Mia! That was a close one! Let's keep going and finish this out strong, like a cheetah on espresso!",
output_path=Path("data/audio/pit_call.mp3")
)
print(f"✓ Generated: {audio_file}")