MeysamSh commited on
Commit
edbbc3c
·
1 Parent(s): 0b3fb1f

change interface to visualize the wavform

Browse files
Files changed (2) hide show
  1. Web/index.html +119 -82
  2. Web/script.js +190 -102
Web/index.html CHANGED
@@ -1,116 +1,153 @@
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>Audio Analysis API</title>
7
- <link rel="stylesheet" href="/static/styles.css" />
8
 
9
- <!-- Bootstrap CSS -->
10
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
11
 
12
- <!-- Wavesurfer -->
13
  <script src="https://unpkg.com/wavesurfer.js"></script>
14
 
15
  <style>
16
- body { background-color: #f8f9fa; padding: 20px; }
17
- .container { max-width: 900px; margin: 0 auto; background:#fff; padding:30px; border-radius:10px; box-shadow:0 4px 6px rgba(0,0,0,0.1); }
18
- h1 { text-align:center; margin-bottom:20px; color:#333; font-weight:bold; }
19
-
20
- .wave-item {
21
- margin-bottom: 18px;
22
- padding: 12px;
23
- border-radius: 8px;
24
- background: #fbfbfb;
25
- border: 1px solid #eee;
26
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
- .wave-controls {
 
29
  display:flex;
30
- gap:10px;
31
  align-items:center;
32
- margin-top:8px;
33
- flex-wrap:wrap;
 
 
 
34
  }
 
 
 
 
35
 
36
- .remove-btn {
37
- background: #dc3545;
38
  border: none;
39
- padding: 4px 10px;
40
- color: white;
41
- border-radius: 5px;
42
- cursor: pointer;
43
- font-size: 0.8rem;
44
  }
45
 
46
- .remove-btn:hover {
47
- background: #b52b38;
48
- }
49
 
50
- #recordingsList .list-group-item {
51
- display:block;
52
- padding:0;
53
- border:none;
54
- }
55
 
56
- .metadata, .response { margin-top:20px; padding:15px; border-radius:5px; }
57
- .metadata { background:#f1f3f4; }
58
- .response { background:#e9ecef; }
 
59
  </style>
60
  </head>
61
-
62
  <body>
63
- <div class="container">
64
- <h1>Audio Analysis API</h1>
65
- <h2>Upload or Record Audio Files</h2>
 
 
 
 
 
 
 
 
66
 
67
- <form id="upload-form" class="mb-4">
 
68
  <div class="mb-3">
69
- <input type="file" id="audio-file" class="form-control" accept="audio/*" multiple />
 
 
 
 
70
  </div>
71
- <button type="button" id="upload-button" class="btn btn-primary w-100">Upload & Analyze</button>
72
- </form>
73
-
74
- <hr>
75
-
76
- <div id="controls" class="mb-4 text-center">
77
- <button id="recordButton" class="btn btn-success">Record</button>
78
- <button id="pauseButton" class="btn btn-warning" disabled>Pause</button>
79
- <button id="stopButton" class="btn btn-danger" disabled>Stop</button>
80
  </div>
81
 
82
- <div id="formats" class="mb-3 text-center">Format: Start recording to see sample rate</div>
83
-
84
- <p class="text-center"><strong>Recordings & Uploads:</strong></p>
85
- <ol id="recordingsList" class="list-group"></ol>
 
 
 
 
 
 
86
 
87
- <!-- MOVED ABOVE METADATA -->
88
- <div class="response mt-4">
89
- <h3>Analysis Results</h3>
90
- <div id="response"></div>
91
  </div>
92
 
93
- <div class="metadata mt-4">
94
- <h3>File Metadata</h3>
95
- <div class="mb-3 d-flex flex-wrap gap-3">
96
- <i>Choisir un Label</i>
97
- <select id="filter-label" class="form-select"><option value="">All Labels</option></select>
98
- <i>Choisir un System</i>
99
- <select id="filter-system" class="form-select"><option value="">All Systems</option></select>
100
- <i>Choisir un Codec</i>
101
- <select id="filter-codec" class="form-select"><option value="">All Codecs</option></select>
102
- <i>Choisir un Genre</i>
103
- <select id="filter-genre" class="form-select"><option value="">All Genres</option></select>
104
- <i>Choisir une Année</i>
105
- <select id="filter-year" class="form-select"><option value="">All Years</option></select>
106
  </div>
107
- <div id="metadata-display"></div>
108
  </div>
109
-
110
  </div>
111
 
112
- <!-- Recorder.js -->
113
- <script src="/static/recorder.js"></script>
114
- <script src="/static/script.js"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  </body>
116
- </html>
 
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" />
6
+ <title>Speech DeepFake detection </title>
 
7
 
8
+ <!-- Bootstrap for quick UI -->
9
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
10
 
11
+ <!-- WaveSurfer -->
12
  <script src="https://unpkg.com/wavesurfer.js"></script>
13
 
14
  <style>
15
+ :root{
16
+ --accent:#ff5500; /* SoundCloud-esque orange */
17
+ --muted:#6c757d;
18
+ --card-bg: #ffffff;
19
+ --page-bg: #fbfbfc;
 
 
 
 
 
20
  }
21
+ body{
22
+ background: linear-gradient(180deg, #fbfbfc 0%, #f6f7f9 100%);
23
+ font-family: Inter, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
24
+ padding: 28px;
25
+ color:#222;
26
+ }
27
+ .app {
28
+ max-width: 960px;
29
+ margin: 0 auto;
30
+ background: var(--card-bg);
31
+ border-radius: 12px;
32
+ box-shadow: 0 8px 30px rgba(34,41,47,0.06);
33
+ padding: 26px;
34
+ }
35
+ h1 { color:#111; font-weight:700; margin-bottom:6px; }
36
+ p.lead { color:var(--muted); margin-top:0; }
37
+
38
+ /* Controls */
39
+ .control-row { display:flex; gap:10px; flex-wrap:wrap; align-items:center; }
40
+ #formats { color:var(--muted); font-size:0.95rem; }
41
 
42
+ /* Wave card (SoundCloud-like compact list) */
43
+ .wave-item{
44
  display:flex;
45
+ gap:14px;
46
  align-items:center;
47
+ padding:12px;
48
+ border-radius:10px;
49
+ background: linear-gradient(90deg, rgba(255,255,255,0.6), rgba(250,250,251,0.6));
50
+ border:1px solid #eee;
51
+ margin-bottom:12px;
52
  }
53
+ .wave-meta { min-width:160px; max-width:220px; }
54
+ .file-title { font-weight:600; color:#111; margin-bottom:6px; }
55
+ .wave-canvas { flex:1; min-width:240px; }
56
+ .wave-controls{ display:flex; gap:8px; align-items:center; }
57
 
58
+ .btn-play{
59
+ background: var(--accent);
60
  border: none;
61
+ color: #fff;
 
 
 
 
62
  }
63
 
64
+ .small-muted { color:var(--muted); font-size:0.9rem; }
 
 
65
 
66
+ /* Analysis & metadata panels */
67
+ .panel { margin-top:18px; padding:12px; border-radius:8px; background:#fafafa; border:1px solid #eee; }
68
+ .response pre{ white-space:pre-wrap; word-break:break-word; }
 
 
69
 
70
+ input[type="file"] { padding:4px; }
71
+
72
+ /* Make waves visually similar to SoundCloud: subtle rounded waves and orange progress */
73
+ .wavesurfer .wave { border-radius:4px; overflow:hidden; }
74
  </style>
75
  </head>
 
76
  <body>
77
+ <div class="app">
78
+ <div class="d-flex justify-content-between align-items-start mb-3">
79
+ <div>
80
+ <h1>SoundCloud-style Recorder & Analyzer</h1>
81
+ <p class="lead">Record audio, upload WAV/MP3, preview as waveform, play and analyze.</p>
82
+ </div>
83
+ <div class="text-end small-muted">
84
+ <div>Format: <span id="formats">—</span></div>
85
+ <div class="mt-2">Files shown in SoundCloud-like waveform</div>
86
+ </div>
87
+ </div>
88
 
89
+ <!-- Upload -->
90
+ <div class="panel">
91
  <div class="mb-3">
92
+ <label class="form-label">Upload audio files</label>
93
+ <input id="audio-file" type="file" class="form-control" accept="audio/*,.wav" multiple />
94
+ </div>
95
+ <div class="d-grid gap-2">
96
+ <button id="upload-button" type="button" class="btn btn-outline-primary">Upload & Analyze</button>
97
  </div>
 
 
 
 
 
 
 
 
 
98
  </div>
99
 
100
+ <!-- Recorder -->
101
+ <div class="panel mt-3">
102
+ <div class="mb-2"><strong>Record audio</strong></div>
103
+ <div class="control-row">
104
+ <button id="recordButton" class="btn btn-success">Start recording</button>
105
+ <button id="pauseButton" class="btn btn-warning" disabled>Pause</button>
106
+ <button id="stopButton" class="btn btn-danger" disabled>Stop</button>
107
+ <div class="small-muted ms-2">Recorded files appear below with waveform preview</div>
108
+ </div>
109
+ </div>
110
 
111
+ <!-- Recordings List -->
112
+ <div class="mt-3">
113
+ <h5>Recordings & Uploads</h5>
114
+ <ol id="recordingsList" class="list-unstyled p-0"></ol>
115
  </div>
116
 
117
+ <!-- Analysis / Metadata -->
118
+ <div class="row mt-3">
119
+ <div class="col-md-6">
120
+ <div class="panel response">
121
+ <h6>Analysis Results</h6>
122
+ <div id="response"><small class="small-muted">No analysis yet</small></div>
123
+ </div>
124
+ </div>
125
+ <div class="col-md-6">
126
+ <div class="panel metadata">
127
+ <h6>File Metadata</h6>
128
+ <div id="metadata-display"><small class="small-muted">Upload or record to see metadata</small></div>
129
+ </div>
130
  </div>
 
131
  </div>
 
132
  </div>
133
 
134
+ <!-- Use the uploaded recorder.js and script.js files from your workspace -->
135
+ <!-- per your instruction we reference the uploaded file paths -->
136
+ <script src="/mnt/data/recorder.js"></script>
137
+ <script src="/mnt/data/script.js"></script>
138
+
139
+ <!-- small inline initializer to ensure script.js hooks run after DOM loads -->
140
+ <script>
141
+ // The provided script.js expects DOM elements with IDs used above.
142
+ // If your script.js exposes an init() or similar, call it here; otherwise it will run on load.
143
+ // We'll just ensure basic elements exist and (optionally) show sample rate when available.
144
+ document.addEventListener('DOMContentLoaded', () => {
145
+ const formatsEl = document.getElementById('formats');
146
+ if (!formatsEl) return;
147
+
148
+ // If script.js attaches the recorder and updates #formats, it will override this.
149
+ formatsEl.textContent = 'Ready — click Record or Upload a file';
150
+ });
151
+ </script>
152
  </body>
153
+ </html>
Web/script.js CHANGED
@@ -1,113 +1,201 @@
1
- // ... (everything above stays unchanged)
2
-
3
- function createWaveformItem({ blob, filename, filetype, origin = 'upload' }) {
4
- const id = uid('wave');
5
-
6
- const li = document.createElement('li');
7
- li.className = 'list-group-item';
8
-
9
- const wrapper = document.createElement('div');
10
- wrapper.className = 'wave-item';
11
-
12
- const title = document.createElement('div');
13
- title.className = 'file-title';
14
- title.textContent = filename || 'Audio';
15
-
16
- const canvasDiv = document.createElement('div');
17
- canvasDiv.className = 'wave-canvas';
18
- canvasDiv.id = id;
19
-
20
- const controls = document.createElement('div');
21
- controls.className = 'wave-controls';
22
-
23
- const playBtn = document.createElement('button');
24
- playBtn.className = 'btn btn-sm btn-primary btn-small';
25
- playBtn.textContent = 'Play';
26
-
27
- const pauseBtn = document.createElement('button');
28
- pauseBtn.className = 'btn btn-sm btn-secondary btn-small';
29
- pauseBtn.textContent = 'Pause';
30
-
31
- const downloadBtn = document.createElement('a');
32
- downloadBtn.className = 'btn btn-sm btn-outline-success btn-small';
33
- downloadBtn.textContent = 'Download';
34
- const blobUrl = URL.createObjectURL(blob);
35
- downloadBtn.href = blobUrl;
36
- downloadBtn.download = filename || 'audio.wav';
37
-
38
- // ----------------------------
39
- // NEW: REMOVE BUTTON
40
- // ----------------------------
41
- const removeBtn = document.createElement("button");
42
- removeBtn.className = "remove-btn";
43
- removeBtn.innerText = "Remove";
44
- // ----------------------------
45
-
46
- const analyzeBtn = document.createElement('button');
47
- analyzeBtn.className = 'btn btn-sm btn-info btn-small';
48
- analyzeBtn.textContent = 'Analyze';
49
-
50
- controls.appendChild(playBtn);
51
- controls.appendChild(pauseBtn);
52
- controls.appendChild(downloadBtn);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
- // ADD REMOVE BUTTON TO CONTROLS
55
- controls.appendChild(removeBtn);
 
56
 
57
- controls.appendChild(analyzeBtn);
 
 
 
58
 
59
- wrapper.appendChild(title);
60
- wrapper.appendChild(canvasDiv);
61
- wrapper.appendChild(controls);
62
 
63
- li.appendChild(wrapper);
64
- recordingsList.prepend(li);
65
 
66
- const ws = WaveSurfer.create({
67
- container: `#${id}`,
68
- waveColor: '#8ab4f8',
69
- progressColor: '#0d6efd',
70
- height: 80,
71
- responsive: true,
72
- normalize: true,
73
- backend: 'WebAudio'
74
- });
75
 
76
- ws.loadBlob(blob);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
- playBtn.addEventListener('click', () => ws.play());
79
- pauseBtn.addEventListener('click', () => ws.pause());
 
 
 
 
80
 
81
- ws.on('finish', () => { playBtn.textContent = 'Play'; });
82
- ws.on('play', () => { playBtn.textContent = 'Playing'; });
83
- ws.on('pause', () => { playBtn.textContent = 'Play'; });
84
 
85
- analyzeBtn.addEventListener('click', async () => {
86
- responseDiv.textContent = 'Analyzing ' + (filename || 'file') + ' ...';
87
- try {
88
- const res = await sendBlobToAPI(blob, filename);
89
- if (Array.isArray(res) && res.length > 0) {
90
- responseDiv.innerHTML =
91
- `File: <b>${res[0].filename || filename}</b>, ` +
92
- `Label: <b>${res[0].label}</b>, ` +
93
- `Confidence: <b>${res[0].confidence}</b>`;
94
- } else {
95
- responseDiv.textContent = 'No response from API';
96
- }
97
  } catch (err) {
98
- responseDiv.textContent = 'Error: ' + err.message;
 
99
  }
100
- });
101
-
102
- // -----------------------------------------------------------
103
- // ⭐ NEW: REMOVE FUNCTIONALITY — ERASES THIS ENTIRE WAVE CARD
104
- // -----------------------------------------------------------
105
- removeBtn.addEventListener("click", () => {
106
- try { ws.destroy(); } catch (e) {}
107
- URL.revokeObjectURL(blobUrl);
108
- li.remove();
109
- });
110
- // -----------------------------------------------------------
111
-
112
- return { listItem: li, wavesurfer: ws };
113
- }
 
1
+ /* ============================================================
2
+ SoundCloud-Style Audio Recorder + Waveform Display + Analyzer
3
+ ============================================================
4
+ */
5
+
6
+ // Global variables
7
+ let audioContext;
8
+ let gumStream;
9
+ let rec;
10
+ let input;
11
+
12
+ // DOM Elements
13
+ const recordBtn = document.getElementById("recordButton");
14
+ const stopBtn = document.getElementById("stopButton");
15
+ const pauseBtn = document.getElementById("pauseButton");
16
+ const recordingsList = document.getElementById("recordingsList");
17
+ const uploadButton = document.getElementById("upload-button");
18
+ const uploadInput = document.getElementById("audio-file");
19
+ const responseBox = document.getElementById("response");
20
+ const metadataBox = document.getElementById("metadata-display");
21
+
22
+ // ==============================
23
+ // Recording Controls
24
+ // ==============================
25
+
26
+ recordBtn.addEventListener("click", startRecording);
27
+ stopBtn.addEventListener("click", stopRecording);
28
+ pauseBtn.addEventListener("click", pauseRecording);
29
+
30
+ // Start recording
31
+ function startRecording() {
32
+ console.log("Recording started...");
33
+
34
+ recordBtn.disabled = true;
35
+ stopBtn.disabled = false;
36
+ pauseBtn.disabled = false;
37
+
38
+ navigator.mediaDevices.getUserMedia({ audio: true }).then(function (stream) {
39
+ audioContext = new (window.AudioContext || window.webkitAudioContext)();
40
+ document.getElementById("formats").innerText =
41
+ "Sample rate: " + audioContext.sampleRate + " Hz";
42
+
43
+ gumStream = stream;
44
+ input = audioContext.createMediaStreamSource(stream);
45
+
46
+ rec = new Recorder(input, { numChannels: 1 });
47
+ rec.record();
48
+
49
+ }).catch(function (e) {
50
+ console.error("Could not start recording:", e);
51
+ recordBtn.disabled = false;
52
+ stopBtn.disabled = true;
53
+ pauseBtn.disabled = true;
54
+ });
55
+ }
56
+
57
+ // Pause recording
58
+ function pauseRecording() {
59
+ if (rec.recording) {
60
+ rec.stop();
61
+ pauseBtn.innerText = "Resume";
62
+ } else {
63
+ rec.record();
64
+ pauseBtn.innerText = "Pause";
65
+ }
66
+ }
67
 
68
+ // Stop recording
69
+ function stopRecording() {
70
+ console.log("Recording stopped.");
71
 
72
+ stopBtn.disabled = true;
73
+ recordBtn.disabled = false;
74
+ pauseBtn.disabled = true;
75
+ pauseBtn.innerText = "Pause";
76
 
77
+ rec.stop();
78
+ gumStream.getAudioTracks()[0].stop();
 
79
 
80
+ rec.exportWAV(createWaveformItem);
81
+ }
82
 
83
+ // ==============================
84
+ // File Upload
85
+ // ==============================
 
 
 
 
 
 
86
 
87
+ uploadButton.addEventListener("click", () => {
88
+ if (!uploadInput.files.length) {
89
+ alert("Select at least one file.");
90
+ return;
91
+ }
92
+ Array.from(uploadInput.files).forEach((file) => handleUploadedFile(file));
93
+ });
94
+
95
+ function handleUploadedFile(file) {
96
+ const url = URL.createObjectURL(file);
97
+ createWaveformItem(file, url);
98
+ }
99
+
100
+ // ==============================
101
+ // Create SoundCloud-like Waveform
102
+ // ==============================
103
+
104
+ function createWaveformItem(blob, forcedUrl = null) {
105
+ const url = forcedUrl || URL.createObjectURL(blob);
106
+
107
+ const li = document.createElement("li");
108
+ li.className = "wave-item";
109
+
110
+ const container = document.createElement("div");
111
+ container.className = "wave-canvas";
112
+
113
+ const meta = document.createElement("div");
114
+ meta.className = "wave-meta";
115
+ meta.innerHTML = `<div class="file-title">${blob.name || "recording.wav"}</div>`;
116
+
117
+ // Play button
118
+ const playBtn = document.createElement("button");
119
+ playBtn.className = "btn btn-play btn-sm";
120
+ playBtn.innerText = "Play";
121
+
122
+ // Analyze button
123
+ const analyzeBtn = document.createElement("button");
124
+ analyzeBtn.className = "btn btn-outline-secondary btn-sm";
125
+ analyzeBtn.innerText = "Analyze";
126
+
127
+ const controls = document.createElement("div");
128
+ controls.className = "wave-controls";
129
+ controls.appendChild(playBtn);
130
+ controls.appendChild(analyzeBtn);
131
+
132
+ li.appendChild(meta);
133
+ li.appendChild(container);
134
+ li.appendChild(controls);
135
+ recordingsList.appendChild(li);
136
+
137
+ // Create WaveSurfer
138
+ const wavesurfer = WaveSurfer.create({
139
+ container,
140
+ waveColor: "#bbb",
141
+ progressColor: "#ff5500",
142
+ cursorColor: "#333",
143
+ barWidth: 2,
144
+ height: 70,
145
+ responsive: true,
146
+ });
147
+
148
+ wavesurfer.load(url);
149
+
150
+ // Toggle play/pause
151
+ playBtn.addEventListener("click", () => {
152
+ wavesurfer.playPause();
153
+ playBtn.innerText = wavesurfer.isPlaying() ? "Pause" : "Play";
154
+ });
155
+
156
+ // Analyze
157
+ analyzeBtn.addEventListener("click", () => {
158
+ analyzeAudio(blob);
159
+ });
160
+
161
+ // Metadata
162
+ blob.arrayBuffer().then((buffer) => {
163
+ const ctx = new AudioContext();
164
+ ctx.decodeAudioData(buffer).then((decoded) => {
165
+ metadataBox.innerHTML = `
166
+ <strong>Channels:</strong> ${decoded.numberOfChannels}<br>
167
+ <strong>Duration:</strong> ${decoded.duration.toFixed(2)}s<br>
168
+ <strong>Sample Rate:</strong> ${decoded.sampleRate} Hz
169
+ `;
170
+ });
171
+ });
172
+ }
173
+
174
+ // ==============================
175
+ // Audio Analysis API (placeholder)
176
+ // ==============================
177
+
178
+ async function analyzeAudio(blob) {
179
+ responseBox.innerHTML = "<em>Analyzing...</em>";
180
+
181
+ // Convert Blob to File
182
+ const file = new File([blob], blob.name || "audio.wav", { type: blob.type });
183
+
184
+ const formData = new FormData();
185
+ formData.append("file", file);
186
 
187
+ try {
188
+ // Replace the URL with your real backend endpoint
189
+ const res = await fetch("http://localhost:7860/predict", {
190
+ method: "POST",
191
+ body: formData,
192
+ });
193
 
194
+ const data = await res.json();
 
 
195
 
196
+ responseBox.innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`;
 
 
 
 
 
 
 
 
 
 
 
197
  } catch (err) {
198
+ responseBox.innerHTML = `<span style="color:red;">Analysis failed.</span>`;
199
+ console.error(err);
200
  }
201
+ }