Struggling with project structure

I’m a long time programmer - with old languages , Assembler , Fortran , C , Java etc etc.
But I’m struggling to find out from the docs how to structure a modular project.
I’m thinking for example of gui modules and synth modules. The way I think is to write synth modules then stash each one in some kind of library - to be invoked from guis - then just execute the gui.
Cheers - jjhm

1 Like

this question might be a bit left-field, but i’m curious — how long have you been using supercollider and what artistic goals are you trying to achieve?

As far as I know there is no require or import or include in sclang.
(May be someone else corrects me)
The only way to go are SC Classes files that are loaded on interpreter init.
http://doc.sccode.org/Guides/WritingClasses.html

I’ve published probably over 10,000 lines of code written using object prototypes – using object-oriented design, but with “soft” objects that can be loaded any time during a session.

Prototype-based programming is not quite as transparent, and it’s slightly slower, but the last 14 or so years that I’ve been working with it suggest that it’s definitely a practical and well-functioning approach.

ddwChucklib-livecode, a live coding dialect for SC, implements its parser completely using prototypes (including a complete abstract syntax tree), no hardcoded classes.

So it’s not true that class files are the only way to go.

hjh

I’m not sure what you mean by how long - do you mean 1)since my first use or 2)how long in man days. if #1 then a few years. if #2 then my use has been in bursts. Up to now I have followed the templates in the examples - for instance with guis and synthdefs in the same file - then executing blocks of code within those files.
Please indicate how the artistic goals to relate the subject of my question - maybe I’m missing something. I want to use guis to trigger and modify samples and synths .

1 Like

Please indicate how the artistic goals to relate the subject of my question - maybe I’m missing something. I want to use guis to trigger and modify samples and synths.

It helps the answers to be more specific if we have some more details about what you’re trying to do. Like, I could spend the next hour writing a really long message about topic A and then find out that’s totally irrelevant to your objective.

Help us to help you more efficiently.

It occurs to me just now that some of the stuff in my ddwChucklib quark might help you. The relevant design goals in chucklib were:

  • “Soft” objects: object-oriented modeling without the requirement to load all classes at startup. You can load the modules you need by "/path/to/definitions.scd".load;.

  • Automatic constructors/destructors: I wanted to be able to invoke a module with a simple command, and it takes care of its own initialization, and also remove a module (and it cleans up its own resources). “I want to use guis to trigger and modify samples and synths” – something has to load the samples. Better to make that part of the module rather than something external.

  • Binding an event player (a pattern) to its resources. Instead of a workflow of “create output busses, sample buffers etc. as freestanding objects and then reference them in patterns,” I prefer modeling a musical behavior in terms of a process object, where the process owns all the resources and the pattern.

  • Global storage.

In a very high-level outline, you would create a prototype for an abstract module. Then you can clone the prototype for specific modules. To use a module, you would instantiate the prototype (at which point, it runs its constructor).

(
SynthDef(\bufGrainPan, { |start, time, bufnum, pan, rate = 1, amp = 1,
		attack = 0.001, decay = 0.02, outbus|
	var sig;
	sig = PlayBuf.ar(1, bufnum, rate * BufRateScale.kr(bufnum), 1, start, 0)
		* EnvGen.kr(Env.linen(attack, time, decay), doneAction:2);
	OffsetOut.ar(outbus, Pan2.ar(sig, pan, amp));
}).add;

// abstract module
// I'll use a starting point defined in chucklib
// PR is a "Process prototype"
PR(\abstractProcess).clone {
	~path = Platform.resourceDir ++ "/sounds/a11wlk01.wav";  // default, can override

	// constructor
	~prep = {
		~buf = Buffer.read(s, ~path);
		// other things: mixing framework, GUI controls, etc.
		currentEnvironment  // return "self"
	};

	// destructor
	~freeCleanup = { ~buf.free };

	// the player
	// which event prototype?
	~event = (eventKey: \default, instrument: \bufGrainPan);
	~asPattern = {
		Pbind(
			\bufnum, Pfunc { ~buf },
			\dur, BPStream(\dur),
			\time, Pkey(\dur) / Pfunc { thisThread.clock.tempo },
			\pan, BPStream(\pan),
			\rate, BPStream(\rate),
			\start, BPStream(\start)
		)
	};

	~dur = 0.5;
	~pan = Pwhite(-1.0, 1.0, inf);
	~rate = Pexprand(0.5, 2, inf);
	~start = Pwhite(0, 5000, inf);
} => PR(\sampleModule);
)

s.boot;

// instantiate
PR(\sampleModule) => BP(\s1);

// or, to load a different sample, you could use the "long form"
// PR(\sampleModule).chuck(BP(\s1), nil, (path: "......."));

BP(\s1).buf  // yep, it's there

BP(\s1).play;

// new rhythm, on the fly
BP(\s1).dur = Pexprand(0.05, 0.8, inf);

BP(\s1).stop;

s.dumpOSC(1);  // to prove buffer goes away
BP(\s1).free;

// prints:
-> BP('s1')
[ "/b_free", 0, 0 ]  // buffer's gone

s.dumpOSC(0);

With some care, you can also implement MVC-style GUIs (using one BP for a module – the model – and another as the controller containing the views).

I’ve been using this strategy in some form or another for about 14 years now.

hjh

2 Likes

Right - thanks for that.
Background = I’ve been a programmer for > 50 years ( first prog at uni in '64) and I’m used to writing small modules that go into libraries or on search paths to build up bigger projects - anything from device drivers to business simulations.
I have 2 current projects that are relevant.
1 - Over the last year or so I developed ( non commercially - I retired 10 years ago) for somebody else’s research project a system to allow collaborative music composition. It allowed several composers to use - in real time - Android clients to select music fragments. These were synchronised real time by a windows server and the results were played either on the client(s) or server so they could ‘jam’ jazz style - that’s one project
2 - I have a small PC based recording rig that I use for my folk music. Sometimes I ‘mix’ multiple music and sound effects to listen to during my after-lunch siesta/OM session.
3 - OK that’s more than 2 but some years ago I did a masters in evolutionary robotics and one of the opt in courses was generative music. Over the last year I’ve been getting into EDM ( much to the amusement of my adult sons - I’m 75) and I fancy playing with some generative EDM.

I’d like to experiment with implementing bits of these projects in SC. I have a few more vapourware projects but I want to build up a library of re-usable modules - and as I said I didn’t find the docs particularly illuminating on the recommended method.

I’m unfamiliar with the style bunging lots of fragments in a single file ,then - I assume - selectively executing the fragments. Maybe it’s age related mental inertia.

Thanks so much for the example - I’ll go through it later . Today I need to start chopping up some wood - a couple of my trees blew down last year and I want to get some wood ready for winter.

2 Likes

To add a note on this: there’s a built-in gui, that just does that: makeGui. Besides you could consider using VarGui from the miSCellaneous_lib quark. An example for both is given here (as a side note in a thread with a different topic):

Concerning the storage of data, I often did it like this (but others might disagree): store SynthDefs in one or more scd files and load these from the startup file ( Sclang Startup File | SuperCollider 3.12.2 Help ). If you go on to work on a different project you can easily outcomment load messages in the startup file.