Synchronous Sample Rate Conversion Problem

Technical questions regarding the XTC tools and programming with XMOS.
Post Reply
User avatar
zhengyang0512
Member++
Posts: 27
Joined: Tue Aug 23, 2016 6:14 am

Synchronous Sample Rate Conversion Problem

Post by zhengyang0512 »

Hi, I'm doing a project about receiving the music from the spdif receive module and output the music to the I2S module. My reference software is the AN00231_ASRC_SPDIF_TO_DAC. However, the software is about the asynchronous sample rate conversion and the supported the sample rate is between the 44.1kHz and 96kHz. I need to transfer the music whose maximum sample rate is 192kHz. The asrc function don't work well. I don't need to make a sample rate conversion, instead, I just want to transfer the music without change the sample rate. So, I try to use the ssrc function. Another disappointing result is that there is no reference software about this. I once found the statement like this: AN00230 - [Adding Synchronous Sample Rate Conversion to the USB Audio reference design]. But I can't find the download link on the offical website. Could someone give me some help about using the synchronous sample rate conversion? Thank you very much !!!


User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

Hi, AN00230 has not been written yet. But actually I don't think this is what you need.
I don't need to make a sample rate conversion, instead, I just want to transfer the music without change the sample rate.
Do you need to cross asynchronous clock domains? If so, ASRC is what you need even if the fIN and fOUT are nominally the same. However ASRC cannot currently work with above 176.4KHz on both sides without further optimisation, although it might be possible with a large block size

https://www.xmos.com/support/libraries/lib_src

The asrc function don't work well.
Can you expand on this? It should give 130db+ SNR which is way into audiophile quality..
User avatar
zhengyang0512
Member++
Posts: 27
Joined: Tue Aug 23, 2016 6:14 am

Post by zhengyang0512 »

infiniteimprobability wrote:Do you need to cross asynchronous clock domains? If so, ASRC is what you need even if the fIN and fOUT are nominally the same. However ASRC cannot currently work with above 176.4KHz on both sides without further optimisation, although it might be possible with a large block size
Yes, the clock of the spdif is from the xmos, but the I2S is at the slave mode and the clock is from the other clock source.
The asrc function don't work well.
Can you expand on this? It should give 130db+ SNR which is way into audiophile quality..
That means when I transfer the music whose sample rate is 176.4kHz and 192kHz, I can't hear the sound from the Line Out on Multichannel Audio Board.

So, could you please give me some suggestions about how to fix the problem?
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

I'm still not 100% clear about your clock architecture. If you are slave on one side and master on the then you don't need any SRC at all? Can't the I2S master drive the I2S slave LRCLK and BCLK lines?
when I transfer the music whose sample rate is 176.4kHz and 192kHz, I can't hear the sound from the Line Out on Multichannel Audio Board.
That's because it's not currently supported.


From ASRC user guide:
Configurations requiring more than 100MHz cannot currently be run in real time on a single core.
APPENDIX A - Known Issues. Certain ASRC configurations, mainly conversions between 176.4/192KHz to 176.4/192KHz, require greater than 100MHz for a single audio channel and so cannot currently be run in real time on a single core.
And from AN00231:
The sample rates supported for this demonstration are 44.1, 48, 88.2 and 96KHz. This restriction is applied by the SPDIF receiver which is only rated up to 96KHz for optical sources and the ASRC library which can currently only handle rates of 176.4/192KHz on one side.
So, could you please give me some suggestions about how to fix the problem?
The code in ASRC app note AN00231 is optimised for size rather than performance, but it may work at 192kHz/176kHz in AND out. You could try:
increasing this to 64:

Code: Select all

#define ASRC_N_IN_SAMPLES 
You could temporarily try this (overclock by 20%) to see how far off the MIPs are (please note this is not a guaranteed operation mode):

Code: Select all

        <Node Id="0" InPackageId="0" Type="XS2-L16A-512" Oscillator="24MHz" SystemFrequency="600MHz" referencefrequency="100MHz">
