Taking some baby steps making my first module. The goal is to implement a Brownian Bridge, that simulates the stochastic ODE
dy = (y_f - y)/(t_f - t)dt + r dB
starting from an initial value of y, where y_f is a final value and t_f is the final time. dB is a Brownian noise process increment.
The code for my first stab at this is at:
It more or less does what I want but I am sure I am making lots of mistakes and there is a lot of room for improvement. Any constructive comments are welcome.
This is exactly what I was looking for!
Would you share your mac build?
Haha, only this community will you get this response after someone writes a chaotic ODE noise maker.
Welcome! And neat stuff!
A few thoughts which may help from browsing your repo:
I notice you have Brownian as a subdirectory of your repo. Most repos have Makefile and plugins.json in the top directory and then all their plugins in the source which is a direct subdirectories of the root. This allows you to meet the requirement (or perhaps convention) of “git clone plugs; cd plugs; git submodule update; make” so perhaps a bit of file shuffling. Will make it easier to submit to the community manager also. If you write a second plugin it can use the same makefile and stuff; basically one plugin.json and Makefile per ‘bundle’ of modules.
I notice you have your dist and dylib checked into your repo. Most folks who do distributions outside the community manager put their binaries in GitHub releases rather than in their source repo, and putting binaries in your source repo will make checkout times go up. Lots of ways to do this but here’s one using azure pipelines which is x-platform and automatic. Azure Pipelines for Github allows build of plugin
You may want to consider CV control of your params? So have both an input and a param for your rates and stuff and then read (and appropriately scale) the CV and add it to your param. That way you can change the speed with an LFO and the like.
Seems this module would be easy to make polyphonic, if you trigger polyphony by the polyphony of the gate trigger, and that might be useful? Then if you do have a polyphonic gate voila. In this case it would really just be a matter of doing something like
chans = std::max( 1, inputs[GATE].getChannels() and then looping over chan and setting your output polyphony. You could use the midi input module in polyphonic mode to generate a polyphonic gate for instance.
Hope this is helpful!
Very helpful, thank you. That is exactly the kind of feedback I need.
At the top you have all your variables as global variables. This is very bad, because if you have two instances of your module they well stomp on top of each other. Make these be member variables of you Module struct/class.
Cool! I will be improving it soon, hopefully this week, but if you really want to check out this crude version I made a release: https://github.com/mhampton/ZetaCarinaeModules/releases/tag/0.1
Among baconpaul’s suggestions I will definitely add more CV controls for the parameters, and try to make it polyphonic, both of those should be pretty straightforward. I think I need a broader range of timespans too, maybe a fast/slow mode like some LFOs have.
Also, don’t put the build products in git, for the same reason not to put the compiled modules in there. You need a .gitignore file to make it easy to keep unwanted folders and files out of git. Look at some other repos to see what they put in theirs.
Obviously the panel is just thrown together, but when you do it for real the output jack should be visually differentiated from the input like most modules. It’s also difficult to tell which title goes with which knob. It’s inconvenient to have a module name that requires a line break in it. Probable when re do it you can put the input and output next to each other, freeing some vertical space.
Very important note this one. Instance variables not globals for almost everything is key. The times you need a true global are almost vanishingly small in a module implementation (other than the model object obviously)
Times and the like are often modeled as 2^p values and perhaps that would work here also to get your range? The voltages doc has info on cv to bpm so you could perhaps use that to calibrate? Or a slow mode fast mode sure could work also.
I wonder if you want to be able to provide a seed and have a rested trigger to be more deterministic? Also your algo is very sample rate dependent. That’s a bit trickier to solve but perhaps you want to run your integrstor at a constant dt and then sample it into sampletime chunks. Or at least check if the result is kinda the same at 96k as 44.1
Ah thanks. The input/output next to each other is a good idea. I am a beginner at Inkscape but I will definitely try to improve the layout and appearance.
Yes, and a very common mistake!
@baconpaul is quite right here. Parameters that’s deal with speed and time and such usually are run through some kind of exponential function to make them feel more natural. This lets you get a large range and still have the knob be controllable. The idea is that you perceive these things on a log scale so having the knobs model perception is much better all around. If you don’t do this you tend to end up with knobs where all the stuff is at the very bottom, and everything after 3:00 sounds the same. That’s why the volume control on a stereo (I know, people don’t have these any more) is “audio taper”, not linear, many modules use a low order polynomial rather than an exponential, for various reasons. Look at the code in other modules to see more of this in action.
What a wonderful module!
Thank you so much!
Please keep in mind that some developers here even do not unterstand what its possible to do with their own software!
Modulations of the parameters work great with the Stoermelders Map modules btw.
Oh, one more thing. Not really important here, but in general… I notice that you do std::sqrt(args.sampleTime) every process call. std::sqrt might be kind of a slow function. So you should measure the performance of your plugin, or not use it so much. when you make this polyphonic it’s going to multiply the number of times you do this.
Note that the sample rate rarely changes, so every time you call this (44100 times per second) you are going to get the same answer back. Why not hold on to the square root and only update it when the sample rate changes? You could override Module::onSampleRateChanged (or whatever that function is called), or just look at the sample time every process call.
Here’s a paper I wrote a year or two ago that covers this kind of thing: https://github.com/squinkylabs/SquinkyVCV/blob/main/docs/efficient-plugins.md
Thanks, I’ll take a look at your paper. Once things are in a more final form I will work on the efficiency. I was actually surprised that my first attempt used less than 1% CPU, I thought it would be awful.
I teach an introductory numerical analysis course so I am hoping to find some interesting examples of efficient computation in VCV (e.g. interpolation or FFT) although maybe most of that is in DSP libraries.
The VCV SDK has a small but growing DSP library included. Well worth checking out. I think there is now an FFT in there. I know there is a simple utility in there for doing a linear interpolating between two numbers, too.
btw my goal is usually to have all my modules each take at most 1%, because 100 modules in a patch, each taking 1% is… Also you’ve probably noticed the CPU meters aren’t insanely accurate. One easy thing is to boost your sample rate to 16X so the numbers are all bigger.
So I do some VCV stuff but mostly work on surge (https://surge-synthesizer.github.io/). We are implementing an MSEG modulation source and you inspired me to add a brownian bridge MSEG segment this afternoon. Seems I can’t embed an animated gif in the community server here but you can see it https://cdn.discordapp.com/attachments/744324663383031821/750818873493815296/BrownianSegment.gif if you want. Fun!
OK, I’ve made quite a few of the changes suggested:
- Tried to clean up the repository with just the source needed, made a .gitignore file
- Added CV inputs for all of the parameters
- Gave the output a black background to indicate its an output
- Tried Squinky.Labs’ suggestion of sqrt efficiency
- Converted the time control to exponential
- Got rid of the global variables
I have tried to make it polyphonic, once I do that and make it slightly more visually attractive I may move onto an idea I find more exciting, a kind of distortion/filter effect using driven van der Pol and other chaotic oscillators.
Thanks for all the help so far everyone!