1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
# Study Player — Input adapter.
#
# Reads raylib keyboard input and translates it into flecs component
# mutations (seek intents, play/pause toggles, smart-play flags).
#
# Registered as a PRE_UPDATE system so intents are available before
# the seek/update systems run in ON_UPDATE.
#
# See: notes/study-player-rewrite-plan.md §7 (non-blocking seek design)
module StudyPlayer
module InputAdapter
# Key bindings (from ../source/README.md):
# SPACE — play/pause toggle
# LEFT / RIGHT — seek -5s / +5s
# UP / DOWN — seek -15s / +15s
# J / L — seek -10% / +10%
# 0 .. 9 — seek to N×10% of duration
# ESC — quit
#
# Phase 4 adds: M (study mode toggle), S (smart play hold), V/B (portion nav)
SEEK_SMALL = 5.0 # seconds: LEFT / RIGHT
SEEK_LARGE = 15.0 # seconds: UP / DOWN
SEEK_PCT_STEP = 0.10 # 10%: J / L
def self.build_system(world, player_entity, runtime, playback_state)
world.system("Input", with: [], phase: Flecs::PRE_UPDATE) do
pb = player_entity.get(playback_state)
next unless pb
# --- Play/pause toggle ---
if Rl.key_pressed?(:space)
if pb[:playing]
runtime.audio.pause
pb[:playing] = false
elsif pb[:loaded]
runtime.audio.resume
pb[:playing] = true
end
player_entity.set(playback_state, pb)
end
# --- Seek: small step ---
duration = runtime.audio.duration
if Rl.key_pressed?(:right)
target = StudyPlayer::Core.offset_to_seek(pb[:current_time], SEEK_SMALL, duration)
pb[:seek_target] = target
pb[:seek_pending] = true
player_entity.set(playback_state, pb)
elsif Rl.key_pressed?(:left)
target = StudyPlayer::Core.offset_to_seek(pb[:current_time], -SEEK_SMALL, duration)
pb[:seek_target] = target
pb[:seek_pending] = true
player_entity.set(playback_state, pb)
end
# --- Seek: large step ---
if Rl.key_pressed?(:up)
target = StudyPlayer::Core.offset_to_seek(pb[:current_time], SEEK_LARGE, duration)
pb[:seek_target] = target
pb[:seek_pending] = true
player_entity.set(playback_state, pb)
elsif Rl.key_pressed?(:down)
target = StudyPlayer::Core.offset_to_seek(pb[:current_time], -SEEK_LARGE, duration)
pb[:seek_target] = target
pb[:seek_pending] = true
player_entity.set(playback_state, pb)
end
# --- Seek: percentage jump (J/L) ---
if Rl.key_pressed?(:j) && duration > 0
target = StudyPlayer::Core.offset_to_seek(pb[:current_time], SEEK_PCT_STEP * duration, duration)
pb[:seek_target] = target
pb[:seek_pending] = true
player_entity.set(playback_state, pb)
elsif Rl.key_pressed?(:l) && duration > 0
target = StudyPlayer::Core.offset_to_seek(pb[:current_time], -SEEK_PCT_STEP * duration, duration)
pb[:seek_target] = target
pb[:seek_pending] = true
player_entity.set(playback_state, pb)
end
# --- Seek: 0..9 → N×10% ---
if duration > 0
(0..9).each do |n|
key = n.to_s.to_sym
if Rl.key_pressed?(key)
target = StudyPlayer::Core.ratio_to_seek(n * 0.1, duration)
pb[:seek_target] = target
pb[:seek_pending] = true
player_entity.set(playback_state, pb)
end
end
end
end
end
end
end
|