Spaces:
Sleeping
Sleeping
File size: 25,449 Bytes
a822e58 a86ed1a c9d301e a822e58 945d533 cf45c32 945d533 cf45c32 945d533 cf45c32 945d533 cf45c32 945d533 cf45c32 945d533 cf45c32 945d533 cf45c32 945d533 cf45c32 945d533 cf45c32 945d533 a86ed1a 945d533 c0e0664 945d533 c0e0664 945d533 c0e0664 a86ed1a 945d533 c0e0664 945d533 c0e0664 945d533 c0e0664 945d533 c0e0664 945d533 c0e0664 945d533 a86ed1a 945d533 a86ed1a 945d533 c0e0664 945d533 c0e0664 945d533 a86ed1a c0e0664 945d533 a86ed1a a7003ac c0e0664 c9d301e c0e0664 945d533 c9d301e b388dc0 c0e0664 b388dc0 c0e0664 a7003ac c0e0664 945d533 c9d301e b388dc0 c0e0664 c9d301e c0e0664 c9d301e 945d533 c0e0664 945d533 c0e0664 945d533 c0e0664 945d533 c0e0664 945d533 c0e0664 945d533 c0e0664 945d533 c0e0664 945d533 c0e0664 18f03e6 c0e0664 a7003ac 945d533 a86ed1a cf45c32 a86ed1a cf45c32 b388dc0 cf45c32 aef9194 cf45c32 aef9194 cf45c32 945d533 aef9194 945d533 aef9194 945d533 aef9194 cf45c32 aef9194 c0e0664 cf45c32 aef9194 945d533 aef9194 c0e0664 cf45c32 b388dc0 945d533 b388dc0 945d533 a86ed1a a7003ac 945d533 a7003ac a86ed1a a7003ac a86ed1a 945d533 c9d301e 945d533 a86ed1a c9d301e a7003ac c9d301e a7003ac a86ed1a c9d301e a86ed1a c9d301e 945d533 a86ed1a c9d301e 945d533 c9d301e cf45c32 a86ed1a c9d301e a86ed1a cf45c32 a86ed1a b388dc0 945d533 b388dc0 945d533 b388dc0 7c36044 945d533 b388dc0 cf45c32 c0e0664 cf45c32 c0e0664 cf45c32 aef9194 945d533 aef9194 18f03e6 cf45c32 945d533 18f03e6 945d533 aef9194 945d533 cf45c32 aef9194 cf45c32 c9d301e 7c36044 cf45c32 7c36044 c9d301e 7c36044 c9d301e 7c36044 c9d301e b388dc0 c9d301e b388dc0 cf45c32 a86ed1a 3dfda62 fd2a5d3 cf45c32 945d533 f39d666 c0e0664 f39d666 945d533 f39d666 a86ed1a ddc04c6 945d533 c0e0664 945d533 cf45c32 945d533 cf45c32 945d533 f39d666 945d533 f39d666 c9d301e 945d533 f39d666 a86ed1a cf45c32 f39d666 945d533 b388dc0 945d533 f39d666 945d533 b388dc0 945d533 a86ed1a c9d301e c0e0664 a86ed1a 945d533 9054c84 c0e0664 c9d301e c0e0664 9054c84 cf45c32 9054c84 c9d301e 945d533 cf45c32 945d533 c9d301e 945d533 cf45c32 a822e58 0ecc9ec cf45c32 a822e58 9054c84 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 |
import gradio as gr
import random
import re
import json
# --- Strudel Code Generation and Playback Functions ---
def generate_simple_beat():
"""Generates a very simple Strudel beat as a starting point."""
code = '''// π΅ Simple Voice-Generated Beat
// Try saying: "Make a beat" or "Play music"
// Then click PLAY or press Ctrl+Enter!
stack(
sound("bd").struct("x ~ x ~").gain(0.8),
sound("sd").struct("~ x ~ x").gain(0.7),
sound("hh").struct("x x x x").gain(0.4)
).cpm(120)'''
return "β
Simple beat generated!", code
def clear_code():
"""Clears the Strudel code editor to a default state."""
initial_code = '''// π΅ Voice Strudel Synth
// Speak a command to generate music!
// Try: "Make a beat"
// Then: "Play music" (or Ctrl+Enter)
stack(
// Your generated code will appear here...
).cpm(120) // Master tempo
'''
return "ποΈ Code cleared - Ready for new ideas!", initial_code
# --- Voice Command Processing ---
def process_voice_command(command_text, current_code):
"""
Processes a voice command to generate or control Strudel code.
This version is highly simplified for bare-minimum functionality.
"""
if not command_text:
return "β No voice command received.", current_code
command_lower = command_text.lower()
if any(word in command_lower for word in ["make a beat", "generate beat", "create music", "new beat"]):
return generate_simple_beat()
elif "play music" in command_lower or "start music" in command_lower:
# This message will instruct the user to use the play button/shortcut
return "βΆοΈ Click PLAY MUSIC or press Ctrl+Enter to hear!", current_code
elif "stop music" in command_lower:
# This message will instruct the user to use the stop button/shortcut
return "βΉοΈ Click STOP MUSIC or press Ctrl+Space!", current_code
elif "clear code" in command_lower or "reset" in command_lower or "start over" in command_lower:
return clear_code()
else:
return "π€ Command not recognized. Try: 'Make a beat', 'Play music', 'Stop music', 'Clear code'.", current_code
# --- Gradio Interface Setup ---
def create_interface():
# --- Simplified CSS for a clean, futuristic look ---
custom_css = """
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');
body {
font-family: 'Inter', sans-serif;
overflow: hidden; /* Hide scrollbars due to matrix rain */
background: #000;
}
.gradio-container {
background: linear-gradient(135deg, #0a0a0a, #1a1a1a);
color: #00ff00;
font-family: 'Share Tech Mono', monospace !important;
min-height: 100vh;
border-radius: 15px;
box-shadow: 0 0 50px rgba(0, 255, 0, 0.3);
padding: 20px;
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
h1 {
font-size: 3.5em;
color: #00ff00;
text-shadow: 0 0 15px rgba(0, 255, 0, 0.7);
animation: neon-flicker 1.5s infinite alternate;
text-align: center;
margin-bottom: 5px;
}
p {
color: #00ee00;
font-size: 1.5em;
text-shadow: 0 0 10px #00ff00;
text-align: center;
margin-top: 0;
margin-bottom: 30px;
}
@keyframes neon-flicker {
0% { opacity: 1; text-shadow: 0 0 15px rgba(0, 255, 0, 0.7); }
100% { opacity: 0.9; text-shadow: 0 0 20px rgba(0, 255, 0, 0.9), 0 0 30px rgba(0, 255, 0, 0.5); }
}
.gr-textbox, .gr-code {
background: rgba(0, 10, 0, 0.7) !important;
border: 1px solid #00ff00 !important;
color: #00ff41 !important;
border-radius: 10px !important;
box-shadow: inset 0 0 8px rgba(0, 255, 0, 0.3);
padding: 15px;
margin-bottom: 20px;
width: 100%;
}
.gr-code textarea {
background: rgba(0, 0, 0, 0.9) !important;
color: #00ff41 !important;
font-family: 'Fira Code', 'Share Tech Mono', monospace !important;
font-size: 15px !important;
line-height: 1.4;
}
.gr-button {
background: linear-gradient(45deg, #008800, #00bb00) !important;
border: 2px solid #33ff33 !important;
color: #00ff00 !important;
font-weight: bold !important;
text-shadow: 0 0 10px #00ff00 !important;
border-radius: 8px !important;
transition: all 0.2s ease-in-out !important;
box-shadow: 0 0 10px rgba(51, 255, 51, 0.7);
padding: 12px 25px;
text-transform: uppercase;
letter-spacing: 1px;
font-size: 1.1em;
margin: 5px;
}
.gr-button:hover {
background: linear-gradient(45deg, #00bb00, #00ee00) !important;
box-shadow: 0 0 30px rgba(51, 255, 51, 1);
transform: translateY(-2px) scale(1.02);
}
#voice-status {
background: rgba(0, 30, 0, 0.5) !important;
border: 2px solid #00ff00 !important;
border-radius: 12px !important;
padding: 15px !important;
text-align: center;
font-size: 1.2em;
font-weight: bold;
box-shadow: 0 0 15px rgba(0, 255, 0, 0.5);
animation: pulse 2s infinite alternate;
}
.listening {
animation: listening-pulse 1s infinite alternate !important;
background: rgba(100, 0, 0, 0.3) !important;
border-color: #ff0000 !important;
box-shadow: 0 0 25px rgba(255, 0, 0, 0.7) !important;
}
@keyframes pulse {
0% { opacity: 1; box-shadow: 0 0 15px rgba(0, 255, 0, 0.5); }
100% { opacity: 0.8; box-shadow: 0 0 25px rgba(0, 255, 0, 0.8); }
}
@keyframes listening-pulse {
0% { box-shadow: 0 0 15px #ff0000; }
100% { box-shadow: 0 0 40px #ff0000; }
}
.instructions {
background: rgba(0, 15, 0, 0.6) !important;
border: 1px dashed #00ff00 !important;
border-radius: 15px !important;
padding: 25px !important;
margin: 30px auto; /* Centered */
max-width: 800px;
box-shadow: 0 0 20px rgba(0, 255, 0, 0.2);
}
.instructions h3 {
color: #33ff33;
text-shadow: none;
text-align: center;
margin-bottom: 15px;
}
.instructions ul {
list-style: none;
padding-left: 0;
margin-bottom: 15px;
}
.instructions ul li::before {
content: 'Β» ';
color: #00ff00;
font-weight: bold;
margin-right: 5px;
}
.instructions p {
font-size: 1em;
color: #ccffcc;
text-shadow: none;
}
#matrix-canvas {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: radial-gradient(ellipse at center, rgba(0,20,0,0.8) 0%, rgba(0,0,0,0.9) 100%);
z-index: -2;
pointer-events: none;
}
.gr-row, .gr-column {
width: 100%; /* Make rows/columns take full width */
max-width: 900px; /* Constrain main content width */
margin: 0 auto; /* Center content */
}
.main-controls {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
}
.control-buttons {
display: flex;
justify-content: center;
width: 100%;
}
"""
# --- JavaScript for Voice Recognition and Strudel.js Integration ---
voice_strudel_js = """
function() {
console.log('π΅ Initializing Voice Recognition and Strudel.js...');
let recognition = null;
let isListening = false;
let strudelPlayer = null;
let strudelReady = false;
// Initialize Strudel.js Player
async function initStrudel() {
if (strudelReady) {
console.log("πΆ Strudel.js already initialized.");
return true;
}
try {
// Using a direct import from unpkg for the 'strudel' package itself
const { default: Strudel } = await import('https://unpkg.com/strudel/strudel.js');
// Create or resume AudioContext
if (!window.audioContext || window.audioContext.state === 'closed') {
window.audioContext = new (window.AudioContext || window.webkitAudioContext)();
console.log("New AudioContext created.");
}
// Attempt to resume audio context, crucial for autoplay policies
if (window.audioContext.state === 'suspended') {
await window.audioContext.resume();
console.log("AudioContext resumed during initialization.");
}
strudelPlayer = Strudel.Player({ audioContext: window.audioContext }); // Pass the existing context
strudelReady = true;
console.log("πΆ Strudel.js player created and initialized successfully!");
return true;
} catch (error) {
console.error("Failed to load or initialize Strudel.js:", error);
const statusElement = document.querySelector('#audio-status textarea');
if (statusElement) {
statusElement.value = `β Audio Engine Error: ${error.message}. Check browser console for details.`;
}
return false;
}
}
// Check if browser supports speech recognition
if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
recognition = new SpeechRecognition();
recognition.continuous = false;
recognition.interimResults = false;
recognition.lang = 'en-US';
recognition.maxAlternatives = 1;
console.log('β
Speech Recognition Available');
} else {
console.log('β Speech Recognition Not Available. Please use a Chromium-based browser (Chrome, Edge).');
}
// Matrix rain effect (simplified and integrated)
function createMatrixRain() {
const existingCanvas = document.querySelector('#matrix-canvas');
if (existingCanvas) existingCanvas.remove();
const canvas = document.createElement('canvas');
canvas.id = 'matrix-canvas';
canvas.style.cssText = `
position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
background: radial-gradient(ellipse at center, rgba(0,20,0,0.8) 0%, rgba(0,0,0,0.9) 100%);
z-index: -2; pointer-events: none;
`;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789βͺβ«β¬β©π΅πΆβ‘ππ€π§';
const fontSize = 16;
let columns, drops;
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
columns = Math.floor(canvas.width / fontSize);
drops = Array(columns).fill(1);
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
function draw() {
ctx.fillStyle = 'rgba(0, 0, 0, 0.08)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
gradient.addColorStop(0, '#00ff41');
gradient.addColorStop(0.5, '#00aa00');
gradient.addColorStop(1, 'rgba(0, 255, 0, 0.1)');
ctx.fillStyle = gradient;
ctx.font = `${fontSize}px 'Share Tech Mono', monospace`;
for (let i = 0; i < drops.length; i++) {
const text = chars[Math.floor(Math.random() * chars.length)];
const x = i * fontSize;
const y = drops[i] * fontSize;
if (text.match(/[βͺβ«β¬β©π΅πΆβ‘ππ€π§]/)) {
ctx.shadowColor = '#00ff41'; ctx.shadowBlur = 20;
} else { ctx.shadowBlur = 0; }
ctx.fillText(text, x, y);
if (y * fontSize > canvas.height && Math.random() > 0.975) {
drops[i] = 0;
}
drops[i]++;
}
}
setInterval(draw, 60);
}
createMatrixRain(); // Initialize matrix effect
// Start voice recognition
window.startVoiceRecognition = function() {
if (!recognition) {
return "β Speech recognition not supported in this browser. Try Chrome/Edge.";
}
if (isListening) {
return "π€ Already listening...";
}
isListening = true;
recognition.onstart = function() {
console.log('π€ Voice recognition started');
const statusElement = document.querySelector('#voice-status textarea');
if (statusElement) {
statusElement.value = "π€ LISTENING... Speak your command now!";
statusElement.parentElement.classList.add('listening');
}
};
recognition.onresult = function(event) {
const transcript = event.results[0][0].transcript;
console.log('π£οΈ Voice command:', transcript);
const voiceInputs = document.querySelectorAll('textarea');
for (let input of voiceInputs) {
if (input.placeholder && input.placeholder.includes('Voice commands')) {
input.value = transcript;
input.dispatchEvent(new Event('input', { bubbles: true }));
break;
}
}
setTimeout(() => {
const processBtn = Array.from(document.querySelectorAll('button')).find(
button => button.textContent.includes('PROCESS') || button.querySelector('span')?.textContent.includes('PROCESS')
);
if (processBtn) processBtn.click();
}, 100);
};
recognition.onerror = function(event) {
console.error('π« Voice recognition error:', event.error);
const statusElement = document.querySelector('#voice-status textarea');
if (statusElement) {
statusElement.value = `β Error: ${event.error}. Please try again.`;
statusElement.parentElement.classList.remove('listening');
}
isListening = false;
};
recognition.onend = function() {
console.log('π€ Voice recognition ended');
const statusElement = document.querySelector('#voice-status textarea');
if (statusElement) {
if (statusElement.value.includes('LISTENING')) {
statusElement.value = "β
Voice command captured! Processing...";
}
statusElement.parentElement.classList.remove('listening');
}
isListening = false;
};
try {
recognition.start();
return "π€ Voice recognition started - speak now!";
} catch (error) {
console.error('Failed to start recognition:', error);
isListening = false;
return "β Failed to start voice recognition";
}
};
// Stop voice recognition
window.stopVoiceRecognition = function() {
if (recognition && isListening) {
recognition.stop();
isListening = false;
return "βΉοΈ Voice recognition stopped";
}
return "βΉοΈ Voice recognition not active";
};
// --- Strudel Playback Functions ---
window.playStrudelCode = async function(code) {
const isInitialized = await initStrudel();
if (!isInitialized) {
return "β Strudel audio engine could not be initialized.";
}
try {
if (strudelPlayer && strudelPlayer.stop) {
strudelPlayer.stop();
console.log("Previous Strudel pattern stopped.");
}
if (window.audioContext && window.audioContext.state === 'suspended') {
await window.audioContext.resume();
console.log("AudioContext resumed on user interaction for playback.");
}
if (strudelPlayer && strudelPlayer.setSynth && strudelPlayer.play) {
await strudelPlayer.setSynth(code);
strudelPlayer.play();
console.log("βΆοΈ Playing Strudel code.");
return "βΆοΈ Playing music...";
} else {
console.error("Strudel player methods (setSynth, play) not available.");
return "β Strudel player not fully ready for playback.";
}
} catch (error) {
console.error("Error playing Strudel code:", error);
let errorMessage = `β Audio Error: ${error.message}.`;
if (error.message.includes("Unexpected token") || error.message.includes("SyntaxError")) {
errorMessage = "β Strudel code syntax error. Check code!";
} else if (error.message.includes("Failed to load module")) {
errorMessage = "β Strudel.js library failed to load. Check browser console for network/CORS issues.";
} else if (error.message.includes("AudioContext") || error.message.includes("Web Audio API")) {
errorMessage = "β Browser audio engine issue. Try refreshing.";
}
return errorMessage;
}
};
window.stopStrudelCode = function() {
if (strudelPlayer && strudelReady && strudelPlayer.stop) {
strudelPlayer.stop();
console.log("βΉοΈ Strudel music stopped.");
return "βΉοΈ Music stopped";
}
return "βΉοΈ Music not active";
};
// Add keyboard shortcuts
document.addEventListener('keydown', function(e) {
if (e.ctrlKey || e.metaKey) { // Ctrl for Windows/Linux, Cmd for Mac
switch(e.key.toLowerCase()) {
case 'enter':
e.preventDefault();
const playBtn = Array.from(document.querySelectorAll('button')).find(
button => button.textContent.includes('PLAY') || button.querySelector('span')?.textContent.includes('PLAY')
);
if (playBtn) playBtn.click();
break;
case ' ':
e.preventDefault();
const stopBtn = Array.from(document.querySelectorAll('button')).find(
button => button.textContent.includes('STOP') || button.querySelector('span')?.textContent.includes('STOP')
);
if (stopBtn) stopBtn.click();
break;
case 'm':
e.preventDefault();
window.startVoiceRecognition();
break;
}
}
});
console.log('π΅ Voice-Controlled Strudel Generator Ready!');
console.log('πΌ Shortcuts: Ctrl+Enter (Play), Ctrl+Space (Stop), Ctrl+M (Voice)');
return "π΅ Real Voice Recognition and Strudel.js Initialized!";
}
"""
# --- Gradio Interface Layout ---
with gr.Blocks(css=custom_css, js=voice_strudel_js, title="π΅ Voice Strudel Synth") as interface:
# Header Section
gr.HTML("""
<div style="text-align: center; padding: 20px;">
<h1>π΅ VOICE STRUDEL SYNTH</h1>
<p>Speak Your Beats Into Existence β Code, See, Hear!</p>
</div>
""")
with gr.Column(elem_classes="main-controls"):
# Voice Input and Status
voice_status = gr.Textbox(
value="π€ Ready - Click 'Start Voice' or Ctrl+M!",
label="VOICE STATUS",
elem_id="voice-status",
interactive=False,
max_lines=2
)
with gr.Row(elem_classes="control-buttons"):
start_voice_btn = gr.Button("π€ START VOICE", variant="primary")
stop_voice_btn = gr.Button("βΉοΈ STOP VOICE", variant="secondary")
voice_input = gr.Textbox(
label="π£οΈ CAPTURED VOICE COMMAND",
placeholder="Voice commands appear here automatically...",
lines=1, # Simplified to 1 line for minimal display
interactive=False
)
process_voice_btn = gr.Button("π― PROCESS VOICE COMMAND", variant="primary")
gr.Markdown("---") # Separator
# Audio Status and Controls
audio_status = gr.Textbox(
value="βΉοΈ Ready to play",
label="π AUDIO STATUS",
interactive=False,
max_lines=2
)
with gr.Row(elem_classes="control-buttons"):
play_btn = gr.Button("βΆοΈ PLAY MUSIC", variant="primary")
stop_btn = gr.Button("βΉοΈ STOP MUSIC", variant="secondary")
# Strudel Code Editor
music_code = gr.Code(
value='''// π΅ Voice Strudel Synth
// Speak a command to generate music!
// Try: "Make a beat"
// Then: "Play music" (or Ctrl+Enter)
stack(
// Your generated code will appear here...
).cpm(120) // Master tempo
''',
label="πΌ STRUDEL CODE EDITOR",
language="javascript", # Strudel is JavaScript-like
lines=15, # Reduced lines for more compact view
interactive=True
)
# Instructions Section (Simplified)
gr.HTML("""
<div class="instructions">
<h3>HOW TO USE</h3>
<ul>
<li>Click <strong>"START VOICE"</strong> or press <strong>Ctrl+M</strong>.</li>
<li>Say: <strong>"Make a beat"</strong></li>
<li>Then: <strong>"Play music"</strong> (or click <strong>"PLAY MUSIC"</strong> / <strong>Ctrl+Enter</strong>)</li>
<li>To stop: <strong>"Stop music"</strong> (or click <strong>"STOP MUSIC"</strong> / <strong>Ctrl+Space</strong>)</li>
<li>To clear: <strong>"Clear code"</strong></li>
</ul>
<p style="text-align: center;">π€ IMPORTANT: Allow microphone access when prompted!</p>
</div>
""")
# --- Event Handlers (Python functions triggered by Gradio events) ---
start_voice_btn.click(
fn=lambda: "π€ Voice recognition started - speak now!",
outputs=voice_status,
js="() => window.startVoiceRecognition()"
)
stop_voice_btn.click(
fn=lambda: "βΉοΈ Voice recognition stopped",
outputs=voice_status,
js="() => window.stopVoiceRecognition()"
)
process_voice_btn.click(
fn=process_voice_command,
inputs=[voice_input, music_code],
outputs=[voice_status, music_code]
)
# This handles the play button click, which also triggers JS play function
def handle_play_button_click_status(js_status_message): # Only receive the status message from JS
return js_status_message
play_btn.click(
fn=handle_play_button_click_status,
inputs=[music_code], # music_code is passed to the JS function, not the Python fn directly
outputs=audio_status,
js="(code) => window.playStrudelCode(code)" # Pass code to JS, JS handles audio and returns status
)
stop_btn.click(
fn=lambda: "βΉοΈ Music stopped",
outputs=audio_status,
js="() => window.stopStrudelCode()"
)
clear_btn = gr.Button("ποΈ Clear Code", variant="secondary")
clear_btn.click(fn=clear_code, outputs=[voice_status, music_code])
return interface
# --- Main execution block for Gradio ---
if __name__ == "__main__":
create_interface().launch() |