ofxPDSP - audio DSP and generative music addon


#1

hello everyone! i’ve just released ofxPDSP, an audio synthesis addon/library for oF

some quick screencasts of the examples




#2

Hey, this looks like a really great addon, however, I’m getting a ton of build errors running of 9.0 on Mac OS X 10.10.4.

My C++ chops leave a lot to be desired so any help is much appreicated.


#3

to me it seems that ofxPDSP isn’t finding ofxSIMDFloats. ofxPDSP requires both ofxSIMDFloats and ofxMidi, you have to add them as addons to your build.
PS: As it seems you want to make some grainy thing, on OSX the SampleBuffer don’t load wav natively still without linking libsndfile or using ofxAudioDecoder and this load method (it is on top on the TODO list to add native wav loading in some way).


#4

Thanks for replying Nicola, I’ve included both addons but I’ve noticed you’ve used relative paths to the headers. I’ll check my directory structure!

And I was aware of the file loading issue on OS X, thanks for the link.


#5

So I’ve managed to get a demo project to build but I’m not getting any audio out.

`[notice ] ofxMidiOut: 1 ports available
[notice ] ofxMidiOut: 0: IAC Driver Bus 1
[ofxPDSP]: connected to midi port 0

RtApiCore::getDeviceInfo: system error (CoreAudio unknown error) getting sample rate info.

RtApiCore::getDeviceInfo: system error (CoreAudio unknown error) getting sample rate info.

RtApiCore::getDeviceInfo: system error (CoreAudio unknown error) getting sample rate info.

RtApiCore::getDeviceInfo: system error (CoreAudio unknown error) getting sample rate info.

[notice ] ofSoundStreamListDevices:
[0] Apple Inc.: Built-in Microphone [in:2 out:0] (default in)
[1] Apple Inc.: Built-in Input [in:2 out:0]
[2] Apple Inc.: Built-in Output [in:0 out:2] (default out)
[3] ma++ ingalls for Cycling '74: Soundflower (2ch) [in:2 out:2]
[4] ma++ ingalls for Cycling '74: Soundflower (64ch) [in:64 out:64]
[5] Apple Inc.: After Effects 11.0 [in:0 out:0]
[6] Apple Inc.: After Effects 11.0.1 [in:0 out:0]
[7] Apple Inc.: After Effects 11.0.4 [in:0 out:0]
[8] Apple Inc.: Premiere Pro 6.0 [in:0 out:0]

[ error ] RtApiCore::probeDeviceOpen: the device (0) does not support the requested channel count.`

Any idea what might be causing this?


#6

ofxPDSP uses ofSoundStream. In your code at the line that displays

audioStream.setDeviceID(0);

you have to set 2 ( shown as Apple Inc.: Built-in Output [in:0 out:2] in your list), otherwise you are trying to open the built-in microphone as audio output


#7

v0.1.1 changelog
- added native audio loading on OSX using libaudiodecoder
- corrected behavior for example-graincloud
- added meter function to CRSlew


#8

Great! this seems to be very promising!. I’ll take a look at it right now.
You should take a look at this addon I made


It would be great to have both addons working together.

all the best!


#9

I’m just looking at the object, all the objects are already implemented in ofxPDSP:
DigitalDelay ----> pdsp::DampedDelay or pdsp::MainDelay
NoiseGenerator ----> pdsp:PRNoiseGen
LowPassFilter ----> pdsp::OnePole (it offers both 1 pole LPF and HPF), pdsp::BiquadLPF2, pdsp::MultiLadder4, pdsp::SVF2
SineWaveGenerator ----> pdsp::SineFM
ofxSoundFile —> pdsp::SampleBuffer ( i just need to make it work with libaudiodecoder on win)

for mixing there is no need for a mixer in pdsp as when you patch many things to the same input they are automatically summed. For example if you have declared the objects

pdsp::VAOscillator osc1;
pdsp::VAOscillator osc2;
pdsp::MultiLadder4 filter;
pdsp::Amp amp;

you can do :

osc1.out_saw() * 0.5f  >> filter.out_bpf4() >> amp;
osc2.out_saw() * 0.25f >> filter;

now the oscs are scaled and connected to the filter input, and the filter 4 pole bandpass output is connected to the amp. At the end you connect everything to a pdsp::Processor, when you call the processAndCopyInterleaved() method of the pdsp::Processor all the module connected to the processor channels are evaluated and the result is put into the buffer passed as argument.

