Ravisil commited on
Commit
7af88f5
·
verified ·
1 Parent(s): 6e694cf

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +160 -2
index.html CHANGED
@@ -41,6 +41,31 @@
41
  .pulse {
42
  animation: pulse 2s infinite;
43
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  </style>
45
  </head>
46
  <body class="bg-gradient-to-br from-blue-50 to-purple-50 min-h-screen">
@@ -82,7 +107,7 @@
82
 
83
  <!-- Controls -->
84
  <div class="p-8">
85
- <div class="flex flex-col sm:flex-row gap-4">
86
  <button id="test-btn" class="flex-1 bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-6 rounded-lg transition-all flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed" disabled>
87
  <i class="fas fa-play"></i> Testar Microfone
88
  </button>
@@ -91,6 +116,29 @@
91
  </button>
92
  </div>
93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  <div class="mt-8 bg-blue-50 border border-blue-100 rounded-lg p-4" id="instructions">
95
  <h3 class="font-medium text-blue-800 mb-2 flex items-center gap-2">
96
  <i class="fas fa-info-circle"></i> Instruções
@@ -99,6 +147,7 @@
99
  <li>Permita o acesso ao microfone quando solicitado</li>
100
  <li>Selecione seu dispositivo de microfone</li>
101
  <li>Clique em "Testar Microfone" e comece a falar</li>
 
102
  <li>O visualizador mostrará o nível de entrada de áudio</li>
103
  </ol>
104
  </div>
@@ -123,12 +172,22 @@
123
  const audioLevel = document.getElementById('audio-level');
124
  const visualizer = document.getElementById('visualizer');
125
  const instructions = document.getElementById('instructions');
 
 
 
 
 
 
126
 
127
  let audioContext;
128
  let microphone;
129
  let analyser;
130
  let isTesting = false;
131
  let devices = [];
 
 
 
 
132
 
133
  // Check microphone availability
134
  checkMicrophoneAvailability();
@@ -148,8 +207,9 @@
148
  statusText.textContent = `${audioDevices.length} microfone(s) disponível(is)`;
149
  statusIcon.innerHTML = '<i class="fas fa-microphone text-green-500 pulse"></i>';
150
 
151
- // Show device selection
152
  deviceSection.style.display = 'block';
 
153
  populateDeviceSelect(audioDevices);
154
 
155
  // Enable test button
@@ -197,6 +257,8 @@
197
 
198
  testBtn.addEventListener('click', startTest);
199
  stopBtn.addEventListener('click', stopTest);
 
 
200
 
201
  async function startTest() {
202
  if (isTesting) return;
@@ -297,6 +359,95 @@
297
  requestAnimationFrame(visualize);
298
  }
299
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
  // Handle device changes
301
  navigator.mediaDevices.addEventListener('devicechange', async () => {
302
  devices = await navigator.mediaDevices.enumerateDevices();
@@ -306,9 +457,16 @@
306
  if (audioDevices.length > 0) {
307
  statusText.textContent = `${audioDevices.length} microfone(s) disponível(is)`;
308
  statusIcon.innerHTML = '<i class="fas fa-microphone text-green-500 pulse"></i>';
 
309
  } else {
310
  statusText.textContent = 'Nenhum microfone encontrado';
311
  statusIcon.innerHTML = '<i class="fas fa-microphone-slash text-red-500"></i>';
 
 
 
 
 
 
312
  }
313
  });
314
  });
 
41
  .pulse {
42
  animation: pulse 2s infinite;
43
  }
44
+
45
+ .volume-slider {
46
+ -webkit-appearance: none;
47
+ width: 100%;
48
+ height: 8px;
49
+ border-radius: 4px;
50
+ background: #e2e8f0;
51
+ outline: none;
52
+ }
53
+
54
+ .volume-slider::-webkit-slider-thumb {
55
+ -webkit-appearance: none;
56
+ appearance: none;
57
+ width: 20px;
58
+ height: 20px;
59
+ border-radius: 50%;
60
+ background: #3b82f6;
61
+ cursor: pointer;
62
+ transition: all 0.2s;
63
+ }
64
+
65
+ .volume-slider::-webkit-slider-thumb:hover {
66
+ background: #2563eb;
67
+ transform: scale(1.1);
68
+ }
69
  </style>
70
  </head>
