awacke1 commited on
Commit
fe87343
·
verified ·
1 Parent(s): 621a9fb

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +176 -203
index.html CHANGED
@@ -1,252 +1,225 @@
1
  <!DOCTYPE html>
2
- <html>
3
  <head>
4
- <title>Audio Recorder with Silence Detection</title>
 
 
5
  <style>
6
  body {
7
- font-family: system-ui, sans-serif;
8
  max-width: 800px;
9
  margin: 0 auto;
10
  padding: 20px;
 
11
  }
12
- .controls {
13
- margin: 20px 0;
14
- }
15
- button {
16
- padding: 10px 20px;
17
- margin: 5px;
 
 
 
 
 
 
 
18
  cursor: pointer;
 
19
  }
20
- #recordingsList {
21
- margin-top: 20px;
22
- border: 1px solid #ccc;
23
- padding: 10px;
24
- min-height: 100px;
25
  }
26
- .recording-item {
27
- display: flex;
28
- justify-content: space-between;
29
- align-items: center;
30
  padding: 10px;
31
- border-bottom: 1px solid #eee;
 
32
  }
33
- #timer {
34
- font-size: 1.2em;
35
- margin: 10px 0;
 
36
  }
37
- #volumeMeter {
38
- width: 300px;
39
  height: 20px;
40
- border: 1px solid #ccc;
41
- margin: 10px 0;
 
 
42
  }
43
- #volumeBar {
44
  height: 100%;
45
  width: 0%;
46
  background-color: #4CAF50;
47
  transition: width 0.1s;
48
  }
 
 
 
 
 
 
 
 
49
  </style>
50
  </head>
51
  <body>
52
- <h1>Audio Recorder with Silence Detection</h1>
53
- <div class="controls">
54
- <button id="startButton">Start Recording</button>
55
- <button id="stopButton" disabled>Stop Recording</button>
 
 
 
 
 
 
56
  </div>
57
- <div id="timer">00:00</div>
58
- <div id="volumeMeter">
59
- <div id="volumeBar"></div>
60
- </div>
61
- <div id="recordingsList"></div>
62
 
63
  <script>
64
- let mediaRecorder;
65
- let audioChunks = [];
66
- let recordings = [];
67
- let startTime;
68
- let timerInterval;
69
- let silenceTimeout;
70
- let audioContext;
71
- let analyser;
72
- let isRecording = false;
73
- let totalDuration = 0;
74
- const SILENCE_THRESHOLD = -50; // dB
75
- const SILENCE_DURATION = 1000; // 1 second
76
-
77
- document.getElementById('startButton').addEventListener('click', startRecording);
78
- document.getElementById('stopButton').addEventListener('click', stopRecording);
79
-
80
- async function startRecording() {
81
- try {
82
- const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
83
- setupAudioAnalysis(stream);
84
-
85
- mediaRecorder = new MediaRecorder(stream);
86
- audioChunks = [];
87
- isRecording = true;
88
- startTime = Date.now();
89
 
90
- mediaRecorder.ondataavailable = event => {
91
- audioChunks.push(event.data);
92
- };
 
 
 
93
 
94
- mediaRecorder.onstop = () => {
95
- if (audioChunks.length > 0) {
96
- saveRecording();
97
- }
98
- };
99
 
100
- mediaRecorder.start();
101
- updateUI(true);
102
- startTimer();
103
- } catch (err) {
104
- console.error('Error:', err);
105
- alert('Error accessing microphone');
106
  }
107
- }
108
 
