Open Sound Control (OSC) Teensy Audio Library Implementation

Fixed the crash with the boolean parameter, but you now need to adopt my fork of the OSC library here. Looks like boolean has never worked properly... Can't take the credit, there's a nearly two-year-old PR from szekelyisz with the fix in, which I happened to notice. Would have taken me forever to find without that!
 
Things that don't work by live update:
  • DynMixers are not fully supported, workaround is to define the required size for the moment.
  • Bus wires (will just get skipped by live update)
  • Arrays or wires going to/from them
But good that you pointed out these use cases.
That make my priorities easier to sort.

also
Wires marked in red are invalid(in the current implementation) wires, if you hover over them you will get a description.

This gives me a "length mismatch count" error in the output log; if I put a dummy 0 parameter in this goes away, and I think the correct message gets sent

that is not really a error, but will not send anything
if you just do
Code:
OSC.SendData(OSC.CreateMessageData(addr,'T'));

this is because my code don't take care of types that don't have any values
I can however fix this later

the workaround is to use dummy values as you did
Code:
OSC.SendData(OSC.CreateMessageData(addr,'TF',0,0));

note. you can use the following shorter form
Code:
OSC.SendMessage(addr,'TF',0,0));
 
Glad to help, but I'm not pretending to have any sort of system when using your GUI++, just playing about with simple stuff to test the OSCAudio and dynamic objects libraries. So if you have better priorities, do use those... I tried your workaround of setting the mixer width to 5, but it still exports at width=4 unless the last input is connected :(

I'm currently fairly happy with the overall operation, though I do need to go back and repair and test the static library - every time I make a change I break that, and while I don't think it's likely to be a major use case, it should probably work for people who just want to add OSCAudio on top of the existing libraries. Can you think of anything obvious missing from the OSCAudio library which I should be working on?
 
but it still exports at width=4 unless the last input is connected

ok need to correct myself there
but the following
DynMixerStill3.png

will export into a 3 input mixer
because of the new dynmixer behavior at OSC export

side note.
c++ export don't use that behavior yet, just saying.

Also I fixed the OSC send message part to make it possible to have non value boolean types T F
now it have much nicer parsing

now you can do the following
Code:
OSC.SendMessage(addr,'iiTFiiFTii',0,1,2,3,4,5);

other odd cases:

this will send 6 and 7 as strings (this taken care of by the osc-lib parser, just needed to test it)
Code:
OSC.SendMessage(addr,'iiTFiiFTiiss',0,1,2,3,4,5,6,7);

this will not send "text", instead it will be converted to a 0 integer (this taken care of by the osc-lib parser, just needed to test it)
Code:
OSC.SendMessage(addr,'iiTFiiFTiiss',"text",1,2,3,4,5,6,7);

more types than values:
Code:
OSC.SendMessage(addr,'iiTFiiFTiiss',0,1,2,3,4,5);
will generate a warning
Code:
skipped following types (because of types and values mismatch):
[s, s]

more values than types:
Code:
OSC.SendMessage(addr,'iiTFiiFTii',0,1,2,3,4,5,6,7);
will generate a warning
Code:
skipped following values: (because of types and values mismatch):
[6, 7]
 
just the git clone cmd:s to the latest branches
Code:
git clone -b feature/utils-class https://github.com/h4yn0nnym0u5e/OSCAudio.git

git clone -b features/cut-down https://github.com/h4yn0nnym0u5e/Audio.git
git clone -b features/dynamic-updates https://github.com/h4yn0nnym0u5e/Audio.git

git clone -b feature/Audio/dynamic-Teensy3 https://github.com/h4yn0nnym0u5e/cores.git
git clone -b feature/Audio/dynamic-updates https://github.com/h4yn0nnym0u5e/cores.git

git clone -b feature/getAddressLength https://github.com/h4yn0nnym0u5e/OSC.git
 
before I forget to mention it
if you hold ctrl down and double-clicking a class node you will "open"/show that class
 
I've pushed a few changes up to various libraries. Most important is the OSCAudio one, where I discovered another horrible OSC library behaviour that needed programming around...

Another thought on GUI++ export to code: there "needs" to be an option to create OSCAudio rather than Audio objects, so the compiled code is externally controllable. My rule for changing the names is simple: wherever Audio is in the class name, change it to OSCAudio. This is almost invariably the same as "add OSC to the start", except for the AsyncAudioInputSPDIF3 class (which clearly failed Quality Control, should have been AudioInputAsyncSPDIF3): by the simple rule this thus becomes AsyncOSCAudioInputSPDIF3.

Constructors for the OSCAudio versions require a text name, and if given an OSCAudioGroup& reference as a second parameter will be put in that group. This is also true for OSCAudioConnection objects, which can be connected later or at construction time. OSCAudioGroup objects are slightly different, they take an optional pointer to a parent group: a NULL pointer says the new group's parent group is the root.

Here's some example code with non-OSC and OSC variants of a voice class:
Code:
class Voice1 
{
public:
    AudioSynthWaveform               wav;
    AudioSynthWaveform               wav2;
    AudioMixer4                      mixer;
    AudioEffectEnvelope              env;
    AudioConnection                  *patchCord[3]; // total patchCordCount:3 including array typed ones.

    Voice1() { // constructor (this is called when class-object is created)
        int pci = 0; // used only for adding new patchcords


        patchCord[pci++] = new AudioConnection(wav, 0, mixer, 0);
        patchCord[pci++] = new AudioConnection(wav2, 0, mixer, 1);
        patchCord[pci++] = new AudioConnection(mixer, 0, env, 0);
        
    }
};


class OSCVoice1 
{
public:
    OSCAudioSynthWaveform               wav;
    OSCAudioSynthWaveform               wav2;
    OSCAudioMixer4                      mixer;
    OSCAudioEffectEnvelope              env;
    OSCAudioConnection                  *patchCord[3]; // total patchCordCount:3 including array typed ones.

    OSCVoice1() : // constructor (this is called when class-object is created)
      wav(OSCAudioSynthWaveform{"wav"}),
      wav2(OSCAudioSynthWaveform{"wav2"}),
      mixer(OSCAudioMixer4{"mixer"}),
      env(OSCAudioEffectEnvelope{"env"})
    { 
        int pci = 0; // used only for adding new patchcords

        patchCord[pci++] = new OSCAudioConnection("wav_mixer",  wav, 0, mixer, 0);
        patchCord[pci++] = new OSCAudioConnection("wav2_mixer", wav2, 0, mixer, 1);
        patchCord[pci++] = new OSCAudioConnection("mixer_env",  mixer, 0, env, 0);
        
    }
          
    OSCVoice1(OSCAudioGroup& grp) : // constructor for grouped version
      wav(OSCAudioSynthWaveform{"wav",grp}),
      wav2(OSCAudioSynthWaveform{"wav2",grp}),
      mixer(OSCAudioMixer4{"mixer",grp}),
      env(OSCAudioEffectEnvelope{"env",grp})
    { 
        int pci = 0; // used only for adding new patchcords

        patchCord[pci++] = new OSCAudioConnection("wav_mixer", grp, wav, 0, mixer, 0);
        patchCord[pci++] = new OSCAudioConnection("wav2_mixer",grp, wav2, 0, mixer, 1);
        patchCord[pci++] = new OSCAudioConnection("mixer_env", grp, mixer, 0, env, 0);        
    } 

