Hide navigation sidebar
Hide table of contents sidebar
Skip to content
Back to top
Toggle table of contents sidebar

# C++ API Reference

## MicArray

template<unsigned MIC_COUNT, class TDecimator, class TPdmRx, class TSampleFilter, class TOutputHandler>
class MicArray

  Represents the microphone array component of an application.

  Template Parameters:

    - MIC_COUNT – Number of microphone output channels from the the mic array component

    - TDecimator – Type for the decimator. See Decimator.

    - TPdmRx – Type for the PDM rx service used. See PdmRx.

    - TSampleFilter – Type for the output filter used. See SampleFilter.

    - TOutputHandler – Type for the output handler used. See OutputHandler.

  Public Functions

  inline MicArray()

    Construct a `MicArray`.

    This constructor uses the default constructor for each of its components, PdmRx, Decimator, SampleFilter, and OutputHandler.

  void ThreadEntry()

    Entry point for the decimation thread.

    This function does not return. It loops indefinitely, collecting blocks of PDM data from PdmRx (which must have already been started), uses Decimator to filter and decimate the sample stream to the output sample rate, applies any post-processing with SampleFilter, and then delivers the stream of output samples through OutputHandler.

  Public Members

  TPdmRx PdmRx

    The PDM rx service.

    The template parameter `TPdmRx` is the concrete class implementing the microphone array’s PDM rx service, which is responsible for collecting PDM samples from a port and delivering them to the decimation thread.

    `TPdmRx` is only required to implement one function, `GetPdmBlock()`:

        uint32_t* GetPdmBlock();

    `GetPdmBlock()` returns a pointer to a block of PDM data, formatted as expected by the decimator. `GetPdmBlock()` is called from the decimator thread and is expected to block until a new full block of PDM data is available to be decimated.

    For example, StandardPdmRxService::GetPdmBlock() waits to receive a pointer to a block of PDM data from a streaming channel. The pointer is sent from the PdmRx interrupt (or thread) when the block has been completed. This is used for capturing PDM data from a port.

  TDecimator Decimator

    The Decimator.

    The template parameter `TDecimator` is the concrete class implementing the microphone array’s decimation procedure. `TDecimator` is only required to implement one function, `ProcessBlock()`:

        void ProcessBlock(
            int32_t sample_out[MIC_COUNT],
            uint32_t *pdm_block);

    `ProcessBlock()` takes a block of PDM samples via its `pdm_block` parameter, applies the appropriate decimation logic, and outputs a single (multi-channel) sample via its `sample_out` parameter. The size and formatting of the PDM block expected by the decimator depends on its particular implementation.

  TSampleFilter SampleFilter

    The output filter.

    The template parameter `TSampleFilter` is the concrete class implementing the microphone array’s sample filter component. This component can be used to apply additional non-decimating, non-interpolating filtering of samples. `TSampleFilter()` is only required to implement one function, `Filter()`:

        void Filter(int32_t sample[MIC_COUNT]);

    `Filter()` takes a single (multi-channel) sample from the decimator component’s output and may update the sample in-place.

    For example a sample filter based on the DcoeSampleFilter class template applies a simple first-order IIR filter to the output of the decimator, in order to eliminate the DC component of the audio signals.

    If no additional filtering is required, the NopSampleFilter class template can be used for `TSampleFilter`, which leaves the sample unmodified. In this case, it is expected that the call to NopSampleFilter::Filter() will ultimately get completely eliminated at build time. That way no addition run-time compute or memory costs need be introduced for the additional flexibility.

    Even though `TDecimator` and `TSampleFilter` both (possibly) apply filtering, they are separate components of the `MicArray` because they are conceptually independent.

  TOutputHandler OutputHandler

    The output handler.

    The template parameter `TOutputHandler` is the concrete class implementing the microphone array’s output handler component. After the PDM input stream has been decimated to the appropriate output sample rate, and after any post-processing of that output stream by the sample filter, the output samples must be delivered to another thread for any additional processing. It is the responsibility of this component to package and deliver audio samples to subsequent processing stages.

    `TOutputHandler` is only required to implement one function, `OutputSample()`:

        void OutputSample(int32_t sample[MIC_COUNT]);

    `OutputSample()` is called exactly once for each mic array output sample. `OutputSample()` may block if necessary until the subsequent processing stage is ready to receive new data. However, the decimator thread (in which `OutputSample()` is called) as a whole has a real-time constraint - it must be ready to pull the next block of PDM data while it is available.