71
  <body class="bg-gradient-to-br from-blue-50 to-purple-50 min-h-screen">
 
107
 
108
  <!-- Controls -->
109
  <div class="p-8">
110
+ <div class="flex flex-col sm:flex-row gap-4 mb-6">
111
  <button id="test-btn" class="flex-1 bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-6 rounded-lg transition-all flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed" disabled>
112
  <i class="fas fa-play"></i> Testar Microfone
113
  </button>
 
116
  </button>
117
  </div>
118
 
119
+ <!-- Monitor Section -->
120
+ <div class="bg-purple-50 border border-purple-100 rounded-lg p-4 mb-6" id="monitor-section" style="display: none;">
121
+ <div class="flex items-center justify-between mb-3">
122
+ <h3 class="font-medium text-purple-800 flex items-center gap-2">
123
+ <i class="fas fa-headphones"></i> Monitorar Áudio
124
+ </h3>
125
+ <div class="flex items-center gap-2">
126
+ <span id="volume-value" class="text-sm text-purple-700">50%</span>
127
+ <i class="fas fa-volume-up text-purple-600"></i>
128
+ </div>
129
+ </div>
130
+ <div class="flex items-center gap-4">
131
+ <button id="monitor-btn" class="bg-purple-600 hover:bg-purple-700 text-white font-medium py-2 px-4 rounded-lg transition-all flex items-center justify-center gap-2">
132
+ <i class="fas fa-play"></i> Ouvir
133
+ </button>
134
+ <input type="range" min="0" max="100" value="50" class="volume-slider flex-1" id="volume-slider">
135
+ </div>
136
+ <div class="mt-3 flex items-center gap-2 text-sm text-purple-700">
137
+ <div class="h-3 w-3 rounded-full bg-purple-400" id="monitor-indicator"></div>
138
+ <span id="monitor-status">Pronto para monitorar</span>
139
+ </div>
140
+ </div>
141
+
142
  <div class="mt-8 bg-blue-50 border border-blue-100 rounded-lg p-4" id="instructions">
143
  <h3 class="font-medium text-blue-800 mb-2 flex items-center gap-2">
144
  <i class="fas fa-info-circle"></i> Instruções
 
147
  <li>Permita o acesso ao microfone quando solicitado</li>
148
  <li>Selecione seu dispositivo de microfone</li>
149
  <li>Clique em "Testar Microfone" e comece a falar</li>
150
+ <li>Use o botão "Ouvir" para monitorar seu microfone em tempo real</li>
151
  <li>O visualizador mostrará o nível de entrada de áudio</li>
152
  </ol>
153
  </div>
 
172
  const audioLevel = document.getElementById('audio-level');
173
  const visualizer = document.getElementById('visualizer');
174
  const instructions = document.getElementById('instructions');
175
+ const monitorSection = document.getElementById('monitor-section');
176
+ const monitorBtn = document.getElementById('monitor-btn');
177
+ const volumeSlider = document.getElementById('volume-slider');
178
+ const volumeValue = document.getElementById('volume-value');
179
+ const monitorStatus = document.getElementById('monitor-status');
180
+ const monitorIndicator = document.getElementById('monitor-indicator');
181
 
182
  let audioContext;
183
  let microphone;
184
  let analyser;
185
  let isTesting = false;
186
  let devices = [];
187
+ let monitorStream = null;
188
+ let monitorGain = null;
189
+ let isMonitoring = false;
190
+ let volume = 0.5;
191
 
192
  // Check microphone availability
193
  checkMicrophoneAvailability();
 
207
  statusText.textContent = `${audioDevices.length} microfone(s) disponível(is)`;
208
  statusIcon.innerHTML = '<i class="fas fa-microphone text-green-500 pulse"></i>';
209
 
210
+ // Show device selection and monitor section
211
  deviceSection.style.display = 'block';
212
+ monitorSection.style.display = 'block';
213
  populateDeviceSelect(audioDevices);
214
 
215
  // Enable test button
 
257
 
258
  testBtn.addEventListener('click', startTest);
259
  stopBtn.addEventListener('click', stopTest);
260
+ monitorBtn.addEventListener('click', toggleMonitor);
261
+ volumeSlider.addEventListener('input', updateVolume);
262
 
263
  async function startTest() {
264
  if (isTesting) return;
 
359
  requestAnimationFrame(visualize);
360
  }