    ~OSCVoice1() // destructor
    {
        // delete the patchCords, otherwise
        // we'd have a memory leak
        int pci = 0; 
        delete patchCord[pci++];
        delete patchCord[pci++];
        delete patchCord[pci++];
    }
};
It does compile, but I haven't tested it! Thinking further, I can't really see the point of a class unless you're going to instantiate it multiple times, so it may be better to put an OSCAudioGroup member in every class, and pass its name and parent group in at construction time. For example
Code:
// Make a grouped class derived from the "simple" one
class OSCVoice1grp : public OSCVoice1
{
  public:
    OSCVoice1grp(const char* _name,OSCAudioGroup* parent) : OSCVoice1(*(grp = new OSCAudioGroup(_name,parent))) {}
    ~OSCVoice1grp() {delete grp;}
    OSCAudioGroup* grp; 
};

 // make our "voice1" group object
OSCAudioGroup voice1{"voice1",NULL};

// manual creation of the first voice in the group ... 
OSCAudioGroup v1i0Grp{"i0",&voice1}; // ... make the sub-group ...
OSCVoice1 v1i0{v1i0Grp}; // ... and the audio objects

// use derived class to do the above in one step
OSCVoice1grp v1i1{"i1",&voice1};
This gives the flexibility to use either OCSVoice1 or OSCVoice1grp. It might be slightly more efficient only to implement OSCVoice1 with the OSCAudioGroup built directly in: it ensures users have a group, and as I said I can't see why they wouldn't. OK, one use: you have a class which is your output device and controls, which you construct once and never destroy.
 
I would like to create it like this
Code:
class OSCVoice1 
{
public:
    OSCAudioGroup* grp; 
    OSCAudioSynthWaveform               wav;
    OSCAudioSynthWaveform               wav2;
    OSCAudioMixer4                      mixer;
    OSCAudioEffectEnvelope              env;
    OSCAudioConnection                  *patchCord[3]; // total patchCordCount:3 including array typed ones.

    OSCVoice1() : // constructor (this is called when class-object is created)
      wav(OSCAudioSynthWaveform{"wav"}), // how do these work? wont this create two instances first one at the top and one here?
      wav2(OSCAudioSynthWaveform{"wav2"}),
      mixer(OSCAudioMixer4{"mixer"}),
      env(OSCAudioEffectEnvelope{"env"})
    { 
        int pci = 0; // used only for adding new patchcords

        patchCord[pci++] = new OSCAudioConnection("wav_mixer",  wav, 0, mixer, 0);
        patchCord[pci++] = new OSCAudioConnection("wav2_mixer", wav2, 0, mixer, 1);
        patchCord[pci++] = new OSCAudioConnection("mixer_env",  mixer, 0, env, 0);
        
    }
          
    OSCVoice1(const char* _name,OSCAudioGroup* parent) : // constructor for grouped version
      wav(OSCAudioSynthWaveform{"wav",grp}),
      wav2(OSCAudioSynthWaveform{"wav2",grp}), 
      mixer(OSCAudioMixer4{"mixer",grp}),
      env(OSCAudioEffectEnvelope{"env",grp})
    { 
        grp = new OSCAudioGroup(_name,parent);
        int pci = 0; // used only for adding new patchcords

        patchCord[pci++] = new OSCAudioConnection("wav_mixer", grp, wav, 0, mixer, 0);
        patchCord[pci++] = new OSCAudioConnection("wav2_mixer",grp, wav2, 0, mixer, 1);
        patchCord[pci++] = new OSCAudioConnection("mixer_env", grp, mixer, 0, env, 0);        
    } 

    ~OSCVoice1() // destructor
    {
        // delete the patchCords, otherwise
        // we'd have a memory leak
        int pci = 0; 
        delete grp; // do this need null check?
        delete patchCord[pci++];
        delete patchCord[pci++];
        delete patchCord[pci++];
    }
};

that way no extra classes are needed

Also the points of classes are that they are much easier to generate patchCord code for,
as the patchCord:s don't need any long path:s and the naming of them is much easer (I believe)


I did make the dyn. input objects (dyn. mixers) visually dynamic
now you never have to change the input count manually
settings:
Global-Node/Links-'Auto append dropped links' (enabled by default)
Global-Node/Links-'Dyn. Input Objects Auto Expand' (enabled by default) need 'Auto append dropped links' to be enabled
Global-Node/Links-'Dyn. Input Objects Auto Reduce' (disabled by default)

this dynamic mixer adds inputs when new links are dropped on to it
and inputs are removed up to the last used when you delete links at the end

