Cabbage Logo
Back to Cabbage Site

Advice on reusable code

Hi there

Sorry more of a conceptual Csound query really, rather than Cabbage specifically. I’m sure this have been done to death, but I’m trying to come up with a standard way of creating re-usable modular code for my patches. For example, below is an instr definition for a reverb that I can #include, that takes either p-fields or if supplied with a string, creates a k-rate chnget so that it can be controlled externally:

#define SET_CTL_TYPE(VAR'CHNID'PARAM) #
	ilen strlen $CHNID
	if ilen > 0 then 
		$VAR chnget $CHNID
 	else
	 	$VAR = $PARAM
	endif
#

#define REVERBSC_ALT(NUM ' B_IN ' B_OUT ' K_SIZE ' K_DAMP ' K_MIX) #
instr $NUM
prints {{
REVERBSC_ALT =>
B_IN: %s
B_OUT: %s
SIZE: %s, {float} [0..1]
DAMP: %s, {float} [0.. israte/2]
MIX: %s, {float} [0..1]
PTCHMOD: %f, {float} [0..10]
}}, $B_IN, $B_OUT, $K_SIZE, $K_DAMP, $K_MIX, p7

	ainl chnget strcat($B_IN, "L")
	ainr chnget strcat($B_IN, "R")

	$SET_CTL_TYPE(ksize'$K_SIZE'p4)
	$SET_CTL_TYPE(kdamp'$K_DAMP'p5)
	$SET_CTL_TYPE(kmix'$K_MIX'p6)
	iptchmod = p7

	aRevL, aRevR	reverbsc ainl, ainr, ksize, kdamp, sr, iptchmod, 0
	aMixL  	ntrpol ainl, aRevL, kmix
	aMixR   ntrpol ainr, aRevR, kmix
	outs aMixL, aMixR
	chnclear strcat($B_IN, "L"), strcat($B_IN, "R")

endin
#

and it’s called in the main orchestra as:

$REVERBSC_ALT(VerbAlt ' "Verb" ' "" ' "Size" ' "Damp" ' "Mix")
schedule("VerbAlt", 0, -1, .95, 5000, 0.5, .2)

The empty string denotes whether a channel needs to be used, or can be skipped to use a p-field instead (SET_CTL_TYPE macro handles all this). The prints stuff is to document inputs/outputs so I don’t have to open the src file, as I’m a bit lazy!

I did try a udo for the $SET_CTL_TYPE bit to use inline, but I wasn’t having much success with it. I think a macro was the way to go anyhow.

So, what are your thoughts, is this way over engineered? - am I overlooking something obvious, like a better way to achieve my aim of drop in re-usability? (sidenote, I’m inspired by Pd’s abstraction system when I say reusability).

cheers

Personally I avoid macros at all costs, and just use UDOs. They can be included using #include statements. I also find them easier to read and maintain. I think UDOs are the closest to Pd abstractions we have in Csound.

Hi Rory

huh interesting…how would you encapsulate a whole instrument like the above? It might be a poor example given that it wraps a reverbsc and adds a ntrpol for crossfading (mix parameter). But with something more complex say, like one of the grain3 examples, you’d still be doing:

#include "mygrain3opcode.udo"

instr Grain3Example
kp1 chnget p4
kp2 chnget p5
...
aout mygrain3opcode kp1, kp2, ...
endin

It’s all the boilerplate of integrating I’m trying to minimise really. Ideally I’d throw a few strings at a schedule for the control channels…Perhaps I need to rethink this :upside_down_face:

Yes, you still need instruments, but you could do:

instr GrainExample
    aOut = myGainOpcodes:a("param1", "param2", "param3")
    outs:a(aOut, aOut)
endin

Sure that works, I’m not that lazy that I couldn’t type instr haha.

I’m wondering tho, can udos have smart (overloaded) inputs , as in if it’s marked as a S type, then could it accept an i type if no string is present. In other words, if I didn’t want a external channel controlling the parameter, then I could feed it a p-field? ( like what $SET_CTL_TYPE macro is doing)

Yes. You can do something like this:

opcode MyOpcode, i, kSk

endop

opcode MyOpcodes, i, kii

endop

Oh of course, yes

thanks again