109
- function setupAudioAnalysis(stream) {
110
- audioContext = new AudioContext();
111
- analyser = audioContext.createAnalyser();
112
- const source = audioContext.createMediaStreamSource(stream);
113
- source.connect(analyser);
114
-
115
- analyser.fftSize = 2048;
116
- const bufferLength = analyser.frequencyBinCount;
117
- const dataArray = new Float32Array(bufferLength);
118
-
119
- function checkAudioLevel() {
120
- if (!isRecording) return;
121
-
122
- analyser.getFloatTimeDomainData(dataArray);
123
- let sum = 0;
124
- for (let i = 0; i < bufferLength; i++) {
125
- sum += Math.abs(dataArray[i]);
126
  }
127
- const average = sum / bufferLength;
128
- const db = 20 * Math.log10(average);
129
-
130
- // Update volume meter
131
- const volumeBar = document.getElementById('volumeBar');
132
- const normalizedVolume = Math.max(0, (db + 90) / 90) * 100;
133
- volumeBar.style.width = `${normalizedVolume}%`;
134
-
135
- if (db < SILENCE_THRESHOLD) {
136
- if (!silenceTimeout) {
137
- silenceTimeout = setTimeout(() => {
138
- if (mediaRecorder.state === 'recording') {
139
- mediaRecorder.stop();
140
- startNewRecording();
141
- }
142
- }, SILENCE_DURATION);
143
- }
144
  } else {
145
- if (silenceTimeout) {
146
- clearTimeout(silenceTimeout);
147
- silenceTimeout = null;
148
- }
149
- if (mediaRecorder.state === 'inactive' && isRecording) {
150
- startNewRecording();
151
- }
152
  }
153
-
154
- requestAnimationFrame(checkAudioLevel);
155
  }
156
-
157
- checkAudioLevel();
158
- }
159
 
160
- function startNewRecording() {
161
- if (isRecording) {
162
- audioChunks = [];
163
- mediaRecorder.start();
164
- }
165
- }
 
 
166
 
167
- function stopRecording() {
168
- isRecording = false;
169
- if (mediaRecorder.state === 'recording') {
170
- mediaRecorder.stop();
171
- }
172
- clearInterval(timerInterval);
173
- updateUI(false);
174
- if (audioContext) {
175
- audioContext.close();
 
 
 
176
  }
177
- if (silenceTimeout) {
178
- clearTimeout(silenceTimeout);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  }
180
- }
181
 
182
- function saveRecording() {
183
- const blob = new Blob(audioChunks, { type: 'audio/webm' });
184
- const reader = new FileReader();
185
-
186
- reader.onload = function() {
187
- const base64String = reader.result.split(',')[1];
188
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
189
- const filename = `recording-${timestamp}.webm`;
190
-
191
- recordings.push({
192
- filename,
193
- base64: base64String,
194
- duration: (Date.now() - startTime) / 1000
195
- });
196
-
197
- updateRecordingsList();
198
- };
199
-
200
- reader.readAsDataURL(blob);
201
- }
202
 
203
- function updateRecordingsList() {
204
- const list = document.getElementById('recordingsList');
205
- list.innerHTML = '';
206
-
207
- recordings.forEach((recording, index) => {
208
- const item = document.createElement('div');
209
- item.className = 'recording-item';
210
-
211
- const info = document.createElement('span');
212
- info.textContent = `${recording.filename} (${recording.duration.toFixed(1)}s)`;
213
-
214
- const downloadBtn = document.createElement('button');
215
- downloadBtn.textContent = 'Download';
216
- downloadBtn.onclick = () => downloadRecording(recording);
217
-
218
- item.appendChild(info);
219
- item.appendChild(downloadBtn);
220
- list.appendChild(item);
221
- });
222
- }
223
 
224
- function downloadRecording(recording) {
225
- const link = document.createElement('a');
226
- link.href = `data:audio/webm;base64,${recording.base64}`;
227
- link.download = recording.filename;
228
- document.body.appendChild(link);
229
- link.click();
230
- document.body.removeChild(link);
231
- }
232
 
233
- function startTimer() {
234
- const timerElement = document.getElementById('timer');
235
- const startTime = Date.now();
236
-
237
- timerInterval = setInterval(() => {
238
- const elapsed = Date.now() - startTime;
239
- const seconds = Math.floor(elapsed / 1000);
240
- const minutes = Math.floor(seconds / 60);
241
- const remainingSeconds = seconds % 60;
242
- timerElement.textContent = `${String(minutes).padStart(2, '0')}:${String(remainingSeconds).padStart(2, '0')}`;
243
- }, 1000);
244
- }
245
 
246
- function updateUI(recording) {
247
- document.getElementById('startButton').disabled = recording;
248
- document.getElementById('stopButton').disabled = !recording;
249
  }
 
 
 
 
 
