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

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +166 -143
index.html CHANGED
@@ -1,166 +1,154 @@
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 Recorder with Silence Detection</title>
7
  <style>
8
  body {
9
- font-family: Arial, sans-serif;
10
  max-width: 800px;
11
- margin: 20px auto;
12
  padding: 20px;
13
- background-color: #f5f5f5;
14
  }
15
- .container {
16
- background-color: white;
17
- padding: 20px;
18
- border-radius: 8px;
19
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
20
  }
21
  button {
22
- background-color: #4CAF50;
23
- color: white;
24
  padding: 10px 20px;
25
- border: none;
26
- border-radius: 4px;
27
- cursor: pointer;
28
  margin: 5px;
 
29
  }
30
- button:disabled {
31
- background-color: #cccccc;
32
- cursor: not-allowed;
 
 
33
  }
34
- #status {
35
- margin: 10px 0;
 
 
36
  padding: 10px;
37
- border-radius: 4px;
38
  }
39
- #visualizer {
40
- width: 100%;
41
- height: 100px;
42
- background-color: #f0f0f0;
43
  margin: 10px 0;
44
  }
45
- .recording {
46
- background-color: #ffebee !important;
 
 
 
 
 
 
 
 
 
47
  }
48
  </style>
49
  </head>
50
  <body>
51
- <div class="container">
52
- <h1>Audio Recorder</h1>
53
- <div id="status">Ready to record</div>
54
- <canvas id="visualizer"></canvas>
55
- <div>
56
- <button id="recordButton">Start Recording</button>
57
- <button id="playButton" disabled>Play Recording</button>
58
- </div>
59
- <audio id="audioPlayer" controls style="display: none; margin-top: 20px;"></audio>
60
  </div>
 
61
 
62
  <script>
63
  let mediaRecorder;
64
  let audioChunks = [];
 
 
 
 
65
  let audioContext;
66
  let analyser;
67
- let silenceTimeout;
68
  let isRecording = false;
69
- let recordingStartTime;
70
-
71
- const recordButton = document.getElementById('recordButton');
72
- const playButton = document.getElementById('playButton');
73
- const status = document.getElementById('status');
74
- const audioPlayer = document.getElementById('audioPlayer');
75
- const canvas = document.getElementById('visualizer');
76
- const canvasCtx = canvas.getContext('2d');
77
-
78
- // Set up canvas size
79
- canvas.width = canvas.offsetWidth;
80
- canvas.height = canvas.offsetHeight;
81
-
82
- recordButton.addEventListener('click', toggleRecording);
83
- playButton.addEventListener('click', playRecording);
84
 
85
- async function toggleRecording() {
86
- if (!isRecording) {
87
- startRecording();
88
- } else {
89
- stopRecording();
90
- }
91
- }
92
 
93
  async function startRecording() {
94
  try {
95
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
96
- audioContext = new AudioContext();
97
- analyser = audioContext.createAnalyser();
98
- const source = audioContext.createMediaStreamSource(stream);
99
- source.connect(analyser);
100
-
101
  mediaRecorder = new MediaRecorder(stream);
102
  audioChunks = [];
 
 
103
 
104
- mediaRecorder.ondataavailable = (event) => {
105
  audioChunks.push(event.data);
106
  };
107
 
108
  mediaRecorder.onstop = () => {
109
- const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
110
- const audioUrl = URL.createObjectURL(audioBlob);
111
- audioPlayer.src = audioUrl;
112
- audioPlayer.style.display = 'block';
113
- playButton.disabled = false;
114
-
115
- // Auto-download the file
116
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
117
- const link = document.createElement('a');
118
- link.href = audioUrl;
119
- link.download = `recording-${timestamp}.wav`;
120
- link.click();
121
  };
122
 
123
  mediaRecorder.start();
124
- isRecording = true;
125
- recordingStartTime = Date.now();
126
- status.textContent = 'Recording...';
127
- recordButton.textContent = 'Stop Recording';
128
- document.querySelector('.container').classList.add('recording');
129
-
130
- // Start silence detection
131
- detectSilence();
132
- // Start visualizer
133
- drawVisualizer();
134
-
135
  } catch (err) {
136
  console.error('Error:', err);
137
- status.textContent = 'Error: ' + err.message;
138
  }
139
  }
140
 
141
- function detectSilence() {
 
 
 
 
 
 
142
  const bufferLength = analyser.frequencyBinCount;
143
- const dataArray = new Uint8Array(bufferLength);
144
 
145
  function checkAudioLevel() {
146
  if (!isRecording) return;
147
 
148
- analyser.getByteFrequencyData(dataArray);
149
- const average = dataArray.reduce((a, b) => a + b) / bufferLength;
 
 
 
 
 
150
 
151
- if (average < 5) { // Silence threshold
 
 
 
 
 
152
  if (!silenceTimeout) {
153
  silenceTimeout = setTimeout(() => {
154
- if (Date.now() - recordingStartTime > 3000) { // Minimum 3 seconds
155
- stopRecording();
 
156
  }
157
- }, 3000); // Wait 3 seconds of silence
158
  }
159
  } else {
160
  if (silenceTimeout) {
161
  clearTimeout(silenceTimeout);
162
  silenceTimeout = null;
163
  }
 
 
 
164
  }
165
 
166
  requestAnimationFrame(checkAudioLevel);
@@ -169,61 +157,96 @@
169
  checkAudioLevel();
170
  }
171
 
172
- function drawVisualizer() {
173
- if (!isRecording) return;
174
-
175
- const bufferLength = analyser.frequencyBinCount;
176
- const dataArray = new Uint8Array(bufferLength);
177
-
178
- analyser.getByteTimeDomainData(dataArray);
179
-
180
- canvasCtx.fillStyle = '#f0f0f0';
181
- canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
182
- canvasCtx.lineWidth = 2;
183
- canvasCtx.strokeStyle = '#4CAF50';
184
- canvasCtx.beginPath();
185
-
186
- const sliceWidth = canvas.width * 1.0 / bufferLength;
187
- let x = 0;
 
 
 
 
 
 
 
 
 
188
 
189
- for (let i = 0; i < bufferLength; i++) {
190
- const v = dataArray[i] / 128.0;
191
- const y = v * canvas.height / 2;
 
192
 
193
- if (i === 0) {
194
- canvasCtx.moveTo(x, y);
195
- } else {
196
- canvasCtx.lineTo(x, y);
197
- }
198
 
199
- x += sliceWidth;
200
- }
201
 
202
- canvasCtx.lineTo(canvas.width, canvas.height / 2);
203
- canvasCtx.stroke();
 
 
 
 
204
 
205
- requestAnimationFrame(drawVisualizer);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  }
207
 
208
- function stopRecording() {
209
- if (mediaRecorder && mediaRecorder.state !== 'inactive') {
210
- mediaRecorder.stop();
211
- mediaRecorder.stream.getTracks().forEach(track => track.stop());
212
- isRecording = false;
213
- status.textContent = 'Recording stopped';
214
- recordButton.textContent = 'Start Recording';
215
- document.querySelector('.container').classList.remove('recording');
216
- if (silenceTimeout) {
217
- clearTimeout(silenceTimeout);
218
- silenceTimeout = null;
219
- }
220
- }
 
 
 
 
 
 
 
221
  }
222
 
223
- function playRecording() {
224
- audioPlayer.play();
 
225
  }
226
  </script>
227
- </div>
228
  </body>
229
  </html>
 
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);
 
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>