Crosspoint Mixer

Status
Not open for further replies.

macaba

Well-known member
I've got this new object:

crosspoint.png

Which replaces all this spaghetti (not all wires shown - it got difficult to see where I had wired to):

matrix.png

The CPU load scales with inputs, so if only half the inputs are used, the object uses half the CPU load.
[This is done by checking the result of receiveReadOnly(channel) for null]

Is there a way to detect that there are no AudioConnections on an output?
(Therefore skip the calculations for that output bus)

Thanks.
 
Last edited:
yes found it
https://forum.pjrc.com/threads/62718-Arbitrary-sized-mixers

funny thing is that I also have done that,
mention in the beginning of
https://forum.pjrc.com/threads/61630-Audio-System-Design-Tool-update-AudioMixerX-lt-n-gt


Anyway here is a prototype of the n-input n-output c++ template based design
of the Crosspoint Mixer:
Code:
/* Audio Library for Teensy 3.X
 * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
 *
 * Modified by Macaba
 * then by Manicksan to n-input and n-output c++ template
 *
 * Development of this audio library was funded by PJRC.COM, LLC by sales of
 * Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
 * open source software by purchasing Teensy or other PJRC products.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, development funding notice, and this permission
 * notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef mixercrosspoint16_h_
#define mixercrosspoint16_h_

#include "Arduino.h"
#include "AudioStream.h"

template <int NI, int NO> class AudioMixerCrosspoint : public AudioStream
{
#if defined(KINETISK)
public:
	AudioMixerCrosspoint(void) : AudioStream(NI, inputQueueArray) {
		for (int i=0; i<NO; i++) {
			outputGains[NO] = 1.0;
			for(int j=0; j<NI; j++) {
				gains[i][j] = 1.0;
				integerMultipliers[i][j] = 65536;
			}
		}
	}
	virtual void update(void);
	void gain(unsigned int bus, unsigned int channel, float gain) {
		if (bus >= NO) return;
		if (channel >= NI) return;
		if (gain > 32767.0f) gain = 32767.0f;
		else if (gain < -32767.0f) gain = -32767.0f;
		gains[bus][channel] = gain;
		integerMultipliers[bus][channel] = gains[bus][channel] * outputGains[bus] * 65536.0f;
	}
	void outputGain(unsigned int bus, float gain) {
		if (bus >= 16) return;
		if (gain > 32767.0f) gain = 32767.0f;
		else if (gain < -32767.0f) gain = -32767.0f;
		outputGains[bus] = gain;
		for (int i=0; i<NI; i++) {
			integerMultipliers[bus][i] = gains[bus][i] * outputGains[bus] * 65536.0f;
		}
	}
private:
	float gains[NO][NI];					//For each bus n, there are x inputs -> gains[n][x]
	float outputGains[NO];					//For each bus, there is an output gain
	int32_t integerMultipliers[NO][NI];		//Computed channel/bus gains with bus output gain mixed in
	audio_block_t *inputQueueArray[NI];

#elif defined(KINETISL)
public:
	AudioMixerCrosspoint(void) : AudioStream(NI, inputQueueArray) {
		for (int i=0; i<NO; i++) {
			outputGains[i] = 1.0;
			for(int j=0; j<NI; j++) {
				gains[i][j] = 1.0;
				integerMultipliers[i][j] = 256
			}
		}
	}
	virtual void update(void);
	void gain(unsigned int bus,unsigned int channel, float gain) {
		if (bus >= NO) return;
		if (channel >= NI) return;
		if (gain > 127.0f) gain = 127.0f;
		else if (gain < -127.0f) gain = -127.0f;
		gains[bus][channel] = gain;
		integerMultipliers[bus][channel] = gains[bus][channel] * outputGains[bus] * 256.0f;
	}
	void outputGain(unsigned int bus, float gain) {
		if (bus >= NO) return;
		if (gain > 127.0f) gain = 127.0f;
		else if (gain < -127.0f) gain = -127.0f;
		outputGains[bus] = gain;
		calculateIntegerMultipliers(bus);
		for (int i=0; i<NI; i++) {
			integerMultipliers[bus][i] = gains[bus][i] * outputGains[bus] * 256.0f;
		}
	}
private:
	float gains[NO][NI];				//For each bus n, there are x inputs -> gains[n][x]
	float outputGains[NO];
	int16_t integerMultipliers[NO][NI];	//Computed channel/bus gains with bus output gain mixed in
	audio_block_t *inputQueueArray[NI];
	
#endif
};

//#include "mixer_crosspoint_16.h"
#include "utility/dspinst.h"

// the following Forward declarations 
// must be defined when we use template 
// the compiler throws some warnings that should be errors otherwise
static inline int32_t signed_multiply_32x16b(int32_t a, uint32_t b); 
static inline int32_t signed_multiply_32x16t(int32_t a, uint32_t b);
static inline int32_t signed_saturate_rshift(int32_t val, int bits, int rshift);
static inline uint32_t pack_16b_16b(int32_t a, int32_t b);
static inline uint32_t signed_add_16_and_16(uint32_t a, uint32_t b);

#if defined(KINETISK)
#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);
}

static void applyGainThenAdd(int16_t *data, const int16_t *in, int32_t mult)
{
	uint32_t *dst = (uint32_t *)data;
	const uint32_t *src = (uint32_t *)in;
	const uint32_t *end = (uint32_t *)(data + AUDIO_BLOCK_SAMPLES);

	if (mult == MULTI_UNITYGAIN) {
		do {
			uint32_t tmp32 = *dst;
			*dst++ = signed_add_16_and_16(tmp32, *src++);
			tmp32 = *dst;
			*dst++ = signed_add_16_and_16(tmp32, *src++);
		} while (dst < end);
	} else {
		do {
			uint32_t tmp32 = *src++; // 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);
			tmp32 = pack_16b_16b(val2, val1);
			uint32_t tmp32b = *dst;
			*dst++ = signed_add_16_and_16(tmp32, tmp32b);
		} while (dst < 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);
}

static void applyGainThenAdd(int16_t *dst, const int16_t *src, int32_t mult)
{
	const int16_t *end = dst + AUDIO_BLOCK_SAMPLES;

	if (mult == MULTI_UNITYGAIN) {
		do {
			int32_t val = *dst + *src++;
			*dst++ = signed_saturate_rshift(val, 16, 0);
		} while (dst < end);
	} else {
		do {
			int32_t val = *dst + ((*src++ * mult) >> 8); // overflow possible??
			*dst++ = signed_saturate_rshift(val, 16, 0);
		} while (dst < end);
	}
}

#endif

template <int NI, int NO> void AudioMixerCrosspoint::update(void)
{
	audio_block_t *in[NI], *out=NULL;
	unsigned int channel;
	unsigned int bus;
	unsigned int sample;
	
	//First get audio_blocks to all the input channels as readonly. 
	//Readonly because we'll be using them multiple times so don't want to modify contents.
	for (channel=0; channel < NI; channel++) {
		in[channel] = receiveReadOnly(channel);
	}

	//Now crosspoint mix.
	for(bus=0; bus < NO; bus++) {
		out = allocate();		//get an audio_block that we'll sum the other channels to
		for(sample=0; sample < AUDIO_BLOCK_SAMPLES; sample++){
			out->data[sample] = 0;
		}
		//Using explicit loop instead of memset reduced CPU usage from 53% to 48.50%
		//memset(out->data, 0, AUDIO_BLOCK_SAMPLES * sizeof(int16_t)); 
		if(out){
			for (channel=0; channel < NI; channel++) {
				if (in[channel]) {
					applyGainThenAdd(out->data, in[channel]->data, integerMultipliers[bus][channel]);	
				}
			}
			transmit(out, bus);
			release(out);
		}
	}
	
	//Now release all the input audio_blocks
	for (channel=0; channel < NI; channel++) {
		if(in[channel])
			release(in[channel]);
	}
}

// this class and function forces include 
// of functions applyGainThenAdd and applyGain used by the template
class DummyClassCrossPointMixer
{
  public:
    virtual void dummyFunction();
};
void DummyClassCrossPointMixer::dummyFunction() {
  applyGainThenAdd(0, 0, 0);
  applyGain(0,0);
    
}
#endif
 
I found a error
at the line in the #if defined(KINETISK) part:
Code:
void outputGain(unsigned int bus, float gain) {
		if (bus >= 16) return;

it should be
Code:
void outputGain(unsigned int bus, float gain) {
		if (bus >= NO) return;
 
Status
Not open for further replies.
Back
Top