BarVOx / index.html
Dmtlant's picture
Update index.html
a0a02fd verified
<!DOCTYPE html>
<html>
<head>
<title>Широкополосный FM и ELF Demodulator</title>
<style>
body { font-family: Arial, sans-serif; }
#controls { margin-bottom: 20px; }
</style>
</head>
<body>
<div id="controls">
<label for="frequencySlider">Частота (Hz): </label>
<input type="range" id="frequencySlider" min="3" max="20000" step="1" value="1000">
<span id="frequencyValue">1000</span> Hz
<button id="toggleAudio">Включить звук</button>
<button id="toggleELF">Включить ELF режим</button>
</div>
<canvas id="elfCanvas" width="800" height="150" style="border:1px solid #000000;"></canvas>
<script>
let audioContext;
let analyser;
let microphone;
let canvas;
let canvasCtx;
let scriptProcessor;
let prevPhase = 0;
let demodulatedSignal = [];
let gainNode;
let isAudioEnabled = false;
let isELFMode = false;
let centerFrequency = 1000; // Hz
let elfBuffer = [];
let elfCanvas;
let elfCtx;
function startAudioAnalysis() {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
scriptProcessor = audioContext.createScriptProcessor(1024, 1, 1);
gainNode = audioContext.createGain();
gainNode.gain.setValueAtTime(0, audioContext.currentTime);
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
.then(function(stream) {
microphone = audioContext.createMediaStreamSource(stream);
microphone.connect(analyser);
analyser.connect(scriptProcessor);
scriptProcessor.connect(gainNode);
gainNode.connect(audioContext.destination);
canvas = document.createElement('canvas');
canvas.width = 800;
canvas.height = 300;
document.body.appendChild(canvas);
canvasCtx = canvas.getContext('2d');
elfCanvas = document.getElementById('elfCanvas');
elfCtx = elfCanvas.getContext('2d');
drawSpectrum();
})
.catch(function(err) {
console.error('Ошибка доступа к микрофону:', err);
});
scriptProcessor.onaudioprocess = processAudio;
}
function drawSpectrum() {
requestAnimationFrame(drawSpectrum);
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
analyser.getByteFrequencyData(dataArray);
canvasCtx.fillStyle = 'rgb(0, 0, 0)';
canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
const barWidth = (canvas.width / bufferLength) * 2.5;
let x = 0;
for(let i = 0; i < bufferLength; i++) {
const barHeight = dataArray[i] / 2;
canvasCtx.fillStyle = `rgb(${barHeight + 100}, 50, 50)`;
canvasCtx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);
x += barWidth + 1;
}
// Рисуем демодулированный сигнал
canvasCtx.strokeStyle = 'rgb(0, 255, 0)';
canvasCtx.beginPath();
for(let i = 0; i < demodulatedSignal.length; i++) {
const x = (i / demodulatedSignal.length) * canvas.width;
const y = (0.5 - demodulatedSignal[i] / 2) * canvas.height;
if(i === 0) {
canvasCtx.moveTo(x, y);
} else {
canvasCtx.lineTo(x, y);
}
}
canvasCtx.stroke();
// Рисуем маркер центральной частоты
const centerX = (centerFrequency / 20000) * canvas.width;
canvasCtx.strokeStyle = 'rgb(255, 255, 0)';
canvasCtx.beginPath();
canvasCtx.moveTo(centerX, 0);
canvasCtx.lineTo(centerX, canvas.height);
canvasCtx.stroke();
// Рисуем ELF сигнал
if (isELFMode) {
elfCtx.fillStyle = 'rgb(0, 0, 0)';
elfCtx.fillRect(0, 0, elfCanvas.width, elfCanvas.height);
elfCtx.strokeStyle = 'rgb(0, 255, 0)';
elfCtx.beginPath();
for(let i = 0; i < elfBuffer.length; i++) {
const x = (i / elfBuffer.length) * elfCanvas.width;
const y = (0.5 - elfBuffer[i] / 2) * elfCanvas.height;
if(i === 0) {
elfCtx.moveTo(x, y);
} else {
elfCtx.lineTo(x, y);
}
}
elfCtx.stroke();
}
}
function processAudio(audioProcessingEvent) {
const inputBuffer = audioProcessingEvent.inputBuffer;
const outputBuffer = audioProcessingEvent.outputBuffer;
const inputData = inputBuffer.getChannelData(0);
const outputData = outputBuffer.getChannelData(0);
demodulatedSignal = [];
for(let i = 0; i < inputData.length; i++) {
const phase = Math.atan2(inputData[i], prevPhase);
const instantFreq = (phase - prevPhase + Math.PI) % (2 * Math.PI) - Math.PI;
let demodulated;
if (isELFMode) {
demodulated = lowPassFilter(instantFreq);
} else {
demodulated = instantFreq * (centerFrequency / 1000);
}
demodulatedSignal.push(demodulated);
outputData[i] = demodulated;
prevPhase = inputData[i];
}
if (isELFMode) {
elfBuffer = elfBuffer.concat(demodulatedSignal);
if (elfBuffer.length > 1000) {
elfBuffer = elfBuffer.slice(-1000);
}
}
}
function lowPassFilter(input) {
// Простой фильтр нижних частот
const alpha = 0.1;
this.filtered = (this.filtered || 0) * (1 - alpha) + input * alpha;
return this.filtered;
}
function toggleAudio() {
if (isAudioEnabled) {
gainNode.gain.setValueAtTime(0, audioContext.currentTime);
document.getElementById('toggleAudio').textContent = 'Включить звук';
} else {
gainNode.gain.setValueAtTime(1, audioContext.currentTime);
document.getElementById('toggleAudio').textContent = 'Выключить звук';
}
isAudioEnabled = !isAudioEnabled;
}
function toggleELF() {
isELFMode = !isELFMode;
if (isELFMode) {
document.getElementById('toggleELF').textContent = 'Выключить ELF режим';
document.getElementById('frequencySlider').min = 3;
document.getElementById('frequencySlider').max = 30;
centerFrequency = 15;
document.getElementById('frequencySlider').value = centerFrequency;
updateFrequency();
} else {
document.getElementById('toggleELF').textContent = 'Включить ELF режим';
document.getElementById('frequencySlider').min = 3;
document.getElementById('frequencySlider').max = 20000;
centerFrequency = 1000;
document.getElementById('frequencySlider').value = centerFrequency;
updateFrequency();
}
elfBuffer = [];
}
function updateFrequency() {
centerFrequency = parseFloat(document.getElementById('frequencySlider').value);
document.getElementById('frequencyValue').textContent = centerFrequency.toFixed(0);
}
window.onload = function() {
startAudioAnalysis();
document.getElementById('toggleAudio').addEventListener('click', toggleAudio);
document.getElementById('toggleELF').addEventListener('click', toggleELF);
document.getElementById('frequencySlider').addEventListener('input', updateFrequency);
};
</script>
</body>
</html>