Otherwise, work to natively support 176.4/192 in < 100MHz, the user guide explains what is needed:
A number of potential optimizations have been identified to permit these rates:
• Further inner loop optimization using assembler
• Increase in scope of assembler sections removing additional function calls • Pipelining of the FIR filter stages into separate tasks
• Calculation of adaptive filter coefficients in a separate task
User avatar
zhengyang0512
Member++
Posts: 27
Joined: Tue Aug 23, 2016 6:14 am

Post by zhengyang0512 »

Thank you for your reply, my clock architecture is on the attachments file. I am now trying to follow your suggestion.
Attachments
20170622162146.png
(19.29 KiB) Not downloaded yet
20170622162146.png
(19.29 KiB) Not downloaded yet
User avatar
zhengyang0512
Member++
Posts: 27
Joined: Tue Aug 23, 2016 6:14 am

Post by zhengyang0512 »

If you are slave on one side and master on the then you don't need any SRC at all? Can't the I2S master drive the I2S slave LRCLK and BCLK lines?
Did you mean that if the data path is spdif in->spdif rx(xmos)->i2s slave(xmos) -> i2s master(dsp) then I don't need to use the asrc or the ssrc lib function ?
I have modify the main.xc in SPDIF Receive to I2S output using Asynchronous Sample Rate Conversion.

Code: Select all

// Copyright (c) 2016, XMOS Ltd, All rights reserved
//Standard includes
#include <xs1.h>
#include <platform.h>
#include <string.h>

//Supporting libraries
#include "src.h"
#include "spdif.h"
#include "i2s.h"
#include "i2c.h"
#include "gpio.h"
#include "assert.h"

//Application specific includes
#include "main.h"
#include "block_serial.h"
#include "cs4384_5368.h"    //CODEC setup
#include "app_config.h"     //General settings

//Debug includes
#include <debug_print.h> //Enabled by -DDEBUG_PRINT_ENABLE=1 in Makefile
#include <xscope.h>


//These port assignments all correspond to XU216 multichannel audio board 2V0
//The port assignments can be changed for a different port map.

#define AUDIO_TILE                       0
in buffered port:32  ports_i2s_adc[4]    = on tile[AUDIO_TILE]: {XS1_PORT_1I,
                                                     XS1_PORT_1J,
                                                     XS1_PORT_1K,
                                                     XS1_PORT_1L};
port port_i2s_mclk                       = on tile[AUDIO_TILE]: XS1_PORT_1F;
clock clk_mclk                           = on tile[AUDIO_TILE]: XS1_CLKBLK_2;
out buffered port:32 port_i2s_bclk       = on tile[AUDIO_TILE]: XS1_PORT_1H;
out buffered port:32 port_i2s_wclk       = on tile[AUDIO_TILE]: XS1_PORT_1G;
clock clk_i2s                            = on tile[AUDIO_TILE]: XS1_CLKBLK_1;
out buffered port:32 ports_i2s_dac[4]    = on tile[AUDIO_TILE]: {XS1_PORT_1M,
                                                     XS1_PORT_1N,
                                                     XS1_PORT_1O,
                                                     XS1_PORT_1P};


#define SPDIF_TILE                       1
port port_spdif_rx                       = on tile[SPDIF_TILE]: XS1_PORT_1P;
clock clk_spdif_rx                       = on tile[SPDIF_TILE]: XS1_CLKBLK_1;

out port port_leds_col                   = on tile[SPDIF_TILE]: XS1_PORT_4C;     //4x4 LED matrix
out port port_leds_row                   = on tile[SPDIF_TILE]: XS1_PORT_4D;

port port_i2c                            = on tile[AUDIO_TILE]: XS1_PORT_4A;     //I2C for CODEC configuration