## StandardPdmRxService

struct pdm_rx_isr_context_t

  PDM rx interrupt configuration and context.

  Public Members

  port_t p_pdm_mics

    Port on which PDM samples are received.

  uint32_t *pdm_buffer[2]

    Pointers to a pair of buffers used for storing captured PDM samples.

    The buffers themselves are allocated by the application and passed to mic_array::StandardPdmRxService::Init The idea is that while the PDM rx ISR is filling one buffer, the decimation thread is busy processing the contents of the other buffer. If the real-time constraint is maintained, the decimation thread will be finished with the contents of its buffer before the PDM rx ISR fills the other buffer. Once full, the PDM rx ISR does a double buffer pointer swap and hands the newly-filled buffer to the decimation thread.

  unsigned phase

    Tracks the completeness of the buffer currently being filled.

    Each read of samples from `p_pdm_mics` gives one word of data. This variable tracks how many more port reads are required before the current buffer has been filled.

  unsigned phase_reset

    The number of words to read from `p_pdn_mics` to fill a buffer.

  chanend_t c_pdm_data

    Streaming chanend the PDM rx ISR uses to signal the decimation thread that another buffer is full and ready to be processed.

    The streaming channel itself is allocated by mic_array::StandardPdmRxService, which owns the other end of the channel.

  unsigned credit

    Used for detecting when the real-time constraint is violated by the decimation thread.

    Each time the decimation thread is given a block of PDM data to process, `credit` is reset to `2`. Each time the PDM rx ISR hands a block of PDM data to the decimation thread, this is decremented.

    Deadlock Condition

      mic_array::StandardPdmRxService uses a streaming channel to facilitate communication between the two execution contexts used by the mic array, the decimation thread and the PDM rx ISR. A streaming channel is used because it allows the contexts to operate asynchronously.

      A channel has a 2 word buffer, and as long as there is room in the buffer, an `OUT` instruction putting a word (in this case, a pointer) into the channel is guaranteed not to block. This is important because the PDM rx ISR is typically configured on the same hardware thread as the decimation thread.

      If a thread is blocked on an `OUT` instruction to a channel, in order to unblock the thread, an `IN` must be issued on the other end of that channel. But because the PDM rx ISR is blocked, it cannot hand control back to the decimation thread, which means the decimation thread can never issue an `IN` instruction to unblock the ISR. The result is a deadlock.

      Unfortunately, there is no way for a thread to query a chanend to determine whether it will block if an `OUT` instruction is issued. That is why `credit` is used. Before issuing an `OUT` to `c_pdm_data`, the PDM rx ISR checks whether `credit` is non-zero. If so, the ISR issues the `OUT` instruction as normal and decrements `credit`.

      If `credit` is zero, the default behavior of PDM rx ISR is to raise an exception (`ET_ECALL`). This reflects the idea that it is generally better if system-breaking errors loudly announce themselves (at least by default). If using mic_array::StandardPdmRxService, this behavior can be changed by passing `false` in a call to mic_array::StandardPdmRxService::AssertOnDroppedBlock(), which will allow blocks of PDM data to be silently dropped (while still avoiding a permanent deadlock).

  unsigned missed_blocks

    Controls and records anti-deadlock behavior.

    If the PDM rx ISR finds that `credit` is `0` when it’s time to send a filled buffer to the decimation thread, it uses `missed_blocks` to control whether the PDM rx ISR should raise an exception or silently drop the block of PDM data.

    If `missed_blocks` is `-1` (its default value) an exception is raised. Otherwise `missed_blocks` is used to record the number of blocks that have been quietly dropped.

pdm_rx_isr_context_t pdm_rx_isr_context

  Configuration and context of the PDM rx ISR when mic_array::StandardPdmRxService is used in interrupt mode.

  `pdm_rx_isr` (`pdm_rx_isr.S`) directly allocates this object as configuration and state parameters required by that interrupt routine.

