Posts tagged collaboration

Posted 1 year ago

Sine Tone Electronics Interface

I have been collaborating with the guitarist Christopher Riggs on a duo set involving his prepared guitar and an electronic interface of mine using only sine tones. I am interested in the different kinds of texture that this approach juxtaposes. Chris’s playing is obviously very textural and he gets an impressive variety of sounds out of his guitar. The sine tones (despite their simplicity) can also be very textural especially when combined with each other and tuned to obtain close beating patterns.

In order to explore this concept I created a SuperCollider patch (with a lot of help from the SC List) that allows me to play up to 4 sine tones at once, tune each tone individually, control the attack/release of the envelope of each tone, and panning and volume of each tone. I also adjusted the behavior of the keyboard so that I could turn a note on by pressing a key once, as opposed to holding a key down or using a sustain pedal.

This patch uses the Voicer quark in order to manage the different synth nodes. Voicer basically handles all of the background tasks of turning synths on and off, and matching the control busses to synth parameters.

//4 voice SineTone Interface
//Version 2
//Carl Testa
(
SynthDef("sineTone",
	{ |outbus=0, freq=440, amp=0.1, gate=1, pan=0, dur = 1, width=1, attack=1, tune=0|
	var sine, env, sound;
	sine = SinOsc.ar((freq+tune).midicps, 0);
	env = Env.asr(attack,1,attack,'sine');
	env = EnvGen.ar(env,gate,doneAction:2);
	sound = sine*env;
	Out.ar(outbus, Pan2.ar(sound, pan, amp));
	}).add;
)
(
//Control busses
~tunespec = ControlSpec(-1, 1, \linear, 0.001, 0);
~panspec = ControlSpec(-1, 1, \linear, 0.001, 0);
~attackspec = ControlSpec(0.01, 10, \linear, 0.01, 1, "secs");
~ampspec = ControlSpec(-60.dbamp, -10.dbamp, \linear, 0.01, -20.dbamp);
~busses = (0..15);
~bus = ~busses.collect { |bus| Bus.new(\control, bus, 1, s) };

//~voices = Voicer.new(4, \sineTone, ~tunebus.collect { |bus| [tune: bus.asMap] });
	
~voices = Voicer.new(4, \sineTone, [
	[tune: ~bus.at(0).asMap, attack: ~bus.at(4).asMap, pan: ~bus.at(8).asMap, amp: ~bus.at(12).asMap;], //first voice
	[tune: ~bus.at(1).asMap, attack: ~bus.at(5).asMap, pan: ~bus.at(9).asMap, amp: ~bus.at(13).asMap;], //second voice
	[tune: ~bus.at(2).asMap, attack: ~bus.at(6).asMap, pan: ~bus.at(10).asMap, amp: ~bus.at(14).asMap;], //third voice
	[tune: ~bus.at(3).asMap, attack: ~bus.at(7).asMap, pan: ~bus.at(11).asMap, amp: ~bus.at(15).asMap;] //fourth voice
]);

~voices.nodes.do { |vnode| vnode.initArgs.postln };

~voices.stealer_(\arrayOrder); //custom 
)

(
	~noteOn = NoteOnResponder({ |src,chan,note,vel|
		if(~voices.firstNodeFreq(note).notNil,
		{~voices.release(note);},
		{~voices.trigger(note);}
		);
		
		},
		nil, // any source
		nil, // any channel
		nil, // any note
		nil // any vel
	);
	
	~control = CCResponder({ |src,chan,num,value|
		x = case
		{num == 21} {~bus.at(0).value = ~tunespec.map(value/127.0)}
		{num == 22} {~bus.at(1).value = ~tunespec.map(value/127.0)}
		{num == 23} {~bus.at(2).value = ~tunespec.map(value/127.0)}
		{num == 24} {~bus.at(3).value = ~tunespec.map(value/127.0)}
		{num == 25} {~bus.at(4).value = ~attackspec.map(value/127.0)}
		{num == 26} {~bus.at(5).value = ~attackspec.map(value/127.0)}
		{num == 27} {~bus.at(6).value = ~attackspec.map(value/127.0)}
		{num == 28} {~bus.at(7).value = ~attackspec.map(value/127.0)}
		{num == 70} {~bus.at(8).value = ~panspec.map(value/127.0)}
		{num == 71} {~bus.at(9).value = ~panspec.map(value/127.0)}
		{num == 72} {~bus.at(10).value = ~panspec.map(value/127.0)}
		{num == 73} {~bus.at(11).value = ~panspec.map(value/127.0)}
		{num == 91} {~bus.at(12).value = ~ampspec.map(value/127.0)}
		{num == 93} {~bus.at(13).value = ~ampspec.map(value/127.0)}
		{num == 82} {~bus.at(14).value = ~ampspec.map(value/127.0)}
		{num == 83} {~bus.at(15).value = ~ampspec.map(value/127.0)}
	},
	nil,
	nil,
	nil,
	nil
	);
)

//CleanUp
NoteOnResponder.removeAll;
CCResponder.removeAll;

In order to use the custom voice stealing algorithm I use for this you need to add the following code to your SuperCollider Extensions folder:

+ Voicer { 
        arrayOrder { 
                var node = nodes.detect { |node| node.reserved.not }; 
                if(node.notNil) { ^node } { ^this.earliest } 
        } 
}

What this does is tell Voicer to always use the first node that is available. So if you play two notes you will be using the first 2 nodes. If you release the first node and play another note it will put that new note on the first node. It sounds confusing, but the effect is that if you have two notes playing you will know that you can adjust them by using the knobs for the first two voices.

Download these two SuperCollider documents here: sinetones_V2.scd and ArrayOrder.sc.

Video or audio coming soon. Questions?