I have experimental Live Edit while using that mixer (don't yet support array sources and bus lines)
 
That scheme is close to working, I think.

The member initialisers don't create two instances, they just say how to create the previously declared class member; a key point to remember is they execute in the same order the members are declared, not the order you write the initialisers. C++ is weird... The grouped version won't work, because grp doesn't have an initialiser (doing it in the constructor body is too late), so all the OSCAudio objects will get a bad pointer when they're constructed: just needs grp(new OSCAudioGroup(_name,parent)), adding to the initialisation list. As you've declared it first, that should be enough.

Having said all that, my experimental lash-up of a similar scheme is not quite working. It builds OK, but crashes when I destroy it. So I'd suggest you don't put in too much (i.e. any!) effort just yet, until I figure out what we should be doing.
 
OK, I think I have a concept working, though right now I still have a residual memory leak... there's new demo uploaded to the OSCAudio repo.

Here's our "simple" voice class:
Code:
class OSCVoice1 
{
public:
    OSCAudioSynthWaveform&               wav;
    OSCAudioSynthWaveform&               wav2;
    OSCAudioMixer4&                      mixer;
    OSCAudioEffectEnvelope&              env;
    OSCAudioConnection                  *patchCord[3]; // total patchCordCount:3 including array typed ones.

    OSCVoice1() : // constructor (this is called when class-object is created)
      wav(*new OSCAudioSynthWaveform{"wav"}),
      wav2(*new OSCAudioSynthWaveform{"wav2"}),
      mixer(*new OSCAudioMixer4{"mixer"}),
      env(*new OSCAudioEffectEnvelope{"env"})
    { 
        int pci = 0; // used only for adding new patchcords

        patchCord[pci++] = new OSCAudioConnection("wav_mixer",  wav, 0, mixer, 0);
        patchCord[pci++] = new OSCAudioConnection("wav2_mixer", wav2, 0, mixer, 1);
        patchCord[pci++] = new OSCAudioConnection("mixer_env",  mixer, 0, env, 0);        
    }
          
    OSCVoice1(OSCAudioGroup& grp) : // constructor for grouped version
      wav(*new OSCAudioSynthWaveform{"wav",grp}),
      wav2(*new OSCAudioSynthWaveform{"wav2",grp}),
      mixer(*new OSCAudioMixer4{"mixer",grp}),
      env(*new OSCAudioEffectEnvelope{"env",grp})
    { 
        int pci = 0; // used only for adding new patchcords

        patchCord[pci++] = new OSCAudioConnection("wav_mixer", grp, wav, 0, mixer, 0);
        patchCord[pci++] = new OSCAudioConnection("wav2_mixer",grp, wav2, 0, mixer, 1);
        patchCord[pci++] = new OSCAudioConnection("mixer_env", grp, mixer, 0, env, 0);                
    } 

    ~OSCVoice1()
    {
        // delete the patchCords, otherwise
        // we'd have a memory leak
        
        int pci = 0; 
        delete patchCord[pci++];
        delete patchCord[pci++];
        delete patchCord[pci++];

        // also the voice objects:
        delete &wav;
        delete &wav2;
        delete &mixer;
        delete &env;
    }
};
Note the following:
  • the various voice objects are declared as references: this forces at least a warning at compile time if there's no initialiser
  • it would be possible to use initialisers for the connections, and dispense with the constructor code, but not have them as an array
  • there are two constructors, depending on whether the class is constructed as part of a group or standalone
  • all the weird new and delete stuff appears to be required: if I try to construct the voice's member objects directly it works, but then crashes when I try to destroy the voice
You will need to get the latest v0.9-alpha cores code to test with the AudioMixerStereo object.
 
And here's the group-only option, which I think was your preference. Note that because deleting an OSCAudioGroup automatically deletes all its members, the destructor is much simpler:
Code:
class OSCVoice1grp 
{
public:
    OSCAudioGroup&                       grp;
    OSCAudioSynthWaveform&               wav;
    OSCAudioSynthWaveform&               wav2;
    OSCAudioMixer4&                      mixer;
    OSCAudioEffectEnvelope&              env;
    OSCAudioConnection                  *patchCord[3]; // total patchCordCount:3 including array typed ones.

       
    OSCVoice1grp(const char* _name,OSCAudioGroup* parent) : // constructor 
      grp(*new OSCAudioGroup{_name,parent}),
      wav(*new OSCAudioSynthWaveform{"wav",grp}),
      wav2(*new OSCAudioSynthWaveform{"wav2",grp}),
      mixer(*new OSCAudioMixer4{"mixer",grp}),
      env(*new OSCAudioEffectEnvelope{"env",grp})
    { 
        int pci = 0; // used only for adding new patchcords

        patchCord[pci++] = new OSCAudioConnection("wav_mixer", grp, wav, 0, mixer, 0);
        patchCord[pci++] = new OSCAudioConnection("wav2_mixer",grp, wav2, 0, mixer, 1);
        patchCord[pci++] = new OSCAudioConnection("mixer_env", grp, mixer, 0, env, 0);                
    } 

    ~OSCVoice1grp()
    {
        delete &grp; // automatically deletes all group members!
    }
};
 
Here's an improved export class, which doesn't suffer from the memory leak problem:
Code:
class OSCVoice1grp : public OSCAudioGroup
{
public:
    // the actual voice elements we want to group together
    OSCAudioSynthWaveform&  wav;
    OSCAudioSynthWaveform&  wav2;
    OSCAudioMixer4&         mixer;
    OSCAudioEffectEnvelope& env;

    // internal patch cords
    // total patchCordCount:3 including array typed ones.
    OSCAudioConnection*     patchCord[3];

    // create as sub-group
    OSCVoice1grp(const char* _name,OSCAudioGroup* parent) : // constructor 
      OSCAudioGroup(_name,parent), // construct our base class instance
      wav(*new OSCAudioSynthWaveform{"wav",*((OSCAudioGroup*) this)}),
      wav2(*new OSCAudioSynthWaveform{"wav2",*((OSCAudioGroup*) this)}),
      mixer(*new OSCAudioMixer4{"mixer",*((OSCAudioGroup*) this)}),
      env(*new OSCAudioEffectEnvelope{"env",*((OSCAudioGroup*) this)})
    { 
        int pci = 0; // used only for adding new patchcords
        OSCAudioGroup& grp = *((OSCAudioGroup*) this);

        patchCord[pci++] = new OSCAudioConnection("wav_mixer", grp, wav,   0, mixer, 0);
        patchCord[pci++] = new OSCAudioConnection("wav2_mixer",grp, wav2,  0, mixer, 1);
        patchCord[pci++] = new OSCAudioConnection("mixer_env", grp, mixer, 0, env,   0);                
    } 

    // create at root
    OSCVoice1grp(const char* _name) : OSCVoice1grp(_name,NULL) {}

    // no destructor needed, the base OSCAudioGroup   
    // destroys its members when it's destroyed
};
The leak was due to the OSCVoice1grp instances not being destroyed, although all their elements were! Making it derived from the OSCAudioGroup class fixes that, and also eliminates the need for any destructor code.

It's not possible to construct the patchCords the same way, as far as I can see... I tried it, but it crashed when destroying the object :(
 
Good work.

It's not possible to construct the patchCords the same way, as far as I can see... I tried it, but it crashed when destroying the object :(

The whole point with creating audio connections in the constructor is that they can be initialized in a for loop
like this:
Code:
for (int i = 0; i < 8; i++) {
    patchCord[pci++] = new AudioConnection(wfm[i], 0, mixer, i);
}
think that would be even more impossible to do 'the same way'



I have been working with improving the internal structure of the tool:

before all nodes and all links was put into two arrays for all tabs/workspaces

now they are put into the respective tab/workspace instead
more like the json structure

that makes much cleaner looking code when accessing nodes/links belonging to a specific workspace
as needed for the OSC live edit, +makes it much easier to write export code.
Also in theory, it will improve the performance.

also did some improvements while working with huge 'one tab' designs ~2000nodes/~2000links
that will also make small designs much faster to handle

I did a separate branch for that, and will update the master branch when
I have been testing it out completely, don't want any left over gremlins to
make a huge mess of anyone's designs.
 
Good work.

The whole point with creating audio connections in the constructor is that they can be initialized in a for loop
like this:
Code:
for (int i = 0; i < 8; i++) {
    patchCord[pci++] = new AudioConnection(wfm[i], 0, mixer, i);
}
think that would be even more impossible to do 'the same way'
Here's the nearest I can get to "the same way", i.e. using initialisers rather than constructor code for the internal connections:
Code:
// OSC version of the Voice1 class
class OSCVoice1grp : public OSCAudioGroup
{
public:
    // the actual voice elements we want to group together
    OSCAudioSynthWaveform&  wav;
    OSCAudioSynthWaveform&  wav2;
    OSCAudioMixer4&         mixer;
    OSCAudioEffectEnvelope& env;

    // internal patch cords
    // total patchCordCount:3 including array typed ones.
    OSCAudioConnection *pc1,*pc2,*pc3;

    // create as sub-group
    OSCVoice1grp(const char* _name,OSCAudioGroup* parent) : // constructor 
      OSCAudioGroup(_name,parent), // construct our base class instance
      wav(*new OSCAudioSynthWaveform{"wav",*this}),
      wav2(*new OSCAudioSynthWaveform{"wav2",*this}),
      mixer(*new OSCAudioMixer4{"mixer",*this}),
      env(*new OSCAudioEffectEnvelope{"env",*this}),
      
      pc1(new OSCAudioConnection{"wav_mixer", *this, wav,   0, mixer, 0}),
      pc2(new OSCAudioConnection{"wav2_mixer",*this, wav2,  0, mixer, 1}),
      pc3(new OSCAudioConnection{"mixer_env", *this, mixer, 0, env,   0})  
            
    { 
    } 

    // create at root
    OSCVoice1grp(const char* _name) : OSCVoice1grp(_name,NULL) {}

    // no destructor needed, the base OSCAudioGroup   
    // destroys its members when it's destroyed
};
I'd have preferred to use references for the connections, not pointers, as the compiler gives a warning if a reference class member isn't initialised. For some reason references worked for the OSCAudioStream-derived objects (wav, wav2, mixer, env), but not for the connections. Either way of initialising the internal connections works, and as you can rarely use a loop to initialise them, it's really up to you to decide whether you prefer your old patchCord[pci++] method or the initialisers.

Note that we can remove the ugly cast *((OSCAudioGroup*) this) and replace it with just *this: I thought it didn't work for me before, but I must have made some other error.

I have been working with improving the internal structure of the tool:

<snip>

I did a separate branch for that, and will update the master branch when
I have been testing it out completely, don't want any left over gremlins to
make a huge mess of anyone's designs.
Sounds good, look forward to working with it!
 
and as you can rarely use a loop to initialise them

this is how many connections that are needed for a 'simple' 16 polysynth using two LFO as inputs

Code:
AudioConnection        patchCord1(LFO, 0, monovoice[0].wavA, 0);    
AudioConnection        patchCord2(LFO, 0, monovoice[1].wavA, 0);    
AudioConnection        patchCord3(LFO, 0, monovoice[2].wavA, 0);    
AudioConnection        patchCord4(LFO, 0, monovoice[3].wavA, 0);    
AudioConnection        patchCord5(LFO, 0, monovoice[4].wavA, 0);    
AudioConnection        patchCord6(LFO, 0, monovoice[5].wavA, 0);    
AudioConnection        patchCord7(LFO, 0, monovoice[6].wavA, 0);    
AudioConnection        patchCord8(LFO, 0, monovoice[7].wavA, 0);    
AudioConnection        patchCord9(LFO, 0, monovoice[8].wavA, 0);    
AudioConnection        patchCord10(LFO, 0, monovoice[9].wavA, 0);    
AudioConnection        patchCord11(LFO, 0, monovoice[10].wavA, 0);    
AudioConnection        patchCord12(LFO, 0, monovoice[11].wavA, 0);    
AudioConnection        patchCord13(LFO, 0, monovoice[12].wavA, 0);    
AudioConnection        patchCord14(LFO, 0, monovoice[13].wavA, 0);    
AudioConnection        patchCord15(LFO, 0, monovoice[14].wavA, 0);    
AudioConnection        patchCord16(LFO, 0, monovoice[15].wavA, 0);    
AudioConnection        patchCord17(LFO, 0, monovoice[0].wavB, 0);    
AudioConnection        patchCord18(LFO, 0, monovoice[1].wavB, 0);    
AudioConnection        patchCord19(LFO, 0, monovoice[2].wavB, 0);    
AudioConnection        patchCord20(LFO, 0, monovoice[3].wavB, 0);    
AudioConnection        patchCord21(LFO, 0, monovoice[4].wavB, 0);    
AudioConnection        patchCord22(LFO, 0, monovoice[5].wavB, 0);    
AudioConnection        patchCord23(LFO, 0, monovoice[6].wavB, 0);    
AudioConnection        patchCord24(LFO, 0, monovoice[7].wavB, 0);    
AudioConnection        patchCord25(LFO, 0, monovoice[8].wavB, 0);    
AudioConnection        patchCord26(LFO, 0, monovoice[9].wavB, 0);    
AudioConnection        patchCord27(LFO, 0, monovoice[10].wavB, 0);    
AudioConnection        patchCord28(LFO, 0, monovoice[11].wavB, 0);    
AudioConnection        patchCord29(LFO, 0, monovoice[12].wavB, 0);    
AudioConnection        patchCord30(LFO, 0, monovoice[13].wavB, 0);    
AudioConnection        patchCord31(LFO, 0, monovoice[14].wavB, 0);    
AudioConnection        patchCord32(LFO, 0, monovoice[15].wavB, 0);    
AudioConnection        patchCord33(LFO1, 0, monovoice[0].wavA, 1);    
AudioConnection        patchCord34(LFO1, 0, monovoice[1].wavA, 1);    
AudioConnection        patchCord35(LFO1, 0, monovoice[2].wavA, 1);    
AudioConnection        patchCord36(LFO1, 0, monovoice[3].wavA, 1);    
AudioConnection        patchCord37(LFO1, 0, monovoice[4].wavA, 1);    
AudioConnection        patchCord38(LFO1, 0, monovoice[5].wavA, 1);    
AudioConnection        patchCord39(LFO1, 0, monovoice[6].wavA, 1);    
AudioConnection        patchCord40(LFO1, 0, monovoice[7].wavA, 1);    
AudioConnection        patchCord41(LFO1, 0, monovoice[8].wavA, 1);    
AudioConnection        patchCord42(LFO1, 0, monovoice[9].wavA, 1);    
AudioConnection        patchCord43(LFO1, 0, monovoice[10].wavA, 1);    
AudioConnection        patchCord44(LFO1, 0, monovoice[11].wavA, 1);    
AudioConnection        patchCord45(LFO1, 0, monovoice[12].wavA, 1);    
AudioConnection        patchCord46(LFO1, 0, monovoice[13].wavA, 1);    
AudioConnection        patchCord47(LFO1, 0, monovoice[14].wavA, 1);    
AudioConnection        patchCord48(LFO1, 0, monovoice[15].wavA, 1);    
AudioConnection        patchCord49(LFO1, 0, monovoice[0].wavB, 1);    
AudioConnection        patchCord50(LFO1, 0, monovoice[1].wavB, 1);    
AudioConnection        patchCord51(LFO1, 0, monovoice[2].wavB, 1);    
AudioConnection        patchCord52(LFO1, 0, monovoice[3].wavB, 1);    
AudioConnection        patchCord53(LFO1, 0, monovoice[4].wavB, 1);    
AudioConnection        patchCord54(LFO1, 0, monovoice[5].wavB, 1);    
AudioConnection        patchCord55(LFO1, 0, monovoice[6].wavB, 1);    
AudioConnection        patchCord56(LFO1, 0, monovoice[7].wavB, 1);    
AudioConnection        patchCord57(LFO1, 0, monovoice[8].wavB, 1);    
AudioConnection        patchCord58(LFO1, 0, monovoice[9].wavB, 1);    
AudioConnection        patchCord59(LFO1, 0, monovoice[10].wavB, 1);    
AudioConnection        patchCord60(LFO1, 0, monovoice[11].wavB, 1);    
AudioConnection        patchCord61(LFO1, 0, monovoice[12].wavB, 1);    
AudioConnection        patchCord62(LFO1, 0, monovoice[13].wavB, 1);    
AudioConnection        patchCord63(LFO1, 0, monovoice[14].wavB, 1);    
AudioConnection        patchCord64(LFO1, 0, monovoice[15].wavB, 1);    
AudioConnection        patchCord65(monovoice[0].env, 0, mixerStereo, 0);    
AudioConnection        patchCord66(monovoice[1].env, 0, mixerStereo, 1);    
AudioConnection        patchCord67(monovoice[2].env, 0, mixerStereo, 2);    
AudioConnection        patchCord68(monovoice[3].env, 0, mixerStereo, 3);    
AudioConnection        patchCord69(monovoice[4].env, 0, mixerStereo, 4);    
AudioConnection        patchCord70(monovoice[5].env, 0, mixerStereo, 5);    
AudioConnection        patchCord71(monovoice[6].env, 0, mixerStereo, 6);    
AudioConnection        patchCord72(monovoice[7].env, 0, mixerStereo, 7);    
AudioConnection        patchCord73(monovoice[8].env, 0, mixerStereo, 8);    
AudioConnection        patchCord74(monovoice[9].env, 0, mixerStereo, 9);    
AudioConnection        patchCord75(monovoice[10].env, 0, mixerStereo, 10);    
AudioConnection        patchCord76(monovoice[11].env, 0, mixerStereo, 11);    
AudioConnection        patchCord77(monovoice[12].env, 0, mixerStereo, 12);    
AudioConnection        patchCord78(monovoice[13].env, 0, mixerStereo, 13);    
AudioConnection        patchCord79(monovoice[14].env, 0, mixerStereo, 14);    
AudioConnection        patchCord80(monovoice[15].env, 0, mixerStereo, 15); 
AudioConnection        patchCord81(mixerStereo, 0, dac, 0);
AudioConnection        patchCord82(mixerStereo, 0, i2s, 0);
AudioConnection        patchCord83(mixerStereo, 1, dac, 1);
AudioConnection        patchCord84(mixerStereo, 1, i2s, 1);
imagine having that in the constructor

instead of just
Code:
patchCord[pci++] = new AudioConnection(mixerStereo, 0, i2s, 0);
patchCord[pci++] = new AudioConnection(mixerStereo, 1, dac, 1);
patchCord[pci++] = new AudioConnection(mixerStereo, 1, i2s, 1);
for (int i = 0; i < 16; i++) {
    patchCord[pci++] = new AudioConnection(LFO, 0, monovoice[i].wavA, 0);
    patchCord[pci++] = new AudioConnection(LFO, 0, monovoice[i].wavB, 0);
    patchCord[pci++] = new AudioConnection(LFO1, 0, monovoice[i].wavA, 1);
    patchCord[pci++] = new AudioConnection(LFO1, 0, monovoice[i].wavB, 1);
    patchCord[pci++] = new AudioConnection(monovoice[i].env, 0, mixerStereo, i);
}
 
I'd have preferred to use references for the connections, not pointers, as the compiler gives a warning if a reference class member isn't initialised.

I have preferred to use a array of references, but c++ don't allow that

c++ is strange

OSCAudioSynthWaveform wav[2];

do call the () constructor if available on all array members automatically.

C# on the other hand needs each of the array members to be initialized

wav[0] = new OSCAudioSynthWaveform("wav_0");
wav[1] = new OSCAudioSynthWaveform("wav_1");

this is what I would c++ to be like

so that the audio connections
could be written like this

AudioConnection acs[2];

then in the constructor
acs[0] = new AudioConnection(source1,0,target1,0);
acs[1] = new AudioConnection(source2,0,target2,0);


But then I remembered you have actually made that possible
by having a AudioConnection constructor that takes zero parameters

then the above can be written like this?:
Code:
class Test {
    public:
    AudioSynthWaveform wf1;
    AudioSynthWaveform wf2;
    AudioMixer4 mix;

    OSCAudioConnection acs[2];

    Test() {
        acs[0].linkInSrc(*this); // is this possible or do I misunderstand something
        acs[1].linkInSrc(*this);
        //acs[0].setName("acs_0"); // gives error: 'void OSCAudioBase::setName(const char*)' is inaccessible
        //acs[1].setName("acs_1"); // gives error: 'void OSCAudioBase::setName(const char*)' is inaccessible
        acs[0].connect(wf1,0,mix,0);
        acs[1].connect(wf2,0,mix,1);
    }
};
 
Fair point, provided GUI++ can reliably spot when iteration is possible - no reason why not, the monovoice[16] is a big clue!

I prefer an approach where monovoice owns its in/out connections and provides functions to connect them, so you get something like:
Code:
for (int i = 0; i < 16; i++) 
{
    monovoice[i] = new OSCMonoVoice();
    monovoice[i]->LFO1connect(LFO,0);
    monovoice[i]->LFO2connect(LFO1,0);
    monovoice[i]->outputconnect(mixerStereo,i);
}
It may be possible to deduce the right code for this:
2022-02-09 20_54_01-Audio System Design Tool++ for Teensy Audio Library.png
I've put an example in the io connectors and code objects in this fragment:
Code:
{"version":1,"settings":{"main":{},"OSC":{"LiveUpdate":false,"UseDebugLinkName":true},"arduino":{"useExportDialog":true,"ProjectName":"1Voice","StandardIncludeHeader":"#include <Arduino.h>\n#include <OSCAudio.h>\n","Board":{"Platform":"","Board":"","Options":""}},"BiDirDataWebSocketBridge":{},"workspaces":{},"sidebar":{"autoSwitchTabToInfoTab":false},"palette":{},"editor":{},"devTest":{},"IndexedDBfiles":{"testFileNames":"testFile.txt"},"NodeDefGenerator":{},"NodeDefManager":{},"NodeHelpManager":{}},"workspaces":[{"type":"tab","id":"de8d0e25.32ebb","label":"Voice1","inputs":0,"outputs":0,"export":true,"isMain":false,"mainNameType":"main","mainNameExt":".ino","isAudioMain":false,"generateCppDestructor":true,"extraClassDeclarations":"","settings":{"workspaceBgColor":"#EDFFDF","showGridHminor":false,"showGridHmajor":false,"showGridVminor":false,"showGridVmajor":false,"useCenterBasedPositions":false},"nodes":[{"id":"Voice1_In1","type":"TabInput","isClass":false,"name":"LFO1","comment":"","outputs":1,"x":460,"y":260,"z":"de8d0e25.32ebb","bgColor":"#cce6ff","wires":[["Voice1_waveformMod1:0","Voice1_waveformMod2:0"]]},{"id":"Voice1_Out1","type":"TabOutput","isClass":false,"name":"output","comment":"","inputs":1,"x":1015,"y":295,"z":"de8d0e25.32ebb","bgColor":"#cce6ff","wires":[]},{"id":"Voice1_In2","type":"TabInput","isClass":false,"name":"LFO2","comment":"","outputs":1,"x":460,"y":325,"z":"de8d0e25.32ebb","bgColor":"#cce6ff","wires":[["Voice1_waveformMod1:1","Voice1_waveformMod2:1"]]},{"id":"Voice1_waveformMod1","type":"AudioSynthWaveformModulated","isClass":false,"name":"wavA","comment":"","x":630,"y":265,"z":"de8d0e25.32ebb","bgColor":"#E6E0F8","wires":[["Voice1_mixer1:0"]]},{"id":"Voice1_waveformMod2","type":"AudioSynthWaveformModulated","isClass":false,"name":"wavB","comment":"","x":630,"y":320,"z":"de8d0e25.32ebb","bgColor":"#E6E0F8","wires":[["Voice1_mixer1:1"]]},{"id":"Voice1_constructor code1","type":"ConstructorCode","isClass":false,"name":"constructor: set gains","comment":"// from GUI++\nmixer.gain(0,0.5);\nmixer.gain(1,0.5);\n","x":655,"y":470,"z":"de8d0e25.32ebb","bgColor":"#DDFFBB","wires":[]},{"id":"Voice1_vars1","type":"Variables","isClass":false,"name":"io connectors","comment":"AudioConnection LFO1_0_wavA_0\nAudioConnection LFO2_0_wavA_1\nAudioConnection LFO1_0_wavB_0\nAudioConnection LFO2_0_wavB_1\n\nAudioConnection env_0_output_0;\n","x":655,"y":520,"z":"de8d0e25.32ebb","bgColor":"#DDFFBB","wires":[]},{"id":"Voice1_code1","type":"Function","isClass":false,"name":"code","comment":"// Connect to an external destination\nvoid outputconnect(OSCAudioStream& dst, // object to connect to\n                   uint8_t port)        // port to use\n{\n    outputConnection.connect(env,0,dst,port);\n}\n\n// LFO connections\nvoid LFO1connect(OSCAudioStream& src\n                 uin8_t port)\n{\n    LFO1_0_wavA_0.connect(src,port,wavA,0);\n    LFO1_0_wavB_0.connect(src,port,wavB,0);\n}\n\n// LFO connections\nvoid LFO2connect(OSCAudioStream& src\n                 uin8_t port)\n{\n    LFO2_0_wavA_1.connect(src,port,wavA,1);\n    LFO2_0_wavB_1.connect(src,port,wavB,1);\n}\n\n","x":646,"y":565,"z":"de8d0e25.32ebb","bgColor":"#DDFFBB","wires":[]},{"id":"Voice1_mixer1","type":"AudioMixer","isClass":false,"name":"mixer1","comment":"","inputs":2,"x":770,"y":295,"z":"de8d0e25.32ebb","bgColor":"#E6E0F8","wires":[["MonoSynth_envelope1:0"]]},{"id":"MonoSynth_envelope1","type":"AudioEffectEnvelope","isClass":false,"name":"env","comment":"","x":905,"y":295,"z":"de8d0e25.32ebb","bgColor":"#E6E0F8","wires":[["Voice1_Out1:0"]]}]}],"nodeAddons":{"h4yn0nnyNodes":{"isAddon":true,"label":"h4yn0nnyNodes","description":"","credits":"h4yn0nnym0u5e","homepage":"","url":"https://api.github.com/repos/h4yn0nnym0u5e","types":{"AudioEffectExpEnvelope":{"defaults":{"name":{"value":"Poly4withLFO_expEnv1"},"comment":{},"color":{"value":"#c6c0d8"}},"shortName":"expEnv","inputs":1,"outputs":1,"inputTypes":{"0":"i16"},"outputTypes":{"0":"i16"},"category":"effect","help":"","color":"#c6c0d8","icon":"arrow-in.png"},"AudioMixerStereo":{"defaults":{"name":{"value":"Poly4withLFO_mixerS1"},"id":{},"inputs":{"value":1,"maxval":255,"minval":1,"type":"int"},"comment":{},"color":{"value":"#E6E0F8"}},"dynInputs":"","shortName":"mixerS","inputs":1,"outputs":2,"category":"mixer","color":"#E6E0F8","icon":"arrow-in.png"}}}}
}
But this is obviously extra work, and it's definitely secondary to getting bus connections and live editing working!
 
But then I remembered you have actually made that possible
by having a AudioConnection constructor that takes zero parameters

then the above can be written like this?:
Code:
class Test {
    public:
    AudioSynthWaveform wf1;
    AudioSynthWaveform wf2;
    AudioMixer4 mix;

    OSCAudioConnection acs[2];

    Test() {
        acs[0].linkInSrc(*this); // is this possible or do I misunderstand something
        acs[1].linkInSrc(*this);
        //acs[0].setName("acs_0"); // gives error: 'void OSCAudioBase::setName(const char*)' is inaccessible
        //acs[1].setName("acs_1"); // gives error: 'void OSCAudioBase::setName(const char*)' is inaccessible
        acs[0].connect(wf1,0,mix,0);
        acs[1].connect(wf2,0,mix,1);
    }
};
Sorry - I really must go round and make all the internal functions private or protected: linkInSrc() is not intended for the user... clearly I got it right for setName()!

You're right, the dynamic audio library does allow creation of an AudioConnection without specifying what it's connected to. The OSCAudio library does not allow an OSCAudioConnection to be created without a name, because you might (as you have sort of done) create duplicate NULL names. There may be a case for allowing it, though I'd have to keep all the anonymous objects on a separate list somehow, until they got names. Not sure it's worth the effort.

Your Test class is a hybrid of OSCAudio and ordinary objects - I'm not sure if you meant to use e.g. OSCAudioSynthWaveform? Actually, it would sort of work with AudioConnection objects, I think. They would be invisible to the OSC system, though, but actually for a class like this there's perhaps no need to access them. It depends on whether your intent is to re-connect during operation; you could still do it, but not using OSC.

I think I've got the grouped class about as simple as it can be in post #262. If you think there's merit in looking further then I can try to do it, but there are probably better things I could work on ... like getting the access specifiers sorted out ;)
 