static inline void enable_pdm_rx_isr(const port_t p_pdm_mics)

  Configure port to use `pdm_rx_isr` as an interrupt routine.

  This function configures `p_pdm_mics` to use `pdm_rx_isr` as its interrupt vector and enables the interrupt on the current hardware thread.

  This function does NOT unmask interrupts.

  Parameters:

    p_pdm_mics – Port resource to enable ISR on.

template<unsigned CHANNELS_IN, unsigned CHANNELS_OUT>
class StandardPdmRxService

  PDM rx service which collects PDM sample data from a port and uses a streaming channel to send a block of data by pointer further down the mic array pipeline.

  This class template is intended to be used for the `TPdmRx` template parameter of MicArray, where it represents the MicArray::PdmRx component of the mic array.

  This class can run the PDM rx service either as a stand-alone thread or through an interrupt.

  Inter-context Transfer

    A streaming channel is used to transfer control of the PDM data block between execution contexts (i.e. thread->thread or ISR->thread).

    The mic array unit receives blocks of PDM data from an instance of this class by calling GetPdmBlock(), which blocks until a new PDM block is available.

  `StandardPdmRxService` collects blocks of PDM samples from a port and makes them available to the decimation thread as the blocks are completed.

  This class provides the logic for aggregating PDM data taken from a port into blocks, and provides methods `ReadPort()`, `SendBlock()` and `GetPdmBlock()`.

  `ReadPort()` is responsible for reading 1 word of data from `p_pdm_mics`.

  `SendBlock()` is provided a block of PDM data as a pointer and is responsible for signaling that to the subsequent processing stage.

  `ReadPort()` and `SendBlock()` are used by `StandardPdmRxService` itself (when running as a thread, rather than ISR).

  `GetPdmBlock()` is responsible for receiving a block of PDM data from `SendBlock()` as a pointer, deinterleaving the buffer contents, and returning a pointer to the PDM data in the format expected by the mic array unit’s decimator component.

  `GetPdmBlock()` is called by the decimation thread. The pair of functions, `SendBlock()` and `GetPdmBlock()` facilitate inter-thread communication, `SendBlock()` being called by the transmitting end of the communication channel, and `GetPdmBlock()` being called by the receiving end.

  Layouts

    The buffer transferred by `SendBlock()` contains `CHANNELS_IN * this->pdm_out_words_per_channel` words of PDM data for `CHANNELS_IN` microphone channels. The words are stored in reverse order of arrival.

    See `mic_array::deinterleave_pdm_samples()` for additional details on this format.

    Within `GetPdmBlock()` (i.e. mic array thread) the PDM data block is deinterleaved and copied to another buffer in the format required by the decimator component, which is returned by `GetPdmBlock()`. This buffer contains `CHANNELS_OUT * this->pdm_out_words_per_channel` words for `CHANNELS_OUT` microphone channels.

  - Channel Filtering

      In some cases an application may be required to capture more microphone channels than should actually be processed by subsequent processing stages (including the decimator component). For example, this may be the case if 4 microphone channels are desired but only an 8 bit wide port is physically available to capture the samples.

      This class template has a parameter both for the number of channels to be captured by the port (`CHANNELS_IN`), as well as for the number of channels that are to be output for consumption by the `MicArray`’s decimator component (`CHANNELS_OUT`).

      When the PDM microphones are in an SDR configuration, `CHANNELS_IN` must be the width (in bits) of the XCore port to which the microphones are physically connected. When in a DDR configuration, `CHANNELS_IN` must be twice the width (in bits) of the XCore port to which the microphones are physically connected.

      `CHANNELS_OUT` is the number of microphone channels to be consumed by the mic array’s decimator component (i.e. must be the same as the `MIC_COUNT` template parameter of the decimator component). If all port pins are connected to microphones, this parameter will generally be the same as `CHANNELS_IN`.

  Channel Index (Re-)Mapping

    The input channel index of a microphone depends on the pin to which it is connected. Each pin connected to a port has a bit index for that port, given in the ‘Signal Description and GPIO’ section of your package’s datasheet.

    Suppose an `N`-bit port is used to capture microphone data, and a microphone is connected to bit `B` of that port. In an SDR microphone configuration, the input channel index of that microphone is `B`, the same as the port bit index.

    In a DDR configuration, that microphone will be on either input channel index `B` or `B+N`, depending on whether that microphone is configured for in-phase capture or out-of-phase capture.

    Sometimes it may be desirable to re-order the microphone channel indices. This is likely the case, for example, when `CHANNELS_IN > CHANNELS_OUT`.

    By default output channels are mapped from the input channels with the same index. If `CHANNELS_IN > CHANNELS_OUT`, this means that the input channels with the highest `CHANNELS_IN-CHANNELS_OUT` indices are dropped by default.

    The `MapChannel()` and `MapChannels()` methods can be used to specify a non-default mapping from input channel indices to output channel indices. It takes a pointer to a `CHANNELS_OUT`-element array specifying the input channel index for each output channel.

  Template Parameters:

    - CHANNELS_IN – The number of microphone channels to be captured by the port. For example, if using a 4-bit port to capture 6 microphone channels in a DDR configuration (because there are no 3 or 6 pin ports) `CHANNELS_IN` should be `8`, because that’s how many must be captured, even if two of them are stripped out before passing audio frames to subsequent application stages.

    - CHANNELS_OUT – The number of output microphone channels to be delivered by this `StandardPdmRxService` instance.

  Public Functions

  uint32_t ReadPort()

    Read a word of PDM data from the port.

    Returns:

      A `uint32_t` containing 32 PDM samples. If `MIC_COUNT >= 2` the samples from each port will be interleaved together.

  void SendBlock(uint32_t *block)

    Send a block of PDM data to a listener.

    Parameters:

      block – PDM data to send.

  void Init(port_t p_pdm_mics, pdm_rx_conf_t &pdm_rx_config)

    Initialize the PDM RX service.

    Sets the input port and binds application-provided buffers from pdm_rx_conf_t `pdm_rx_config`.

    Requirements:

    - `pdm_rx_config.pdm_in_double_buf` must be sized 2 * CHANNELS_IN * `pdm_rx_config.pdm_out_words_per_channel` words and remain valid for the lifetime of the service.

    - `pdm_rx_config.pdm_out_block` must be sized CHANNELS_OUT * `pdm_rx_config.pdm_out_words_per_channel` words and remain valid for the lifetime of the service.

    Parameters:

      - p_pdm_mics – Port from which PDM samples are captured.

      - pdm_rx_config – PDM RX configuration

  void MapChannels(const unsigned map[CHANNELS_OUT])

    Set the input-output mapping for all output channels.

    By default, input channel index `k` maps to output channel index `k`.

    This method overrides that behavior for all channels, re-mapping each output channel such that output channel `k` is derived from input channel `map[k]`.

    Note

    Changing the channel mapping while the mic array unit is running is not recommended.

    Parameters:

      map – Array containing new channel map.

  void MapChannel(unsigned out_channel, unsigned in_channel)

    Set the input-output mapping for a single output channel.

    By default, input channel index `k` maps to output channel index `k`.

    This method overrides that behavior for a single output channel, configuring output channel `out_channel` to be derived from input channel `in_channel`.

    Note

    Changing the channel mapping while the mic array unit is running is not recommended.

    Parameters:

      - out_channel – Output channel index to be re-mapped.

      - in_channel – New source channel index for `out_channel`.

  void InstallISR()

    Install ISR for PDM reception on the current core.

    Note

    This does not unmask interrupts.

  void UnmaskISR()

    Unmask interrupts on the current core.

  uint32_t *GetPdmBlock()

    Get a block of PDM data.

    Because blocks of PDM samples are delivered by pointer, the caller must either copy the samples or finish processing them before the next block of samples is ready, or the data will be clobbered.

    Note

    This is a blocking call.

    Returns:

      Pointer to block of PDM data.

  void AssertOnDroppedBlock(bool doAssert)

    Set whether dropped PDM samples should cause an assertion.

    If `doAssert` is set to `true` (default), the PDM rx ISR will raise an exception (`ET_CALL`) if it is ready to deliver a PDM block to the mic array thread when the mic array thread is not ready to receive it. If `false`, dropped blocks can be tracked through `pdm_rx_isr_context.missed_blocks`.

  void SetPort(port_t p_pdm_mics)

    Set the port from which to collect PDM samples.

  void ThreadEntry()

    Entry point for PDM processing thread.

    This function loops forever, performing a port read and if a new block has completed, signal a block send, every iteration.

