[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[sc-dev] scsynth audio driver, behavior of oscTime variable
- To: sc-dev <sc-dev@xxxxxxxxxxxxxxxx>
- Subject: [sc-dev] scsynth audio driver, behavior of oscTime variable
- From: James Harkins <jamshark70@xxxxxx>
- Date: Sun, 29 Nov 2015 19:06:33 +0800
- List-id: SuperCollider developers mailing list <sc-devel.create.ucsb.edu>
- Reply-to: sc-dev@xxxxxxxxxxxxxxxx
- Sender: owner-sc-dev@xxxxxxxxxxxxxxxx
- User-agent: Wanderlust/2.15.9 (Almost Unreal) SEMI-EPG/1.14.7 (Harue) FLIM/1.14.9 (Gojō) APEL/10.8 EasyPG/1.0.0 Emacs/24.3 (x86_64-pc-linux-gnu) MULE/6.0 (HANACHIRUSATO)
I was trying today to track down those discrepancies in the behavior of OffsetOut that I mentioned in a thread on sc-users. I found that OffsetOut looks up its offset in the graph's sample offset member variable, so I looked up where that comes from. This is set in the audio driver's Run function. (As I'm in Linux, I'm looking at the Jack driver.)
void SC_JackDriver::Run()
.....
int64 oscTime = mOSCbuftime = (int64)((mDLL.PeriodTime() - mMaxOutputLatency) * kSecondsToOSCunits + .5);
The offset comes from the bundle's scheduled timestamp minus "oscTime" -- I'm assuming this is the timestamp st the beginning of the current control block but I could be wrong.
while ((schedTime = mScheduler.NextTime()) <= nextTime) {
float diffTime = (float)(schedTime - oscTime) * oscToSamples + 0.5;
float diffTimeFloor = floor(diffTime);
world->mSampleOffset = (int)diffTimeFloor;
world->mSubsampleOffset = diffTime - diffTimeFloor;
// scprintf("sched %llu, osc %llu, osc2samps %f, diffTime %f, offset %d\n", schedTime, oscTime, oscToSamples, diffTime, world->mSampleOffset);
if (world->mSampleOffset < 0) world->mSampleOffset = 0;
else if (world->mSampleOffset >= world->mBufLength) world->mSampleOffset = world->mBufLength-1;
SC_ScheduledEvent event = mScheduler.Remove();
event.Perform();
}
I confirmed, with an scprintf, that the scheduled timestamps are exactly 1 second apart, matching the language-side code. I don't know how to read the oscTimes as yet.
I'm running 44.1kHz with a 64-sample block size. 44100 % 64 = 4. So, for events 1 second apart and assuming perfect sample accuracy, I'd expect the offset of each subsequent event to increase by 4. Because the sample clock is inexact, I can't assume that, but still I would expect a deviation of no more than 1-2 samples per second. Instead, when I print the offsets, they appear to be entirely arbitrary. The sound is also far, far wrong, and recording impulses into a buffer and analyzing the IOIs reveals really quite bad timing errors.
The one piece I've observed that doesn't immediately make sense is oscTime. It appears, in Linux, that this comes directly from Jack. Are we doing something wrong with this number?
I'm attaching an scd file with analysis code and data. A WAV file with the impulse stream that I was analyzing is at:
http://www.dewdrop-world.net/download/OffsetOut-data.wav
hjh
s.boot;
// if you want to use my data file:
f = SoundFile.openRead(thisProcess.nowExecutingPath.dirname +/+ "OffsetOut-data.wav");
f.readData(d = FloatArray.newClear(f.numFrames));
f.close;
// or generate your own
~pulseBus = Bus.audio(s, 1);
(
SynthDef(\pulseSynth, { |out, freq = 1000, numPulses = 1000|
var pulse = Impulse.ar(freq),
count = PulseCount.ar(pulse),
done = count > numPulses;
FreeSelf.kr(done);
OffsetOut.ar(out, pulse * (done <= 0));
}).add;
)
(
~pulses = Pseq([
(type: \on, instrument: \rec, in: ~pulseBus, bufnum: b, id: 999, delta: 0),
Pbind(
\instrument, \pulseSynth,
\out, ~pulseBus,
\freq, 100,
\numPulses, 100,
\dur, Pn(1, 10),
\group, RootNode(s),
\addAction, \addToHead
),
(type: \kill, id: 999)
]).play(quant: 1);
)
b.getToFloatArray(0, nil, wait: -1, timeout: 30, action: { |data| d = data; debug("done") });
// analyze
e = d.collectIndices(_ > 0); // collectIndices is from ddwCommon quark
f = e.differentiate.drop(1);
f.reject(_ == 441)
-> [ 482, 439, 484, 439, 484, 449, 439, 483, 438 ]
f.reject(_ == 441).differentiate
-> [ 482, -43, 45, -45, 45, -35, -10, 44, -45 ]
f.clump(100).collect(_.sum);
[ 44141, 44098, 44143, 44098, 44143, 44108, 44098, 44142, 44097, 43659 ]
e[100, 200 .. ].differentiate // same except first and last
// server posted data
sched 15710048945895194087, osc 15710048945894349041, osc2samps 0.000010, diffTime 9.176790, offset 9
sched 15710048945895194087, osc 15710048945894349041, osc2samps 0.000010, diffTime 9.176790, offset 9
sched 15710048950190161383, osc 15710048950184887090, osc2samps 0.000010, diffTime 54.655552, offset 54
sched 15710048954485128679, osc 15710048954479651031, osc2samps 0.000010, diffTime 56.743565, offset 56
sched 15710048958780095975, osc 15710048958776309013, osc2samps 0.000010, diffTime 39.383888, offset 39
sched 15710048963075063271, osc 15710048963071068111, osc2samps 0.000010, diffTime 41.521629, offset 41
sched 15710048967370030567, osc 15710048967367703957, osc2samps 0.000010, diffTime 24.389238, offset 24
sched 15710048971664997198, osc 15710048971661458276, osc2samps 0.000010, diffTime 36.837055, offset 36
sched 15710048975959964494, osc 15710048975956280493, osc2samps 0.000010, diffTime 38.326702, offset 38
sched 15710048980254931790, osc 15710048980252945473, osc2samps 0.000010, diffTime 20.895168, offset 20
sched 15710048984549899086, osc 15710048984547809670, osc2samps 0.000010, diffTime 21.953772, offset 21
sched 15710048988844866382, osc 15710048988844344480, osc2samps 0.000010, diffTime 5.858802, offset 5
m /*oscToSamples*/ = 44100 / pow(2, 32)
// 64-bit integer differences between osc times, from emacs calc
// remarkably widely divergent from 44100 Hz
[4290538049.0, 4294763941.0, 4296657982.0, 4294759098.0, 4296635846.0, 4293754319.0, 4294822217.0, 4296664980.0, 4294864197.0, 4296534810.0] * m
-> [ 44054.521238641, 44097.911985149, 44117.359678773, 44097.862258041, 44117.132390058, 44087.54535669, 44098.51035329, 44117.431533057, 44098.941396852, 44116.094969446 ]
[ 44054.521238641, 44097.911985149, 44117.359678773, 44097.862258041, 44117.132390058, 44087.54535669, 44098.51035329, 44117.431533057, 44098.941396852, 44116.094969446 ].round.differentiate;
-> [ 44055, 43, 19, -19, 19, -29, 11, 18, -18, 17 ]
// If you want to print the 64-bit timestamps as binary:
(
var longdiv = { |str, div|
var new = String.new,
divDigits = div.asString.size,
num = str.keep(divDigits).asInteger, quotient;
str = str.drop(divDigits);
block { |break|
loop {
if(num >= div) {
quotient = num div: div;
new = new ++ quotient.asString;
num = num - (quotient * div); // remainder
} {
if(new.size > 0) {
new = new.add($0);
};
};
if(str.size > 0) {
num = num * 10 + str[0].digit;
str = str.drop(1);
} { break.(new) };
if(str.size == 0) {
new = new ++ (num div: div).asString;
break.(new);
};
};
};
};
f = { |str64|
var binary = String.newClear(64);
64.do { |i|
if("13579".includes(str64.last)) {
binary[63-i] = $1;
str64 = str64.copy;
str64.putLast((str64.last.ascii - 1).asAscii);
} {
binary[63-i] = $0
};
str64 = longdiv.(str64, 2);
};
binary
};
~print = { |strArray|
strArray.do { |stamp|
var binary = f.(stamp);
binary.insert(32, $ ).postln;
};
""
};
)
// sched times
~print.(["15710048945895194087", "15710048945895194087", "15710048950190161383", "15710048954485128679", "15710048958780095975", "15710048963075063271", "15710048967370030567", "15710048971664997198", "15710048975959964494", "15710048980254931790", "15710048984549899086", "15710048988844866382"]);
// osc times
~print.(["15710048945894349041", "15710048945894349041", "15710048950184887090", "15710048954479651031", "15710048958776309013", "15710048963071068111", "15710048967367703957", "15710048971661458276", "15710048975956280493", "15710048980252945473", "15710048984547809670", "15710048988844344480"]);