You don't need to do the grouped class simpler. :)

The test class was just a example and I did not intend to mix OSC and ordinary objects.
 
:D

But this is obviously extra work

Do you mean the above 'owns its in/out connections' quote?
or the bus connections + live edit?

by the way I can not find SLIPEncodedTemplateSerial.h anywhere?


also here is my PlatformIO project branch using the latest stuff

Code:
https://github.com/manicken/OSCAudioPlatformIO/tree/UsingNewestOSCAudio

git clone -b UsingNewestOSCAudio https://github.com/manicken/OSCAudioPlatformIO.git OSCAudioPlatformIO_manicken

note. using platformio
here is the folders you need to replace
(userhomedir)\.platformio\packages\framework-arduinoteensy\cores
(userhomedir)\.platformio\packages\framework-arduinoteensy\libraries\Audio


there will soon be a new post about an idea that I have
regarding the AudioConnections

but thinkin it was better to post this first
 
But this is obviously extra work, and it's definitely secondary to getting bus connections and live editing working!

Now I did read that again,
what you really mean is that:
it's higher priority to get the bus connections and live editing working.


Then the following things are just suggestions that I did have and did get while reading the following 'quote'

I prefer an approach where monovoice owns its in/out connections and provides functions to connect them

Yes I can see the benefit of doing so, at least for the inputs when multiple destinations exists inside the class
for the output I can see a problem when connecting one class to annother,
then there needs to be a getOutput function that returns a struct (simplified) {source, sourcePort}