anyway if you want you can also mix pdsp together with other systems that process buffer of floats, you can use a pdsp::ExternalInput , and at the end of the processing pdsp always copy its result to a float buffer (usually with processAndCopyInterleaved(), but you can use processAndCopyOutput() for copying to a non-interleaved array of float pointers

I’m looking forward to do a wiki for the basic principles of pdsp next week, i know that things can look difficult without even knowing how the patch operator >> and in_tag() and out_tag() method works.

All the best!


#10

Hi, the addon I made is just a very basic modular sound object.
The digital delay, noise generator, etc are just examples of how to use it. It is not the idea to clog it with infinite dsp objects, instead allowing to easily create new sound objects and to provide a common platform over which to develop and connect with other addons or libraries. That’s why I was telling you that it would be good to make both things work together. I think that the inner workings of both addons are quite much the same, just arrays of floats being passed form one dsp object to the next until it reaches the output.
Your addon is really impressive and fully featured, although I think that the naming of several classes and functions is a bit cryptic, making it a bit harder to grasp for new comers and people not so used to dsp jargon.
I’ll take a look at mixing both addons and let you know.

All the best!


#11

that won’t be that easy without making the steps i wrote above. The “problem” is that pdsp is extremely optimized so its units don’t always process a full buffer of floats. Sometimes when the result should be always the same value the OutputNodes of the units just switch state from “AudioRate” to “Changed” and set just the first value of the buffer, and the connected Units know how to manage buffers running in that state. For managing all those possible states of the inputs templated functions with boolean arguments are used, so that many branch-free function are generated and the right function is loaded each time after processing the inputs. It is a bit convoluted programming in that way, but it generate really fast code (i also use ofxSIMDFloats to process buffer using SIMD to make it even faster).

All the units have just in_tagname(), out_tagname() and meter_metername() methods and they work all the same. I tried my best to make the naming of tags and classes not too much cryptic (and to document everything with doxy) but when necessary i preferred to stick with the DSP jargon (i know, it isn’t the most clear matter).


#12

@npisanti I completely understand what you are saying. Actually it would be great to use that kind of optimizations for ofxSoundObjects. The first approach could be just to have an ofxSoundObject wrapping a whole ofxPDSP dsp chain, thus letting it to connect to other addons/libraries/objects either as in or outputs.
One thing that I would like to get rid of, or at least be able to turn off in ofxSoundObjects is that when the data is passed from one object to the next the floats buffer is copied, in order to avoid corruption or what ever, but it makes it very unoptimal. Right now I’m super busy finishing a project but next week I’ll have some time to throw at this.
I’ll let you know how it goes.
BTW, the ofxSIMDFloats is super nice!


#13

well you could just pass the pointer to the float buffer when copying is not necessary, it is actually what i’m doing in the InputNodes when there is just one input and it hasn’t have to be scaled or clamped to a range (the Units DSP code access InputNode’s buffers as const float *, so there is no way they could change those values).


#14

RIght! I’ll take a look at how you are handling this.
Best!


#15

Hi,
I’m really enjoying the addon. It seems more similar to PD and Max patching than any other gen audio addon I’ve seen which for someone like myself makes things much easier. I’m curious though how difficult it would be to implement other audio effects code? Is that why you have that ‘modules’ folder? Thanks again.


#16

Making “modules” by patching together basic DSPs “units” is really easy, it’s what i made with the classes in the “modules” folder. You already have all the basic building blocks in the form of dsp units, and the third lesson of the tutorials in the wiki explains everything well:


also check the included modules, they are good examples.

Making new DSP is a little more convoluted as you need to be able to patch other units outputs to them. This is because each input node can run at “audio rate” or “control rate” (it just set the first value of the buffer). The state of the inputs is checked before processing, so sometimes to deal with all the different combination i’ve used templated function with boolean values ( so that dead branches are eliminated by the compiler ). Then the output is set with different methods for switching it to audiorate/control rate. You could check the Unit documentation http://npisanti.com/ofxPDSP/classpdsp_1_1_unit.html
and also look at the source code of the different included units.


#17

I’ve worked on the ofxPDSP API lately, and now is a lot simpler than before (there isn’t even need for creating the ofApp::audioOut and ofApp::exit() routines, everything is managed by ofxPDSPEngine). I’ve also added this example of wolfram-generated rhythms and improved the tutorials on the wiki.


#18

Hi there,
I’m trying to build a polyphonic synth going off the example but I want to swap out the midi control part for pdsp::TriggerControl as to have a keypress trigger the tones instead. My thought process was to just swap it out for a a vector of triggers like this:

ofApp.h
vector<pdsp::TriggerControl> gates

ofApp.cpp
        pdsp::TriggerControl temp = pdsp::TriggerControl();
        gate2.push_back(temp);
        gate2[i].out_trig() >> synth2[i].in("trig");

When I do this however xcode throws back call to implicitly-deleted copy constructor Any ideas would be greatly appreciated. Thanks in advance.


#19

Sorry that was my fault. I want to avoid copy-construction but if you have to directly use an Unit into a std::vector you have to declare a copy constructor, so for some Units i declare some dummy copy constructor that sends you a message when you use them and also trigger an assert. I forgot to add a dummy copy constructor to pdsp::TriggerControl, i just did it and pushed to the repo.

Also the correct way to use a vector of units is just to resize it and then patch it, when you make a vector grow all the Units are constructed and when you make it shrink they are deleted. So the correct code shoudl be:

ofApp.h
        vector<pdsp::TriggerControl> gates

ofApp.cpp
        gates.resize( synths.size() );
        for (int i=0; i<(int)gates.size(); ++i){
            gates[i].out_trig() >> synths[i].in("trig");
        }

so long story short: fixed in the repo, remember to never use copy constructor on pdsp classes, just resize and patch.


#20

I have added a little communication protocol and class to ofxPDSP to make it work easily with arduino and other boards. In the last version of the repo there is also this example that outputs to pwm 5-6, and some other examples i made in those days (expecially more example for explaining the basic workings of pdsp)