Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 8 of 8

Thread: Crosspoint Mixer

  1. #1
    Senior Member
    Join Date
    Jul 2014
    Posts
    139

    Crosspoint Mixer

    I've got this new object:

    Click image for larger version. 

Name:	crosspoint.png 
Views:	103 
Size:	29.4 KB 
ID:	10491

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

    Click image for larger version. 

Name:	matrix.png 
Views:	94 
Size:	48.8 KB 
ID:	10492

    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 by macaba; 05-05-2017 at 01:36 PM.

  2. #2
    Senior Member
    Join Date
    Aug 2016
    Location
    Australia
    Posts
    183
    I've updated macaba's very useful crosspoint mixer to work with Teensy 4s and gave it flexible numbers of inputs and outputs.

    I haven't tried it past 8 x 8.

    The code is at https://github.com/palmerr23/TeensyAudioMatrixMixer

  3. #3
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    262
    @palmerr
    You could use c++ templates instead
    That way nothing is unnessesary allocated.

  4. #4
    Junior Member
    Join Date
    Aug 2020
    Posts
    15
    Already did that! Code posted somewhere here - I guess click on my post history...

  5. #5
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    262
    yes found it
    https://forum.pjrc.com/threads/62718...y-sized-mixers

    funny thing is that I also have done that,
    mention in the beginning of
    https://forum.pjrc.com/threads/61630...MixerX-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

  6. #6
    Senior Member
    Join Date
    Aug 2016
    Location
    Australia
    Posts
    183
    Thanks, very neat!

    16x16 should be enough for anyone.

  7. #7
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    262
    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;

  8. #8
    Senior Member
    Join Date
    Aug 2016
    Location
    Australia
    Posts
    183
    Thanks again.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •