Design patterns for simple apps for beginner developers

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

Hello all!

I've just started programming in C++, and have been playing around with Juce, and have got some basic sound working (a metronome!), and was planning on building more things, but as I go along and visualise how all the classes will talk to each other, I see holes in my idea, and I keep thinking of important things like keeping the audio render loop/gui seperate, even though they must talk to each other. My nooby doubt leads me to believe that I am doing things the "wrong" way (it will still work, it's just probably a bit weird).

I was wondering whether anybody had seen some sort of (language agnostic) general tutorials or such where there was a discussion of "If I was building a Drum Sequencer, I would organise it like this....", along the lines of "the foot bone's connected to the ankle bone".

I know that copying the plans of others doesn't lead toward innovation, but just like all game programmers probably start with pong, tetris and breakout clones, I thought there might be a few recommended projects for beginning audio software developers. I was thinking sequencing type stuff initially, but the whole gamut is pretty interesting, no?

Thanks for letting me barge in!

Post

Best I can think of is that you could look at some open source projects. See how they do it.

Only one i can remember of the top of my head is..

http://mda.smartelectronix.com/

But there's probably a bunch of stuff for linux.

Also get yourself a good book on object orientated design patterns if you dont have one already.

Post

How to structure multi-threaded applications is hard to answer.

In principle, it can be done much like a VST plug-in. One object hierarchy for the gui (like a widgte tree), and one for the audio, and some way for them to communicate.

The communication part can be done in a number of ways, none of them good. When a widget at the bottom of the gui tree needs to inform an object at the bottom of the audio tree of something, you are either looking at serial method calls up and down the trees, or the objects has to have some means of talking directly, tying them together. Either can descend into mayhem as the program grows.

Signals, subscribers, listeners etc are design patterns for solving this problem, but they don't in and of them selves. You have to make judgment calls on what and where to expose the trees to each other and apply thread saftying stuff where appropriate. Then use what ever messaging method, or direct mutual knowledge to cross the chasm.

You can also do it microsoft stylee, and let the widgets own their own data, and have the two threads coexisting in the same object tree. If your gui and audio processing graphs match closely it's worth considering. This simplifies communication a lot, but it can be tricky to keep the threading straight.
And if you misjudge the structure and have to branch the gui or audio out away from the unified structure you're back at the communication problem, only now with extra complicated threading.

So, design patterns for communication is a part of the answer, making judgment calls and thinking things through is another part. And perhaps, to expect a certain level of chaos, but to keep hacking anyway is another part.

Post

Rock Hardbuns wrote:Either can descend into mayhem
Woah, already one step ahead of you!

As I've been doing a little more research, I'm realising that it's not just finding a nice little API and hoping for the best. I did manage to put a little drum sequencer together, and was hoping to move onto adding a loop player that would pitch the loop to fit the tempo on the fly, or to make a basic drum synth, neither of which I seem to be able to do easily. I was just hoping for too much from one API. I'm using Juce, and the event stuff seems reasonable (no, I'm not one to make a decent assessment), but it's a little bit reassuring that there is no right way, and we're all making do.

Thanks, kids!

Post

Rock Hardbuns wrote:And perhaps, to expect a certain level of chaos, but to keep hacking anyway is another part.
Agreed! From my experience, I've noticed that the best way to get a good understanding of when and why to use a particular design pattern (style, idiom, etc) is to first have a basic understanding of the concept and then get one's hands dirty coding and making sense of the intricacies.

Post

I'm a semi-competent, self-taught, somtimes programmer. I frequently need to seek advice from my brother, who does this full-time for a living. I've found it very comforting when he explains the "correct" process for application development.

Apparently, the first thing the development team does is to map out the structure of the program, identify classes and methods they will need to create, draw lots of diagrams and hang post-its on the wall. Then they agree to pretend that this somehow reflects the actual program they will develop.

Actual development consists of slogging though code, realizing its all wrong, making endless revisions, revamping the entire architecture repeatedly, and loads of trial and error. People who become successful programmers are those with the stubborn perserverance to keep hacking away in the face of certain failure. :)
Incomplete list of my gear: 1/8" audio input jack.

Post

+1 deastman . What I have been doing as of late is to do mockups in either Synthmaker or Cockos JS language. Cockos JS language is very cool, because you can get fast results and not have to mess with the Gui(knobs,graphics).

You have to be aware of feature creep. Hhehehe, some times a plug ends up with so many features, it looks like the inside of an airplane cock pit.

It's fun to jump in and code, but damn, You have to mockup. Not sure this is entirely on topic, but just thought I would add :)

~Rob.

Post

One way to achieve high flexibility is through the use of 'frameworks'. There are probably lots of other names for them, but that's what I call them.

I'm just getting into the DSP side of things, but I have a huge amount of experience in other areas, and I'd imagine this type of concept would apply well enough in this area as well. They are pretty widely used concepts in OO programming.

