Creating new objects using existing ones (AudioStreams, AudioConnections)

Status
Not open for further replies.

dclyne

New member
Hello,

This is my first post here and it is probably more a question related to general object-oriented coding practices then it is to the audio library specifically, but I'm hoping someone might be able to give me some guidance, point me in the right direction, or let me know if I am spinning my wheels. I normally program PLC's (please don't judge:D) and have done some C programming, but C++ and true OOP are fairly new to me. I haven't had any luck searching through the forum but that might be because I am not 100% sure what I am searching for.

What I am hoping to accomplish is to create a new object (most likely extending AudioStream as new objects are normally created) that encapsulates several other AudioStream/AudioConnection class objects for use in a larger project. To test this I have created a small test project where the new object would basically be a mixer with each of the inputs being fed through a filter with the same fc (as attached). This application itself is of no use to me (or anyone probably) but I am using it as a POP before trying to tackle a more difficult application.

I am stuck on the implementation of the new object (AudioFilteredMixer), I niavely thought I could just use AudioConnections in my class declaration but the compiler does not agree with this:

Code:
private:
  float _freq; //gain of both filters
  float _gain; //gain of both mixer inputs
  AudioFilterStateVariable _filter2;        //xy=480.00001525878906,737.0000228881836
  AudioFilterStateVariable _filter1;        //xy=487,626
  AudioMixer4              _mixer1;         //xy=690.0000152587891,657.0000190734863
  AudioConnection          _patchCord5(_filter2, 1, _mixer1, 1); //<-- This doesn't work, "_filter2(etc) is not a type name" error
  AudioConnection          _patchCord6(_filter1, 1, _mixer1, 0); //<-- This doesn't work
  audio_block_t *inputQueueArray[2];

I am also confuse don how to make the connections from the inputs of my new object to the inputs of the existing objects that I hope to use within. Is what I am trying to do possible? or am I going about this all wrong? I think the picture probably gives the most clear explanation of what I am looking to accomplish, I'm sure my explanation could be a bit more clear but I am running on fumes tonight.

Full source code is attached, I had just started the implementation of this new object. I sandwiched most of it into the one file for testing purposes. I am using a Teensy 4.0 with Blackaddr audio shield (WM8731). Thanks for reading!

AudioFilteredMixer.pngView attachment main.cpp
 
IMHO you need to first identify exactly what your goals are, and I can see two possibilities: 1: you are trying to make a wrapper class to encapsulate existing functionality of the mixer and filter for some purpose or another, or 2: you want to make a new class of Audio object that can be reused in any project.

If you just want a simple wrapper class, you don't need to worry about audio_block_t and implementing the update() method to actually synthesize sounds. You can do something like this:
Code:
class AudioFilteredMixer {
	public:
		AudioFilteredMixer(AudioConnection *patchCord_, AudioMixer4 *mixer_) {
			patchCord = patchCord_;
			mixer = mixer_;
		}
	private:
		AudioConnection *patchCord;
		AudioMixer4 *mixer;
}

This class has pointers to the objects as private members, and you must pass instances to the constructor like so:
Code:
AudioFilteredMixer audioFilteredMixer = AudioFilteredMixer(&patchCord1, &mixer1);

Where patchCord1 and mixer1 are generated by the GUI tool.

If you want to tackle synthesizing sounds, this won't help you at all. In that case, see this thread where we helped someone learn the ropes: https://forum.pjrc.com/threads/59212-Audio-Router-Module
 
Thanks for your reply, my goal is both of those items... I think. The final end goal will be to eventually build a wrapper object that will represent one channel of a Vocoder with several filters, rectifier, VCA, etc. that I can plop into a project. At this time I'd like to break this up into smaller Audio objects connected together as I think this will be a bit easier for me to code then trying to write a new object that tries to take care of all this functionality, and I can test the components individually.

I spent some more time on it this morning and I ended up with a class that meets my needs and it is similar to the example you gave in that it is just a wrapper (no update call, no AudioStream inheritence). The difference is that the audio stream / connection objects I am using are declared within my AudioFilteredMixer class and not passed as pointers, and I added amplifiers as public objects where I want to hook up inputs and outputs to my new object. Maybe this is not the best approach but it's functional and I think it gets me what I need.

Code:
class AudioFilteredMixer
{

public:
  AudioAmplifier in1;
  AudioAmplifier in2;
  AudioAmplifier out;
	AudioFilteredMixer(void) : 
  _patchCord1(in1, 0, _filter1, 0),_patchCord2(in2, 0, _filter2, 0),_patchCord3(_filter1, 1, _mixer1, 0),_patchCord4(_filter2, 1, _mixer1, 1),_patchCord5(_mixer1, out) {

    _freq = 1000.0f;
    _gain = 1.0f;
    setObj();
	}

  //set gain for both mixer inputs
	void gain(float gain) {
		if (gain > 32767.0f) gain = 32767.0f;
		else if (gain < -32767.0f) gain = -32767.0f;
		_gain = gain;
    setObj();
	}
  //set fc for both filters
  void freq(float f) {
		if (f > 10000.0f) f = 10000.0f;
		else if (f < 20.0f) f = 20.0f;
		_freq = f;
    setObj();
	}
  
  //friend class AudioStream;
  //friend class AudioConnection;

private:
	float _freq; //gain of both filters
  float _gain; //gain of both mixer inputs
    
  void setObj(void){
    _filter1.frequency(_freq);
    _filter2.frequency(_freq);
    _mixer1.gain(0,_gain);
    _mixer1.gain(1,_gain);
  }

protected:
  AudioFilterStateVariable _filter2;        //xy=480.00001525878906,737.0000228881836
  AudioFilterStateVariable _filter1;        //xy=487,626
  AudioMixer4              _mixer1;         //xy=690.0000152587891,657.0000190734863
  AudioConnection          _patchCord1;
  AudioConnection          _patchCord2;
  AudioConnection          _patchCord3;
  AudioConnection          _patchCord4;
  AudioConnection          _patchCord5; 
};

And in my main code:
Code:
AudioFilteredMixer       mixerTest;
AudioConnection          patchCord2(waveform2, 0, mixerTest.in1, 0);
AudioConnection          patchCord4(mixer2, 0, mixerTest.in2, 0);
AudioConnection          patchCord7(mixerTest.out, fft1024_1);
AudioConnection          patchCord8(mixerTest.out, peakOut);

Thank you for the link, I think this info will help me with building/modifying new objects in order to make some of the building blocks for my vocoder.
 
If you only need one instance of AudioFilteredMixer per project, that approach will work fine. If you need multiple instances, you'll probably want to switch over to using pointers for your members.
 
There will be many instances per project, what is the advantage of using pointers? What kind of issue(s) would the approach I took bring?
 
Basically you would need to have different class definitions, how else would your class be able to work with other patchCords, mixers, and filters? If you use pointers for members, you can reuse the same class definition by passing in pointers to different objects like so:

Code:
AudioFilteredMixer audioFilteredMixer1 = AudioFilteredMixer(&patchCord1, &mixer1, &filter1);
AudioFilteredMixer audioFilteredMixer2 = AudioFilteredMixer(&patchCord2, &mixer2, &filter2);
AudioFilteredMixer audioFilteredMixer3 = AudioFilteredMixer(&patchCord3, &mixer3, &filter3);
AudioFilteredMixer audioFilteredMixer4 = AudioFilteredMixer(&patchCord4, &mixer4, &filter4);
 
I'm sorry, I think I should have read your code more closely. I was misunderstanding how you were intending to use your class instances. I think your approach is fine for multiple instances.
 
Status
Not open for further replies.
Back
Top