baouws commited on
Commit
3192191
·
verified ·
1 Parent(s): 8e6c593

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +293 -10
app.py CHANGED
@@ -1,4 +1,15 @@
1
- import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
2
  import json
3
  import re
4
  import random
@@ -296,6 +307,269 @@ def load_example(example_key):
296
  """Load example description"""
297
  return examples.get(example_key, "")
298
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  # Custom CSS for anime theme
300
  anime_css = """
301
  <style>
@@ -467,6 +741,10 @@ with gr.Blocks(
467
  label="🔍 Musical Analysis"
468
  )
469
 
 
 
 
 
470
  # Examples section
471
  gr.Markdown("### 🎨 Try These Anime-Inspired Examples")
472
 
@@ -508,19 +786,24 @@ with gr.Blocks(
508
  - Mention specific instruments or sounds
509
  - Describe the genre or style for better results
510
  - Use the "Add Magic" button for instant variations!
 
 
 
 
 
 
 
511
  """)
512
 
513
- # Event handlers
514
- generate_btn.click(
515
- fn=generate_music,
516
- inputs=[description, tempo, style],
517
- outputs=[code_output, status_output, analysis_output]
518
- )
519
 
520
- magic_btn.click(
521
- fn=add_magic,
522
  inputs=[code_output],
523
- outputs=[code_output, status_output]
524
  )
525
 
526
  # Example button handlers
 
1
+ # Event handlers
2
+ generate_btn.click(
3
+ fn=generate_music,
4
+ inputs=[description, tempo, style],
5
+ outputs=[code_output, status_output, analysis_output]
6
+ )
7
+
8
+ magic_btn.click(
9
+ fn=add_magic,
10
+ inputs=[code_output],
11
+ outputs=[code_output, status_output]
12
+ )import gradio as gr
13
  import json
14
  import re
15
  import random
 
307
  """Load example description"""
308
  return examples.get(example_key, "")
309
 
