omar1232 commited on
Commit
489a593
·
1 Parent(s): cd95b46

Add index.html and ensure all files are included

Browse files
Files changed (3) hide show
  1. app.py +117 -0
  2. index.html +162 -0
  3. requirements.txt +0 -0
app.py CHANGED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import numpy as np
3
+ import librosa
4
+ import soundfile as sf
5
+ import json
6
+ import websocket
7
+ import threading
8
+ import time
9
+ from io import BytesIO
10
+
11
+ # Global variables for WebSocket communication
12
+ ws = None
13
+ clients = []
14
+
15
+ # WebSocket server to send visualization data to frontend
16
+ def start_websocket_server():
17
+ def on_open(ws):
18
+ print("WebSocket server opened")
19
+ clients.append(ws)
20
+
21
+ def on_close(ws):
22
+ print("WebSocket server closed")
23
+ clients.remove(ws)
24
+
25
+ def on_message(ws, message):
26
+ print(f"Received: {message}")
27
+
28
+ global ws
29
+ ws = websocket.WebSocketApp("ws://localhost:8765",
30
+ on_open=on_open,
31
+ on_message=on_message,
32
+ on_close=on_close)
33
+ threading.Thread(target=ws.run_forever, daemon=True).start()
34
+
35
+ # Process audio file or recording
36
+ def process_audio(audio_input, sample_rate=44100):
37
+ # Handle Gradio audio input (tuple of (sample_rate, numpy_array))
38
+ if isinstance(audio_input, tuple):
39
+ sr, audio_data = audio_input
40
+ else:
41
+ # Load audio file
42
+ audio_data, sr = librosa.load(audio_input, sr=sample_rate)
43
+
44
+ # Extract frequency data (spectrogram)
45
+ fft = np.abs(librosa.stft(audio_data))
46
+ freq_data = np.mean(fft, axis=1)[:200] # Average across time, take first 200 bins
47
+
48
+ # Beat detection
49
+ tempo, beats = librosa.beat.beat_track(y=audio_data, sr=sr)
50
+ beat_times = librosa.frames_to_time(beats, sr=sr)
51
+
52
+ # Prepare visualization data
53
+ vis_data = {
54
+ "frequencies": freq_data.tolist(),
55
+ "beat_times": beat_times.tolist(),
56
+ "volume": float(np.mean(np.abs(audio_data)) * 100)
57
+ }
58
+
59
+ # Send data to connected WebSocket clients
60
+ if ws and clients:
61
+ for client in clients:
62
+ try:
63
+ client.send(json.dumps(vis_data))
64
+ except:
65
+ pass
66
+
67
+ return vis_data, audio_data, sr
68
+
69
+ # Gradio interface function
70
+ def audio_visualizer(audio_file, audio_record):
71
+ if audio_file:
72
+ vis_data, audio_data, sr = process_audio(audio_file)
73
+ elif audio_record:
74
+ vis_data, audio_data, sr = process_audio(audio_record)
75
+ else:
76
+ return "Please upload an audio file or record audio."
77
+
78
+ # Save audio to a temporary file for playback
79
+ temp_audio = BytesIO()
80
+ sf.write(temp_audio, audio_data, sr, format='wav')
81
+ temp_audio.seek(0)
82
+
83
+ return vis_data, temp_audio
84
+
85
+ # Start WebSocket server
86
+ start_websocket_server()
87
+
88
+ # Gradio interface
89
+ with gr.Blocks() as demo:
90
+ gr.Markdown("# Advanced Audio Visualizer")
91
+ gr.Markdown("Upload an audio file or record audio to visualize frequencies and beats.")
92
+
93
+ with gr.Row():
94
+ audio_file = gr.Audio(sources=["upload"], type="filepath", label="Upload Audio")
95
+ audio_record = gr.Audio(sources=["microphone"], type="numpy", label="Record Audio")
96
+
97
+ with gr.Row():
98
+ vis_output = gr.JSON(label="Visualization Data")
99
+ audio_output = gr.Audio(label="Audio Playback", type="filepath")
100
+
101
+ with gr.Row():
102
+ submit = gr.Button("Visualize")
103
+ clear = gr.Button("Clear")
104
+
105
+ submit.click(
106
+ fn=audio_visualizer,
107
+ inputs=[audio_file, audio_record],
108
+ outputs=[vis_output, audio_output]
109
+ )
110
+ clear.click(
111
+ fn=lambda: (None, None),
112
+ inputs=[],
113
+ outputs=[audio_file, audio_record]
114
+ )
115
+
116
+ # Launch Gradio app
117
+ demo.launch()
index.html ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Advanced Audio Visualizer with Gradio</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
9
+ <style>
10
+ .visualizer-container {
11
+ position: relative;
12
+ width: 100%;
13
+ height: 500px;
14
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
15
+ border-radius: 16px;
16
+ overflow: hidden;
17
+ box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
18
+ transition: background 0.5s;
19
+ }
20
+
21
+ .bar {
22
+ position: absolute;
23
+ bottom: 0;
24
+ background: linear-gradient(to top, #00b4db, #0083b0);
25
+ border-radius: 6px 6px 0 0;
26
+ transition: height 0.1s cubic-bezier(0.4, 0, 0.2, 1);
27
+ }
28
+
29
+ .audio-wave {
30
+ position: absolute;
31
+ bottom: 0;
32
+ left: 0;
33
+ width: 100%;
34
+ height: 120px;
35
+ background: linear-gradient(to top, rgba(0, 180, 219, 0.2), transparent);
36
+ transition: clip-path 0.1s;
37
+ }
38
+
39
+ .loading-spinner {
40
+ display: none;
41
+ position: absolute;
42
+ top: 50%;
43
+ left: 50%;
44
+ transform: translate(-50%, -50%);
45
+ border: 4px solid rgba(255,255,255,0.3);
46
+ border-top: 4px solid #00b4db;
47
+ border-radius: 50%;
48
+ width: 40px;
49
+ height: 40px;
50
+ animation: spin 1s linear infinite;
51
+ }
52
+
53
+ @keyframes spin {
54
+ 0% { transform: translate(-50%, -50%) rotate(0deg); }
55
+ 100% { transform: translate(-50%, -50%) rotate(360deg); }
56
+ }
57
+
58
+ .bg-animated {
59
+ animation: gradient 15s ease infinite;
60
+ background-size: 400% 400%;
61
+ }
62
+
63
+ @keyframes gradient {
64
+ 0% { background-position: 0% 50%; }
65
+ 50% { background-position: 100% 50%; }
66
+ 100% { background-position: 0% 50%; }
67
+ }
68
+ </style>
69
+ </head>
70
+ <body class="bg-gray-900 text-white min-h-screen flex flex-col items-center justify-center p-6">
71
+ <div class="max-w-6xl w-full">
72
+ <h1 class="text-5xl font-extrabold text-center bg-gradient-to-r from-cyan-400 to-pink-500 bg-clip-text text-transparent mb-6">
73
+ Advanced Audio Visualizer
74
+ </h1>
75
+ <p class="text-gray-300 text-center mb-8 font-medium">Visualize audio frequencies using Gradio</p>
76
+
77
+ <div class="visualizer-container mb-8 bg-animated" id="visualizer" style="background: linear-gradient(135deg, #1a1a2e, #16213e, #2a2a4e, #1a1a2e);">
78
+ <div class="audio-wave" id="audioWave"></div>
79
+ <div class="loading-spinner" id="loadingSpinner"></div>
80
+ </div>
81
+
82
+ <div class="mt-6">
83
+ <iframe src="https://huggingface.co/spaces/omar1232/Advanced_Audio_Visualizer" width="100%" height="500" frameborder="0"></iframe>
84
+ </div>
85
+ </div>
86
+
87
+ <script>
88
+ document.addEventListener('DOMContentLoaded', () => {
89
+ const visualizer = document.getElementById('visualizer');
90
+ const audioWave = document.getElementById('audioWave');
91
+ const loadingSpinner = document.getElementById('loadingSpinner');
92
+ let bars = [];
93
+ const barCount = 80;
94
+ const barSpacing = 2;
95
+
96
+ // Create bars
97
+ function createBars() {
98
+ visualizer.querySelectorAll('.bar').forEach(bar => bar.remove());
99
+ bars = [];
100
+
101
+ const containerWidth = visualizer.clientWidth;
102
+ const barWidth = (containerWidth / barCount) - barSpacing;
103
+
104
+ for (let i = 0; i < barCount; i++) {
105
+ const bar = document.createElement('div');
106
+ bar.className = 'bar';
107
+ bar.style.left = `${i * (barWidth + barSpacing)}px`;
108
+ bar.style.width = `${barWidth}px`;
109
+ bar.style.height = '0px';
110
+ visualizer.appendChild(bar);
111
+ bars.push(bar);
112
+ }
113
+ }
114
+
115
+ createBars();
116
+
117
+ // Poll Gradio API for visualization data
118
+ setInterval(async () => {
119
+ try {
120
+ // Fetch the latest visualization data from Gradio's JSON output
121
+ const response = await fetch('https://huggingface.co/spaces/omar1232/Advanced_Audio_Visualizer/api/predict', {
122
+ method: 'POST',
123
+ headers: { 'Content-Type': 'application/json' },
124
+ body: JSON.stringify({ data: [] }) // Adjust based on Gradio API
125
+ });
126
+ const data = await response.json();
127
+ if (data && data.data && data.data[0]) {
128
+ updateVisualization(data.data[0]); // First output is JSON
129
+ }
130
+ } catch (error) {
131
+ console.error('Error fetching data:', error);
132
+ }
133
+ }, 500); // Poll every 500ms
134
+
135
+ function updateVisualization(data) {
136
+ const { frequencies } = data;
137
+
138
+ // Update bars
139
+ const freqCount = Math.min(frequencies.length, barCount);
140
+ for (let i = 0; i < freqCount; i++) {
141
+ const height = (frequencies[i] / Math.max(...frequencies)) * visualizer.clientHeight * 0.8;
142
+ bars[i].style.height = `${height}px`;
143
+ }
144
+
145
+ // Update waveform
146
+ const wavePoints = frequencies.map((f, i) => [
147
+ (i / frequencies.length) * visualizer.clientWidth,
148
+ visualizer.clientHeight * (1 - f / Math.max(...frequencies))
149
+ ]);
150
+ let wavePath = `path('M0 ${visualizer.clientHeight / 2} `;
151
+ wavePoints.forEach(([x, y]) => {
152
+ wavePath += `L${x} ${y} `;
153
+ });
154
+ wavePath += `L${visualizer.clientWidth} ${visualizer.clientHeight / 2} Z')`;
155
+ audioWave.style.clipPath = wavePath;
156
+ }
157
+
158
+ window.addEventListener('resize', createBars);
159
+ });
160
+ </script>
161
+ </body>
162
+ </html>
requirements.txt ADDED
File without changes