mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-04 13:24:13 +08:00
Calibrate prepared audio from silent callback timestamps
This commit is contained in:
@@ -222,6 +222,7 @@ def audio_worker(a):
|
||||
done = False
|
||||
errors = []
|
||||
clock_errors = []
|
||||
clock_observations = []
|
||||
bridge = None
|
||||
flags = 0
|
||||
max_drift = 0.
|
||||
@@ -231,7 +232,11 @@ def audio_worker(a):
|
||||
def callback(out, n, ti, status):
|
||||
nonlocal position,rendered,done,flags,max_drift,first_frame,callback_revision
|
||||
out.fill(0)
|
||||
if anchor is None or bridge is None or done:return
|
||||
if bridge is None:
|
||||
if len(clock_observations)<24:
|
||||
clock_observations.append((float(time.monotonic()),float(ti.currentTime),float(ti.outputBufferDacTime)))
|
||||
return
|
||||
if anchor is None or done:return
|
||||
now=time.monotonic()
|
||||
active_anchor=anchor
|
||||
expected=None;dac=None;start=position
|
||||
@@ -266,7 +271,14 @@ def audio_worker(a):
|
||||
server = control_server(a.project_root,a.out,shared,a.port,forwarder,follow_peer=forwarder is not None)
|
||||
write_json(a.out/'controls.json',{'url':f'http://127.0.0.1:{server.server_port}/'})
|
||||
with sd.OutputStream(device=a.audio_device,samplerate=rate,channels=2,blocksize=960,dtype='float32',callback=callback) as stream:
|
||||
bridge=StreamClockBridge.measure(lambda:stream.time)
|
||||
calibration_deadline=time.monotonic()+2.
|
||||
while len(clock_observations)<20:
|
||||
if not stream.active:raise RuntimeError('Output stream stopped during silent clock calibration')
|
||||
if time.monotonic()>=calibration_deadline:raise TimeoutError('Output clock did not provide 20 silent callbacks')
|
||||
time.sleep(.01)
|
||||
observations=list(clock_observations[:20])
|
||||
write_json(a.out/'stream_clock_calibration.json',dict(silent_callback_samples=observations))
|
||||
bridge=StreamClockBridge.from_callbacks(observations)
|
||||
(a.out/'prepared_ready').write_text('ready')
|
||||
while not done:
|
||||
sm.update(50);now=time.monotonic();controls.poll()
|
||||
|
||||
@@ -120,9 +120,21 @@ class ReplaySyncTests(unittest.TestCase):
|
||||
playback_clock=PreparedClock(),audio=np.zeros((480000,2),np.float32),
|
||||
processor=SimpleNamespace(process=lambda chunk,*args:(chunk,{})),state={},
|
||||
controls=SimpleNamespace(selection=('recorded','recorded')),a=SimpleNamespace(muted=True),
|
||||
errors=[],clock_errors=[])
|
||||
errors=[],clock_errors=[],clock_observations=[])
|
||||
exec(compile(ast.Module(body=[callback],type_ignores=[]),str(path),'exec'),context)
|
||||
out=np.ones((960,2),np.float32)
|
||||
# Startup gathers only a bounded set of silent observations. No stream.time
|
||||
# getter, model anchor, source-frame processing or output is required.
|
||||
saved_bridge=context['bridge'];context['bridge']=None
|
||||
for i in range(30):
|
||||
context['time'].monotonic=lambda i=i:99.+i*.02
|
||||
context['callback'](out,960,SimpleNamespace(currentTime=499.+i*.02,outputBufferDacTime=499.04+i*.02),False)
|
||||
self.assertFalse(out.any())
|
||||
self.assertEqual(len(context['clock_observations']),24)
|
||||
self.assertIsNone(context['position'])
|
||||
calibrated=StreamClockBridge.from_callbacks(context['clock_observations'][:20])
|
||||
self.assertAlmostEqual(calibrated.offset,-400.)
|
||||
context['bridge']=saved_bridge;context['time'].monotonic=lambda:100.08
|
||||
context['callback'](out,960,SimpleNamespace(currentTime=500.,outputBufferDacTime=500.056),False)
|
||||
context['time'].monotonic=lambda:100.04
|
||||
context['callback'](out,960,SimpleNamespace(currentTime=500.02,outputBufferDacTime=500.076),False)
|
||||
|
||||
Reference in New Issue
Block a user