Lots of improvements for ofxPDSP (addon for synthesis and generative music)

#21

i think i will deprecate some methods when i have time, deprecated methods will stay a lot in the code for backward-compatibility anyway but at least they will trigger a warning.

As rule a class has in_pitch() is when the input is in semitones and in_freq() when the input is in hertz, EQ and basic building blocks have in_freq(), musical oscillators and filters have in_pitch().

#22

reply to that answer of yours earlier in this thread…

std::vector<pdsp::PatchNode*> nodes;

thanks for that insight… still have problems getting it to work… maybe you can post a simple example?
anyway… great work… thanks for this amazing addon…

#24

are you talking about resizing the vector in realtime, using pointers? maybe it can be done even safer than what i wrote, by using c++ smart pointers

#25

hi nicola…
thanks for your quick reply…
exactly… resizing vectors in realtime… referring to your answer from september '18… did not manage to post my reply right below…
i try to use your addon for an interactive installation where soundobjects can be added during runtime… so like everytime you click the mouse a new node (with a little higher pitch) is created… sounds are then triggered by a moving scanline… or contours from the kinect…
still learning/figuring out your examples…

#26

to add or remove objects by pdsp in realtime you should use pointers as i stated above, but it would be hard to me to explain it to you if you don’t know about pointers and allocating / deallocating memory in c++. Have you already read this chapter of the ofBook?
https://openframeworks.cc/ofBook/chapters/memory.html

1 Like
#27

i did read the chapter about memory as you already recommended this earlier… but i have to admit that i’m not considering myself as such an advanced programmer to fully understand this topic… specially the last chapter about smart pointers gets a little confusing…

#28

it is a bit confusing because it tries to explain why using raw pointers is such a bad move, but i still have to study smart pointers so i couldn’t explain it better ( it’s in my TODO list ). Anyway you can do the same with raw pointers, when a new object is needed you allocate with

  yourvector.push_back( new YourObject() ); 

and when is not needed anymore you have to rememeber to delete it, otherwise you will leak memory:

    delete yourvector.back();
    yourvector.resize( yourvector.size() -1 );
#29

also: if you are making this to play a new note each time the mouse is clicked, this wouldn’t be the best solution, allocating and deallocating memory is a really expensive operation for the computer, in that case allocate a reasonable number of voices in advance and then use the mouse click to trigger on/off the envelope of one of the voices

1 Like
#30

thanks a lot…
right now re-reading the memory chapter again…
also checking your example_basics5_channels and probably use this as a starting point…
is there a maximum of voices playing at the same time you recommend? does this depend on the computers memory?
specially when the code should also work on a rPi…
or is the number of voices related to the soundcard used?

#31

the number of voices that can play together is related mostly to the cpu and to the complexity of the voice architecture. The number of available voices can be a lot higher and it is only bound by memory (as each voice allocate memory for the buffers). I think the best solution would be anyway to have different voices and to trigger them on/off with by having a pdsp::TriggerControl" connected to the trigger input of apdsp::ADSR` controlling the voice amp

    triggerControl >> adsr.in_trig();
                      adsr >> amp.in_mod();
               voicesignal >> amp;

everything in your class, you can use triggerControl.trigger(1.0f) to trigger the voice and triggerControl.off() or triggerControl.trigger(0.0f) to release the voice, and then you have control about the fade in and fade out of the voice with the adsr attack and release inputs.

1 Like
#32

still working on the vector issue…
i tried both in .h…

std::vector<pdsp::PatchNode*> nodes;
std::vector <shared_ptr<pdsp::PatchNode> > nodes;

in combination in .cpp…

shared_ptr<pdsp::PatchNode> n = shared_ptr<pdsp::PatchNode>(new pdsp::PatchNode);
nodes.push_back(n);

for(size_t i = 0; i < nodes.size(); i++){
	pitches[i] >> oscillators[i].in_pitch();
	oscillators[i].out_saw() >> amps[i] >> nodes[i];
	pitches[i].set(36 + 8*i);
	amps[i].set(0.1f);
}

but the compiler (xcode) doesn’t like this…
how would i combine this >> operator used in ofxPDSP with -> for pointers?

if i do…

pdsp::PatchNode node;

for(size_t i = 0; i < nodes.size(); i++){
	pitches[i] >> oscillators[i].in_pitch();
	oscillators[i].out_saw() >> amps[i] >> node;
	pitches[i].set(36 + 8*i);
	amps[i].set(0.1f);
}

it compiles… but crashes most of the time when i add a 2nd object… for some strange reason it works sometimes… which is even more confusing…

#33

wait, the best thing you can do is to make a class that is representing your whole voice (like in some pdsp examples, like the polysynth one), patching all the things for each voice is doable but it is a bad design.

To access a pointer to pdsp::PatchNode for patching you should use the dereference operator, so

*(nodes[i])

instead of

nodes[i]
1 Like
#34

awesome… that does the trick…
thanks a lot…

1 Like
#35

hello nicola…

