Stop / Start USB Stream in firmware?

New to XMOS and XCore? Get started here.
RitchRock
XCore Addict
Posts: 199
Joined: Tue Jan 17, 2017 9:25 pm

Stop / Start USB Stream in firmware?

Post by RitchRock »

Hello,

I'm using the MC-audio development board and a modified version of the reference design app. I am doing some FIR DSP processing and it has different modes that are changed by push-button. Once in a while (not very often), when I change modes, I get complete distortion. Usually changing modes again clears it up. Testing USB audio loopback, by taking the audio() thread out of the picture works OK. Creating a pure sine tone in firmware with no USB also works OK (verified on xscope). I think what is happening, is occasionally the timing breaks between my DSP mixer, audio() and decouple().

So, if I want to stop and then start the USB stream on one of these DSP mode changes, or stop using USB streaming and instead just use Analog Input -> DSP processing --> Analog Output, what would be the best way to do this?

I understand what to do if I was just having to deal with the audio() thread, however, what about on the decouple / USB side?

Perhaps there something already built-in to the reference design firmware, like for example what happens on Sample Rate change, that I can use to stop the USB stream, do my DSP mode change (or just do analog in to analog out) and then restart the USB stream.

Any suggestion to get started on this firmware change would be helpful. Thanks!!
User avatar
infiniteimprobability
Verified
XCore Legend
Posts: 1143
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

I'm using the MC-audio development board and a modified version of the reference design app. I am doing some FIR DSP processing and it has different modes that are changed by push-button. Once in a while (not very often), when I change modes, I get complete distortion. Usually changing modes again clears it up. Testing USB audio loopback, by taking the audio() thread out of the picture works OK. Creating a pure sine tone in firmware with no USB also works OK (verified on xscope). I think what is happening, is occasionally the timing breaks between my DSP mixer, audio() and decouple().
Quite possibly. You can normally tell if timing is very broken by checking the LR clock period on a scope. Because we are generally master, broken timing pushes out the I2S cycle.
So, if I want to stop and then start the USB stream on one of these DSP mode changes, or stop using USB streaming and instead just use Analog Input -> DSP processing --> Analog Output, what would be the best way to do this?
The audio loop deliver() runs continuously and handles I2S in/out (aka analog in/out). There is no reason at all why you couldn't remap channels or insert DSP there. The best place to do this is in DoSampleTransfer() in between the sample exchange:

Code: Select all

        {
            int tmp = inuint(c_out);
            samplesOut[i] = tmp;
        }
#else
        inuint(c_out);
#endif
<<<<HERE!!!!
#if NUM_USB_CHAN_IN > 0
#pragma loop unroll
#if  NUM_USB_CHAN_IN < I2S_CHANS_ADC
        for(int i = 0; i < NUM_USB_CHAN_IN; i++)
#else
        for(int i = 0; i < I2S_CHANS_ADC; i++)