## TwoStageDecimator

template<unsigned MIC_COUNT>
class TwoStageDecimator

  First and Second Stage Decimator.

  This class template represents a two stage decimator which converts a stream of PDM samples to a lower sample rate stream of PCM samples.

  Concrete implementations of this class template are meant to be used as the `TDecimator` template parameter in the MicArray class template.

  Template Parameters:

    MIC_COUNT – Number of microphone channels.

  Public Functions

  void Init(mic_array_decimator_conf_t &decimator_conf)

    Initialize the two-stage decimator from a configuration struct mic_array_decimator_conf_t `decimator_conf`.

    Reads stage-1 and stage-2 filter parameters from `decimator_conf` and prepares internal state: The caller must ensure all pointers inside `decimator_conf.filter_conf`[0] and `decimator_conf.filter_conf`[1] are valid and persist for the lifetime of the decimator.

    Parameters:

      decimator_conf – Decimator pipeline configuration.

  void ProcessBlock(int32_t sample_out[MIC_COUNT], uint32_t *pdm_block)

    Process one block of PDM data.

    Processes a block of PDM data to produce an output sample from the second stage decimator.

    `pdm_block` contains exactly enough PDM samples to produce a single output sample from the second stage decimator. The layout of `pdm_block` should (effectively) be:

        struct {
          struct {
            // lower word indices are older samples.
            // less significant bits in a word are older samples.
            uint32_t samples[S2_DEC_FACTOR];
          } microphone[MIC_COUNT]; // mic channels are in ascending order
        } pdm_block;

    A single output sample from the second stage decimator is computed and written to `sample_out[]`.

    Parameters:

      - sample_out – Output sample vector.

      - pdm_block – PDM data to be processed.

  Public Members

  const uint32_t *filter_coef

    Pointer to filter coefficients for Stage 1

  uint32_t *pdm_history_ptr

    Pointer to filter state (PDM history) for stage-1 filter.

  unsigned pdm_history_sz

    Per-mic channel filter state (PDM history) size in 32-bit words for stage-1 filter.

  filter_fir_s32_t filters[MIC_COUNT]

    Stage 2 FIR filters

  unsigned decimation_factor

    Stage 2 filter decimation factor.

