16 to 1 Multiplexor with Gain

Status
Not open for further replies.

FRS

Active member
I'm creating a library object that selects 1 of 16 input blocks, applies gain, and then outputs a single block of audio.
I've been testing this on the Teensy 4.0. The multiplexing/switch of inputs seems to work fine, but when the gain of the output is set to 0.0 my code seems to crash.
The gain stage is code taken from the Amplifier library.
Would appreciate some input on what I might be doing wrong in my code.


muxAmp.H
Code:
#ifndef muxamp_h_
#define muxamp_h_
#include "Arduino.h"
#include "AudioStream.h"

class AudioMuxAmp : public AudioStream
{
public:
  AudioMuxAmp(void) : AudioStream(16, inputQueueArray), multiplier(65536){}
  
  virtual void update(void);
  
  void gain(float n) 
  {
    if (n > 32767.0f) n = 32767.0f;
    else if (n < -32767.0f) n = -32767.0f;
    multiplier = n * 65536.0f;
  }

  void select(byte input) 
  {
    input_select = input;
  }  
private:
  int32_t multiplier;
  byte input_select;
  audio_block_t *inputQueueArray[16];
};

#endif

muxAmp.cpp
Code:
#include <Arduino.h>
#include "muxamp.h"
#include "utility/dspinst.h"
#if defined(__ARM_ARCH_7EM__)
#define MULTI_UNITYGAIN 65536
static void applyGain(int16_t *data, int32_t mult)
{
  uint32_t *p = (uint32_t *)data;
  const uint32_t *end = (uint32_t *)(data + AUDIO_BLOCK_SAMPLES);
  do 
  {
    uint32_t tmp32 = *p; // read 2 samples from *data
    int32_t val1 = signed_multiply_32x16b(mult, tmp32);
    int32_t val2 = signed_multiply_32x16t(mult, tmp32);
    val1 = signed_saturate_rshift(val1, 16, 0);
    val2 = signed_saturate_rshift(val2, 16, 0);
    *p++ = pack_16b_16b(val2, val1);
  } 
  while (p < end);
}

//I added this to write all "0"s to the output block
static void makeZeros(int16_t *data)
{
  uint32_t *p = (uint32_t *)data;
  const uint32_t *end = (uint32_t *)(data + AUDIO_BLOCK_SAMPLES);
  do 
  {
    *p++ = 0;
  } 
  while (p < end);
}

#elif defined(KINETISL)
#define MULTI_UNITYGAIN 256
static void applyGain(int16_t *data, int32_t mult)
{
  const int16_t *end = data + AUDIO_BLOCK_SAMPLES;
  do 
  {
    int32_t val = *data * mult;
    *data++ = signed_saturate_rshift(val, 16, 0);
  } 
  while (data < end);
}

//I added this to write all "0"s to the output block
static void makeZeros(int16_t *data)
{
  const int16_t *end = data + AUDIO_BLOCK_SAMPLES;
  do 
  {
    *data++ = 0;
  } 
  while (data < end);
}
#endif


void AudioMuxAmp::update(void)
{
  audio_block_t *block;
  int32_t mult = multiplier;
  byte inSource = input_select;
  if (mult == 0) 
  {
    // zero gain, discard any input and transmit nothing
    //block = receiveReadOnly(inSource);//original code
    //if (block) release(block);//original code

    //code I changed
    block = receiveWritable(inSource);
    if (block) 
    {
      makeZeros(block->data);
      
      transmit(block);
      release(block);
    }
  } 
  else if (mult == MULTI_UNITYGAIN) 
  {
    // unity gain, pass input to output without any change
    block = receiveReadOnly(inSource);
    if (block) 
    {
      transmit(block);
      release(block);
    }
  } 
  else 
  {
    // apply gain to signal
    block = receiveWritable(inSource);
    if (block) 
    {
      applyGain(block->data, mult);
      transmit(block);
      release(block);
    }
  }
}
 
It was not working before with that in there. I admit I don't fully understand the handling of blocks, and with the original code that says if gain is 0, release block. Seems like the block needs to be transmitted, not simply released to flow on through the objects output and on to other devices. So if I don't understand how a bit of someone else's code works, I try to write something that makes more sense to me.

