awacke1 commited on
Commit
7cbbda6
·
verified ·
1 Parent(s): 084666d

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +222 -11
index.html CHANGED
@@ -1,14 +1,225 @@
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>Whisper Web</title>
7
- <script type="module" crossorigin src="b81.js"></script>
8
- <link rel="stylesheet" href="/assets/index-9018b2d2.css">
9
- </head>
10
- <body>
11
- <div id="root"></div>
12
-
13
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  </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>