elevenlabs stuff
This commit is contained in:
BIN
scripts/data/audio/pit_call.mp3
Normal file
BIN
scripts/data/audio/pit_call.mp3
Normal file
Binary file not shown.
2
scripts/fetch_race_data.py
Normal file → Executable file
2
scripts/fetch_race_data.py
Normal file → Executable file
@@ -175,7 +175,7 @@ def prepare_telemetry_stream(telemetry: pd.DataFrame, sample_rate_hz: float = 10
|
||||
# Resample to target rate if needed
|
||||
telemetry = telemetry.copy()
|
||||
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
|
||||
telemetry['TimeMs'] = (telemetry['Time'].dt.total_seconds() * 1000).astype(int)
|
||||
|
||||
31
scripts/strategy_voice_integration.py
Normal file
31
scripts/strategy_voice_integration.py
Normal 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
90
scripts/voice_service.py
Normal 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}")
|
||||
Reference in New Issue
Block a user