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 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 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. See

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[0] 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 kth 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 kth 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.