comparison blender/time_sync/time_from_graph.py @ 2457:d94480bfb179

more work on blender time sync. Might be working, aside from blender play-button
author drewp@bigasterisk.com
date Tue, 20 May 2025 13:48:07 -0700
parents 2d454737a916
children 0e27ba33118c
comparison
equal deleted inserted replaced
2456:917bc2eaf4f4 2457:d94480bfb179
1 import asyncio 1 import asyncio
2 import threading 2 import threading
3 import time 3 import time
4 from typing import Coroutine 4 from typing import Coroutine
5 5
6 from attr import dataclass
6 from rdfdb.patch import Patch 7 from rdfdb.patch import Patch
7 from rdfdb.syncedgraph.syncedgraph import SyncedGraph 8 from rdfdb.syncedgraph.syncedgraph import SyncedGraph
8 from rdflib import Literal 9 from rdflib import Literal
9 10
10 from light9 import networking, showconfig 11 from light9 import networking, showconfig
11 from light9.ascoltami.graph_context import ascoltamiContext 12 from light9.ascoltami.graph_context import ascoltamiContext
13 from light9.ascoltami.play_state import AscoPlayState
12 from light9.namespaces import L9 14 from light9.namespaces import L9
13 from light9.newtypes import decimalLiteral 15 from light9.newtypes import decimalLiteral
14 from light9.run_local import log 16 from light9.run_local import log
15 from light9.typedgraph import typedValue 17 from light9.typedgraph import typedValue
16 18
22 SceneLoaded, 24 SceneLoaded,
23 _TimeEvent, 25 _TimeEvent,
24 ) 26 )
25 27
26 28
27
28
29
30 class Sync: 29 class Sync:
31 """asco is the authority on playback status. Sync maintains a copy of the state""" 30 """asco is the authority on playback status. Sync maintains a copy of the state"""
32 lock = threading.Lock() 31 lock = threading.Lock()
33 32
34 # these are written ONLY by bg thread 33 # this is edited ONLY by bg thread
35 duration: float = 1 34 ascoPlayState: AscoPlayState
36 wallStartTime: float | None = None
37 pausedSongTime: float | None = None
38 latestSongTime: float
39 playing = False
40 35
41 def __init__(self): 36 def __init__(self):
42 # main thread 37 # main thread
43 self.blenderTime = BlenderTime(self.onEvent) 38 self.ascoPlayState = AscoPlayState(None, None, False, False, 1.0)
39 self.blenderTime = BlenderTime(self.onBlenderEvent, self.ascoPlayState)
44 self.blenderTime.start() 40 self.blenderTime.start()
45 41
46 self.ctx = ascoltamiContext(showconfig.showUri()) 42 self.ctx = ascoltamiContext(showconfig.showUri())
47 log.debug('🚋3 startLoopInThread') 43 log.debug('🚋3 startLoopInThread')
48 self._loop = startLoopInThread(self.connectGraph()) 44 self._loop = startLoopInThread(self.connectGraph())
49 log.info('🚋10 Sync initd') 45 log.info('🚋10 Sync initd')
50 46
51 def onEvent(self, event: _TimeEvent): 47 def onBlenderEvent(self, event: _TimeEvent):
52 # main thread 48 # main thread
53 match event: 49 match event:
54 case SceneLoaded(): 50 case SceneLoaded():
55 if hasattr(self, "latestSongTime"): 51 self.blenderTime.setRange(self.ascoPlayState.duration)
56 self.blenderTime.setBlenderTime(self.latestSongTime, self.duration) 52 self.blenderTime.setCurrentTime(self.ascoPlayState.getCurrentSongTime() or 0.0)
57 case PausedGotoTime(t): 53 case PausedGotoTime(t):
58 self.runInBackgroundLoop(self.setInGraph(t, False)) 54 self.runInBackgroundLoop(self.setInGraph(t, False))
59 case PlayingGotoTime(t): 55 case PlayingGotoTime(t):
60 self.runInBackgroundLoop(self.setInGraph(t, True)) 56 self.runInBackgroundLoop(self.setInGraph(t, True))
61 57
71 67
72 def syncFromGraph(self): 68 def syncFromGraph(self):
73 # bg thread 69 # bg thread
74 with self.lock: 70 with self.lock:
75 asco = L9['ascoltami'] 71 asco = L9['ascoltami']
76 self.wallStartTime = typedValue(float | None, self.graph, asco, L9['wallStartTime']) 72
77 self.pausedSongTime = typedValue(float | None, self.graph, asco, L9['pausedSongTime']) 73 self.ascoPlayState.wallStartTime = typedValue(float | None, self.graph, asco, L9['wallStartTime'])
78 self.duration = typedValue(float | None, self.graph, asco, L9['duration']) or 1.0 74 self.ascoPlayState.pausedSongTime = typedValue(float | None, self.graph, asco, L9['pausedSongTime'])
79 self.playing = typedValue(bool | None, self.graph, asco, L9['playing']) or False 75 self.ascoPlayState.duration = typedValue(float | None, self.graph, asco, L9['duration']) or 1.0
80 76 self.blenderTime.durationDirty = True # todo: called too often
81 def currentTime(self) -> float | None: 77 self.ascoPlayState.playing = typedValue(bool | None, self.graph, asco, L9['playing']) or False
82 if self.wallStartTime is not None: 78 self.ascoPlayState.endOfSong = typedValue(bool | None, self.graph, asco, L9['endOfSong']) or False
83 return time.time() - self.wallStartTime 79 log.info(f'🍇 syncFromGraph {self.ascoPlayState=}')
84 if self.pausedSongTime is not None: 80 self.blenderTime.curFrameDirty = True
85 return self.pausedSongTime
86 log.warn('no time data')
87 return None
88 81
89 async def setGraphPlaying(self, isBlenderPlaying: bool): 82 async def setGraphPlaying(self, isBlenderPlaying: bool):
83 return
90 # bg thread 84 # bg thread
91 log.info(f'set graph playing to {isBlenderPlaying}') 85 log.info(f'set graph playing to {isBlenderPlaying}')
92 self.graph.patchObject(self.ctx, L9['ascoltami'], L9['playing'], Literal(isBlenderPlaying)) 86 self.graph.patchObject(self.ctx, L9['ascoltami'], L9['playing'], Literal(isBlenderPlaying))
93 87
94 async def setInGraph(self, t: float, isBlenderPlaying: bool): 88 async def setInGraph(self, t: float, isBlenderPlaying: bool):