from transformers import pipeline
import gradio as gr
import matplotlib.pyplot as plt
# device=0 uses GPU if available (Runtime -> Change runtime type -> GPU, then rerun this)
classifier = pipeline("text-classification", model="j-hartmann/emotion-english-distilroberta-base", top_k=None, device=0)
transcriber = pipeline("automatic-speech-recognition", model="openai/whisper-tiny", device=0)
lap_numbers = [1, 2, 3, 4, 5, 6]
lap_times = [82.1, 81.8, 82.0, 85.4, 84.9, 82.3]
mood_map = {
'anger': 'stressed', 'fear': 'stressed', 'disgust': 'stressed', 'surprise': 'stressed',
'sadness': 'tired',
'joy': 'calm', 'neutral': 'calm'
}
mood_colors = {
'stressed': ('#ff4444', '#3a1414'),
'tired': ('#ffcc00', '#3a3314'),
'calm': ('#00cc66', '#143a24')
}
emotion_colors = {
'anger': '#ff4444', 'fear': '#ff8800', 'disgust': '#aa44ff', 'surprise': '#00ccff',
'sadness': '#4488ff', 'joy': '#00cc66', 'neutral': '#888888'
}
def get_advice(driver_mood, lap_times):
pace_drop = max(lap_times) - min(lap_times)
if driver_mood == 'stressed' and pace_drop > 2:
return "โ ๏ธ Driver is stressed AND losing pace. Consider a radio check-in or box call โ this combo often precedes a mistake."
elif driver_mood == 'stressed':
return "๐ Driver sounds stressed but pace is holding. Keep monitoring, no action needed yet."
elif driver_mood == 'tired':
return "๐ก Fatigue signs detected. Watch for late braking or missed apexes in the next few laps."
else:
return "โ
Driver sounds calm and in control. No intervention needed."
# Build the lap chart ONCE, not on every click - it doesn't change per audio clip
plt.style.use('dark_background')
_lap_fig, _ax = plt.subplots(figsize=(5, 4), dpi=80)
_worst_idx = lap_times.index(max(lap_times))
_ax.plot(lap_numbers, lap_times, marker='o', color='#00d4ff', linewidth=2, markersize=8, zorder=2)
_ax.fill_between(lap_numbers, lap_times, min(lap_times) - 1, color='#00d4ff', alpha=0.1)
_ax.scatter(lap_numbers[_worst_idx], lap_times[_worst_idx], color='#ff4444', s=150, zorder=3, label='Slowest lap')
_ax.set_xlabel("Lap Number", fontsize=11)
_ax.set_ylabel("Lap Time (s)", fontsize=11)
_ax.set_title("Lap Performance", fontsize=13, fontweight='bold')
_ax.grid(True, alpha=0.2)
_ax.legend()
_lap_fig.tight_layout()
def make_emotion_pie(emotion_scores):
fig, ax = plt.subplots(figsize=(5, 4), dpi=80)
labels = [e['label'] for e in emotion_scores]
scores = [e['score'] for e in emotion_scores]
colors = [emotion_colors.get(l, '#666666') for l in labels]
ax.pie(scores, labels=labels, colors=colors, autopct='%1.1f%%',
textprops={'fontsize': 9}, wedgeprops={'edgecolor': '#111', 'linewidth': 1})
ax.set_title("Emotion Breakdown", fontsize=13, fontweight='bold')
fig.tight_layout()
return fig
def mood_badge_html(driver_mood, raw_label, score):
fg, bg = mood_colors[driver_mood]
return f"""
{driver_mood.upper()}
raw model: {raw_label} ยท confidence {score:.2f}
"""
def analyze_clip_gradio(audio_file):
transcript = transcriber(audio_file)['text']
emotion_scores = classifier(transcript)[0]
top_emotion = max(emotion_scores, key=lambda x: x['score'])
driver_mood = mood_map.get(top_emotion['label'], 'calm')
badge = mood_badge_html(driver_mood, top_emotion['label'], top_emotion['score'])
advice = get_advice(driver_mood, lap_times)
pie = make_emotion_pie(emotion_scores)
return transcript, badge, advice, _lap_fig, pie
theme = gr.themes.Monochrome(primary_hue="red", secondary_hue="slate")
with gr.Blocks(title="The Silent Co-Driver", theme=theme) as demo:
gr.Markdown("# ๐๏ธ The Silent Co-Driver")
gr.Markdown("Upload a driver radio clip to detect stress and get race engineer advice.")
with gr.Row():
audio_input = gr.Audio(type="filepath", label="Radio Clip")
analyze_btn = gr.Button("๐ Analyze", variant="primary")
gr.Examples(examples=["driver_clip.wav"], inputs=audio_input, label="Try a sample clip")
with gr.Row():
with gr.Column():
transcript_out = gr.Textbox(label="Transcript")
mood_out = gr.HTML(label="Driver Mood")
advice_out = gr.Textbox(label="Engineer Advice")
with gr.Column():
chart_out = gr.Plot(label="Lap Performance")
pie_out = gr.Plot(label="Emotion Breakdown")
analyze_btn.click(analyze_clip_gradio, inputs=audio_input, outputs=[transcript_out, mood_out, advice_out, chart_out, pie_out])
demo.launch(share=True)
import json
import streamlit as st
with open("data.json") as f:
data = json.load(f)
# ---------- PAGE SETUP ----------
st.set_page_config(page_title="The Silent Co-Driver", page_icon="๐๏ธ", layout="wide")
st.title("๐๏ธ The Silent Co-Driver")
st.write("Reading driver stress from radio calls.")
st.divider()
# ---------- LOAD SAMPLE DATA (from data.json) ----------
# This lets you demo instantly using pre-made clips before your AI model is fully wired in
with open("data.json") as f:
sample_clips = json.load(f)
st.subheader("๐ป Sample Radio Clips")
st.write("Pick a pre-loaded clip to see the analysis instantly:")
clip_names = [clip["clip"] for clip in sample_clips]
selected_clip_name = st.selectbox("Choose a clip", clip_names)
# Find the selected clip's data
selected_clip = next(c for c in sample_clips if c["clip"] == selected_clip_name)
col1, col2, col3 = st.columns(3)
col1.metric("Lap Number", selected_clip["lap"])
col2.metric("Mood", selected_clip["mood"].upper())
col3.metric("Clip File", selected_clip["clip"])
st.write("**Transcript:**")
st.info(selected_clip["transcript"])
st.divider()
# ---------- UPLOAD YOUR OWN CLIP ----------
st.subheader("๐๏ธ Or Upload Your Own Radio Clip")
audio_file = st.file_uploader("Upload a .wav or .mp3 file", type=["wav", "mp3"])
if audio_file:
st.audio(audio_file) # lets you play the clip on the page
if st.button("Analyze Clip"):
with st.spinner("Listening to the radio call..."):
# Save uploaded file temporarily so the AI model can read it
with open("temp_audio.wav", "wb") as f:
f.write(audio_file.read())
# ๐ This is where your teammate's Hugging Face code plugs in
# Example (uncomment once the model functions are ready):
#
# from transformers import pipeline
# speech_to_text = pipeline("automatic-speech-recognition", model="openai/whisper-base")
# emotion_detector = pipeline("audio-classification", model="superb/wav2vec2-base-superb-er")
#
# transcript = speech_to_text("temp_audio.wav")["text"]
# mood = emotion_detector("temp_audio.wav")[0]["label"]
# Placeholder values until the model is connected
transcript = "Transcript will appear here once AI model is connected."
mood = "Unknown"
st.write("**Transcript:**")
st.info(transcript)
st.write("**Detected Mood:**")
st.warning(mood)
st.divider()
# ---------- STRESS VS LAP TIME CHART ----------
st.subheader("๐ Stress vs Lap Time")
# Replace this with real data once you have it (e.g. from all clips + lap times)
chart_data = {
"Lap 10": 88, "Lap 11": 89, "Lap 12": 95, "Lap 13": 91, "Lap 14": 90
}
st.line_chart(chart_data)