all that thinkin make me wonder could this be possible also at the OSC connections?

If we can extend the AudioConnection class to take a array for the destinations

first the struct (a workaround to for array of references :) ):
Code:
typedef struct audio_destination_struct {
    AudioStream &destination;
    unsigned char destinationInput;
} audio_destination;

then I did some experiments by extending the AudioConnection class
and after seeing the output of the following:
Code:
for (int i=0; i < destinationCount; i++) {
    dst = &((*_destinations).destination);
    dest_index = (*_destinations).destinationInput;
	_destinations++;
    result = connect();
    Serial.printf("connect():%d\n", result);
}
the output was (simplified): 0 1 1
by looking at the connect source that actually means the connection has allready been made
then I realized, the current implementeation of AudioConnection maybe don't allow multiple targets?

here is my experiments:
https://github.com/manicken/OSCAudi...estOSCAudio/AudioStream_usingAudioTargets.cpp
https://github.com/manicken/OSCAudi...ewestOSCAudio/AudioStream_usingAudioTargets.h

and the usage for the above tests:
Code:
AudioSynthWaveformModulated wfm1;
AudioSynthWaveformModulated wfm2;
AudioSynthWaveformModulated wfm3;
AudioSynthWaveform LFO;
audio_destination dests[3] = {{wfm1,0}, {wfm2,0}, {wfm3,0}};
Serial.printf("sizeof(dests) %d", sizeof(dests)/sizeof(audio_destination));
AudioConnection *ac = new AudioConnection(LFO, 0, dests, sizeof(dests)/sizeof(audio_destination));