250
  </script>
251
  </body>
252
  </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.0">
6
+ <title>Smart Audio Recorder</title>
7
  <style>
8
  body {
9
+ font-family: 'Arial', sans-serif;
10
  max-width: 800px;
11
  margin: 0 auto;
12
  padding: 20px;
13
+ background-color: #f5f5f5;
14
  }
15
+ .container {
16
+ background-color: white;
17
+ padding: 20px;
18
+ border-radius: 10px;
19
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
20
+ }
21
+ .record-button {
22
+ background-color: #ff4444;
23
+ color: white;
24
+ border: none;
25
+ padding: 15px 30px;
26
+ border-radius: 25px;
27
+ font-size: 18px;
28
  cursor: pointer;
29
+ transition: background-color 0.3s;
30
  }
31
+ .record-button.recording {
32
+ background-color: #cc0000;
33
+ animation: pulse 1.5s infinite;
 
 
34
  }
35
+ .status {
36
+ margin-top: 20px;
 
 
37
  padding: 10px;
38
+ border-radius: 5px;
39
+ background-color: #f8f9fa;
40
  }
41
+ @keyframes pulse {
42
+ 0% { transform: scale(1); }
43
+ 50% { transform: scale(1.05); }
44
+ 100% { transform: scale(1); }
45
  }
46
+ .meter {
 
47
  height: 20px;
48
+ background-color: #e9ecef;
49
+ border-radius: 10px;
50
+ margin: 20px 0;
51
+ overflow: hidden;
52
  }
53
+ .meter-fill {
54
  height: 100%;
55
  width: 0%;
56
  background-color: #4CAF50;
57
  transition: width 0.1s;
58
  }
59
+ .error {
60
+ color: #dc3545;
61
+ padding: 10px;
62
+ margin-top: 10px;
63
+ border: 1px solid #dc3545;
64
+ border-radius: 5px;
65
+ display: none;
66
+ }
67
  </style>
68
  </head>
69
  <body>
70
+ <div class="container">
71
+ <h1>Smart Audio Recorder</h1>
72
+ <p>Records when active audio is detected. Saves only recordings with more than 5 seconds of active audio.</p>
73
+
74
+ <button id="recordButton" class="record-button">Start Recording</button>
75
+ <div class="meter">
76
+ <div id="meterFill" class="meter-fill"></div>
77
+ </div>
78
+ <div id="status" class="status">Ready to record</div>
79
+ <div id="error" class="error"></div>
80
  </div>
 
 
 
 
 
81
 
82
  <script>
