[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[sc-dev] SF.net SVN: quarks:[2678] JITLibExtensions



Revision: 2678
          http://sourceforge.net/p/quarks/code/2678
Author:   decampo
Date:     2013-12-28 22:05:02 +0000 (Sat, 28 Dec 2013)
Log Message:
-----------
added NPVoicer2 with voice stealing + help file.

Modified Paths:
--------------
    JITLibExtensions/classes/NPVoicer.sc

Added Paths:
-----------
    JITLibExtensions/HelpSource/Classes/NPVoicer2.schelp
    JITLibExtensions/classes/NPVoicer2.sc

Added: JITLibExtensions/HelpSource/Classes/NPVoicer2.schelp
===================================================================
--- JITLibExtensions/HelpSource/Classes/NPVoicer2.schelp	                        (rev 0)
+++ JITLibExtensions/HelpSource/Classes/NPVoicer2.schelp	2013-12-28 22:05:02 UTC (rev 2678)
@@ -0,0 +1,108 @@
+TITLE:: NPVoicer2
+summary:: a voicer with stealing
+categories:: Libraries/JITLib
+related:: Classes/NPVoicer, Classes/Ndef
+
+DESCRIPTION::
+A voicer that can limit the maximum number of playing voices.
+Also, it can handle voices with fixed-duration synths reasonably well.
+
+Mainly code examples and tests for now:
+
+code::
+
+s.boot; s.latency = nil;
+
+	// make an NPVoicer with an Ndef in it
+	// prime it to play the \default synthdef
+(
+g = NPVoicer2(Ndef(\piano));
+g.prime(\default);
+g.hasGate;
+g.play;
+)
+	// play 4 notes in it, and post voiceHistory
+(
+g.maxVoices = 4;
+g.put(36, [\freq, 36.midicps, \amp, 0.2]); g.postHist;
+g.put(46, [\freq, 46.midicps, \amp, 0.15]); g.postHist;
+g.put(55, [\freq, 55.midicps, \amp, 0.12]); g.postHist;
+g.put(63, [\freq, 63.midicps]); g.postHist;
+)
+	// try releasing a note
+g.release(46); g.postHist;
+g.releaseAll; g.postHist;
+
+	// repeat notes individually
+	// - the previous instance of that note goes away
+g.put(46, [\freq, 46.midicps, \amp, 0.15]);g.postHist;
+g.put(55, [\freq, 55.midicps, \amp, 0.12]);g.postHist;
+g.put(36, [\freq, 36.midicps, \amp, 0.2]);g.postHist;
+g.put(63, [\freq, 63.midicps]);g.postHist;
+
+	// try the steal modes:
+g.stealMode = \oldest; g.checkLimit; g.postHist;
+g.put(74, [\freq, 74.midicps, \amp, 0.1]);g.postHist;
+
+g.stealMode = \lowest; g.checkLimit; g.postHist;
+g.put(38, [\freq, 38.midicps, \amp, 0.1]);g.postHist;
+
+g.stealMode = \softest; g.checkLimit; g.postHist;
+g.put(84, [\freq, 84.midicps, \amp, 0.05]);g.postHist;
+
+
+	// test self-ending voices with fixed durations:
+	// the NPVoicer2 tries to find out when the note will end,
+	// and removes it from the list and the proxy.
+(
+SynthDef(\prring, { |out, freq = (60.midicps), amp = 0.1, pan, sustain = 1.234|
+	Out.ar(out,
+		Pan2.ar(
+			Formant.ar(freq, freq * 2, freq * 5),
+			pan,
+			XLine.kr(amp, amp * 0.001, sustain)
+		)
+	);
+}).add;
+)
+
+g.prime(\prring);
+g.hasGate;
+
+g.put(64, [\freq, 64.midicps]);g.postHist;
+g.put(64, [\freq, 64.midicps, \sustain, 10]);g.postHist;
+fork { 10.do { |i| 1.wait; (" " + i + ": ").post; g.postHist; } };
+::
+
+
+CLASSMETHODS::
+
+
+INSTANCEMETHODS::
+
+METHOD:: maxVoices
+get and set the maximum number of voices
+
+METHOD:: limitVoices
+get and set flag whether to limit maximum number of voices
+
+METHOD:: stealMode
+get and set mode of voice stealing. Can be \oldest, \lowest, \softest.
+
+METHOD:: defParamValues
+the default parameter values of the current synthdef.
+
+METHOD:: voiceHistory
+the current history of sounding voices
+
+METHOD:: postHist
+prettypost the current history of sounding voices
+
+METHOD:: prime, put, release, releaseAll
+see NPVoicer
+
+private:: cmdPeriod, trackVoice, checkLimit, removeVoiceAt, findSoftestIndex
+
+EXAMPLES::
+
+To do ...

Modified: JITLibExtensions/classes/NPVoicer.sc
===================================================================
--- JITLibExtensions/classes/NPVoicer.sc	2013-12-28 22:02:18 UTC (rev 2677)
+++ JITLibExtensions/classes/NPVoicer.sc	2013-12-28 22:05:02 UTC (rev 2678)
@@ -2,6 +2,7 @@
 NPVoicer {
 
 	var <proxy, <indivParams, <synCtl, <usesSpawn = false;
+	var <synthDesc, <hasGate;
 
 	*new { | proxy, indivParams |
 		^super.newCopyArgs(proxy, indivParams ? []);
@@ -13,6 +14,9 @@
 		usesSpawn = useSpawn ? usesSpawn;
 		proxy.awake_(usesSpawn.not);
 		if (usesSpawn.not) { proxy.put(0, nil) };
+		synthDesc = SynthDescLib.global[synCtl.source];
+		// know whether sounds will end by themselves
+		hasGate = synthDesc.hasGate;
 	}
 
 	put { | key, args |
@@ -35,7 +39,6 @@
 	playingKeys { ^proxy.objects.indices }
 
 		// the most basic messages for the proxy
-	// don't play the source, just the monitor
 	play { | out, numChannels, group, multi=false, vol, fadeTime, addAction |
 		proxy.play(out, numChannels, group, multi, vol, fadeTime, addAction)
 	}
@@ -52,16 +55,38 @@
 
 	resume { proxy.resume }
 
+	filterIndivPairs { |argList|
+		if (indivParams.size > 0) {
+			argList = argList.clump(2).select { |pair|
+				indivParams.every(_ != pair[0]);
+			}.flatten(1);
+		};
+		^argList
+	}
+
 	// set global params: key, val, key, val, ...
-	set { |...args| proxy.set(*args); }
-	unset { |...keys| proxy.unset(*keys); }
+	set { |...args|
+		args = this.filterIndivPairs(args);
+		proxy.set(*args);
+	}
 
-	map { |...args| proxy.map(*args); }
-	unmap { |...keys| proxy.map(*keys); }
+	unset { |...keys|
+		keys = keys.removeAll(indivParams);
+		proxy.unset(*keys);
+	}
 
-	// set params individually per node
+	map { |...args|
+		args = this.filterIndivPairs(args);
+		proxy.map(*args);
+	}
+
+	unmap { |...keys|
+		keys = keys.removeAll(indivParams);
+		proxy.map(*keys);
+	}
+
+		// set params individually per node
 	setAt { |key ... args| proxy.setAt(key, *args); }
 	unsetAt { |key ... keys| proxy.setAt(key, *keys); }
 
-}
-
+}
\ No newline at end of file

Added: JITLibExtensions/classes/NPVoicer2.sc
===================================================================
--- JITLibExtensions/classes/NPVoicer2.sc	                        (rev 0)
+++ JITLibExtensions/classes/NPVoicer2.sc	2013-12-28 22:05:02 UTC (rev 2678)
@@ -0,0 +1,105 @@
+
+NPVoicer2 : NPVoicer {
+	var <>limitVoices = true, <>maxVoices = 16, <voiceHistory, <>stealMode = \oldest;
+	var <defParamValues;
+
+	// in NPVoicer:prime, check whether synthdef hasGate or not;
+	// if not, schedule removal of by sustain
+	// if yes, release hould remove it
+
+	prime { |obj, useSpawn|
+		super.prime(obj, useSpawn);
+		defParamValues = ();
+		synthDesc.controlDict.keysValuesDo { |parName, control|
+			defParamValues.put(parName, control.defaultValue);
+		};
+		voiceHistory = List[];
+	}
+
+	put { |key, args|
+		super.put(key, args);
+		this.removeVoiceAt(key); // super releases earlier voice under that key
+		this.checkLimit(key, args);
+		this.trackVoice(key, args);
+	}
+
+	release {|key|
+		super.release(key);
+		this.removeVoiceAt(key);
+	}
+
+	removeVoiceAt { |key|
+		var voiceHistIndex;
+		voiceHistIndex = voiceHistory.detectIndex { |ev| ev[0] == key };
+		voiceHistIndex !? { voiceHistory.removeAt(voiceHistIndex); };
+	}
+
+	releaseAll {
+		super.releaseAll;
+		voiceHistory.clear;
+	}
+
+	cmdPeriod { voiceHistory.clear.postln; }
+
+	postHist { voiceHistory.printAll; }
+
+	trackVoice {|key, args|
+		voiceHistory.add([key, args]);
+		// why use a voicer when notes end by themselves?
+		// maybe ask proxy who is still playing every now and then
+		if (hasGate.not) {
+			// figure how to estimate time synth will live
+			var susDefault = defParamValues[\sustain];
+			var susArgIndex = args.indexOf(\sustain);
+			var susFromArg = susArgIndex !? { args[susArgIndex + 1] };
+			var soundingTime = susFromArg ? susDefault ? 1;
+			defer ({ this.release(key) }, soundingTime);
+		};
+	}
+
+	findSoftestIndex {
+		var minAmp = 1000, minIndex = nil;
+		var defAmp = defParamValues[\amp];
+
+		if (defAmp.isNil) { ^nil };
+
+		voiceHistory.do { |ev, evi|
+			var ampSymi, ampVali, ampVal = defAmp;
+			ampSymi = ev[1].indexOf(\amp);
+
+			if (ampSymi.notNil) {
+				ampVali = ampSymi + 1;
+				ampVal = ev[1][ampVali]
+			};
+			if (ampVal < minAmp) {
+				minAmp = ampVal;
+				minIndex = evi
+			};
+		};
+		"findSoftest: minAmp: %, minIndex: %\n".postf(minAmp, minIndex);
+
+		^minIndex
+	}
+
+	checkLimit {
+		// check before adding the new voice,
+		// so it can never be killed
+		if (proxy.objects.size <= maxVoices) { ^this };
+
+		stealMode.switch(
+			\oldest, { this.release(voiceHistory[0][0]) },
+			\lowest, { this.release(proxy.objects.indices[0]) },
+			// maybe top and bottom voices will be less dispensable?
+			\middle, {
+				var keys = proxy.objects.indices;
+				var key = keys[keys.size div: 2];
+				this.release(key);
+			},
+			\softest, {
+				var index = this.findSoftestIndex ? 0;
+				this.release(voiceHistory[index][0]);
+			},
+			{ this.release(voiceHistory[0][0]) }
+		);
+	}
+}
\ No newline at end of file

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.


_______________________________________________
sc-dev mailing list

info (subscription, etc.): http://www.beast.bham.ac.uk/research/sc_mailing_lists.shtml
archive: https://listarc.bham.ac.uk/marchives/sc-dev/
search: https://listarc.bham.ac.uk/lists/sc-dev/search/