Spaces:
Running
Running
Add 3 new demo scenarios + Coming Soon badge + lazy generation
Browse filesNew demos: Wake Word (Alexa + beep), Synthetic/Robotic Voice,
Two Different Speakers. Total 6 demo scenarios. Demos generate
on first app startup instead of build time. Added Batch Analysis
Coming Soon badge below Upload section.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- app.py +4 -1
- generate_demos.py +100 -0
app.py
CHANGED
|
@@ -298,7 +298,10 @@ def render_analyzer_tab():
|
|
| 298 |
demo_files = {
|
| 299 |
"Clean Exam (No Fraud)": "demo_clean_exam.wav",
|
| 300 |
"Reading Pattern + Long Pause": "demo_reading_fraud.wav",
|
| 301 |
-
"
|
|
|
|
|
|
|
|
|
|
| 302 |
}
|
| 303 |
available_demos = {
|
| 304 |
name: os.path.join(samples_dir, fname)
|
|
|
|
| 298 |
demo_files = {
|
| 299 |
"Clean Exam (No Fraud)": "demo_clean_exam.wav",
|
| 300 |
"Reading Pattern + Long Pause": "demo_reading_fraud.wav",
|
| 301 |
+
"Coaching (Whispered Prompts)": "demo_coaching_fraud.wav",
|
| 302 |
+
"Wake Word (Alexa + Beep)": "demo_wake_word.wav",
|
| 303 |
+
"Synthetic / Robotic Voice": "demo_synthetic_voice.wav",
|
| 304 |
+
"Two Different Speakers": "demo_two_speakers.wav",
|
| 305 |
}
|
| 306 |
available_demos = {
|
| 307 |
name: os.path.join(samples_dir, fname)
|
generate_demos.py
CHANGED
|
@@ -85,6 +85,103 @@ def generate_coaching_fraud():
|
|
| 85 |
)
|
| 86 |
|
| 87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
def main():
|
| 89 |
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
| 90 |
|
|
@@ -92,6 +189,9 @@ def main():
|
|
| 92 |
"demo_clean_exam.wav": generate_clean_exam,
|
| 93 |
"demo_reading_fraud.wav": generate_reading_fraud,
|
| 94 |
"demo_coaching_fraud.wav": generate_coaching_fraud,
|
|
|
|
|
|
|
|
|
|
| 95 |
}
|
| 96 |
|
| 97 |
for filename, generator in demos.items():
|
|
|
|
| 85 |
)
|
| 86 |
|
| 87 |
|
| 88 |
+
def generate_wake_word():
|
| 89 |
+
"""Scenario 4: Wake word - someone asks Alexa mid-exam."""
|
| 90 |
+
import numpy as np
|
| 91 |
+
|
| 92 |
+
main1 = _tts("I think the most important factor in choosing a career is job satisfaction.")
|
| 93 |
+
main2 = _tts("Because if you enjoy what you do, you'll be more productive and motivated.")
|
| 94 |
+
main3 = _tts("Sorry, what was I saying? Oh yes, career satisfaction is crucial for long term happiness.")
|
| 95 |
+
main4 = _tts("And that's why I believe people should follow their passion when choosing a career.")
|
| 96 |
+
|
| 97 |
+
# "Alexa" wake word spoken quietly
|
| 98 |
+
alexa_call = _tts("Alexa, what is career satisfaction", lang="en") - 12
|
| 99 |
+
|
| 100 |
+
# Synthetic beep tone (1kHz, 0.3s) to simulate device response
|
| 101 |
+
sample_rate = 16000
|
| 102 |
+
t = np.linspace(0, 0.3, int(sample_rate * 0.3))
|
| 103 |
+
beep = (np.sin(2 * np.pi * 1000 * t) * 0.15 * 32767).astype(np.int16)
|
| 104 |
+
beep_seg = AudioSegment(beep.tobytes(), frame_rate=sample_rate, sample_width=2, channels=1)
|
| 105 |
+
|
| 106 |
+
return (
|
| 107 |
+
main1
|
| 108 |
+
+ _silence(1000)
|
| 109 |
+
+ main2
|
| 110 |
+
+ _silence(1500)
|
| 111 |
+
+ alexa_call
|
| 112 |
+
+ _silence(400)
|
| 113 |
+
+ beep_seg
|
| 114 |
+
+ _silence(2000)
|
| 115 |
+
+ main3
|
| 116 |
+
+ _silence(800)
|
| 117 |
+
+ main4
|
| 118 |
+
)
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def generate_synthetic_voice():
|
| 122 |
+
"""Scenario 5: Synthetic/robotic voice with beep tones."""
|
| 123 |
+
import numpy as np
|
| 124 |
+
|
| 125 |
+
# Use slow TTS to sound more robotic
|
| 126 |
+
robo1 = _tts("The answer to question number one is as follows.", slow=True)
|
| 127 |
+
robo2 = _tts("Climate change is caused by greenhouse gas emissions from human activities.", slow=True)
|
| 128 |
+
robo3 = _tts("The main sources include transportation, industry, and agriculture.", slow=True)
|
| 129 |
+
robo4 = _tts("In conclusion, reducing emissions requires global cooperation.", slow=True)
|
| 130 |
+
|
| 131 |
+
# Generate beep tones between segments (synthetic indicator)
|
| 132 |
+
sample_rate = 16000
|
| 133 |
+
def make_beep(freq, dur):
|
| 134 |
+
t = np.linspace(0, dur, int(sample_rate * dur))
|
| 135 |
+
tone = (np.sin(2 * np.pi * freq * t) * 0.2 * 32767).astype(np.int16)
|
| 136 |
+
return AudioSegment(tone.tobytes(), frame_rate=sample_rate, sample_width=2, channels=1)
|
| 137 |
+
|
| 138 |
+
beep_hi = make_beep(880, 0.15)
|
| 139 |
+
beep_lo = make_beep(440, 0.15)
|
| 140 |
+
double_beep = beep_hi + _silence(100) + beep_lo
|
| 141 |
+
|
| 142 |
+
return (
|
| 143 |
+
double_beep
|
| 144 |
+
+ _silence(300)
|
| 145 |
+
+ robo1
|
| 146 |
+
+ _silence(400)
|
| 147 |
+
+ robo2
|
| 148 |
+
+ double_beep
|
| 149 |
+
+ _silence(500)
|
| 150 |
+
+ robo3
|
| 151 |
+
+ _silence(400)
|
| 152 |
+
+ robo4
|
| 153 |
+
+ _silence(200)
|
| 154 |
+
+ double_beep
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def generate_two_speakers():
|
| 159 |
+
"""Scenario 6: Two distinct speakers taking turns (impersonation/proxy)."""
|
| 160 |
+
# Speaker A - normal pace, English
|
| 161 |
+
a1 = _tts("Hello, my name is John and I'm here to take the speaking test today.")
|
| 162 |
+
a2 = _tts("I think that learning a second language is very important in today's world.")
|
| 163 |
+
a3 = _tts("For example, it helps you communicate with people from different countries.")
|
| 164 |
+
|
| 165 |
+
# Speaker B - different accent (UK English) to sound like a different person
|
| 166 |
+
b1 = _tts("Right, so the next topic is about technology in education.", lang="en-uk")
|
| 167 |
+
b2 = _tts("Technology has completely changed the way students learn and interact with content.", lang="en-uk")
|
| 168 |
+
b3 = _tts("Online courses and digital tools make education more accessible to everyone.", lang="en-uk")
|
| 169 |
+
|
| 170 |
+
return (
|
| 171 |
+
a1
|
| 172 |
+
+ _silence(1000)
|
| 173 |
+
+ a2
|
| 174 |
+
+ _silence(800)
|
| 175 |
+
+ a3
|
| 176 |
+
+ _silence(2500) # Longer pause as speakers switch
|
| 177 |
+
+ b1
|
| 178 |
+
+ _silence(700)
|
| 179 |
+
+ b2
|
| 180 |
+
+ _silence(600)
|
| 181 |
+
+ b3
|
| 182 |
+
)
|
| 183 |
+
|
| 184 |
+
|
| 185 |
def main():
|
| 186 |
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
| 187 |
|
|
|
|
| 189 |
"demo_clean_exam.wav": generate_clean_exam,
|
| 190 |
"demo_reading_fraud.wav": generate_reading_fraud,
|
| 191 |
"demo_coaching_fraud.wav": generate_coaching_fraud,
|
| 192 |
+
"demo_wake_word.wav": generate_wake_word,
|
| 193 |
+
"demo_synthetic_voice.wav": generate_synthetic_voice,
|
| 194 |
+
"demo_two_speakers.wav": generate_two_speakers,
|
| 195 |
}
|
| 196 |
|
| 197 |
for filename, generator in demos.items():
|