310
+ def create_strudel_player():
311
+ """Create HTML component with Strudel.js player"""
312
+ return gr.HTML("""
313
+ <div id="strudel-container" style="
314
+ background: linear-gradient(135deg, rgba(255, 107, 157, 0.1), rgba(168, 85, 247, 0.1));
315
+ border: 2px solid rgba(255, 107, 157, 0.3);
316
+ border-radius: 20px;
317
+ padding: 20px;
318
+ margin: 10px 0;
319
+ backdrop-filter: blur(10px);
320
+ ">
321
+ <div style="text-align: center; margin-bottom: 15px;">
322
+ <h3 style="color: #FF6B9D; margin: 0 0 10px 0;">🎵 Strudel Audio Player</h3>
323
+ <p style="color: #E2E8F0; margin: 0; opacity: 0.8;">Click play to hear your generated music!</p>
324
+ </div>
325
+
326
+ <div id="strudel-controls" style="text-align: center; margin-bottom: 15px;">
327
+ <button id="play-btn" onclick="playStrudel()" style="
328
+ background: linear-gradient(45deg, #FF6B9D, #A855F7);
329
+ color: white;
330
+ border: none;
331
+ border-radius: 25px;
332
+ padding: 12px 24px;
333
+ margin: 5px;
334
+ font-weight: 600;
335
+ cursor: pointer;
336
+ transition: all 0.3s ease;
337
+ box-shadow: 0 4px 15px rgba(255, 107, 157, 0.4);
338
+ ">▶️ Play</button>
339
+
340
+ <button id="stop-btn" onclick="stopStrudel()" style="
341
+ background: linear-gradient(45deg, #EF4444, #F59E0B);
342
+ color: white;
343
+ border: none;
344
+ border-radius: 25px;
345
+ padding: 12px 24px;
346
+ margin: 5px;
347
+ font-weight: 600;
348
+ cursor: pointer;
349
+ transition: all 0.3s ease;
350
+ box-shadow: 0 4px 15px rgba(239, 68, 68, 0.4);
351
+ ">⏹️ Stop</button>
352
+
353
+ <button id="volume-btn" onclick="toggleVolume()" style="
354
+ background: linear-gradient(45deg, #06B6D4, #3B82F6);
355
+ color: white;
356
+ border: none;
357
+ border-radius: 25px;
358
+ padding: 12px 24px;
359
+ margin: 5px;
360
+ font-weight: 600;
361
+ cursor: pointer;
362
+ transition: all 0.3s ease;
363
+ box-shadow: 0 4px 15px rgba(6, 182, 212, 0.4);
364
+ ">🔊 Volume</button>
365
+ </div>
366
+
367
+ <div id="strudel-status" style="
368
+ text-align: center;
369
+ color: #E2E8F0;
370
+ font-size: 14px;
371
+ opacity: 0.8;
372
+ margin-bottom: 10px;
373
+ ">Ready to play music ✨</div>
374
+
375
+ <div id="volume-control" style="display: none; text-align: center; margin: 10px 0;">
376
+ <input type="range" id="volume-slider" min="0" max="100" value="70" style="
377
+ width: 200px;
378
+ height: 6px;
379
+ border-radius: 5px;
380
+ background: linear-gradient(to right, #FF6B9D, #A855F7);
381
+ outline: none;
382
+ opacity: 0.8;
383
+ transition: opacity 0.2s;
384
+ ">
385
+ <div style="color: #E2E8F0; font-size: 12px; margin-top: 5px;">Volume: <span id="volume-display">70</span>%</div>
386
+ </div>
387
+ </div>
388
+
389
+ <!-- Strudel.js Integration -->
390
+ <script src="https://unpkg.com/@strudel.cycles/core@latest/dist/index.js"></script>
391
+ <script src="https://unpkg.com/@strudel.cycles/webaudio@latest/dist/index.js"></script>
392
+ <script src="https://unpkg.com/@strudel.cycles/mini@latest/dist/index.js"></script>
393
+
394
+ <script>
395
+ let currentPattern = null;
396
+ let isPlaying = false;
397
+ let audioContext = null;
398
+ let currentCode = '';
399
+ let volume = 0.7;
400
+
401
+ // Initialize Strudel
402
+ async function initStrudel() {
403
+ try {
404
+ if (typeof strudel !== 'undefined') {
405
+ // Initialize audio context on first user interaction
406
+ if (!audioContext) {
407
+ audioContext = new (window.AudioContext || window.webkitAudioContext)();
408
+ await strudel.initAudio();
409
+ }
410
+ updateStatus('🎵 Strudel ready! Generate some code and hit play!', '#06B6D4');
411
+ } else {
412
+ // Fallback mode
413
+ updateStatus('🎮 Demo mode - Generate code to see it in action!', '#FCD34D');
414
+ }
415
+ } catch (error) {
416
+ console.error('Strudel init error:', error);
417
+ updateStatus('⚠️ Audio initialization failed - check browser permissions', '#EF4444');
418
+ }
419
+ }
420
+
421
+ function updateStatus(message, color = '#E2E8F0') {
422
+ const status = document.getElementById('strudel-status');
423
+ status.innerHTML = message;
424
+ status.style.color = color;
425
+ }
426
+
427
+ function updatePlayButton(playing) {
428
+ const btn = document.getElementById('play-btn');
429
+ if (playing) {
430
+ btn.innerHTML = '⏸️ Pause';
431
+ btn.onclick = pauseStrudel;
432
+ } else {
433
+ btn.innerHTML = '▶️ Play';
434
+ btn.onclick = playStrudel;
435
+ }
436
+ }
437
+
438
+ async function playStrudel() {
439
+ // Get current code from the code editor
440
+ const codeElements = document.querySelectorAll('textarea');
441
+ for (let element of codeElements) {
442
+ if (element.value && element.value.includes('//')) {
443
+ currentCode = element.value;
444
+ break;
445
+ }
446
+ }
447
+
448
+ if (!currentCode || !currentCode.trim()) {
449
+ updateStatus('❌ No code to play! Generate some music first.', '#EF4444');
450
+ return;
451
+ }
452
+
453
+ try {
454
+ // Stop any currently playing pattern
455
+ if (currentPattern) {
456
+ currentPattern.stop();
457
+ }
458
+
459
+ if (typeof strudel !== 'undefined' && strudel.Pattern) {
460
+ // Real Strudel playback
461
+ updateStatus('🎵 Evaluating Strudel code...', '#FCD34D');
462
+
463
+ // Remove comments and evaluate
464
+ const cleanCode = currentCode.replace(/\/\/.*$/gm, '').trim();
465
+ const pattern = eval(cleanCode);
466
+
467
+ if (pattern && typeof pattern.play === 'function') {
468
+ currentPattern = pattern;
469
+ await currentPattern.play();
470
+ isPlaying = true;
471
+ updatePlayButton(true);
472
+ updateStatus('🎵 Playing your music! ✨', '#10B981');
473
+ } else {
474
+ throw new Error('Invalid Strudel pattern - check your code syntax');
475
+ }
476
+ } else {
477
+ // Demo mode with visual feedback
478
+ updateStatus('🎮 Demo Mode: Playing ' + currentCode.split('\\n')[0] + '...', '#06B6D4');
479
+ isPlaying = true;
480
+ updatePlayButton(true);
481
+
482
+ // Simulate playback
483
+ setTimeout(() => {
484
+ updateStatus('🎵 Demo playback complete! Integrate Strudel.js for real audio.', '#FCD34D');
485
+ isPlaying = false;
486
+ updatePlayButton(false);
487
+ }, 5000);
488
+ }
489
+ } catch (error) {
490
+ console.error('Playback error:', error);
491
+ updateStatus('❌ Error: ' + error.message, '#EF4444');
492
+ isPlaying = false;
493
+ updatePlayButton(false);
494
+ }
495
+ }
496
+
497
+ function pauseStrudel() {
498
+ if (currentPattern && isPlaying) {
499
+ currentPattern.stop();
500
+ isPlaying = false;
501
+ updatePlayButton(false);
502
+ updateStatus('⏸️ Music paused', '#FCD34D');
503
+ }
504
+ }
505
+
506
+ function stopStrudel() {
507
+ if (currentPattern) {
508
+ currentPattern.stop();
509
+ currentPattern = null;
510
+ }
511
+ isPlaying = false;
512
+ updatePlayButton(false);
513
+ updateStatus('⏹️ Music stopped', '#6B7280');
514
+ }
515
+
516
+ function toggleVolume() {
517
+ const volumeControl = document.getElementById('volume-control');
518
+ volumeControl.style.display = volumeControl.style.display === 'none' ? 'block' : 'none';
519
+ }
520
+
521
+ // Volume control
522
+ const volumeSlider = document.getElementById('volume-slider');
523
+ const volumeDisplay = document.getElementById('volume-display');
524
+
525
+ if (volumeSlider && volumeDisplay) {
526
+ volumeSlider.oninput = function() {
527
+ volume = this.value / 100;
528
+ volumeDisplay.textContent = this.value;
529
+
530
+ // Apply volume to current pattern if playing
531
+ if (currentPattern && typeof currentPattern.gain === 'function') {
532
+ currentPattern.gain(volume);
533
+ }
534
+ };
535
+ }
536
+
537
+ // Initialize when page loads
538
+ document.addEventListener('DOMContentLoaded', initStrudel);
539
+
540
+ // Handle browser audio context restrictions
541
+ document.addEventListener('click', async function() {
542
+ if (audioContext && audioContext.state === 'suspended') {
543
+ await audioContext.resume();
544
+ }
545
+ }, { once: true });
546
+
547
+ // Update code when Gradio updates
548
+ function updateCurrentCode(newCode) {
549
+ currentCode = newCode;
550
+ if (isPlaying) {
551
+ stopStrudel();
552
+ updateStatus('🔄 Code updated - click play to hear changes!', '#06B6D4');
553
+ }
554
+ }
555
+
556
+ // Anime-style button hover effects
557
+ document.addEventListener('DOMContentLoaded', function() {
558
+ const buttons = document.querySelectorAll('#strudel-controls button');
559
+ buttons.forEach(btn => {
560
+ btn.addEventListener('mouseenter', function() {
561
+ this.style.transform = 'translateY(-2px) scale(1.05)';
562
+ this.style.boxShadow = '0 8px 25px rgba(255, 107, 157, 0.6)';
563
+ });
564
+ btn.addEventListener('mouseleave', function() {
565
+ this.style.transform = 'translateY(0) scale(1)';
566
+ this.style.boxShadow = '0 4px 15px rgba(255, 107, 157, 0.4)';
567
+ });
568
+ });
569
+ });
570
+ </script>
571
+ """, elem_id="strudel-player")
572
+
573
  # Custom CSS for anime theme
574
  anime_css = """
575
  <style>
 
741
  label="🔍 Musical Analysis"
742
  )
743
 
744
+ # Audio Player Section
745
+ gr.Markdown("### 🎵 Audio Playback")
746
+ strudel_player = create_strudel_player()
747
+
748
  # Examples section
749
  gr.Markdown("### 🎨 Try These Anime-Inspired Examples")
750
 
 
786
  - Mention specific instruments or sounds
787
  - Describe the genre or style for better results
788
  - Use the "Add Magic" button for instant variations!
789
+ - Click the Play button to hear your generated music!
790
+
791
+ **🎵 Audio Playback:**
792
+ - Generate code first, then click the ▶️ Play button
793
+ - Use ⏹️ Stop to halt playback
794
+ - Adjust volume with the 🔊 Volume button
795
+ - The player works in demo mode and with full Strudel.js integration
796
  """)
797
 
798
+ # Update code in player when it changes
799
+ def update_player_code(code):
800
+ # This will trigger the JavaScript to update the current code
801
+ return gr.update()
 
 
802
 
803
+ code_output.change(
804
+ fn=update_player_code,
805
  inputs=[code_output],
806
+ outputs=[]
807
  )
808
 
809
  # Example button handlers