I'm noticing frequent and repeatable dropouts when using the Chromium Audio worklet with a MediaDevices.getUserMedia media stream (microphone). This is not 100% reproducible, but they do tend to follow pattern when they do occur:
(Time ranges vary slightly per attempt)
- 0:00 -> 0:00.2 : No samples are received. (Able to reproduce 100% of the time, but this feels like a separate issue that I wasn't necessarily tracking down at the moment)
 - 0:00.2 -> 0:00.5 : Samples are received
 - 0:00.5 -> 0:00.6 : Dropout occurs, no samples received (Able to reproduce ~20% of the time).
 - 0:00.6 -> 0:30.0 : Samples received
 - Every 30s from here on out, occasionally dropouts will occur. Tends to happen the most often at the first 30s mark. (First 30s mark I can reproduce around 20% of the time as well).
 
Here is a codepen that illustrates the behavior: https://codepen.io/GJStevenson/pen/GRErPbm
const startRecordingButton = document.getElementById('startRecordingButton');
let mediaStreamSourceNode;
let isRecording = false;
let timer;
const workletString = `
const formatTimeString = s => {
   const m = (s / 60).toFixed(2);
   const h = (m / 60).toFixed(2);
   const ms = Math.trunc(s * 1000) % 1000;
   const ss = Math.trunc(s) % 60;
   const mm = Math.trunc(m) % 60;
   const hh = Math.trunc(h);
   return hh + ":" + mm + ":" + ss + "." + ms;
};
class RecorderWorklet extends AudioWorkletProcessor {
    constructor(options) {
        super(options);
        this.sampleRate = 0;
        this.sampleCount = 0;
        this.port.onmessage = event => {
            if (event.data.message === 'init') {
                this.sampleRate = event.data.sampleRate;
            }
        }
    }
    process(inputs) {
        if (inputs.length > 0 && inputs[0].length > 0) {
            this.sampleCount += inputs[0][0].length; 
            //console.debug(formatTimeString(this.sampleCount/this.sampleRate), ' : ', inputs[0][0]);
            if (inputs[0][0].includes(0)) {
                console.log('Dropped Samples at: ', formatTimeString(this.sampleCount/this.sampleRate), ' : ', ...inputs[0][0])
            }
        }
        return true;
    }
}
registerProcessor('recorder-worklet', RecorderWorklet);
`;
async function listAudioInputs() {
    const devices = await navigator.mediaDevices.enumerateDevices();
    return devices.filter((device) => device.kind === 'audioinput');
}
async function getDefaultInput(fallbackToFirstInput = true) {
    const audioInputs = await listAudioInputs();
    const defaultDevice = audioInputs.find((device) => device.deviceId === 'default');
    if (defaultDevice) {
        return defaultDevice;
    }
    return fallbackToFirstInput && audioInputs.length > 0 ? audioInputs[0] : undefined;
}
async function getAudioStream(device) {
    const constraints = {
        audio: {
            deviceId: device.deviceId,
        },
    };
    return navigator.mediaDevices.getUserMedia(constraints);
}
async function createRecordingPipeline(device) {
    const stream = await getAudioStream(device);
    const audioTracks = stream.getAudioTracks();
    const sampleRate = audioTracks[0].getSettings().sampleRate;
    console.log('Sample Rate: ', sampleRate);
    const context = new AudioContext({ sampleRate, latencyHint: 'interactive' });
    const blob = new Blob([workletString], { type: 'text/javascript' });
    const workletUrl = URL.createObjectURL(blob);
    await context.audioWorklet.addModule(workletUrl);
    const workletNode = new AudioWorkletNode(context, 'recorder-worklet');
    workletNode.port.postMessage({
        message: 'init',
        sampleRate: sampleRate
    });
    mediaStreamSourceNode = context.createMediaStreamSource(stream);
    mediaStreamSourceNode.connect(workletNode)
                         .connect(context.destination);
}
function formatTimeString(s) {
   const m = (s / 60).toFixed(2);
   const h = (m / 60).toFixed(2);
   const ms = Math.trunc(s * 1000) % 1000;
   const ss = Math.trunc(s) % 60;
   const mm = Math.trunc(m) % 60;
   const hh = Math.trunc(h);
   return hh + ":" + mm + ":" + ss + "." + ms;
};
async function startRecording() {
    const device = await getDefaultInput();
    await createRecordingPipeline(device);
    let timeElapsed = 0;
    timer = setInterval(() => {
        timeElapsed++;
        console.log('Time: ', formatTimeString(timeElapsed));
    }, 1000);
  
    startRecordingButton.innerText = "Stop Recording";
}
async function stopRecording() {
    if (mediaStreamSourceNode) {
        mediaStreamSourceNode.mediaStream.getAudioTracks().forEach(track => {
           track.stop();
        });
        mediaStreamSourceNode.disconnect();
    }
    mediaStreamSourceNode = null;
    clearInterval(timer);
    
    startRecordingButton.innerText = "Start Recording";
}
async function toggleRecording() {
    if (!isRecording) {
        await startRecording();
    } else {
        await stopRecording();
    }
    isRecording = !isRecording;
}
<button onclick="toggleRecording()" id="startRecordingButton">Start Recording</button>
The dropped samples will look something like this in the console:
Any thoughts as to what the problem may be?
Edit: Ran chrome://tracing to capture trace of the dropped sample. https://www.dropbox.com/s/veg1vgsg9nn03ty/trace_dropped-sample-trace.json.gz?dl=0. Dropped samples happened from ~.53s -> .61s


                        
Got some answers after opening an issue with Chromium. Summarizing some of the responses from https://bugs.chromium.org/p/chromium/issues/detail?id=1248169:
These first few zero samples are the initial primed values from the underlying buffers and are expected.
This should not happen if the AudioWorklet thread runs on a RT priority. This was difficult to reproduce, so shelving it for now.
This dropout was a result of having configured a destination source, but not utilizing it. After 30 seconds of silence, the AudioWorklet thread switches to a low priority one, causing the dropout.
So changing
to
fixed the problem.