mic array audio sampling rate change, AN00221 Topic is solved

Technical discussions related to any XMOS development kit or reference design. Eg XK-1A, sliceKIT, etc.
woodsb
Experienced Member
Posts: 79
Joined: Thu Nov 17, 2016 11:24 pm

mic array audio sampling rate change, AN00221

Post by woodsb »

Hello!

I am trying to modify AN00221 to work at an audio sampling rate of 16 kHz on an XMOS mic array dev kit. I believe I have done everything correct to get the decimators to make the switch from default of 48 kHz to my desired 16 kHz, but the output I get is distorted and wrong (not sure how best to describe it). Output sounds fine with original 48 kHz configuration.

I made 3 changes to the original code: the DECIMATION_FACTOR from 2 to 6, the decimator FIR coefficient specification from "g_third_stage_div_2_fir" to "g_third_stage_div_6_fir", and the FIR compensation from FIR_COMPENSATOR_DIV_2 to FIR_COMPENSATOR_DIV_6.

From the debugging I've done so far I believe the decimation is actually working correctly but the I2S handler is not: the I2S handler seems to still be operating at 48 kHz. Unfortunately I am still too much of a newbie to figure out why exactly that is. I've spent a goodly amount of time going over array and I2S related XMOS docs and code, but my progress is quite slow so I thought I would see if anyone's got some quick advice.

Thanks in advance.

Sincerely,
Bill
View Solution
User avatar
infiniteimprobability
Verified
XCore Legend
Posts: 1143
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

Hi,
I think the issue will be that the I2S module used can only handle MCLK/ 2*n. So you have two choices to get I2S running at 16KHz:

1) Modify the PLL settings so that the MCLK is a power if 2. You will also need to make sure the MIC clock is modified accordingly so that you can divide down to 3.072MHz

2) Use i2s_frame instead (which is prototyped in the i2s.h) , which can support any MCLK to BCLK ration that is 2n, so 2457600/ (16000*64) = 24, which is OK.

I'd probably go route 2) as it's marginally less work.


Here's an example 8 channel loopback i2s_frame handler. The main difference is that there is a callback for each frame (every channel in sample period) rather than one callback per channel per sample period.

Code: Select all

  int32_t samples[8] = {0, 0, 0, 0, 0, 0, 0, 0};

  while (1) {
    select {
    case i2s.init(i2s_config_t &?i2s_config, tdm_config_t &?tdm_config):
      i2s_config.mode = I2S_MODE_I2S;
      i2s_config.mclk_bclk_ratio = (MASTER_CLOCK_FREQUENCY/SAMPLE_FREQUENCY)/64;

      // Set CODECs in reset
      dac_reset.output(0);
      adc_reset.output(0);

      // Select 48Khz family clock (24.576Mhz)
      mclk_select.output(1);
      pll_select.output(0);

      // Allow the clock to settle
      delay_milliseconds(2);

      // Take CODECs out of reset
      dac_reset.output(1);
      adc_reset.output(1);

      reset_codecs(i2c);
      break;

    case i2s.receive(size_t num_chan_in, int32_t sample[num_chan_in]):
      for (size_t i=0; i<num_chan_in; i++) {
          samples[i] = sample[i];
      }
      break;

    case i2s.send(size_t num_chan_out, int32_t sample[num_chan_out]):
      for (size_t i=0; i<num_chan_out; i++){
        sample[i] = samples[i];
      }
      break;


    case i2s.restart_check() -> i2s_restart_t restart:
      restart = I2S_NO_RESTART;
      break;
    }
  }
}
Engineer at XMOS
User avatar
andrew
Verified
Experienced Member
Posts: 117
Joined: Fri Dec 11, 2009 10:22 am

Post by andrew »

Here's an example of using the voice upsampler to run the i2s at 48k but the input at 16k:

Code: Select all

  int32_t us_data[24] = {0};
