Advanced usage

Note

Even when using a custom integration, including the library in an application follows the same procedure as described in Including in an application.

The advanced use method requires familiarity with the mic array software structure and a basic understanding of C++.

A custom integration involves creating a custom MicArray object. This allows overriding default behaviour such as decimation, PDM reception etc.

Before creating a custom object, the required hardware resources must be identified. This process is the same as described in Identify hardware resources.

Configuring the mic array using a custom MicArray object requires a C++ source file to be added to the application.

The following sections describe the general structure of this file.

Declare resources

The first step is to declare a pdm_rx_resources_t struct listing the hardware resources.

pdm_rx_resources_t pdm_res = PDM_RX_RESOURCES_DDR(
                              PORT_MCLK_IN,
                              PORT_PDM_CLK,
                              PORT_PDM_DATA,
                              MCLK_FREQ,
                              PDM_FREQ,
                              XS1_CLKBLK_1,
                              XS1_CLKBLK_2);

Construct MicArray object

Next, instantiate a MicArray object. The example below creates the object with the standard component classes that are provided by the library. One or more of these can be overridden by a custom class, provided it implements the interface required by the MicArray:

using TMicArray = mic_array::MicArray<APP_N_MICS,
                        mic_array::TwoStageDecimator<APP_N_MICS,
                                            STAGE2_DEC_FACTOR_48KHZ,
                                            MIC_ARRAY_48K_STAGE_2_TAP_COUNT>,
                        mic_array::StandardPdmRxService<APP_N_MICS_IN,
                                                        APP_N_MICS,
                                                        STAGE2_DEC_FACTOR_48KHZ>,
                        typename std::conditional<APP_USE_DC_ELIMINATION,
                                            mic_array::DcoeSampleFilter<APP_N_MICS>,
                                            mic_array::NopSampleFilter<APP_N_MICS>>::type,
                        mic_array::FrameOutputHandler<APP_N_MICS,
                                                      MIC_ARRAY_CONFIG_SAMPLES_PER_FRAME,
                                                      mic_array::ChannelFrameTransmitter>>;

TMicArray mics;
  • TwoStageDecimator, StandardPdmRxService, DcoeSampleFilter, and FrameOutputHandler can all be replaced with custom classes if needed.

  • Any custom class must implement the same interface expected by MicArray.

Note

The examples/app_par_decimator example demonstrates replacing the standard decimator class with a custom multi-threaded decimator.

Note

If the application requires custom decimation filters but they’re compatible with the TwoStageDecimator implementation, refer to Custom decimation filters to see how to do so.

Define app-callable functions

Define the functions that the application will call to initialise and run the mic array - app_mic_array_init() and app_mic_array_start():

MA_C_API
void app_mic_array_init()
{
  mics.Decimator.Init((uint32_t*) stage1_48k_coefs, stage2_48k_coefs, stage2_48k_shift); // Replace stage1_48k_coefs with custom filter coefs if needed
  mics.PdmRx.Init(pdm_res.p_pdm_mics);
  mic_array_resources_configure(&pdm_res, MCLK_DIVIDER);
  mic_array_pdm_clock_start(&pdm_res);
}

DECLARE_JOB(ma_task_start_pdm, (TMicArray&));
void ma_task_start_pdm(TMicArray& m){
  m.PdmRx.ThreadEntry();
}

DECLARE_JOB(ma_task_start_decimator, (TMicArray&, chanend_t));
void ma_task_start_decimator(TMicArray& m, chanend_t c_audio_frames){
  m.ThreadEntry();
}

MA_C_API
void app_mic_array_task(chanend_t c_frames_out)
{
#if APP_USE_PDMRX_ISR
  CLEAR_KEDI()

  mics.OutputHandler.FrameTx.SetChannel(c_frames_out);
  // Setup the ISR and enable. Then start decimator.
  mics.PdmRx.AssertOnDroppedBlock(false);
  mics.PdmRx.InstallISR();
  mics.PdmRx.UnmaskISR();
  mics.ThreadEntry();
#else
  mics.OutputHandler.FrameTx.SetChannel(c_frames_out);
  PAR_JOBS(
      PJOB(ma_task_start_pdm, (mics)),
      PJOB(ma_task_start_decimator, (mics, c_frames_out))
    );
#endif
}
  • app_mic_array_init() initialises the component classes and configures the hardware resources. Note that things like initialising the decimator with custom filters would be done here by passing said filters to the mics.Decimator.Init() call.

  • app_mic_array_task() starts the PDM and decimator threads or ISR-based processing.

Application main function

The application main function is modified to include calls to app_mic_array_init() and app_mic_array_task()

int main() {
  par {
    on tile[1]: {
      app_mic_array_init();
      par {
        app_mic_array_task((chanend_t) c_audio_frames);
        <... other application threads ...>
      }
    }
  }
  return 0;
}