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.
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.
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.
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.
@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.
best
jue
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.
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.
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.