361
 
362
+ async function toggleMonitor() {
363
+ if (isMonitoring) {
364
+ stopMonitor();
365
+ } else {
366
+ await startMonitor();
367
+ }
368
+ }
369
+
370
+ async function startMonitor() {
371
+ const deviceId = deviceSelect.value;
372
+ if (!deviceId) {
373
+ alert('Por favor, selecione um microfone primeiro');
374
+ return;
375
+ }
376
+
377
+ try {
378
+ // Initialize audio context if not already done
379
+ if (!audioContext) {
380
+ audioContext = new (window.AudioContext || window.webkitAudioContext)();
381
+ }
382
+
383
+ // Get microphone stream
384
+ const constraints = {
385
+ audio: {
386
+ deviceId: deviceId ? { exact: deviceId } : undefined,
387
+ echoCancellation: false,
388
+ noiseSuppression: false,
389
+ autoGainControl: false
390
+ }
391
+ };
392
+
393
+ monitorStream = await navigator.mediaDevices.getUserMedia(constraints);
394
+ const source = audioContext.createMediaStreamSource(monitorStream);
395
+
396
+ // Create gain node for volume control
397
+ monitorGain = audioContext.createGain();
398
+ monitorGain.gain.value = volume;
399
+
400
+ // Connect to destination
401
+ source.connect(monitorGain);
402
+ monitorGain.connect(audioContext.destination);
403
+
404
+ // Update UI
405
+ isMonitoring = true;
406
+ monitorBtn.innerHTML = '<i class="fas fa-stop"></i> Parar';
407
+ monitorBtn.classList.remove('bg-purple-600', 'hover:bg-purple-700');
408
+ monitorBtn.classList.add('bg-red-500', 'hover:bg-red-600');
409
+ monitorStatus.textContent = 'Monitorando ativamente';
410
+ monitorIndicator.classList.remove('bg-purple-400');
411
+ monitorIndicator.classList.add('bg-green-500', 'animate-pulse');
412
+
413
+ } catch (error) {
414
+ console.error('Error starting monitor:', error);
415
+ alert('Erro ao iniciar o monitoramento: ' + error.message);
416
+ }
417
+ }
418
+
419
+ function stopMonitor() {
420
+ if (!isMonitoring) return;
421
+
422
+ // Stop all tracks
423
+ if (monitorStream) {
424
+ monitorStream.getTracks().forEach(track => track.stop());
425
+ }
426
+
427
+ // Disconnect audio nodes
428
+ if (monitorGain) {
429
+ monitorGain.disconnect();
430
+ }
431
+
432
+ // Update UI
433
+ isMonitoring = false;
434
+ monitorBtn.innerHTML = '<i class="fas fa-play"></i> Ouvir';
435
+ monitorBtn.classList.remove('bg-red-500', 'hover:bg-red-600');
436
+ monitorBtn.classList.add('bg-purple-600', 'hover:bg-purple-700');
437
+ monitorStatus.textContent = 'Pronto para monitorar';
438
+ monitorIndicator.classList.remove('bg-green-500', 'animate-pulse');
439
+ monitorIndicator.classList.add('bg-purple-400');
440
+ }
441
+
442
+ function updateVolume() {
443
+ volume = volumeSlider.value / 100;
444
+ volumeValue.textContent = `${volumeSlider.value}%`;
445
+
446
+ if (monitorGain) {
447
+ monitorGain.gain.value = volume;
448
+ }
449
+ }
450
+
451
  // Handle device changes
452
  navigator.mediaDevices.addEventListener('devicechange', async () => {
453
  devices = await navigator.mediaDevices.enumerateDevices();
 
457
  if (audioDevices.length > 0) {
458
  statusText.textContent = `${audioDevices.length} microfone(s) disponível(is)`;
459
  statusIcon.innerHTML = '<i class="fas fa-microphone text-green-500 pulse"></i>';
460
+ monitorSection.style.display = 'block';
461
  } else {
462
  statusText.textContent = 'Nenhum microfone encontrado';
463
  statusIcon.innerHTML = '<i class="fas fa-microphone-slash text-red-500"></i>';
464
+ monitorSection.style.display = 'none';
465
+ }
466
+
467
+ // Stop monitoring if device list changes
468
+ if (isMonitoring) {
469
+ stopMonitor();
470
  }
471
  });
472
  });