and maybe it's better to just do a second class instead.


Code:
#include <Audio.h>

typedef struct audio_connection_struct
{
    AudioStream &stream;
    unsigned char portIndex;
} audio_connection;

class AudioConnectionExt
{
    public:
    audio_connection *dests;
    unsigned char destCount;
    AudioConnection **acs;

    AudioConnectionExt(audio_connection destinations[], unsigned char destinationCount)
    {
        dests = destinations;
        destCount = destinationCount;
        acs = (AudioConnection **)malloc(destCount*sizeof(audio_connection));
    }
    ~AudioConnectionExt() {
	    disconnect();
        delete dests;// are theese the proper ways of doing it
        delete acs; 
    }

    void connect(AudioStream &source, unsigned char sourcePortIndex)
    {
        for (int i=0;i<destCount;i++)
        {
            acs[i] = new AudioConnection(source, sourcePortIndex, dests[i].stream, dests[i].portIndex);
        }     
    }
    
    void disconnect () {
        for (int i=0;i<destCount;i++)
        {
            acs[i]->disconnect();
        }
    }
};

voice class:
Code:
class Voice {
public:
  AudioSynthWaveformModulated wfm1;
  AudioSynthWaveformModulated wfm2;
  AudioSynthWaveformModulated wfm3;
  