## SampleFilter

### NopSampleFilter

template<unsigned MIC_COUNT>
class NopSampleFilter

  SampleFilter which does nothing.

  To be used as the `TSampleFilter` template parameter of MicArray when no post-decimation filtering is desired.

  Calls to `NopSampleFilter::Filter()` are intended to be optimized out at compile time.

  Template Parameters:

    MIC_COUNT – Number of microphone channels.

  Public Functions

  inline void Filter(int32_t sample[MIC_COUNT])

    Do nothing.

### DcoeSampleFilter

template<unsigned MIC_COUNT>
class DcoeSampleFilter

  Filter which applies DC Offset Elimination (DCOE).

  To be used as the `TSampleFilter` template parameter of MicArray when DCOE is desired as post-processing after the decimation filter.

  The filter is a simple first-order IIR filter which applies the following filter equation:

      R = 252.0 / 256.0
      y[t] = R * y[t-1] + x[t] - x[t-1]

  Template Parameters:

    MIC_COUNT – Number of microphone channels.

  Public Functions

  void Init()

    Initialize the filter states.

    The filter states must be initialized prior to calls to `Filter()`.

  void Filter(int32_t sample[MIC_COUNT])

    Apply DCOE filter on samples.

    `sample` is an array of samples to be filtered, and is updated in-place.

    The filter states must have been initialized with a call to `Init()` prior to calling this function.

    Parameters:

      sample – Samples to be filtered. Updated in-place.

## OutputHandler

An OutputHandler is a class which meets the requirements to be used as the `TOutputHandler` template parameter of the `MicArray` class template. The basic requirement is that it have a method:

This method is how the mic array communicates its output with the rest of the application’s audio processing pipeline. MicArray calls this method once for each mic array output sample.

See `MicArray::OutputHandler` for more details.

### FrameOutputHandler