Basically the framework is the main body of some sub-system within a program. For instance, let's say it's modelling an analog synth, just to have something to talk about. Someon who has actually done an analog synth may point out that this isn't the best way to do one, but I'm just using it as an example.

The trick is that the framework is written to do nothing in a concrete way. What it does is manage the flow of data through it, from input to output, but along the way it works purely in terms of abstract interfaces (interfaces which don't define the actual functionalty, they use pure virtual methods which are just stubs that must be overridden by some derived class.)

So the framework may define a virtual interface for an oscillator, and one for a filter, and one for chorus and so forth. It knows how to accept the data, pass that data through each of those components of a synth, get the data out, maybe do some standard stuff to it, and pass it out again.

Now, you can derive various types of classes from those virtual interfaces. so you cans have different types of filters, different types of oscillators and so forth. When you construct the synth object, you have to allocate concrete derivatives of each of these virtual interfaces and pass them in to the synth framework. The framework then uses those to do the actual work.

So the framework has pointers to oscillator objects, pointers to some filter objects, etc... each of which (internally within the framework class) is a pointer to the abstract base classes that the framework defined. But when you create the synth framework object, you provide it with real derivatives of those interfaces. It adopts and owns those new objects and sets its pointer members to point to them. So now it operates in terms of those actual oscillators and filters you pass in.

The great thing about this type of system is that it is highly flexible, it avoids redundancy in a way that doesn't require really complicated inheritance hierarchies, and it's dynamic. You can put together a synth of a particular type at runtime, based on configuration, becasue it's not dependent on inheritance in the main class, which is a compile time thing. It's depending on inheritance in a set of small plugin interfaces, which are used by a single framework class, and the particular derivatives of which can be selected at runtime. And it avoids redundancy because the main logic and error handling and so forth of the synth can be reused in many different configurations.

You can of course allow for deriving from the framework class as well, for certain things, or those things could be handled by another virtual interface, again for flexibility. So something error handling, interaction with a driving GUI, etc... are overall framework level things that you can handle in either way.

Hopefully that made sense. You can see a simple example of such a framework in Window's DirectShow architecture. It's basically a 'bus' into which objects 'derived' from some simple abstract interfaces can be plugged. The bus is the framework. It is always the same and just flows the data through the various filters. But the bus can be a lot more powerful than that if you want it to be. It can manipulate the data as it flows between the various plugged in components, make init/term calls on them that set them up for various scenarios, etc...

Of coures a DAW itself is a framework writ large. It's a core framework that defines an abstract interface for plugins, for input sources, for output sources, and so forth, which are dynamically at runtime created from configuration or sensing of host capabilities, and then plugged into the main DAW framework, which then operates in terms of them.

Anyway, it's a highly useful and widely used way of writing object oriented code. I use them extensively throughout my product.
Dean Roddey
Chairman/CTO Charmed Quark Systems, Ltd
www.charmedquark.com

Post

I'm a self-taught programmer that still has a lot to learn.

Leslie Sanford has written a few posts on how he structures different things. They might be useful to read.

One thing I've found to work well is to have a global object that contains tempo, samplerate and max buffer size information. Processing objects are passed a reference to this object when created. Processing objects can also subscribe to be notified of changes. (I think it was one of Leslie's posts that suggested this method).

It's a little bit more involved to set up for the first time but once the plugin template and processes are using the class, samplerate changes etc are all handled automatically.

When communicating between the GUI and audio threads, I tend to use SetParamaterAutomated/GetParameter for any parameters exposed to the host. A downside is all parameters need to be scaled to and from the 0-1 vst parameter range. Otherwise the GUI thread queries the processing objects themselves. It's a bit hodgepodge but seems to work well enough so far.

I use a thread-safe queue for things that can not occur mid-process, such as changing input/output pin counts.

I've not yet found a satisfactory solution for internal parameter automation (LFO's, envelopes, etc).


- Shannon

Post

http://realtimecollisiondetection.net/blog/?p=44

Other than that, I agree with Shannon - anything that Leslie writes. He seems to have a very good sense of structure.
Cakewalk by Bandlab / FL Studio
Squire Stratocaster / Chapman ML3 Modern V2 / Fender Precision Bass

Formerly known as arke, VladimirDimitrievich, bslf, and ctmg. Yep, those bans were deserved.

Post

I try to use the most simple design that could possibly work, and instead spend some effort on trying avoid any heavy-weight structure. The rationale is simply to try to make it easy to change the design later, when requirements change (ie. want new feature that won't work without restructuring). I have projects (including plugins) that have gone through probably 10 iterations of restructuring, yet when the design itself doesn't involve much code, it's just a few hours to turn everything inside-out.

Even if you arrived at the perfect design, you wouldn't want to set it in stone, because sooner or later you will have new requirements that won't fit the old design without lots of ugly kludges which will end up killing your code quality. In such a situation the "design" is doing more harm than good. If the design is simply conceptual, you can often move one part to a new design at a time, which greatly simplifies the restructuring process.

Post Reply

Return to “DSP and Plugin Development”