Forum Rule: Always post complete source code & details to reproduce any issue!
Change block size and sample rate of USB Audio
In my work with my forked (floating point) version of the Teensy Audio library, I've had good success being able to change the sample rate and block size for the audio system. It works well for I2S audio because I replumbed the I2S audio objects to handle the new functionality. Now I'd like to modify the USB Audio code to get it to work.
Poking around the USB_Audio.h and USB_Audio.cpp in the core Teensy library, I can kinda see how it goes together. But, I can't see how (or if?) the Teensy's sample rate and block size come into play.
How does the Teensy's sample rate get communicated through the USB subsystem to the PC?
And, does the Teensy's block size matter? Or, does the USB link have its own block size that is independent from the audio library's block size?
I’m not sure if this is what you’re talking about, and since you’ve already developed classes for the Audio library you probably already know this, but --- from the USB Documentation in the Audio Design Tool: “USB input & output does not cause the Teensy Audio Library to update. At least one non-USB input or output object must be present for the entire library to update properly”.
This means the sample rate is never set by a USB input or output. Looking at the usb_audio code, you’ll notice that the variable ‘update_responsibility’ is always set to ‘false’. The code does not contain any calls like:
So, timing must be established by an object implemented within the Teensy.
update_responsibility = update_setup();
Sorry if this wasn’t what you were asking or this topic is way over my head and this response is inappropriate.
The nominal sample rate is specified in the USB descriptors.
Originally Posted by chipaudette
The actual real-time sample rate is communicated by the isochronous feedback endpoint.
Macintosh also makes use the incoming isochronous IN data rate. Just know that things work very differently on Macs than they do for Windows and Linux.
Maybe. It's never been tried with other sample rates or other block sizes. Subtle dependencies on both probably exist.
And, does the Teensy's block size matter?
Two places to consider are:
1: The copy of data from packet buffer to audio buffer may assume the USB packet data is always smaller.
2: The computation of the isochronous feedback uses a simple proportional-integral algorithm which has only ever had minimal effort to tune to existing data rates. Since there's no derivative component, I believe unstable behavior is unlikely, but it may not necessarily perform well. Even at the default settings, I'm not confident the tuning we have now is optimal.
Other subtle dependencies may also exist in the code, but these are the ones I can think of right now.
If you play with this, I hope you'll share whatever you learn.
If you're not familiar with isochronous synchronization & feedback, the essential info is in section 5.12.4 of the USB spec starting on page 71.
Here's a direct link to a copy of the document, to save you some trouble digging through www.usb.org.
Also keep in mind the upper bmAttributes bits in the USB endpoint descriptor, which are normally zeros for non-isochronous endpoints, are particularly important to tell your PC or Mac how to synchronize. They're documented in Table 9-13 on page 270.
Windows, Linux and Mac might have subtle (or not so subtle) differences in the practical realities of their isochronous synchronization. Beware a false impression things are working by only a short listening test. Especially with Macintosh, problems can occur after many minutes of isochronous streaming if synchronization isn't quite right. Be sure you do long duration listening tests on all 3 platforms.
One more quick piece of advice, for you or anyone else who finds this info and tries to play with this code...
In usb_audio.cpp, look for the places where buffer overrun and underrun are detected. You'll find some commented out code to send bytes on Serial1. If you mess with the USB audio code, I highly recommend uncommenting those and using a FTDI cable or another Teensy running USBtoSerial to watch. Or put your own monitoring code in there, maybe turning on LEDs?
Aside from merely avoiding these conditions, you might wish to investigate whether you can gracefully recover when/if they do occur. You can force them to happen by just making the isochronous feedback wrong for a period of time. As you push to smaller buffer sizes and high sample rates, there's a chance these conditions could cause unstable behavior when they interact with the rest of your code and the behavior of the USB host in response to whatever feedback you send. There can be subtle problems you wouldn't necessarily expect due do the interaction of the PI feedback loop on Teensy and the feedback loop formed by whatever the USB host side does, which can function perfectly under "normal" circumstances but end up doing something wildly different when given the large step change from these underrun & overrun cases. You may need to add other code for graceful recovery.
Last edited by PaulStoffregen; 04-21-2017 at 06:30 PM.
So, by "USB descriptors", I assume you mean usb_desc.h and usb_des.c. In usb_des.c, I see the sample rate showing up (all of those "44100" entries). But, I'm having a hard time seeing where the block size is appearing.
Originally Posted by PaulStoffregen
In usb_desc.h, I see that the AUDIO_TX_SIZE and AUDIO_RX_SIZE are 180. Is that 180 bytes, or 16-bit words? Is it all audio payload, or does it include some overhead info? I'm trying to figure out how this relates to a Teensy block size of 128 16-bit samples per channel per block. Any pointers or clues would accelerate my sleuthing!
Thanks for your help so far. I've read through certain sections of the doc trying to answer my question (though my reading skills appear to be insufficient). The team that pulled together this USB Audio capability in the Teensy are truly amazing!