template<unsigned MIC_COUNT, unsigned SAMPLE_COUNT, template<unsigned, unsigned> class FrameTransmitter, unsigned FRAME_COUNT = 1>
class FrameOutputHandler

  OutputHandler implementation which groups samples into non-overlapping multi-sample audio frames and sends entire frames to subsequent processing stages.

  This class template can be used as an OutputHandler with the MicArray class template. See MicArray::OutputHandler.

  Classes derived from this template collect samples into frames. A frame is a 2 dimensional array with one index corresponding to the audio channel and the other index corresponding to time step, e.g.:

      int32_t frame[MIC_COUNT][SAMPLE_COUNT];

  Each call to OutputSample() adds the sample to the current frame, and then iff the frame is full, uses its FrameTx component to transfer the frame of audio to subsequent processing stages. Only one of every `SAMPLE_COUNT` calls to OutputSample() results in an actual transmission to subsequent stages.

  With `FrameOutputHandler`, the thread receiving the audio will generally need to know how many microphone channels and how many samples to expect per frame (although, strictly speaking, that depends upon the chosen `FrameTransmitter` implementation).

  Template Parameters:

    - MIC_COUNT –

      The number of audio channels in each sample and each frame.

    - SAMPLE_COUNT – Number of samples per frame.

      The `SAMPLE_COUNT` template parameter is the number of samples assembled into each audio frame. Only completed frames are transmitted to subsequent processing stages. A `SAMPLE_COUNT` value of `1` effectively disables framing, transmitting one sample for each call made to OutputSample.

    - FrameTransmitter –

      The concrete type of the FrameTx component of this class.

    - FRAME_COUNT –

      The number of frame buffers an instance of `FrameOutputHandler` should cycle through. Unless audio frames are communicated with subsequent processing stages through shared memory, the default value of `1` is usualy ideal.

  Public Functions

  inline FrameOutputHandler()

    Construct new `FrameOutputHandler`.

    The default no-argument constructor for `FrameTransmitter` is used to create `FrameTx`.

  inline FrameOutputHandler(
    FrameTransmitter<MIC_COUNT, SAMPLE_COUNT> frame_tx,
  )

    Construct new `FrameOutputHandler`.

    Uses the provided FrameTransmitter to send frames.

    Parameters:

      frame_tx – Frame transmitter for sending frames.

  bool OutputSample(int32_t sample[MIC_COUNT])

    Add new sample to current frame and output frame if filled.

    Parameters:

      sample – Sample to be added to current frame.

  void CompleteShutdown()

    Complete mic array shutdown process.

  Public Members

  FrameTransmitter<MIC_COUNT, SAMPLE_COUNT> FrameTx

    `FrameTransmitter` used to transmit frames to the next stage for processing.

    `FrameTransmitter` is the template, template parameter used in this class to control how frames of audio data are communicated with subsequent pipeline stages.

    The type supplied for `FrameTransmitter` must be a class template with two integer template parameters, corresponding to this class’s `MIC_COUNT` and `SAMPLE_COUNT` template parameters respectively, indicating the shape of the frame object to be transmitted.

    The `FrameTransmitter` type is required to implement a single method:

        void OutputFrame(int32_t frame[MIC_COUNT][SAMPLE_COUNT]);

    `OutputFrame()` is called once for each completed audio frame and is responsible for the details of how the frame’s data gets communicated to subsequent stages. For example, the ChannelFrameTransmitter class template uses an XCore channel to send samples to another thread (by value).

    Alternative implementations might use shared memory or an RTOS queue to transmit the frame data, or might even use a port to signal the samples directly to an external DAC.

#### ChannelFrameTransmitter

