USB Audio Record Dropping Samples (and/or inserting 0 samples)

seanb

New member
Setup:
  • Arduino 2.3.3 - CLI version 1.0.4
  • Intel MacBook Pro running MacOS 15.0.1
  • Teensy 4.1
  • Teensy library 1.59
---
I'm trying to record USB audio waveforms and I've noticed that periodically several samples are dropped (or zero samples are inserted - depending on which PC I use for recoding). I noticed the following comment in Arduino15/packages/teensy/hardware/avr/1.59.0/cores/teensy4/usb_audio.cpp:
unsigned int usb_audio_transmit_callback(void)
{
static uint32_t count=5;
uint32_t avail, num, target, offset, len=0;
audio_block_t *left, *right;

if (++count < 10) { // TODO: dynamic adjust to match USB rate
target = 44;
} else {
count = 0;
target = 45;
}
...
I think that the processor clock that is used to generate the samples does not match the USB frame rate - and there is a slight clock drift between the two when using the fixed schedule that averages to 44100 samples/s (the 9 frames of 44 samples and 1 frame of 45 samples with a USB frame rate 1000/s). The drift eventually results in either overrun or underrun of the buffers depending on whether the teensy clock is faster or slower than the USB frame rate. It appears that this is handled for USB playback, but not for USB record.
I was able to add some debugging to usb_audio.cpp and it confirms that overrun is occurring (see the attached modified usb_audio.cpp and I2S_Audio.ino). The serial output while recording is:
18:04:45.713 -> usb_overrun: 3 usb_underrun: 1
18:04:46.817 -> usb_overrun: 6 usb_underrun: 1
18:04:47.923 -> usb_overrun: 7 usb_underrun: 1
18:04:49.032 -> usb_overrun: 8 usb_underrun: 1
18:04:50.137 -> usb_overrun: 9 usb_underrun: 1
18:04:52.319 -> usb_overrun: 10 usb_underrun: 1
18:04:53.426 -> usb_overrun: 11 usb_underrun: 1
18:04:54.533 -> usb_overrun: 12 usb_underrun: 1
18:04:55.639 -> usb_overrun: 13 usb_underrun: 1
18:04:58.925 -> usb_overrun: 14 usb_underrun: 1
With an overrun ~ every 1s.
In the corresponding recording from Audacity you can see the "ticks" that the correspond to the overruns (I've attached the corresponding wave file overrun.wav.zip where you can hear the "ticks").
1732231236424.png


I'm wondering if there is a simple fix?

Note: I am trying to record from I2S - but #if def'ed it out, replacing with the AudioSynthWaveformSine to avoid the use of any HW other than the Teensy 4.1. The issue is present when using I2S as well.
 

Attachments

  • I2S_Audio.ino
    1.5 KB · Views: 9
  • usb_audio.cpp
    14.4 KB · Views: 9
  • overrun.wav.zip
    1.1 MB · Views: 13
Thanks for the link ... reading the entire thread it seems that buffer overrun and under run were identified as the root cause.
The buffer overrun/under run are a result of clock differences between the PC (and the 1ms SOF clock) and the Teensy (44100 Hz sample clock).
The current code uses a fixed schedule for transferring the the buffered data (sampled at 44100 Hz) to the USB 1ms frame - 9 x 44 samples and 1 x 45 samples - which is an average of 44.1 samples in 1ms or 44100 S/s. This fixed schedule is not able to adjust for the (small) clock differences between the 1ms USB clock and the 44100 S/s Teensy sample clock - instead it needs to be adaptive. Note: small clock differences are on the order of 10-100 PPM (for typical crystal tolerances) - for 44100 S/s this would correspond to between 0.5 sample to 5 samples of buffer increase/decrease each second.

The "FRANKB_PATCH" fix near the end of the discussion (https://forum.pjrc.com/index.php?threads/usb-audio-clicking-noise.54239/post-306552) fixes the under run case by only returning the number samples actually available (transferring less than 44 or 45 samples of the fixed schedule) - making transfer schedule "adaptive" for this case. I suspect for this user, that the PC clock was running faster than the Teensy clock which is why the fix worked in their case.

The "FRANKB_PATCH" attempts to fix the overrun case - but it still effectively drops samples (rather than sending more samples or sending 45 samples more often). Depending on the tone that is being generated for testing, the dropped samples may be noticeable as a click or not. The clicks are not easily discernible when looking at the time domain of the recording - even if you zoom in. A better way is to use the spectrogram view of Audacity - the ticks are clearly visible (and you don't need to listen to harsh sounding tones).

For my setup the PC clock is slower than the Teensy clock so I still see the clicks with the changes.
I'm looking at code changes to make both overrun and under run cases adaptive and will post my results here.
 
Back
Top