Re: supernova sample-based scheduling bug?
[email protected] Sun, 23 May 2021 17:57:15 +0200
| Newsgroups | gmane.comp.audio.supercollider.user |
|---|---|
| Message-ID | <[email protected]> |
I think you're right, my mind has slipped. So the problem with the Supernova clock is "only" gradual clock drift, as reported here: https://www.listarc.bham.ac.uk/lists/sc-dev-2014/search/ Christof On 22.05.2021 17:30, [email protected] wrote: > >> Am 10.05.2021 um 15:31 schrieb [email protected] >> <mailto:[email protected]>: >> >> The thing is, even if Supernova is using the sample clack, the sclang >> client always uses the NTP system time. I think, true sample accurate >> scheduling only works between bundles scheduled at the same logical >> time in the client. >> > > I’ve done some testing and cannot exactly confirm this. There seems to > be no difference between using language-based timing and indirect > timing via latency. There is a difference though between supernova and > scsynth. > > On the flip side, compared to my original testing > (https://scsynth.org/t/imperfection-of-language-based-timing/350 > <https://scsynth.org/t/imperfection-of-language-based-timing/350>), > the bespoken issue seems to have dramatically improved. Now I have > tested on OS 10.15.7 with 3.11.2 – no idea what caused the change. > These awful jumps (in the size of /dozens/ of samples) that happened > every 10-20 seconds or so in earlier tests (which I only performed in > scsynth though) seem to have completely disappeared. This has a nice > consequence: some variants of timing-sensitive pattern-based granular > synthesis that produced unsatisfying discontinuities in realtime for > the bespoken reasons are probably working fine now, for the extremely > sensitive cases supernova should do it anyway. > > > scsynth language-based timing (test 1)max deviation of 1 sample > scsynth latency-based timing (test 2)max deviation of 1 sample > > supernova language-based timing (test 1)sample-accurate > supernova latency-based timing (test 2)sample-accurate > > There were no differences between 44.1 and 48 kHz. Further testing > should check longer run times than one minute and other interfaces > (checked with built-in out on Mac now). > > I’d be happy to hear from your results with below tests on other > operating systems as well ! > > > // for tests with scsynth > ( > Server.scsynth; > s.reboot; > ) > > // for tests with supernova > ( > Server.supernova; > s.reboot; > ) > > // prepare for test 1 - language-scheduled timing > ( > // 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, duration = 60| > OffsetOut.ar <http://OffsetOut.ar>(out, [Impulse.ar > <http://Impulse.ar>(10), DC.ar <http://DC.ar>(0)] * EnvGen.ar > <http://EnvGen.ar>(Env([1, 1, 0], [duration + 0.01, 0]), doneAction: 2)) > }).store; > > SynthDef(\dirac, { |out = 0| > OffsetOut.ar <http://OffsetOut.ar>(out, [DC.ar <http://DC.ar>(0), > FreeSelf.kr <http://FreeSelf.kr>(Impulse.ar <http://Impulse.ar>(1))]) > }).store; > > // Synth as Pbind for pattern recording, needs legato = 1 to get equal > duration > p = Pbind(\instrument, \diracs, \duration, 60); > q = Pbind(\instrument, \dirac, \dur, 0.1); > ) > > > // prepare for test 2 - latency-based timing > ( > // 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, duration = 60| > OffsetOut.ar <http://OffsetOut.ar>(out, [Impulse.ar > <http://Impulse.ar>(10), DC.ar <http://DC.ar>(0)] * EnvGen.ar > <http://EnvGen.ar>(Env([1, 1, 0], [duration + 0.01, 0]), doneAction: 2)) > }).store; > > SynthDef(\dirac, { |out = 0| > OffsetOut.ar <http://OffsetOut.ar>(out, [DC.ar <http://DC.ar>(0), > FreeSelf.kr <http://FreeSelf.kr>(Impulse.ar <http://Impulse.ar>(1))]) > }).store; > > // Synth as Pbind for pattern recording > p = Pbind(\instrument, \diracs, \duration, 60); > > ~latency = s.latency; > > // we need a finite Pattern ! > q = Pbind( > \instrument, \dirac, > \dur, 0, > // dur sequencing to be inserted here > \realDur, Pn(0.1, 601), > \latency, Pfunc { |ev| > var latency = ~latency; > ~latency = ~latency + ev[\realDur]; > latency > } > ); > ) > > > // run test 1 or 2 > > ( > // record 60 sec of both pulse trains in realtime to compare, > // wait until finishing confirmed in post window > // mute to avoid playing hard clicks to speakers > > c = TempoClock.(); > t = 60; > s.volume.mute; > > ~date = Date.getDate.stamp; > ~fileName = Platform.recordingsDir +/+ "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) > > > // results of test 1 and 2 with supernova (44100 Hz): > -> > > occurences of sample deltas with single synth: > Set[ [ 4410, 600 ] ] > > occurences of sample deltas with Pbind: > Set[ [ 4410, 600 ] ] > > > // results of test 1 and 2 with scsynth (of course second set can vary) > -> > > occurences of sample deltas with single synth: > Set[ [ 4410, 600 ] ] > > occurences of sample deltas with Pbind: > Set[ [ 4409, 8 ], [ 4411, 52 ], [ 4410, 540 ] ] > > > Greetings > > Daniel > > ----------------------------- > http://daniel-mayer.at <http://daniel-mayer.at> > ----------------------------- > >