port port_audio_config                   = on tile[AUDIO_TILE]: XS1_PORT_8C;
char pin_map_audio_cfg[5]                = {0, 1, 5, 6, 7};
/* Bit map for XS1_PORT_8C
 * 0 DSD_MODE
 * 1 DAC_RST_N
 * 2 USB_SEL0
 * 3 USB_SEL1
 * 4 VBUS_OUT_EN
 * 5 PLL_SELECT
 * 6 ADC_RST_N
 * 7 MCLK_FSEL
 */

port port_buttons                        = on tile[AUDIO_TILE]: XS1_PORT_4D;     //Buttons and switch
char pin_map_buttons[1]                  = {0};                                  //Port map for buttons GPIO task. We are just interested in bit 0

out port port_debug_tile_1               = on tile[SPDIF_TILE]: XS1_PORT_1N;     //MIDI OUT. A good test point to probe..
out port port_debug_tile_0               = on tile[AUDIO_TILE]: XS1_PORT_1D;     //SPDIF COAX TX. A good test point to probe..

//zhengyang
typedef interface get_sample {
    unsigned get_counts(void);
}get_sample;

#define SR_CALC_PERIOD  2000000     //20ms The period over which we count samples to find the rate
                                    //Because we timestamp at 10ns resolution, we get 20000000/10 = 21bits of precision

//Application task prototypes. For functionality of these tasks, see comments in implementations below
void spdif_handler(streaming chanend c_spdif_rx, streaming chanend c_temp);
//void i2s_handler(server i2s_callback_if i2s, client serial_transfer_pull_if i_serial_out, client audio_codec_config_if i_codec, server buttons_if i_buttons, client get_sample i_sample);
void i2s_handler(server i2s_callback_if i2s, client audio_codec_config_if i_codec, streaming chanend c_temp);

int main(void){
    streaming chan c_aud_dsp;

    interface audio_codec_config_if i_codec;
    interface i2c_master_if i_i2c[1];
    interface output_gpio_if i_gpio[5];    //See mapping of bits 0..7 above in port_audio_config
    interface i2s_callback_if i_i2s;

    input_gpio_if i_button_gpio[1];
    streaming chan c_spdif_rx;

    //zhengyang
    get_sample i_sample;


    par{
        on tile[SPDIF_TILE]: spdif_rx(c_spdif_rx, port_spdif_rx, clk_spdif_rx, DEFAULT_FREQ_HZ_SPDIF);
        on tile[AUDIO_TILE]: {
            spdif_handler(c_spdif_rx, c_aud_dsp);
        }


        on tile[AUDIO_TILE]: audio_codec_cs4384_cs5368(i_codec, i_i2c[0], CODEC_IS_I2S_SLAVE, i_gpio[0], i_gpio[1], i_gpio[3], i_gpio[4]);
        on tile[AUDIO_TILE]: i2c_master_single_port(i_i2c, 1, port_i2c, 10, 0 /*SCL*/, 1 /*SDA*/, 0);
        on tile[AUDIO_TILE]: output_gpio(i_gpio, sizeof(pin_map_audio_cfg), port_audio_config, pin_map_audio_cfg);
        on tile[AUDIO_TILE]: {
            configure_clock_src(clk_mclk, port_i2s_mclk); //Connect MCLK clock block to input pin
            start_clock(clk_mclk);
            debug_printf("Starting I2S\n");
            i2s_master(i_i2s, ports_i2s_dac, 1, ports_i2s_adc, 1, port_i2s_bclk, port_i2s_wclk, clk_i2s, clk_mclk);
        }
        on tile[AUDIO_TILE]: i2s_handler(i_i2s, i_codec, c_aud_dsp);

    }
    return 0;
}