template<unsigned MIC_COUNT, unsigned SAMPLE_COUNT>
class ChannelFrameTransmitter

  Frame transmitter which transmits frame over a channel.

  This class template is meant for use as the `FrameTransmitter` template parameter of FrameOutputHandler.

  When using this frame transmitter, frames are transmitted over a channel using the frame transfer API in `mic_array/frame_transfer.h`.

  Usually, a call to `ma_frame_rx()` (with the other end of c_frame_out as argument) should be used to receive the frame on another thread.

  If the receiving thread is not waiting to receive the frame when OutputFrame() is called, that method will block until the frame has been transmitted. In order to ensure there are no violations of the mic array’s real-time constraints, the receiver should be ready to receive a frame as soon as it becomes available.

  Frames can be transmitted between tiles using this class.

  Note

  While OutputFrame() is blocking, it will not prevent the PDM rx interrupt from firing.

  Template Parameters:

    - MIC_COUNT – Number of audio channels in each frame.

    - SAMPLE_COUNT – Number of samples per frame.

  Public Functions

  inline ChannelFrameTransmitter()

    Construct a `ChannelFrameTransmitter`.

    If this constructor is used, SetChannel() must be called to configure the channel over which frames are transmitted prior to any calls to OutputFrame().

  inline ChannelFrameTransmitter(chanend_t c_frame_out)

    Construct a `ChannelFrameTransmitter`.

    The supplied value of `c_frame_out` must be a valid chanend.

    Parameters:

      c_frame_out – Chanend over which frames will be transmitted.

  void SetChannel(chanend_t c_frame_out)

    Set channel used for frame transfers.

    The supplied value of `c_frame_out` must be a valid chanend.

    Parameters:

      c_frame_out – Chanend over which frames will be transmitted.

  chanend_t GetChannel()

    Get the chanend used for frame transfers.

    Returns:

      Channel to be used for frame transfers.

  bool OutputFrame(int32_t frame[MIC_COUNT][SAMPLE_COUNT])

    Transmit the specified frame.

    See ChannelFrameTransmitter for additional details.

    Parameters:

      frame – Frame to be transmitted.

  void CompleteShutdown()

    Complete mic array shutdown process by exchanging end tokens with the app. This causes ma_shutdown() to return indicating mic array shutdown completion.

## Misc

template<unsigned MIC_COUNT>
void mic_array::deinterleave_pdm_samples(

  uint32_t *samples,
  unsigned s2_dec_factor,

)

  Deinterleave the channels of a block of PDM data.

  PDM samples received on a port are shifted into a 32-bit buffer in such a way that the samples for each microphone channel are all interleaved with one another. The first stage decimator, however, requires these to be separated.

  `samples` must point to a buffer containing `(MIC_COUNT*s2_dec_factor)` words of PDM data. Because the decimation factor for the first stage decimator is a fixed value of `32`, `32` PDM samples from each microphone is enough to produce one output sample (a `MIC_COUNT`-element vector) from the first stage decimator. `32*s2_dec_factor` PDM samples for each of the `MIC_COUNT` microphone channels is then exactly what is required to produce a single output sample from the second stage decimator.

  The PDM data will be deinterleaved in-place.

  On input, the format of the buffer to which `samples` points is assumed to be such that the following function will extract (only) the `k`th sample for microphone channel `n` (where `k` is a time index, not a memory index):

  Input Format

      unsigned get_sample(uint32_t* samples,
                          unsigned MIC_COUNT, unsigned s2_dec_factor,
                          unsigned n, unsigned k)
      {
        const end_word = MIC_COUNT * s2_dec_factor - 1; // chronologically first
        const unsigned samp_per_word = 32 / MIC_COUNT;
        const words_from_end = k / samp_per_word;
        const uint32_t word_val = samples[end_word-words_from_end];
        const unsigned bit_offset = (k % end_word) + n;
        return (word_val >> bit_offset) & 1;
      }

  Here, the words of `samples` are stored in reverse order (older samples are at higher word indices), and within a word the oldest samples are the least significant bits. The LSb of a word is always microphone channel `0`, and the MSb of a word is always microphone channel `MIC_COUNT-1`.

  Upon return, the format of the buffer to which samples points will be such that the following function will extract (only) the `k`th sample for microphone channel `n`:

  Output Format

      unsigned get_sample(uint32_t* samples,
                          unsigned MIC_COUNT, unsigned s2_dec_factor,
                          unsigned n, unsigned k)
      {
        const unsigned subblock = (s2_dec_factor-1)-(k/32);
        const unsigned word_val = samples[subblock * MIC_COUNT + n];
        return (word_val >> (k%32)) & 1;
      }

  Here, each word contains samples from only a single channel, with words at higher addresses containing older samples. `samples[0]` contains the newest samples for microphone channel `0`, and `samples[MIC_COUNT-1]` contains the newest samples for microphone channel `MIC_COUNT-1`. `samples[MIC_COUNT]` contains the next-oldest set of samples for channel `0`, and so on.

  Template Parameters:

    MIC_COUNT – Number of channels represented in PDM data. One of `{1,2,4,8}`

  Parameters:

    - samples – Pointer to block of PDM samples.

    - s2_dec_factor – Stage2 decimator decimation factor.