Dynamic Object instances--Dynamic Rewiring

Status
Not open for further replies.

Bob Larkin

Well-known member
I would like to rewire the instances. A simple example would be to process a signal that is either AM or FM. The Audio object circuitry is quite different for the two cases, but only one is generally used at a time. Therefore there is no need to "update" unused objects.

I know that other ways are possible, that leave all instances existing. Before going that direction though, I want to exclude the really clean solution of never trying to update unused objects at all. Is there a way?

Bob
 
As far as I can tell most objects skip their processing if the input buffer contains all 0's or is NULL.
So you could add a mixer that acts as a switch. Set gain to 1 on the channel that you do want in the path, and 0 for the disabled path(s).

Regards,
Patrick
 
A mixer does not help, it just zeroes the output - so the following objects keep working.
We need a real switch.
 
Ah yes I guess you're right.
I misinterpreted info from https://www.pjrc.com/teensy/td_libs_AudioNewObjects.html

It said:
Every input channel should have one of these receive functions used on every run of your update() function. The audio_block_t pointer may be NULL, which should be treated as if silent data was received.

Somehow I was thinking the other way around could also be true. I also had the idea that I saw something like that in the source somewhere... guess not.

I'll see if I can come up with a real switch-like solution in the next days.
 
You can certainly create and destroy audio processing blocks dynamically within your code (using C++ "new" and "delete" operators). The problem is how to connect the objects so that they pass audio from one to the next. Usually, one connects the objects using AudioConnection(). That'll be where the rubber hits the road for dynamically adjusting the processing.

Based on my examination of the AudioStream a month ago, I bet that you could dynamically *add* new AudioConnections. So, you should be able to dynamically expand your audio processing. But, that's only half of what you want to do. Presumably, you want to add new processing and remove the old processing. That looks harder.

If my memory is right, there's no current support for removing the underlying links created by instantiating an AudioConnection. You can delete any instance of an AudioConnection, but that won't unhook the AudioStream links.

It is possible that I missed this unhooking functionality in one of the class's methods (or destructor), so I'm happy to be corrected here. But, its my opinion that dynamic changes to the audio processing chain were not built into the audio library. So, you (or someone) would have to get into the plumbing to add that capability (especially the disconnecting part).

Chip
 
What if there was an switch object extending AudioStream that, in its update method would just transmit the inputbuffer if it is 'on' or transmit NULL if it was 'off'?
Would that not stop the rest of the chain from doing too much processing?

Patrick
 
Hi Guys,

as i need similar functionality to switch between synth patches i did some hacking on the AudioStream & AudioConnection classes.
My fork of the core library supports disconnection of audioconnections and disables unconnected audiostreams. See: https://github.com/b0rg3rt/cores
I do not delete audioconnections and reinstantiate them.

Note that the disconnect routine disables audio processing when a disconnect or connect is in progress so connection / disconnection might not be entirely glitch free.

Example usage from my test class:

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <MIDI.h>
#include "tone2freq.h"

#include "Synth.h"

AudioSynthWaveform * pWaveform1;
AudioEffectEnvelope * pEnvelope1;
AudioOutputAnalogStereo * pDacs1;
AudioEffectChorus * pChorus;
AudioConnection * pPatchCord1;
AudioConnection * pPatchCord2;
AudioConnection * pPatchCord3;
AudioConnection * pPatchCord4;
AudioConnection * pPatchCord5;
AudioConnection * pPatchCord6;
AudioConnection * pPatchCord7;


#define CHORUS_DELAY_LENGTH (16*AUDIO_BLOCK_SAMPLES)
// Allocate the delay lines for left and right channels
short chorus_delay[CHORUS_DELAY_LENGTH];

int numNotes =0;

int numVoices = 2;


static void handleNoteOn(byte inChannel, byte inNote, byte inVelocity)
{
	if (inNote > 128) { inNote = 128; }
	Serial.printf("NoteOn: %d\n", inNote);
	pWaveform1->frequency(tone2freq[inNote]);
	pEnvelope1->noteOn();
	numNotes++;
}

static void handleNoteOff(byte inChannel, byte inNote, byte inVelocity)
{
	numNotes--;
	if (numNotes < 1){
		pEnvelope1->noteOff();
		numNotes = 0;
	}
	Serial.printf("NoteOff: %d\n", inNote);
}


Synth::Synth(void)
{

  Serial.begin(115200);

  AudioMemory(20);

  pWaveform1 = new AudioSynthWaveform();
  pEnvelope1 = new AudioEffectEnvelope();
  pChorus = new AudioEffectChorus();


  pDacs1 = new AudioOutputAnalogStereo();


  pPatchCord1 = new AudioConnection(*pWaveform1,*pEnvelope1);
  pPatchCord2 = new AudioConnection(*pEnvelope1, *pChorus);

  pPatchCord4 = new AudioConnection(*pChorus, 0, *pDacs1, 0);
  pPatchCord5 = new AudioConnection(*pChorus, 0, *pDacs1, 1);

  pPatchCord6 = new AudioConnection(*pEnvelope1, 0, *pDacs1, 0);
  pPatchCord7 = new AudioConnection(*pEnvelope1, 0, *pDacs1, 1);


  MIDI.setHandleNoteOn(handleNoteOn);
  MIDI.setHandleNoteOff(handleNoteOff);
  MIDI.begin(1);

  pWaveform1->amplitude(0.11);
  pWaveform1->frequency(660);
  pWaveform1->begin(WAVEFORM_SAWTOOTH);

  pEnvelope1->attack(2.0);
  pEnvelope1->decay(2.0);
  pEnvelope1->sustain(0.5);
  pEnvelope1->release(500.0);

  pChorus->begin(chorus_delay,CHORUS_DELAY_LENGTH,numVoices);


}


void Synth::loop(void)
{
	MIDI.read();

	delay(10);

	if (Serial.available())
	{
		char c = Serial.read();
		if (c == 'a'){
			pChorus->voices(numVoices++);
			Serial.printf("Num Voices: %d", numVoices);
		}
		else if (c == 'z'){
			pChorus->voices(numVoices--);
			Serial.printf("Num Voices: %d", numVoices);
		}
		else if (c == 'd'){
			Serial.printf("disconnect chorus\n");

			pPatchCord2->disconnect();
			pPatchCord4->disconnect();
			pPatchCord5->disconnect();

			pPatchCord6->connect();
			pPatchCord7->connect();

			if (pChorus->isActive()) {
				Serial.printf("chorus active\n");

			} else {
				Serial.printf("chorus inactive\n");
			}

		} else if (c == 'e'){
			Serial.printf("connect chorus\n");

			pPatchCord6->disconnect();
			pPatchCord7->disconnect();

			pPatchCord2->connect();
			pPatchCord4->connect();
			pPatchCord5->connect();


			if (pChorus->isActive()) {
				Serial.printf("chorus active\n");

			} else {
				Serial.printf("chorus inactive\n");
			}


		}
	}
}
 
Awesome!
I see you abort the connection if source and destination are already connected somehow.
Wouldn't that prevent connecting a mono source to a left AND right channel of a destination?

No i check against the specific input index and destination index so that shouldn't be a problem :)

Code:
p->src_index == this->src_index && p->dest_index == this->dest_index
 
I've far behind on emails and reviewing pull requests at this moment. Been working on EHCI again for the last several days....
 
Status
Not open for further replies.
Back
Top