//Shim task to handle setup and streaming of SPDIF samples from the streaming channel to the interface of serial2block
void spdif_handler(streaming chanend c_spdif_rx, streaming chanend c_temp)
{
    unsigned index;                             //Channel index
    signed long sample;
    signed long sample_temp[2]={0};                         //Sample received from SPDIF

    delay_microseconds(10000);                   //Bug 17263 workaround (race condition in distributable task init)
    while (1) {
        select {
            case spdif_receive_sample(c_spdif_rx, sample, index):
                if(index == 0){
                    for(int i=0; i<2; i++){
                        c_temp <: sample_temp[i];
                    }
                }
                xscope_int(CH_0, sample);
                sample_temp[index] = sample;
            break;
        }

    }
}

#define MUTE_MS_AFTER_SR_CHANGE   350    //350ms. Avoids incorrect rate playing momentarily while new rate is detected

//Shim task to handle setup and streaming of I2S samples from block2serial to the I2S module
//[[distributable]]
#pragma unsafe arrays   //Performance optimisation of i2s_handler task
void i2s_handler(server i2s_callback_if i2s, client audio_codec_config_if i_codec, streaming chanend c_temp)
    {
    unsigned sample_rate = DEFAULT_FREQ_HZ_I2S;
    unsigned mclk_rate;
    unsigned restart_status = I2S_NO_RESTART;
    unsigned mute_counter; //Non zero indicates mute. Initialised on I2S init SR change

//    timer t_case;
//    unsigned t_temp;
//    t_case :> t_temp;
//    t_temp += SR_CALC_PERIOD;

    unsigned long sample_temp[2] = {0};

    while (1) {
        select {
            case i2s.init(i2s_config_t &?i2s_config, tdm_config_t &?tdm_config):
                if (!(sample_rate % 48000)) mclk_rate = MCLK_FREQUENCY_48;  //Initialise MCLK to appropriate multiple of sample_rate
                else mclk_rate  = MCLK_FREQUENCY_44;
                i2s_config.mclk_bclk_ratio = mclk_rate / (sample_rate << 6);
                i2s_config.mode = I2S_MODE_I2S;
                i_codec.reset(sample_rate, mclk_rate);
                debug_printf("Initializing I2S to %dHz and MCLK to %dHz\n", sample_rate, mclk_rate);
                restart_status = I2S_NO_RESTART;
                mute_counter = (sample_rate * MUTE_MS_AFTER_SR_CHANGE) / 1000; //Initialise to a number of milliseconds
            break;

            //Start of I2S frame
            case i2s.restart_check() -> i2s_restart_t ret:
                ret = restart_status;
            break;

            //Get samples from ADC
            case i2s.receive(size_t index, int32_t sample):
            break;

            //Send samples to DAC
            case i2s.send(size_t index) -> int32_t sample:
                if(index == 0){
                    for(int i=0; i< 2; i++){
                        c_temp :> sample_temp[i];
                    }
                }
                sample = sample_temp[index];
            break;
        }
    }
}
It works well with sample rate between 44.1kHz and 192kHz without using FIFO or buffer . But the sound from the Line Out has some slight noise, I guess may be the reason is that the clock is not syschronous. How do you think about this ?
User avatar
zhengyang0512
Member++
Posts: 27
Joined: Tue Aug 23, 2016 6:14 am

Post by zhengyang0512 »

infiniteimprobability wrote: The code in ASRC app note AN00231 is optimised for size rather than performance, but it may work at 192kHz/176kHz in AND out. You could try:
increasing this to 64:

Code: Select all

#define ASRC_N_IN_SAMPLES 
You could temporarily try this (overclock by 20%) to see how far off the MIPs are (please note this is not a guaranteed operation mode):

Code: Select all

        <Node Id="0" InPackageId="0" Type="XS2-L16A-512" Oscillator="24MHz" SystemFrequency="600MHz" referencefrequency="100MHz">
Hi, infiniteimprobability, I follow your instruction and make following changes:
1. extract the spdif_handler and i2s_handler thread from the original code and make them run on the seperate core

Code: Select all

on tile[SPDIF_TILE]: spdif_handler(c_spdif_rx, i_serial_in);
on tile[AUDIO_TILE]: rate_server(i_sr_input, i_sr_output, i_fs_ratio, i_leds, i_sample);
I guess may be the above two threads need high operating frequency.