I know release just removes ownership of a block so other code can use it. Transmit a block sends out a block. But I don't understand how a receiveReadOnly input block would get transmitted to the objects output to pass along to any following library object's input with a release(block), instead of a transmit(block).
 
You never release the other blocks, so that upto 15 old blocks may be stuck in the inputQueue, so that when the inSource
changes an ancient block will be picked up from one of the stuck channel. You should receive from every entry in
the inputQueue.

You aren't sanity checking the argument to select(), allowing input_select to be > 15, and you don't initialize input_select
in the constructor either.

Can't see why it might be crashing though.
 
Hi Mark,

Thanks for the tips, I completely understand your last two points and will update the code to initialize and check for limits.

Can you explain further on how I would release all 15 blocks of audio_block_t *inputQueueArray[16]; when only 1 is being assigned to "block"?
Or do I need to create 16 block names in the cpp, one for each input block, then select one for use, then transmit the block used, and release all blocks?
 
Ok, I think I understand...
I've attached new files that should work correctly.

muxAmp.h
Code:
#ifndef muxamp_h_
#define muxamp_h_
#include "Arduino.h"
#include "AudioStream.h"

class AudioMuxAmp : public AudioStream
{
public:
  AudioMuxAmp(void) : AudioStream(16, inputQueueArray), multiplier(65536), input_select(0){}
  
  virtual void update(void);
  
  void gain(float n) 
  {
    if (n > 32767.0f) n = 32767.0f;
    else if (n < -32767.0f) n = -32767.0f;
    multiplier = n * 65536.0f;
  }

  void select(byte input) 
  {
    input_select = input;
    if(input_select > 15)
      {
        input_select = 15;
      }
  }  
private:
  int32_t multiplier;
  byte input_select;
  audio_block_t *inputQueueArray[16];
};

#endif

muxAmp.cpp
Code:
#include <Arduino.h>
#include "muxamp.h"
#include "utility/dspinst.h"

#if defined(__ARM_ARCH_7EM__)
#define MULTI_UNITYGAIN 65536
static void applyGain(int16_t *data, int32_t mult)
{
  uint32_t *p = (uint32_t *)data;
  const uint32_t *end = (uint32_t *)(data + AUDIO_BLOCK_SAMPLES);
  do 
  {
    uint32_t tmp32 = *p; // read 2 samples from *data
    int32_t val1 = signed_multiply_32x16b(mult, tmp32);
    int32_t val2 = signed_multiply_32x16t(mult, tmp32);
    val1 = signed_saturate_rshift(val1, 16, 0);
    val2 = signed_saturate_rshift(val2, 16, 0);
    *p++ = pack_16b_16b(val2, val1);
  } 
  while (p < end);
}

#elif defined(KINETISL)
#define MULTI_UNITYGAIN 256
static void applyGain(int16_t *data, int32_t mult)
{
  const int16_t *end = data + AUDIO_BLOCK_SAMPLES;
  do 
  {
    int32_t val = *data * mult;
    *data++ = signed_saturate_rshift(val, 16, 0);
  } 
  while (data < end);
}
#endif


void AudioMuxAmp::update(void)
{
  audio_block_t *in;
  int32_t mult = multiplier;
  byte inSource = input_select;
  byte channel;
    
  for (channel=0; channel < 16; channel++) 
    {
      if (channel == inSource)
        {
          if (mult == 0) 
            {
              // zero gain, discard any input and transmit nothing
              in = receiveReadOnly(channel);
            } 
            else if (mult == MULTI_UNITYGAIN) 
            {
              // unity gain, pass input to output without any change
              in = receiveReadOnly(channel);
              if (in) 
              {
                transmit(in);
              }
            } 
            else 
            {
              // apply gain to signal
              in = receiveWritable(channel);
              if (in) 
              {
                applyGain(in->data, mult);
                transmit(in);
              }
            }
        }
      else
        {
          in = receiveReadOnly(channel);  
        }
      release(in);  
    }
}
 
Status
Not open for further replies.
Back
Top