基于Audio Web API实现网页循环工作站的录制循环功能技术问询
Hey there! Let's tackle this loop recording feature you need for your web-based looper station. Here's a practical, step-by-step solution that integrates seamlessly with your existing code:
Key Concepts to Implement
We'll use MediaRecorder to capture the microphone input, convert the recorded blob to an AudioBuffer, and then use AudioBufferSourceNode with the loop flag enabled to create continuous playback. We'll also manage state to toggle between recording and playback modes cleanly.
Full Integrated Code
Here's how to update your code to add the loop recording functionality:
window.AudioContext = window.AudioContext || window.webkitAudioContext const audioContext = new AudioContext() let mediaStreamSource; let inputgain; let outputgain; let isRecording = false; let mediaRecorder; let recordedChunks = []; let loopSourceNode = null; // Initialize microphone and audio nodes async function initMicrophone() { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); mediaStreamSource = audioContext.createMediaStreamSource(stream) inputgain = audioContext.createGain() outputgain = audioContext.createGain() // Base audio chain: Mic → Input Gain → Output Gain → Speakers mediaStreamSource.connect(inputgain) inputgain.connect(outputgain) outputgain.connect(audioContext.destination) // Set up MediaRecorder for capturing audio mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm; codecs=opus' }); // Collect recorded data chunks mediaRecorder.ondataavailable = (e) => { if (e.data.size > 0) { recordedChunks.push(e.data); } }; // Process recorded data when recording stops mediaRecorder.onstop = async () => { const audioBlob = new Blob(recordedChunks, { type: 'audio/webm' }); const arrayBuffer = await audioBlob.arrayBuffer(); const audioBuffer = await audioContext.decodeAudioData(arrayBuffer); // Create a loopable playback node loopSourceNode = audioContext.createBufferSource(); loopSourceNode.buffer = audioBuffer; loopSourceNode.loop = true; // Enable continuous looping loopSourceNode.connect(outputgain); loopSourceNode.start(); // Start playback immediately // Reset chunk array for next recording recordedChunks = []; }; } catch (err) { console.error('Failed to access microphone:', err); } } // Toggle recording/playback with a button document.getElementById('recordBtn').addEventListener('click', async () => { // Resume AudioContext if it's suspended (browser security requirement) if (audioContext.state === 'suspended') { await audioContext.resume(); } if (!isRecording) { // Stop existing loop if present before starting new recording if (loopSourceNode) { loopSourceNode.stop(); loopSourceNode = null; } isRecording = true; mediaRecorder.start(); inputgain.connect(outputgain); // Let user hear their live input document.getElementById('recordBtn').textContent = 'Stop Recording'; } else { isRecording = false; mediaRecorder.stop(); inputgain.disconnect(outputgain); // Disable live input monitoring during loop playback document.getElementById('recordBtn').textContent = 'Start Recording'; } }); // Initialize on page load initMicrophone();
Important Notes
- Browser MIME Type Support: The
audio/webm; codecs=opusformat is widely supported, but you can check compatibility withMediaRecorder.isTypeSupported('your-mime-type')if you run into issues. - AudioContext Suspension: Browsers automatically suspend
AudioContextuntil a user interaction (like a button click) occurs—this code handles resuming it when the button is pressed. - Clean Resource Management: We stop and discard old loop nodes when starting a new recording to avoid memory leaks and overlapping audio.
- Input Monitoring: We disconnect the input gain from the output when playing loops to prevent feedback or live input mixing with the recorded loop.
内容的提问来源于stack exchange,提问作者corsiin