2. change the SystemFrequency from 500MHz to 600 MHz

Code: Select all

<Node Id="0" InPackageId="0" Type="XS2-L16A-512" Oscillator="24MHz" SystemFrequency="500MHz" referencefrequency="100MHz">
It's amazing, the asrc function could receive music with 196kHz and transfer it without changing the sample rate. I make some experiment with a series of sample rates, the result is below this
input 176.4kHz/196kHz output 176.4kHz/196kHz
input 44.1kHz/48kHz/88.2kHz/96kHz output 44.1kHz/48kHz/88.2kHz/96kHz
As you can see, there are two kinds of sample rates. And they can't make a sample conversion to each other .Howeve, my ultimate purpose is to make the sample rate change freely without any B.stop. I think the xmos system frequency is too small, so I change the frequency to 800MHz. But the music playing is not stable and the program would get into crashed just for a moment.

You have given me these suggestions
Otherwise, work to natively support 176.4/192 in < 100MHz, the user guide explains what is needed:
A number of potential optimizations have been identified to permit these rates:
• Further inner loop optimization using assembler
• Increase in scope of assembler sections removing additional function calls • Pipelining of the FIR filter stages into separate tasks
• Calculation of adaptive filter coefficients in a separate task
I think it's really difficult for me. Do you have some ideas about this?
User avatar
zhengyang0512
Member++
Posts: 27
Joined: Tue Aug 23, 2016 6:14 am

Post by zhengyang0512 »

infiniteimprobability wrote:Hi, AN00230 has not been written yet. But actually I don't think this is what you need.
I don't need to make a sample rate conversion, instead, I just want to transfer the music without change the sample rate.
Do you need to cross asynchronous clock domains? If so, ASRC is what you need even if the fIN and fOUT are nominally the same. However ASRC cannot currently work with above 176.4KHz on both sides without further optimisation, although it might be possible with a large block size

https://www.xmos.com/support/libraries/lib_src

The asrc function don't work well.
Can you expand on this? It should give 130db+ SNR which is way into audiophile quality..
I also have some confusion about the synchronous sample rate conversion and the asynchronous sample rate conversion. So, what's the difference between this?
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

A number of potential optimizations have been identified to permit these rates:
• Further inner loop optimization using assembler
• Increase in scope of assembler sections removing additional function calls • Pipelining of the FIR filter stages into separate tasks
• Calculation of adaptive filter coefficients in a separate task

I think it's really difficult for me. Do you have some ideas about this?
I have to be honest and say that these items do represent a significant chunk of work and require in depth ISA understanding and a robust test bench (which is actually open at github). If they were easy they would have already been done during the development.

Perhaps an alternative (which is still a big piece of work but less than the above), if you have spare cores, is to break the processing from F1 + F2 (initial synchronous conversion) and F3 (spline coefficient calculation and adaptive FIR) into two separate cores. In the worst case for MIPS (192->192) about 60% of the MIPS are needed by F1/F2 and 40% by F3, so it's a reasonable load split. You would need to break the function asrc_process() in src_mrhf_asrc_wrapper.c into two (each running in it's own core) and add channel communication between them to create a pipeline. Basically you need to pass the 'piStack' contents (output from F1/F2 and input to F3) by value across the channel which would act a synch too.

The above will require getting to know the ASRC codebase quite well and getting your hands dirty with xcore features. It is definitely feasible from a technical perspective as long as you can spare 4 x 100MHz cores to do the heavy lifting.

One other idea which may be easier to implement is to never have the output higher than 96kHz. Then have a SSRC following to scale back up to 192/176.4 if needed. That would keep MIPS down but consume a lot more cores, and bandwidth limit you to 48KHz. That is less work still, but still a non-trivial exercise. But at least you wouldn't need to touch the internals of the SRC library.
Post Reply