annotate blender/time_sync/blender_time.py @ 2458:0e27ba33118c default tip

better blender<->asco playback cooperation. still no play support in blender; only seek
author drewp@bigasterisk.com
date Tue, 20 May 2025 16:25:06 -0700
parents d94480bfb179
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
1 from dataclasses import dataclass
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
2 from typing import Callable
2432
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
3
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
4 import bpy
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
5 from bpy.app.handlers import persistent
2432
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
6
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
7 from light9.run_local import log
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
8 from light9.ascoltami.play_state import AscoPlayState
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
9
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
10 UPDATE_PERIOD = 1 / 20
2432
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
11
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
12
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
13 def clamp(lo, hi, x):
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
14 return max(lo, min(hi, x))
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
15
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
16
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
17 class _TimeEvent():
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
18 pass
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
19
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
20
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
21 class SceneLoaded(_TimeEvent):
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
22 pass
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
23
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
24
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
25 @dataclass
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
26 class PausedGotoTime(_TimeEvent):
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
27 t: float
2432
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
28
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
29
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
30 @dataclass
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
31 class PlayingGotoTime(_TimeEvent):
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
32 t: float
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
33
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
34
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
35 @dataclass
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
36 class BlenderPlayState:
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
37 isAnimationPlaying: bool
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
38 isScrubbing: bool
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
39
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
40
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
41 class BlenderTime:
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
42 """all methods to run in main thread"""
2432
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
43
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
44 def __init__(self, onEvent: Callable[[_TimeEvent], None], ascoltamiPlayStateRO: AscoPlayState):
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
45 self.ascoltamiPlayStateRO = ascoltamiPlayStateRO
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
46 self._onEvent = onEvent
2432
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
47 self.lastSetFrame = -1
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
48 self.lastPlayState = BlenderPlayState(False, False)
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
49 self.loaded = False
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
50 self.curFrameDirty = True
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
51 self.durationDirty = True
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
52
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
53 def start(self):
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
54 bpy.app.handlers.load_post.append(self._on_load_post)
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
55 bpy.app.handlers.frame_change_post.append(self._on_frame_change_post)
2454
405abed9a45c fix up asyncio-in-bg-thread sorcery
drewp@bigasterisk.com
parents: 2453
diff changeset
56
2453
b23afde50bc2 blender addons get thier own pdm setup for now. fix time_from_graph startup race
drewp@bigasterisk.com
parents: 2436
diff changeset
57 # need persistent because `blender --addons ...` seems to start addon
b23afde50bc2 blender addons get thier own pdm setup for now. fix time_from_graph startup race
drewp@bigasterisk.com
parents: 2436
diff changeset
58 # before loading scene.
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
59 bpy.app.timers.register(self._update, persistent=True)
2432
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
60
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
61 self._emitEvent(SceneLoaded())
2432
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
62
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
63 def setSceneDuration(self, duration: float):
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
64 if not self.loaded:
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
65 return
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
66 self.duration = duration
2432
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
67 scene = bpy.context.scene
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
68 fps = scene.render.fps
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
69 scene.frame_start = 0
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
70 scene.frame_end = int(duration * fps)
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
71
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
72 def frameDuration(self):
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
73 """press Home on the timeline to frame the whole range"""
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
74 return
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
75 # todo: need to be in screen context or something
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
76 context_override = bpy.context.copy()
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
77 # context_override["selected_objects"] = list(context.scene.objects)
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
78 with bpy.context.temp_override(**context_override):
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
79 bpy.ops.action.view_all()
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
80
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
81 def setCurrentTime(self, t: float):
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
82 if not self.loaded:
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
83 return
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
84 scene = bpy.context.scene
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
85 fps = scene.render.fps
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
86 fr = int(clamp(t * fps, 0, scene.frame_end))
2432
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
87 self.lastSetFrame = fr
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
88 scene.frame_set(fr)
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
89
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
90 def setRange(self, duration: float):
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
91 self.setSceneDuration(duration)
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
92 self.frameDuration()
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
93
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
94 def _emitEvent(self, event: _TimeEvent):
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
95 # log.info(f'🌹 emitEvent {event}')
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
96 self._onEvent(event)
2432
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
97
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
98 @persistent
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
99 def _update(self):
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
100 if self.curFrameDirty or self.ascoltamiPlayStateRO.playing:
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
101 self._followAscoTime()
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
102 self.curFrameDirty=False
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
103 if self.durationDirty:
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
104 if (d := self.ascoltamiPlayStateRO.duration) is not None:
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
105 self.setRange(d)
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
106 self.durationDirty = False
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
107 # # todo: playing in blender should start ascoltami playback
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
108 # cur = BlenderPlayState(bpy.context.screen.is_animation_playing, bpy.context.screen.is_scrubbing)
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
109
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
110 return UPDATE_PERIOD
2432
b8a408caf115 start blender sync
drewp@bigasterisk.com
parents:
diff changeset
111
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
112 def _followAscoTime(self):
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
113 if (t := self.ascoltamiPlayStateRO.getCurrentSongTime()) is not None:
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
114 self.setCurrentTime(t)
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
115
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
116 @persistent
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
117 def _on_load_post(self, scene, deps):
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
118 self._emitEvent(SceneLoaded())
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
119 self.loaded = True
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
120 self.curFrameDirty = True
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
121
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
122 @persistent
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
123 def _on_frame_change_post(self, scene, deps):
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
124 # log.info(f' _on_frame_change_post {self.lastPlayState}')
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
125 if scene.frame_current == self.lastSetFrame:
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
126 return
2455
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
127 # blender requested this frame change, either playing or paused (scrubbing timeline)
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
128 self.lastSetFrame = scene.frame_current
2d454737a916 split blender code to new file
drewp@bigasterisk.com
parents: 2454
diff changeset
129 t = round(scene.frame_current / scene.render.fps, 3)
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
130 if self.lastPlayState.isAnimationPlaying and not self.lastPlayState.isScrubbing:
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
131 self._emitEvent(PlayingGotoTime(t=t))
2454
405abed9a45c fix up asyncio-in-bg-thread sorcery
drewp@bigasterisk.com
parents: 2453
diff changeset
132 else:
2457
d94480bfb179 more work on blender time sync. Might be working, aside from blender play-button
drewp@bigasterisk.com
parents: 2455
diff changeset
133 self._emitEvent(PausedGotoTime(t=t))