unsafe {
[[distributable]]
void i2s_handler(server i2s_callback_if i2s,
                 client i2c_master_if i2c,
                 client interface bufget_i filler
                 ) {
  multichannel_audio_block_s * unsafe buffer = 0; // invalid
  unsigned sample_idx=0;

  p_rst_shared <: 0xF;


  mabs_init_pll(i2c, ETH_MIC_ARRAY);
  i2c_regop_res_t res;
  int i = 0x4A;
  uint8_t data = i2c.read_reg(i, 1, res);

  data = i2c.read_reg(i, 0x02, res);
  data |= 1;
  res = i2c.write_reg(i, 0x02, data); // Power down

  // Setting MCLKDIV2 high if using 24.576MHz.
  data = i2c.read_reg(i, 0x03, res);
  data |= 1;
  res = i2c.write_reg(i, 0x03, data);

  data = 0b01110000;
  res = i2c.write_reg(i, 0x10, data);

  data = i2c.read_reg(i, 0x02, res);
  data &= ~1;
  res = i2c.write_reg(i, 0x02, data); // Power up
  unsigned phase_index = 0;
  while (1) {
    select {
    case i2s.init(i2s_config_t &?i2s_config, tdm_config_t &?tdm_config):
      i2s_config.mode = I2S_MODE_LEFT_JUSTIFIED;
      i2s_config.mclk_bclk_ratio = (MASTER_CLOCK_FREQUENCY/OUTPUT_SAMPLE_RATE)/64;
      break;

    case i2s.restart_check() -> i2s_restart_t restart:
      restart = I2S_NO_RESTART;
      break;

    case i2s.receive(size_t index, int32_t sample):
      break;

    case i2s.send(size_t index) -> int32_t sample:
      if(buffer) {

          if(phase_index == 0){
              int32_t s = buffer->data[index][sample_idx];
              sample = src_us3_voice_input_sample(us_data, src_ff3v_fir_coefs[0], s);
          } else {
              sample = src_us3_voice_get_next_sample(us_data, src_ff3v_fir_coefs[phase_index]);
          }

      } else { // buffer invalid
         sample = 0;
      }
      //xscope_int(index, sample);
      if((index == 1)) {
          phase_index++;
          if(phase_index == 3){
              sample_idx++;
              phase_index = 0;
          }
      }
      if(sample_idx>=FFT_N/2) {
        // end of buffer reached.
        sample_idx = 0;
        filler.get_next_buf(buffer);
        //printf("I2S got next buffer at 0x%x\n", buffer);
      }
      break;
    }
  }
}
}
It will need to import lib_src
woodsb
Experienced Member
Posts: 79
Joined: Thu Nov 17, 2016 11:24 pm

Post by woodsb »

Thanks, Andrew and InfImp, for the help on this topic.

It turns out that after staring at the I2S setup code over the weekend I had discovered the 2^n requirement on the MCLK/BCLK ratio. This requirement is not present in the documented process for changing the mic array "output sampling rate", which indicates that 48kHz, 24kHz, 16kHz, 12kHz and 8kHz rates are all options. In retrospect I believe the "output" referred to is the output of the PDM microphone decimation process, which is in actuality the input to the array processing. If the I2S is used as an output from the array processing the documented sampling-rate-change process will not work.

It would be nice if the Microphone Array Library documentation could reflect the fact that configuration of the input sampling rate (i.e., the output of the decimation process) and the sampling rate of the I2S are not currently fully compatible. Even better would be to add specific instructions on how to change the PLL and associated settings, as outlined by InfiniteImprobability in this thread, to make input and output sampling rates compatible over the typical values of audio sampling rate.

Cheers,
Bill
User avatar
andrew
Verified
Experienced Member
Posts: 117
Joined: Fri Dec 11, 2009 10:22 am

Post by andrew »

agreed, I'll add it to the known issues of the mic array for the future releases. Thanks