  // the following actually compiles
  audio_connection dests[3] = {{wfm1,0}, {wfm2,0}, {wfm3,0}};
  
  // sizeof(dests)/sizeof(audio_connection) is probally overkill but allows the size of dests to be changed in one place
  // If I where to use this style in my exports then everything is 'hardcoded' anyway
  // and I would just simply put 3 (in this case) instead
  AudioConnectionExt &LFOIn = *new AudioConnectionExt(dests, sizeof(dests)/sizeof(audio_connection)); 
  
  // some other experiments:
  // this don't compile
  //AudioConnection ac1(wfm1,0,wfm2,0);
  
  // but this do 
  AudioConnection &ac1 = *new AudioConnection(wfm1,0,wfm2,0);
  
  Voice() {
      
  }
};

usage:
Code:
AudioSynthWaveform LFO;
Voice voice;
voice.LFOIn.connect(LFO,0);


Also I have a suggestion
inside the OSCAudioBase.cpp
there is the findMatch function
and by the looks of it there is
this parameter: OSCAudioBase* ooi

can that be used to allow audio connection patchs to referee to the location where
the AudioConnection is made?

so instead of:
Code:
("/teensy*/dynamic/crCo", "ss", "wav_i1_0_mixer_25", "/stereovoice4/mvL")
("/teensy*/audio/stereovoice4/mvL/wav_i1_0_mixer_25/co", "sisi", "/stereovoice4/mvL/wav/i1", 0, "/stereovoice4/mvL/mixer", 25)

if could be:
Code:
("/teensy*/dynamic/crCo", "ss", "wav_i1_0_mixer_25", "/stereovoice4/mvL")
("/teensy*/audio/stereovoice4/mvL/wav_i1_0_mixer_25/co", "sisi", "./wav/i1", 0, "./mixer", 25)

in the combination of my class and the above suggestion
instead of having:
Code:
("/teensy*/dynamic/crCo", "s", "LFO_0_monovoice_i0_wavA_0")
("/teensy*/audio/LFO_0_monovoice_i0_wavA_0/co", "sisi", "/LFO", 0, "/monovoice/i0/wavA", 0)
("/teensy*/dynamic/crCo", "s", "LFO_0_monovoice_i1_wavA_0")
("/teensy*/audio/LFO_0_monovoice_i1_wavA_0/co", "sisi", "/LFO", 0, "/monovoice/i1/wavA", 0)
("/teensy*/dynamic/crCo", "s", "LFO_0_monovoice_i0_wavB_0")
("/teensy*/audio/LFO_0_monovoice_i0_wavB_0/co", "sisi", "/LFO", 0, "/monovoice/i0/wavB", 0)
("/teensy*/dynamic/crCo", "s", "LFO_0_monovoice_i1_wavB_0")
("/teensy*/audio/LFO_0_monovoice_i1_wavB_0/co", "sisi", "/LFO", 0, "/monovoice/i1/wavB", 0)

we could have:
(the third parameter of crOb AudioConnectionExt is allways a path to a group,
there is no point of having such connection objects in the root?)
Code:
("/teensy*/dynamic/crOb", "ss", "AudioConnectionExt", "LFOIn", "/monovoice/i*", "./wavA", 0, "./wavB", 0)
("/teensy*/audio/monovoice/i*/LFOIn/co", "sisi", "/LFO", 0)

if the audioconnections did just allow ./ it could result in the following:
(little longer)
Code:
("/teensy*/dynamic/crCo", "s", "LFOIn_0_wavA_0", "/monovoice/i*") // this I believe is possible?
("/teensy*/dynamic/crCo", "s", "LFOIn_0_wavB_0", "/monovoice/i*") // this I believe is possible?
("/teensy*/audio/monovoice/i*/LFOIn_0_wavA_0/co", "sisi", "/LFO", 0, "./wavA", 0)
("/teensy*/audio/monovoice/i*/LFOIn_0_wavB_0/co", "sisi", "/LFO", 0, "./wavB", 0)

Theese are just suggestions
 
Brief response - I should have some time at the weekend...

SLIPEncodedTemplateSerial.h is something I threw together to make it easier to use non-standard serial ports; you shouldn't need it, but I've put it on my OSC fork in the feature/SLIPtemplate, if you want to try it out.

[OSC]AudioConnection objects can only have one source and destination, true. So a different approach would be needed for voices where you want multiple destinations - not sure about an "array connection", though. Maybe a voice member function that returns the object and port which is "the" output ... but then, what about multi-output voices?! Need to read your posts more thoroughly, anyway...
 