i have another question about deleting/erasing a vector of synths…
as you recommended i built a class with a synth… first approach was to control it directly with something like…

amp_ctrl.set(a);
amp_ctrl >> synths[sp_index]->in("amp");

that was producing some noises with fast value changes… so i am using now an additional vectors with amps and pitches… having this in .h…

std::vector <shared_ptr<BasicSynth> > synths;
std::vector <shared_ptr<pdsp::Parameter> > synthsPitches;
std::vector <shared_ptr<pdsp::Parameter> > synthsAmps;

and something like this to create the objects in .cpp…

shared_ptr<pdsp::Parameter> sp = shared_ptr<pdsp::Parameter>(new pdsp::Parameter);
sp->enableSmoothing(50.0f);
synthsPitches.push_back(sp);

shared_ptr<pdsp::Parameter> sa = shared_ptr<pdsp::Parameter>(new pdsp::Parameter);
sa->enableSmoothing(50.0f);
synthsAmps.push_back(sa);

shared_ptr<BasicSynth> s = shared_ptr<BasicSynth>(new BasicSynth);
osc_wave_ctrl >> s->in("osc_wave");
filter_ctrl >> s->in("filter");
filter_cutoff_ctrl >> s->in("filter_cutoff");
filter_reso_ctrl >> s->in("filter_reso");
synths.push_back(s);

these additional vectors for amps and pitches are removing the glitches…
unfortunately i’m getting random crashes when i clear the vectors…

if(key == 'x') {
	synths.clear();
	synthsPitches.clear();
	synthsAmps.clear();
}

strange thing is (at least in my opinion…) that it takes longer (freeze frames) to clear as to recreate the vectors again…
is there a recommended order for deleting the objects? like the vector for the synth class at first then connected pitches/amps?
or has the vector to be empty first? i think delete does not work with shared pointers but at one point i’m using erase…

if (synths.size()) {
	synths.erase( synths.end()-1 );
}

to get rid of one element…
or should i stop/close the pdsp engine bevor clearing the vector?

thanks for your time and patience…

#36

as you are deleting the pitches and amps together with the classes, why not having those objects directly inside the BasicSynth class? also, if you are not using them as parameter inside a gui, maybe pdsp::ValueControl is a better choice

i think you should stop and then restart the engine, as allocating and deallocating pdsp objects is not thread safe (so crashes could happen as you are doing it in the main thread and audio is running in another thread)

#37

thanks for the quick reply…

i already changed to pdsp::ValueControl
is pdsp::Parameter using more resources?

i did try having everything within the synth class… which is super simple based on the module example as i’m still try to figure out basic mechanics…
so now module input amp_ctrl goes directly to the amp… you are suggesting to have a pdsp::ValueControl in between?

#38

Thinking about it pdsp::Parameter isn’t using much more resources, it has just a little extra layer of complexity to keep it binded to an ofParameter, so the easier the better.

you should patch the pdsp::ValueControl objects inside the class

synthAmp >> amp.in_mod();
synthPitch >> osc.in_pitch();

so you can avoid exposing thos outputs outside the class. Also, you can get rid of pdsp::PatchNode if you just need to use a single port of one of the units as module input, for example in your case you could have done

addModuleInput( "amp", amp.in_mod() );

without having an additional amp_ctrl patch node

PS: thanks for using ofxPDSP, hopefully as more user are into it i can know what is clearer and what is not and improve the included examples

#39

thank you for doing this great addon…

unfortunately i cannot get your suggestion to work…

as far as i understood addModuleInput needs a pdsp::Patchnode… or you patch it directly… like amp.in_mod()?

when using pdsp::ValueControl instead of Patchnode xcode compiles fine… but the app complains… [pdsp] warning! patching to invalid input…

then i tried using both…

pdsp::ValueControl synthAmp;
pdsp::PatchNode amp_ctrl;

with…
amp_ctrl >> synthAmp;
results in another [pdsp] warning! patching to invalid input

or…
synthAmp.set(amp_ctrl);
but that does not even compile… saying…
No viable conversion from ‚Äėpdsp::PatchNode‚Äô to ‚Äėfloat‚Äô

the provided examples of yours are great… only the learning curve is maybe a little steep…

#40

you can addModuleInput() directly to one of the pdsp objects inputs, without using a patch node, patch node is when you need one of the module input patched internally to multiple objects.

when pdsp is complaining like that probably you are patching to an input with a wrong tag, if you build your app in debug mode it will generate an assert so you can backtrace to the exact line of code where that thing is happening

set() methods are not for patching, they are just for setting default values that operates when nothing is patched, so you can just give them a float number

#41

ah sorry i didn’t saw well, you did it in the wrong direction, you should do

synthAmp >> amp_ctr;

or better

synthAmp >> amp.in_mod();

and deleting the amp_ctrl from your code
amp_ctrl is a patch node, so basically just an empty point to be used to sum things together or as utility to create a module input that is internally connected to more objects.

Instead pdsp::ValueControl is just a value you can patch to other inputs and set in a thread safe matter, but it has no input (it is just generating an output).