#endif
        {
Don't add anything that takes too long here though. Remapping/volume/mixing etc. is fine but much more and you will push out I2S timing. If you need more, send samples over to another task for processing.
I understand what to do if I was just having to deal with the audio() thread, however, what about on the decouple / USB side?
Not sure what you mean. SamplesOut are from the host and are destined for I2S out. SamplesIn are from I2S and destined for the host. So you can do whatever here.

I2S always loops. If the host is disconnected for any reason then I2S still loops.. Decouple/Buffer just under/overflow and discard samples to host or send the underflow word (0 for PCM) to audio. When the host sorts itself out, it should all recover and normal service is resumed.
Perhaps there something already built-in to the reference design firmware, like for example what happens on Sample Rate change, that I can use to stop the USB stream, do my DSP mode change (or just do analog in to analog out) and then restart the USB stream.

Any suggestion to get started on this firmware change would be helpful. Thanks!!
You can't stop the stream from the device. It's USB so it's all host centric and it's the host that decides when to start/stop a stream by changing Alt interface. You can monitor this from the app though using the callbacks provided (hostactive).
Engineer at XMOS
RitchRock
XCore Addict
Posts: 199
Joined: Tue Jan 17, 2017 9:25 pm

Post by RitchRock »

Thank you for the reply. I haven't seen any noticeable problems with the LR clock on the scope.

The reason I asked about the decouple() thread and if it's possible to stop/start, is because that is where my code hangs when I attempt to add a new control token sent from my mixer to audio() / doSampleTransfer(). My new token calls audio hardware init to reset DAC and wait for the I2S clock to stabilize (like on frequency change). When it finishes, it continues on with the normal audio loop in my mixer, but gets stuck when trying to get samples from decouple.
RitchRock
XCore Addict
Posts: 199
Joined: Tue Jan 17, 2017 9:25 pm

Post by RitchRock »

infiniteimprobability wrote:
The audio loop deliver() runs continuously and handles I2S in/out (aka analog in/out). There is no reason at all why you couldn't remap channels or insert DSP there. The best place to do this is in DoSampleTransfer() in between the sample exchange:

Code: Select all

        {
            int tmp = inuint(c_out);
            samplesOut[i] = tmp;
        }
#else
        inuint(c_out);
#endif
<<<<HERE!!!!
#if NUM_USB_CHAN_IN > 0
#pragma loop unroll
#if  NUM_USB_CHAN_IN < I2S_CHANS_ADC
        for(int i = 0; i < NUM_USB_CHAN_IN; i++)
#else
        for(int i = 0; i < I2S_CHANS_ADC; i++)
#endif
        {
Don't add anything that takes too long here though. Remapping/volume/mixing etc. is fine but much more and you will push out I2S timing. If you need more, send samples over to another task for processing.
My DSP Core does take some time. Is there any reason I couldn't use a Streaming Chanend inside DoSampleTransfer() to send/receive samples from my Mixer and DSP Cores?
User avatar
infiniteimprobability
Verified
XCore Legend
Posts: 1143
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

My DSP Core does take some time. Is there any reason I couldn't use a Streaming Chanend inside DoSampleTransfer() to send/receive samples from my Mixer and DSP Cores?
That is exactly what you should do. It doesn't need to be a streaming channel if you get the timing right though, although streaming channels save a few cycles and give you a bit of buffer space.

So the code would do this:

Code: Select all

get samples from host
send samples to DSP task over channel
get processed samples from DSP over channel (now DSP task, previously blocked, processes the samples just provided)
send samples to host
That way it also stays nicely in sync and your DSP task has pretty much a whole sample period to do it's thing. It introduces a one sample delay however but that's because it's effectively a pipeline
Engineer at XMOS
User avatar
infiniteimprobability
Verified
XCore Legend
Posts: 1143
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

that is where my code hangs when I attempt to add a new control token sent from my mixer to audio() / doSampleTransfer(). My new token calls audio hardware init to reset DAC and wait for the I2S clock to stabilize (like on frequency change).
This is classic channel lockup. It means the protocol on one side is not the same as the other (sequence of ins outs inct outct etc.). For example, if both sides were performing an IN instead of one doing an OUT and the other an IN. In some cases you may get an exception if they mismatch or sometimes just a hang. Either way it's a logic bug in the code.

You need to carefully check the sequence of ins and outs on both sides to make sure they match if you have modified this. I do this by writing the sequence down on a piece of paper sometimes...
Engineer at XMOS
RitchRock
XCore Addict
Posts: 199
Joined: Tue Jan 17, 2017 9:25 pm

Post by RitchRock »

I've had success using a channel to send / receive samples from DoSampleTransfer(). I created a test loop which automatically switches between modes and tested it for some time without any distortion at different bit-depths and sample frequencies. No distortion on mode change.

I then proceeded to add my button handler. I had some trouble getting the debounce correct, so I tried different techniques. Then I happened upon this post you made some time ago. I did some bit-masking and got it to work, but still am occasionally getting distortion when I switch my mode.

Finally, I proceeded to turn off real-time XSCOPE printing (something I've tried before and still got the distortion) and everything seems to be working perfectly. Of course I need to continue testing, to make sure. Can you comment on XSCOPE real-time printing and how it might affect my timing here?

Thank you very much for your help!!
User avatar
infiniteimprobability
Verified
XCore Legend
Posts: 1143
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

Can you comment on XSCOPE real-time printing and how it might affect my timing here?
It's pretty efficient but not free. Here's the official line:

https://www.xmos.com/support/tools/docu ... nent=14797

In practical terms I find I can get away with printing a character or two in 192kHz loops but down at 48kHz I can comfortably print signed ints etc.
I usually use <print.h> printintln() for these jobs as a full printf formatter is going to chew up the cycles. debug_printf.h (lib logging) is quite lite too.

This is only a very rough guideline by the way and obviously depends how much slack you have in your loop timing. Waggling an I/O pin in the loop is a very good way of timing. Set the i/o high immediately after the I/O operation and then set it low just before. You can also use delay_milliseconds() or delay_ticks() to see how much slack you have there.
Engineer at XMOS