Synchronous Sample Rate Conversion Problem
-
- Member++
- Posts: 27
- Joined: Tue Aug 23, 2016 6:14 am
Synchronous Sample Rate Conversion Problem
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 !!!
-
- XCore Legend
- Posts: 1126
- Joined: Thu May 27, 2010 10:08 am
Hi, AN00230 has not been written yet. But actually I don't think this is what you need.
https://www.xmos.com/support/libraries/lib_src
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 sizeI don't need to make a sample rate conversion, instead, I just want to transfer the music without change the sample rate.
https://www.xmos.com/support/libraries/lib_src
Can you expand on this? It should give 130db+ SNR which is way into audiophile quality..The asrc function don't work well.
-
- Member++
- Posts: 27
- Joined: Tue Aug 23, 2016 6:14 am
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.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
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.Can you expand on this? It should give 130db+ SNR which is way into audiophile quality..The asrc function don't work well.
So, could you please give me some suggestions about how to fix the problem?
-
- XCore Legend
- Posts: 1126
- Joined: Thu May 27, 2010 10:08 am
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?
From ASRC user guide:
increasing this to 64:
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):
Otherwise, work to natively support 176.4/192 in < 100MHz, the user guide explains what is needed:
That's because it's not currently supported.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.
From ASRC user guide:
Configurations requiring more than 100MHz cannot currently be run in real time on a single core.
And from AN00231: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.
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.
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:So, could you please give me some suggestions about how to fix the problem?
increasing this to 64:
Code: Select all
#define ASRC_N_IN_SAMPLES
Code: Select all
<Node Id="0" InPackageId="0" Type="XS2-L16A-512" Oscillator="24MHz" SystemFrequency="600MHz" referencefrequency="100MHz">
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
-
- Member++
- Posts: 27
- Joined: Tue Aug 23, 2016 6:14 am
Thank you for your reply, my clock architecture is on the attachments file. I am now trying to follow your suggestion.
You do not have the required permissions to view the files attached to this post.
-
- Member++
- Posts: 27
- Joined: Tue Aug 23, 2016 6:14 am
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 ?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?
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;
}
}
}
-
- Member++
- Posts: 27
- Joined: Tue Aug 23, 2016 6:14 am
Hi, infiniteimprobability, I follow your instruction and make following changes: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: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
#define ASRC_N_IN_SAMPLES
Code: Select all
<Node Id="0" InPackageId="0" Type="XS2-L16A-512" Oscillator="24MHz" SystemFrequency="600MHz" referencefrequency="100MHz">
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);
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">
input 176.4kHz/196kHz output 176.4kHz/196kHz
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.input 44.1kHz/48kHz/88.2kHz/96kHz output 44.1kHz/48kHz/88.2kHz/96kHz
You have given me these suggestions
I think it's really difficult for me. Do you have some ideas about this?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
-
- Member++
- Posts: 27
- Joined: Tue Aug 23, 2016 6:14 am
I also have some confusion about the synchronous sample rate conversion and the asynchronous sample rate conversion. So, what's the difference between this?infiniteimprobability wrote:Hi, AN00230 has not been written yet. But actually I don't think this is what you need.
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 sizeI don't need to make a sample rate conversion, instead, I just want to transfer the music without change the sample rate.
https://www.xmos.com/support/libraries/lib_src
Can you expand on this? It should give 130db+ SNR which is way into audiophile quality..The asrc function don't work well.
-
- XCore Legend
- Posts: 1126
- Joined: Thu May 27, 2010 10:08 am
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.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?
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.