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

[sc-dev] sample-accuracy with lang-timing (was: Various timing issues)




Refering to Julian's last remark in this old thread

http://new-supercollider-mailing-lists-forums-use-these.2681727.n2.nabble.com/Various-timing-issues-td7614760i20.html

>What is not currently supported is sample-synchroneous timing in sclang. Is there a test case where this matters? 

>Normally, this should be adjacent samples (no way to test now): 
>( 
>fork { 
>        s.sendBundle(0.2, [\s_new, \x, -1, 1, 1, \amp, 1]); 
>        (1/s.sampleRate).wait; 
>        s.sendBundle(0.2, [\s_new, \x, -1, 1, 1, \amp, -1]); 
>} 
>)


Right, normally you get this, supposing an appropriate SynthDef \x.
The problem becomes apparent with lang-based sequencing,
Gerhard Eckel showed me an example I'm following here
and repeating with NRT synthesis.

The good news (at least for me as I didn't think of it before) is, it seems 
we can have sample accuracy with NRT pattern rendering,


(
s = Server.local;
Server.default = s;
s.boot;
)


(
// use store for later NRT usage,
// define with dummy channel here for use with pattern recording,
// play reference pulse train of synth to channel 0, "real" Pbind pulse train to channel 1
// we need an out arg anyway for aPattern.record

SynthDef(\diracs, { |out = 0| OffsetOut.ar(out, [Impulse.ar(10), DC.ar(0)] * EnvGate(fadeTime: 0)) }).store;
SynthDef(\dirac, { |out = 0| OffsetOut.ar(out, [DC.ar(0), FreeSelf.kr(Impulse.ar(1))]) }).store;

// Synth as Pbind for pattern recording, needs legato = 1 to get equal duration
p = Pbind(\instrument, \diracs, \legato, 1);
q = Pbind(\instrument, \dirac, \dur, 0.1);
)


(
// record one minute of both pulse trains in realtime to compare,
// wait until finishing confirmed in post window
// muted to avoid playing hard clicks to speakers

c = TempoClock.();
t = 60;
s.volume.mute;

~date = Date.getDate.stamp;
~fileName = "diracsRT_synthL_patR" ++ "_" ++ ~date ++ ".aiff";

// Reference pulse train (Pbind with one synth) needs to know overall duration
r = Ppar([p <> Pbind(\dur, Pn(t, 1)), q]);

r.record(~fileName, clock: c, dur: t + 0.05);

// need CmdPeriod here as record doesn't stop (broken)
c.sched(t + 3, { CmdPeriod.run; s.volume.unmute });
)

(
// helper function for index search

~indicesOfEqualWithPrecision = { |seq, item, prec = 0.0001|
	var indices;
	seq.do { |val, i|
		if (item.equalWithPrecision(val, prec)) { indices = indices.add(i) }
	};
	indices
};

// analysing function

~analyse = { |fileName|
	~soundFile = SoundFile.openRead(fileName);
	~data = FloatArray.fill(~soundFile.numFrames * ~soundFile.numChannels, 0);
	~soundFile.readData(~data);
	~soundFile.close;
	~stereo = ~data.clump(2).flop;
	~dataL = ~stereo[0];
	~dataR = ~stereo[1];


	// get indices of diracs
	~leftIndices = ~indicesOfEqualWithPrecision.(~dataL, 1);
	~rightIndices = ~indicesOfEqualWithPrecision.(~dataR, 1);

	// look at deltas
	~leftDeltas = ~leftIndices.differentiate.drop(1);
	~rightDeltas = ~rightIndices.differentiate.drop(1);

	// count occurences of (possibly) different deltas

	~leftDeltaSet = ~leftDeltas.asSet.collect { |x| [x, ~leftDeltas.occurrencesOf(x)] };
	~rightDeltaSet = ~rightDeltas.asSet.collect { |x| [x, ~rightDeltas.occurrencesOf(x)] };

	"".postln;
	"occurences of sample deltas with single synth: ".postln;
	~leftDeltaSet.postln;"".postln;

	"occurences of sample deltas with Pbind: ".postln;
	~rightDeltaSet.postln;"".postln;
};
)

// analyse, takes some seconds

~analyse.(~fileName)


A typical result (core audio, sample rate = 44100, hardware buffer size = 512) looks like this:

> occurences of sample deltas with single synth: 
> Set[ [ 4410, 600 ] ]

> occurences of sample deltas with Pbind: 
> Set[ [ 4448, 3 ], [ 4409, 5 ], [ 4372, 3 ], [ 4410, 584 ], [ 4411, 5 ] ]

With the single synth we get sample accuracy (4410 samples = 0.1 secs) as expected.
With Pbind sequencing there are a few deviations  of 1 sample and a few deviations
of 38 samples (mostly it was 38, 37 or 39 here). 
Those jumps are much larger with shorter latency (smaller hardware buffer size), e.g. going to 
200 samples or so with hardware buffer size = 64.
So smaller latency causes a stronger drift between hardware timing and system clock.
A larger buffer size on the other hand didn't cause much less sample jumps, 
but concerning the latter I didn't perform many tests, also not with external soundcards.


With NRT rendering on the other hand I always got perfect sample accuracy,
I have been rendering some minutes audio at max though.

(
// render one minute of the same stereo audio generation in NRT mode

c = TempoClock.();
t = 60;
s.volume.mute;

~date = Date.getDate.stamp;
~fileName = "diracsRT_synthL_patR" ++ "_" ++ ~date ++ ".aiff";

// Reference pulse train (Pbind with one synth) needs to know overall duration
r = Ppar([p <> Pbind(\dur, Pn(t + 0.05, 1)), q]);

// to be passed to render stereo
o = ServerOptions.new.numOutputBusChannels = 2;
r.render(~fileName, t + 0.05, options: o);
)

~analyse.(~fileName)

> occurences of sample deltas with single synth: 
> Set[ [ 4410, 600 ] ]

> occurences of sample deltas with Pbind: 
> Set[ [ 4410, 600 ] ]


Regards

Daniel

-----------------------------
www.daniel-mayer.at
-----------------------------







_______________________________________________
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/