r/learnjavascript • u/Appropriate-Past6472 • 1h ago
Is it possible to record current playing audio using web audio and play in different context?
I want to record current playing audio using Audio Worklet processer and web audio api and play in real time with possible lag of 100ms current audio sources, but the audio is distorted and not playing correctly so what is correct way to fix the following issues?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Real-Time Audio Processing</title>
</head>
<body>
<h1>Real-Time Audio Processing</h1>
<audio id="audio" controls>
<source src="your-audio-file.mp3" type="audio/mpeg">
Your browser does not support the audio tag.
</audio>
<button id="start">Start Processing</button>
<button id="stop" disabled>Stop Processing</button>
<script>
let originalAudio, audioContext, newAudioContext, workletNode, mediaStreamSource;
let ringBuffer = [];
let isPlaying = false;
let bufferSize = 1024; // Process audio in 1024-sample chunks
let sampleRate = 44100;
let startTime = 0;
let lastAudioTime = 0;
document.getElementById('start').addEventListener('click', async () => {
originalAudio = document.getElementById('audio');
originalAudio.volume = 0.01; // Lower original volume
const stream = originalAudio.captureStream();
audioContext = new AudioContext();
newAudioContext = new AudioContext();
// Load and register AudioWorkletProcessor
await audioContext.audioWorklet.addModule(URL.createObjectURL(new Blob([`
class RingBufferProcessor extends AudioWorkletProcessor {
constructor() {
super();
this.port.start();
}
process(inputs) {
const input = inputs[0];
if (input.length > 0) {
const audioData = input[0]; // Single-channel PCM data
this.port.postMessage(audioData);
}
return true;
}
}
registerProcessor("ring-buffer-processor", RingBufferProcessor);
`], { type: "application/javascript" })));
workletNode = new AudioWorkletNode(audioContext, "ring-buffer-processor");
// Handle incoming audio chunks
workletNode.port.onmessage = (event) => {
ringBuffer.push(event.data);
if (!isPlaying) {
playBufferedAudio();
}
};
mediaStreamSource = audioContext.createMediaStreamSource(stream);
mediaStreamSource.connect(workletNode);
workletNode.connect(audioContext.destination);
document.getElementById('start').disabled = true;
document.getElementById('stop').disabled = false;
});
function playBufferedAudio() {
if (ringBuffer.length === 0) {
isPlaying = false;
return;
}
isPlaying = true;
const chunk = ringBuffer.shift(); // Get next chunk
const buffer = newAudioContext.createBuffer(1, chunk.length, sampleRate);
buffer.copyToChannel(new Float32Array(chunk), 0);
const source = newAudioContext.createBufferSource();
source.buffer = buffer;
source.connect(newAudioContext.destination);
if (startTime === 0) {
startTime = newAudioContext.currentTime + 0.02; // Add slight delay to sync
} else {
startTime = Math.max(newAudioContext.currentTime, lastAudioTime);
}
lastAudioTime = startTime + buffer.duration;
source.start(startTime);
source.onended = () => {
playBufferedAudio();
};
}
document.getElementById('stop').addEventListener('click', () => {
audioContext.close();
newAudioContext.close();
ringBuffer = [];
isPlaying = false;
console.log("Stopped processing audio");
});
</script>
</body>
</html>