83
+ class SmartRecorder {
84
+ constructor() {
85
+ this.mediaRecorder = null;
86
+ this.audioContext = null;
87
+ this.analyser = null;
88
+ this.chunks = [];
89
+ this.activeAudioTime = 0;
90
+ this.lastActiveTime = 0;
91
+ this.isRecording = false;
92
+ this.silenceThreshold = 0.015;
93
+ this.minActiveAudio = 5; // seconds
94
+
95
+ this.recordButton = document.getElementById('recordButton');
96
+ this.status = document.getElementById('status');
97
+ this.meterFill = document.getElementById('meterFill');
98
+ this.errorDiv = document.getElementById('error');
99
+
100
+ this.recordButton.addEventListener('click', () => this.toggleRecording());
 
 
 
 
 
 
 
101
 
102
+ // Check if we're running on localhost or HTTPS
103
+ if (!(window.location.protocol === 'https:' || window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1')) {
104
+ this.showError('This application requires HTTPS or localhost to access the microphone.');
105
+ this.recordButton.disabled = true;
106
+ return;
107
+ }
108
 
109
+ this.setupAudioContext();
110
+ }
 
 
 
111
 
112
+ showError(message) {
113
+ this.errorDiv.textContent = message;
114
+ this.errorDiv.style.display = 'block';
115
+ console.error(message);
 
 
116
  }
 
117
 
118
+ async setupAudioContext() {
119
+ try {
120
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
121
+ this.audioContext = new AudioContext();
122
+ const source = this.audioContext.createMediaStreamSource(stream);
123
+ this.analyser = this.audioContext.createAnalyser();
124
+ this.analyser.fftSize = 2048;
125
+ source.connect(this.analyser);
126
+ this.errorDiv.style.display = 'none';
127
+ } catch (err) {
128
+ this.showError(`Error accessing microphone: ${err.message}`);
129
+ this.recordButton.disabled = true;
 
 
 
 
 
130
  }
131
+ }
132
+
133
+ async toggleRecording() {
134
+ if (!this.isRecording) {
135
+ await this.startRecording();
 
 
 
 
 
 
 
 
 
 
 
 
136
  } else {
137
+ await this.stopRecording();
 
 
 
 
 
 
138
  }
 
 
139
  }
 
 
 
140
 
141
+ async startRecording() {
142
+ try {
143
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
144
+ this.mediaRecorder = new MediaRecorder(stream);
145
+ this.chunks = [];
146
+ this.activeAudioTime = 0;
147
+ this.lastActiveTime = Date.now();
148
+ this.isRecording = true;
149
 
150
+ this.mediaRecorder.ondataavailable = (e) => this.chunks.push(e.data);
151
+ this.mediaRecorder.start();
152
+
153
+ this.recordButton.classList.add('recording');
154
+ this.recordButton.textContent = 'Stop Recording';
155
+ this.status.textContent = 'Recording...';
156
+ this.errorDiv.style.display = 'none';
157
+
158
+ this.startAudioAnalysis();
159
+ } catch (err) {
160
+ this.showError(`Error starting recording: ${err.message}`);
161
+ }
162
  }
163
+
164
+ async stopRecording() {
165
+ if (!this.mediaRecorder) return;
166
+
167
+ this.isRecording = false;
168
+ this.mediaRecorder.stop();
169
+ this.recordButton.classList.remove('recording');
170
+ this.recordButton.textContent = 'Start Recording';
171
+
172
+ this.mediaRecorder.onstop = async () => {
173
+ if (this.activeAudioTime >= this.minActiveAudio) {
174
+ const blob = new Blob(this.chunks, { type: 'audio/wav' });
175
+ const url = URL.createObjectURL(blob);
176
+ const link = document.createElement('a');
177
+ link.href = url;
178
+ link.download = `recording_${new Date().toISOString()}.wav`;
179
+ link.click();
180
+ this.status.textContent = `Saved recording with ${this.activeAudioTime.toFixed(1)} seconds of active audio`;
181
+ } else {
182
+ this.status.textContent = `Recording discarded: Only ${this.activeAudioTime.toFixed(1)} seconds of active audio (minimum ${this.minActiveAudio}s required)`;
183
+ }
184
+ this.meterFill.style.width = '0%';
185
+ };
186
  }
 
187
 
188
+ startAudioAnalysis() {
189
+ const analyzeFrame = () => {
190
+ if (!this.isRecording) return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
 
192
+ const dataArray = new Float32Array(this.analyser.frequencyBinCount);
193
+ this.analyser.getFloatTimeDomainData(dataArray);
194
+ const rms = Math.sqrt(dataArray.reduce((acc, val) => acc + val * val, 0) / dataArray.length);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
 
196
+ if (rms > this.silenceThreshold) {
197
+ const now = Date.now();
198
+ const timeDiff = (now - this.lastActiveTime) / 1000;
199
+ this.activeAudioTime += timeDiff;
200
+ this.lastActiveTime = now;
201
+ }
 
 
202
 
203
+ // Update meter
204
+ const meterLevel = Math.min(100, (rms * 400));
205
+ this.meterFill.style.width = `${meterLevel}%`;
206
+
207
+ // Update status with active audio time
208
+ if (this.isRecording) {
209
+ this.status.textContent = `Recording... Active audio: ${this.activeAudioTime.toFixed(1)}s`;
210
+ }
211
+
212
+ requestAnimationFrame(analyzeFrame);
213
+ };
 
214
 
215
+ analyzeFrame();
216
+ }
 
217
  }
218
+
219
+ // Initialize recorder when page loads
220
+ window.addEventListener('load', () => {
221
+ new SmartRecorder();
222
+ });
223
  </script>
224
  </body>
225
  </html>