Closed julianscheel closed 9 years ago
Hi
As it is the code does not inform you of this.
One method is to make a small modification to the code to inform your ASRC implementation as to whether it is receiving at 48, 96, or 192 KHz. This involves placing an OUTCT/OUT in the code for BFASTER and BSLOWER. For example, you can put
OUTCT r1,11 OUT r1, r4
prior to the SETD in SetClock and this will send a control token 11 and the current divider setting in-band with the data samples. Your ASRC implementation will have to perform a testct() every time it wants to input from the channel and either do a inct()/inuint() pair to retrieve the new divider or an inuint() to retrieve the sample value.
However - that will not distinguish between 44.1 and 48 (or indeed 88.2 and 96 etc). The reason for this is that the S/PDIF receiver statemachine has been designed to be sloppy enough to receive any data rate between 44.1 and 48 in one fell swoop. It cannot distinguish between them.
So the other method that has been used is to, on the receiving side, time how long it takes to receive ten samples or so (using a timer). A single sample at 192 takes 520 clocks give or take 10 clocks or so. A single sample at 176.4 takes 567 clocks give or take 10 clocks. Ten samples of each are 5200 clocks +/- 10 and 5669 clocks +/- 10 so you can easily band them. That is the method that I would use as it does not involve modifying the assembly code.
Hi, thanks for the details. In fact I'll have to take the second route as my design runs with dedicated master clocks for the 44.1 vs 48kHz clock domain, so I need to be able to distinguish them. I'll take a go at implementing it and close the ticket when it's working :)
Using a timer for measuring works well, thank you. Now I just need to find a proper way to make up a mux between USB input and SPDIF input, so I can use the device as SPDIF->I2S bridge and USB->I2S bridge just by toggling a control. Feels a bit tricky...
One way to do it is to restart the I2S thread with a different channel end every time you change the control. It will take hardly any time to do that and may be the least intrusive?
On 16 Jul 2015, at 08:42, Julian Scheel notifications@github.com wrote:
Using a timer for measuring works well, thank you. Now I just need to find a proper way to make up a mux between USB input and SPDIF input, so I can use the device as SPDIF->I2S bridge and USB->I2S bridge just by toggling a control. Feels a bit tricky...
@henkmuller That could be an option. Right now I tried to input two channels (USB+SPDIF) into my asrc module and either read from the one or the other according to the current config. But currently my primary issue is how to deal with the USB input channel when not actually outputting its data to the I2S thread. Leaving it just unconnected deadlocks the USB end for me.... So right now I was trying to make up a dummy endpoint which just reads all incoming data from the USB side and acks commands, but this is not yet successfull. I probably miss some bit of the dataflow right now, which I must not ignore.
This is how my dummy end looks like, right now:
void dummy_end(chanend c_in, chanend c_out)
{
while (1) {
outuint(c_in, 0);
if (testct(c_in)) {
unsigned char command = inct(c_in);
printf("received command %c\n", command);
int n_vals = 0;
switch (command) {
case SET_STREAM_FORMAT_OUT:
case SET_STREAM_FORMAT_IN:
n_vals = 2;
break;
case SET_SAMPLE_FREQ:
n_vals = 1;
break;
}
for (int i = 0; i < n_vals; i++)
inuint(c_in);
outct(c_in, XS1_CT_END);
} else {
int underflow = inuint(c_in);
if (!underflow) {
inuint(c_in);
inuint(c_in);
}
}
}
}
@henkmuller Albeit being offtopic: Do you have any clue what could cause the USB core not to properly answer requests when the I2S thread is not receiving data? I can unblock it by just adding
unuint(c_out);
outuint(c_out, 1);
into my dummy loop. Plus a tiny change to the I2S thread to deal with underflow being set:
diff --git a/sc_usb_audio/module_usb_audio/audio.xc b/sc_usb_audio/module_usb_audio/audio.xc
index c107883..e1eb0a6 100755
--- a/sc_usb_audio/module_usb_audio/audio.xc
+++ b/sc_usb_audio/module_usb_audio/audio.xc
@@ -265,7 +265,7 @@ static inline unsigned DoSampleTransfer(chanend c_out, int readBuffNo, unsigned
else
{
#ifndef MIXER // Interfaces straight to decouple()
- inuint(c_out);
+ underflowWord = inuint(c_out);
#if NUM_USB_CHAN_IN > 0
#pragma loop unroll
for(int i = 0; i < I2S_CHANS_ADC; i++)
@@ -285,18 +285,22 @@ static inline unsigned DoSampleTransfer(chanend c_out, int readBuffNo, unsigned
#if NUM_USB_CHAN_OUT > 0
#pragma loop unroll
- for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
- {
- samplesOut[i] = inuint(c_out);
+ if (!underflowWord) {
+ for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
+ {
+ samplesOut[i] = inuint(c_out);
+ }
}
#endif
#else /* ifndef MIXER */
#if NUM_USB_CHAN_OUT > 0
#pragma loop unroll
- for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
- {
- int tmp = inuint(c_out);
- samplesOut[i] = tmp;
+ if (!underflowWord) {
+ for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
+ {
+ int tmp = inuint(c_out);
+ samplesOut[i] = tmp;
+ }
}
What seems strange to me, when the thread is not unblocked by writing to c_out, is that I don't see any received command
debug prints from the dummy_end loop. If I interrupt the code in the debugger and immediately continue it a received command
is printed. It seems like there is some blocking condition which is resolved by stopping in the debugger, which makes it hard to locate.
When I halt the debugger I see the endpoint0 thread waiting for a XS1_CT_END:
Thread 4 (tile[0] core[3]):
#0 Endpoint0 (c_ep0_out=<value optimized out>, c_ep0_in=<value optimized out>,
c_audioControl=2147553026, c_mix_ctl=0, c_clk_ctl=<value optimized out>,
c_EANativeTransport_ctrl=0, dfuInterface=<value optimized out>)
at /home/julian/git/xmos-audio/sc_usb_audio/module_usb_audio/endpoint0/endpoint0.c:360
#1 0x00010ae6 in _Susb_audio_core_0.task.Endpoint0.2 (frame=<value optimized out>)
at /home/julian/git/xmos-audio/sc_usb_audio/module_usb_audio/main.xc:405
#2 0x000177f6 in __start_core ()
While my dummy thread sits at testct(c_in)... So it seems the endpoint0 has sent a CT out, but dummy_end has not received it - somehow as if the channel is blocked internally. The decouple thread sits in handle_audio_request in the traces, but not always at the same line....
Ok, I just understand what was going on. Due to not waiting for the i2s thread to request samples before requesting samples from the USB core, the loop was not rate-limited anymore. And thus the USB task was blocked by processing all the sample requests from dummy_end(). The problem can be avoided by slowing dummy_end down with a little delay between sample requests.
Is it somehow possible to get informed about the currently detected sampling frequency (thus divider ratio) of the spdif rx core? I am planning to use it with an asrc implementation always generating a 192kHz output signal and for this it would be really helpful to know what the current nominal input rate is.