I think it’s hard to make a list of “things I’m doing right.” It may be more useful to approach the question from the other direction – things that are commonly done in a way that isn’t ideal, and suggest alternate approaches.
Off the top of my head:
Antipattern 1: The base SC class should do everything
This is probably a result of the fact that it’s hard enough to learn what’s in the class library, so the natural tendency is to learn that and then struggle through or hack around the limitations. This shows up in a lot of ways:
- SynthDef should handle dynamic structures
- SynthDef or Synth should handle controller mapping
- Patterns should manage resources
What’s often forgotten in this anti-pattern is that, even if SC had a more powerful class to do the more complex job, it would still be necessary to have the base class supporting it.
The solution is some kind of super structure that uses the base classes.
- Dynamic SynthDefs: Use a helper function to generate variants with different names. (A far extreme of this is crucial library Instr/Patch.)
- Controller mapping: In my own libraries, I’ve got GenericGlobalControl which wraps a language-inside value and a server-side control bus into one object.
- Patterns: My own solution is a “process” object that encapsulates a pattern with all of the resources it needs. If the pattern plays onto an audio bus, the process creates the bus (and releases the bus when freed). If the pattern needs a buffer, the process creates and destroys it. Then the pattern is free to do what it does well (put information into events), while there is still a structure binding that pattern to other resources.
Antipattern 2: GUIs should be interchangeable with values
Nope. A GUI is responsible for interaction. It isn’t data storage and it shouldn’t be treated as such.
// naive design: GUI is free-standing
z = Slider(nil, Rect(800, 200, 200, 50)).front;
p = Pbind(
// and the pattern queries the GUI object directly
\freq, Pfunc { z.value.linexp(0, 1, 200, 800) },
\dur, 0.1
).play;
ERROR: Qt: You can not use this Qt functionality in the current thread. Try scheduling on AppClock instead.
Instead, data storage should be in dedicated variables or objects. The GUI feeds information into the data storage, and other tasks can query the stored data freely.
f = 440; // data storage
z = Slider(nil, Rect(800, 200, 200, 50))
.value_(f.explin(200, 800, 0, 1))
// GUI --> data storage
.action_({ |view| f = view.value.linexp(0, 1, 200, 800) })
.front;
p = Pbind(
// pattern queries data storage
// but does not touch GUI
\freq, Pfunc { f },
\dur, 0.1
).play;
p.stop;
That actually leads to the Model-View-Controller design pattern for interfaces – which is definitely not obvious to wrap your head around at first, but it really is the most flexible design. But this post is already a little long, so I’ll leave it at that for now.
There are probably others, but I’ve written enough for the moment.
hjh