On my two last 'code' parts
I know I did miss some of the OSC-packet-argument-types
(but it's the actual values that matter)


For the c++ code generation (this is used on basic objects for simplification):
A solution to the multi/single output class/module is to allow 'half' AudioConnections
that will 'hide' the internals of the voice class, and also then the connections belongs to the class
my AudioConnectionExt.h including the new AudioHalfConnection class:
Code:
#include <Audio.h>

#ifndef AUDIOCONNECTIONS_EXT_H
#define AUDIOCONNECTIONS_EXT_H
typedef struct audio_connection_struct
{
    AudioStream &stream;
    unsigned char portIndex;
} audio_connection;


class AudioHalfConnection
{
  public:
    // nested class to allow either a MultInputs to connect to AudioHalfConnection or
    // AudioHalfConnection to connect to a MultInputs
    class MultDest
    {
      private:
        audio_connection *dests;
        unsigned char destCount;
        AudioConnection **acs;
      public:  
        bool connected = false;
        MultDest(audio_connection destinations[], unsigned char destinationCount)
        {
            dests = destinations;
            destCount = destinationCount;
            acs = (AudioConnection **)malloc(destCount*sizeof(audio_connection));
        }
        ~MultDest() {
            disconnect();
            delete dests;// are theese the proper way of doing it
            delete acs; 
        }
        int connect(AudioStream &source, unsigned char sourcePortIndex)
        {
            if (connected == true) return 1;
            for (int i=0;i<destCount;i++)
            {
                acs[i] = new AudioConnection(source, sourcePortIndex, dests[i].stream, dests[i].portIndex);
            }
            connected = true;
            return 0;
        }
        int connect(AudioHalfConnection &source)
        {
            if (1 == source._dir) return 2; // this cannot connect to annother input
            return connect(*source._audioStream, source._audioStreamPortIndex);
        }
        void disconnect () {
            for (int i=0;i<destCount;i++)
            {
                acs[i]->disconnect();
            }
        }
    };

  private:
    AudioConnection *ac;
  public:
    AudioStream *_audioStream;
    unsigned char _audioStreamPortIndex;
    unsigned char _dir;
    bool connected = false;

    AudioHalfConnection(unsigned char dir, AudioStream &audioStream, unsigned char audioStreamPortIndex)
    {
        _dir = dir;
        _audioStream = &audioStream;
        _audioStreamPortIndex = audioStreamPortIndex;
    }
    ~AudioHalfConnection() {
        disconnect();
    }
    
    int connect(AudioStream &audioStream, unsigned char audioStreamPortIndex)
    {
        if (true == connected) return 1;
        if (_dir == 0)
            ac = new AudioConnection(*_audioStream, _audioStreamPortIndex, audioStream, audioStreamPortIndex);
        else // should be 1 but as failsafe allow everything
            ac = new AudioConnection(audioStream, audioStreamPortIndex, *_audioStream, _audioStreamPortIndex);
        connected = true;
        return 0;
    }

    int connect(AudioHalfConnection &other) { // this allows classes to connect together
        if (_dir == other._dir) return 2; // cannot connect similar connections together
        if (true == other.connected) return 1; // other is allready connected to something

        return connect(*other._audioStream, other._audioStreamPortIndex);
    }

    int connect(MultDest &other) {
        if (1 == _dir) return 2; // cannot connect a input to a input
        if (true == other.connected) return 1; // other is allready connected to something
        
        return other.connect(*_audioStream, _audioStreamPortIndex);
    }
    void disconnect()
    {
        ac->disconnect();
    }
};
#endif

usage
Code:
class StereoVoice {
public:
    AudioSynthWaveformModulated wav1;
    AudioSynthWaveformModulated wav2;

    audio_connection LFOIn1_dests[2] = {{wav1,0}, {wav2,0}};
    AudioHalfConnection::MultDest &LFOIn1 = *new AudioHalfConnection::MultDest(LFOIn1_dests, sizeof(LFOIn1_dests)/sizeof(audio_connection));
    audio_connection LFOIn2_dests[2] = {{wav1,1}, {wav2,1}};
    AudioHalfConnection::MultDest  &LFOIn2 = *new AudioHalfConnection::MultDest(LFOIn2_dests, sizeof(LFOIn2_dests)/sizeof(audio_connection));

    AudioHalfConnection &stereoOutL = *new AudioHalfConnection(0,wav1,0);
    AudioHalfConnection &stereoOutR = *new AudioHalfConnection(0,wav2,0);

    StereoVoice() {

    }
};

// in 'main' file
AudioSynthWaveform LFO1;
AudioSynthWaveform LFO2;
AudioMixer4        mixer;
StereoVoice        svoice1;
StereoVoice        svoice2; // just to show 'class to class' example
StereoVoice        svoice3; // just to show 'class to class' example

setup()
{
    svoice1.LFOIn1.connect(LFO1,0);
    svoice1.LFOIn2.connect(LFO2,0);
    svoice1.stereoOutL.connect(mixer,0);
    svoice1.stereoOutR.connect(mixer,1);
    
    svoice2.LFOIn1.connect(LFO1,0);
    svoice2.LFOIn2.connect(LFO2,0);
    // the following two maybe makes no sense but are
    // just to show 'class to class' example
    // either this way
    svoice2.stereoOutL.connect(svoice3.LFOIn1);
    svoice2.stereoOutR.connect(svoice3.LFOIn2);
    // or this way
    svoice3.LFOIn1.connect(svoice2.stereoOutL);
    svoice3.LFOIn2.connect(svoice2.stereoOutR);
    // which would be the most logical?
}


Then I have a 'question' about your OSCAudioBase code
I find your callBack function name confusing
as it's only purpose is to find objects?.


I did release the new improved Design Tool++ "ver5"

Anyway I will now start coding on the other improvements needed for the OSC live edit.
 
Last edited:
here is a additional constructor to the AudioHalfConnection
Code:
AudioHalfConnection(AudioHalfConnection &other) {
    _dir = other._dir;
    _audioStream = other._audioStream;
    _audioStreamPortIndex = other._audioStreamPortIndex;
}

that allows classes to be connected direct on a output of another class

consider the following Waves2 class:
StereoVoice_Waves2.png

then this outer class:
StereoVoice_Waves2_OuterClass.png

simplified without inputs:
Code:
class Waves2 {
    AudioSynthWaveformModulated wav1;
    AudioSynthWaveformModulated wav2;
    AudioHalfConnection &OutL = *new AudioHalfConnection(0,wav1,0);
    AudioHalfConnection &OutR = *new AudioHalfConnection(0,wav2,0);
}

class Waves2_outer {
    Waves2 waves2;
    AudioHalfConnection &OutL = *new AudioHalfConnection(waves2.OutL); // here the source and portIndex will be taken from waves2 OutL
    AudioHalfConnection &OutR = *new AudioHalfConnection(waves2.OutR); // here the source and portIndex will be taken from waves2 OutR
}

// Main file/class

Waves2_outer waves2_outer;
AudioMixer4 mixer;
waves2_outer.OutL.connect(mixer, 0);
waves2_outer.OutR.connect(mixer, 1);
 
Last edited:
Back
Top