USB audio customisation

Discussions about USB Audio on XMOS devices
lmariotti
Member++
Posts: 27
Joined: Mon Nov 21, 2022 5:38 pm

USB audio customisation

Post by lmariotti »

Hi everyone,

I’m trying to customise the app_usb_aud_xk_216_mc, so far I was able to make it run on xCORE-200 eXplorer with: custom ADC (2ch), custom DAC (2ch) and 2 external clock (24,576KHz and 22.5792MHz), now I’ve a USB/I2S bridge that works just fine from 44.1KHz to 192KHz.
The next step in my project is to add a button with the following logic:
Button released) Standard USB to I2S bridge (everything keep working as it is);
Button pressed) The USB is fully ignored and the I2S is just a loopback from ADC to DAC with a fixed sample rate (176.4KHz/192KHz);

The I2S loopback works just fine, the trubles come trying to modify the sample rate when switching from USB to I2S loopback.
I've tried to force a return from AudioHub_MainLoop when the button status is changed to setup the new sample rate.
This solution works well from USB to I2S loopback but has some problems from I2S loopback to USB, causing the latter to stop working.

So far I can't figure out a different solution to achieve that.

Could one perhaps signal to the USB source the change of frequency so as to align the two?

Thanks for your time.
You do not have the required permissions to view the files attached to this post.


User avatar
fabriceo
XCore Addict
Posts: 184
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi lmariotti,
I ve been facing the same problem, not for an ADC, but with a SPDIF receiver. so a bit of the same story.
my choice was to make the I2S Loopback in the Decouple.xc because I wanted to send the SPDIF flow to USB.

you have plenty of problem to solve.
1 st you need to have a fixed clock of 176 or 192 as you say so you must recompute bclk and you have to leave the audio task so that it is done when re-entering. Either you force this, either you organize so that the Host send a request for change:

2nd you should inform the host that the clock has changed.
this can be done by enabling a specific endpoint interrupt called ENDPOINT_NUMBER_IN_INTERRUPT in devicedefines.h
the library code enable this endpoint only when you have an SPDIF_RX or ADAT_RX flag... so you have to ovewrite this in different places (buffer.xc also).
also you will have to trigger an interrupt packet to the host. see our discussion with maximliadov about reporting a volume knob change...
Then the host will hopefully send an USB request to get the actual rate. see case ID_CLKSRC_INT in audiorequest.xc. and it will propagate a request to change the rate to the one you gave...

another solution (which I did in fact) is to organize the code to provide the host with a fixed clock rate in the feedback endpoint (see case SOF in buffer.xc) which is aligned with the USB Host speed requested, even if your loopback works at another one. This way you get the USB host running freely. but some extra code is required to decouple the 2 flows and I don't remember all the story.

So this is some base for you to investigate, but be prepared for some hard time :)

br/fabriceo
lmariotti
Member++
Posts: 27
Joined: Mon Nov 21, 2022 5:38 pm

Post by lmariotti »

Hi fabriceo,
thanks for your suggestions, I thought it was a way easier task but anyway I'll try to figure out a solution.
I was expecting it was enough to modify XUA_AUDIOHUB.xc but clearly I was wrong, first of all I think I've to dive deep into the USB software to understand it better.
Your suggestions are certainly a good starting point.
User avatar
fabriceo
XCore Addict
Posts: 184
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Well, when you leave the audio loop, it comes back in immediately, and then the clock divider for bclk are recalculated and the lrclk is rested so this is good that you leave the audio loop when there is a change on your loopback switch. I do the same when for example I see the SPDIF RX rate is changing.
But the problem is the synchronization with buffer and decouple tasks, if the USB Host was running at a different rate.

keep in mind that the Audio task is triggering an interrupt in the core allocated to the decouple task, and this interrupt is used to extract the USB data given by Buffer and to send back data to Buffer. So all of the sudden you change the rules by changing the rates of the interrupt and this brings some under-run situation which jeopardize the whole usb send/receive flow.

thats why you have to either inform the host of the speed change so it will propagate a speed change itself; or you have to implement mechanism to decouple the flows.
Something I have not tested but which might be easier to implement : simply force to disconnect your device from the USB host (logically or electrically) when entering in loopback mode, and restore the connection when back in the normal mode simply by using the reboot procedure.
User avatar
Ross
XCore Expert
Posts: 966
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

Here's an "outside of the box" that might actually be simpler than doing things "properly" - which would involve informing the host that the sample rate has changed. Im assuming you don't care the device disconnects from the bus in loopback mode..

Use two binaries, one usb, one loopback. Reboot into the correct one based on the button event.

Bootloader customisation is quite easy, the tools support it.
lmariotti
Member++
Posts: 27
Joined: Mon Nov 21, 2022 5:38 pm

Post by lmariotti »

Thanks for your support, I managed to solve the probelem electrically as suggested. A much simpler and more functional solution!
User avatar
fabriceo
XCore Addict
Posts: 184
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi, good to hear, good decision are not always the best one :)
Ross, is there an easy / possible way to leave XUD_LLD_IoLoop to detach the device and then to launch again XUD_manager ?
User avatar
Ross
XCore Expert
Posts: 966
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

Try this function:

Code: Select all

/**
 * \brief   Terminate XUD core
 * \param   ep          XUD_ep type (must be endpoint 0 in or out)
 * \warning Must be run on same tile as XUD core
 */
void XUD_Kill(XUD_ep ep);