This is for anyone who experienced USB audio dropouts on Linux based hosts. I could not find any references to this issue online, forgive me if I post something here that is old news.
Our design is based on the sw_usb_audio-[sw]_6.15.2rc1 release. The issue we encountered is that if you play music on Linux based hosts and go from a track with a certain bit depth to another one, the system would hang. A short white noise like sound is to be heard, after which the host looses connection with the USB stack.
So for example switching from a 44.1k/16b to a 48k/24b is no problem, but if the sample rate stays equal; (for example 44.1/16b to 44.1/24b) you will get this bug.
(Assuming you have your alternate defines set to "STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS 16")
Test platform in our case is a Roon Ready app (v1.1.27) running on a Antipodes (v2.5) server, the USB output to our Kii Control. In hindsight we have also experienced this bug with other Linux based streamers, but never have been able to connect the dots.
I've hooked up the XTAG to our Kii Control to see what happened during a crash. It turns out that there was a readout of an array (outAudioBuff) which was out of bounds. The read pointer (g_aud_from_host_rdptr) was way beyond anything valid.
If I am correct, it is caused by the fact that "aud_data_remaining_to_device" does not get reset when there is a "SET_STREAM_FORMAT_OUT" action. So just add "SET_SHARED_GLOBAL(aud_data_remaining_to_device, 0);" to this part of the code:
Code: Select all
else if(tmp == SET_STREAM_FORMAT_OUT)
{
unsigned dataFormat, sampRes;
unsigned dsdMode = DSD_MODE_OFF;
/* Change in OUT channel count - note we expect this on every stream start event */
DISABLE_INTERRUPTS();
SET_SHARED_GLOBAL(g_freqChange_flag, 0);
GET_SHARED_GLOBAL(g_numUsbChan_Out, g_formatChange_NumChans);
GET_SHARED_GLOBAL(g_curSubSlot_Out, g_formatChange_SubSlot);
GET_SHARED_GLOBAL(dataFormat, g_formatChange_DataFormat);
GET_SHARED_GLOBAL(sampRes, g_formatChange_SampRes);
/* Reset OUT buffer state */
SET_SHARED_GLOBAL(g_aud_from_host_rdptr, aud_from_host_fifo_start);
SET_SHARED_GLOBAL(g_aud_from_host_wrptr, aud_from_host_fifo_start);
SET_SHARED_GLOBAL(aud_data_remaining_to_device, 0);
unpackState = 0;
outUnderflow = 1;
if(outOverflow)
{
/* If we were previously in overflow we wont have marked as ready */
XUD_SetReady_OutPtr(aud_from_host_usb_ep, aud_from_host_fifo_start+4);
outOverflow = 0;
}
#ifdef NATIVE_DSD
if(dataFormat == UAC_FORMAT_TYPEI_RAW_DATA)
{
dsdMode = DSD_MODE_NATIVE;
}
#endif
/* Wait for the audio code to request samples and respond with command */
inuint(c_mix_out);
outct(c_mix_out, SET_STREAM_FORMAT_OUT);
outuint(c_mix_out, dsdMode);
outuint(c_mix_out, sampRes);
/* Wait for handshake back */
chkct(c_mix_out, XS1_CT_END);
asm volatile("outct res[%0],%1"::"r"(buffer_aud_ctl_chan),"r"(XS1_CT_END));
SET_SHARED_GLOBAL(g_freqChange, 0);
ENABLE_INTERRUPTS();
}
Another thing i've noticed is that in the start of decouple thread a buffer limit seems to be invalid:
Code: Select all
aud_from_host_fifo_end = aud_from_host_fifo_start + BUFF_SIZE_OUT*4;
Code: Select all
unsigned outAudioBuff[BUFF_SIZE_OUT + (MAX_USB_AUD_PACKET_SIZE>>2) + 4];
Maybe someone at XMOS can shed some light on this?
Cheers,
Bart van der Laan