-
-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathPakettiFollowPagePattern.lua
More file actions
231 lines (211 loc) · 6.17 KB
/
PakettiFollowPagePattern.lua
File metadata and controls
231 lines (211 loc) · 6.17 KB
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
local preferences = renoise.tool().preferences
local follow_page_enabled = false
local internal_update = false
local desired_line = nil
local follow_last_row = false
local last_sequence_index = nil
local sequence_change_pending = false
local sequence_change_expected_line = nil
local last_seen_line_index = nil
local last_seen_sequence_index = nil
local attached_song = nil
local use_idle_notifier = false
local function get_playback_pos_observable(transport)
local ok, obs = pcall(function() return transport.playback_pos_observable end)
if ok then
return obs
end
return nil
end
local function clamp_line(line, max_lines)
if line < 1 then
return 1
end
if line > max_lines then
return max_lines
end
return line
end
local function current_pattern_length(song)
local pattern = song.selected_pattern
if pattern then
return pattern.number_of_lines
end
return 1
end
local function init_desired_line(song)
local line = song.selected_line_index or 1
desired_line = line
follow_last_row = (line == current_pattern_length(song))
end
local function follow_to_sequence(song, sequence_index)
local pattern_index = song.sequencer.pattern_sequence[sequence_index]
local pattern_length = nil
if pattern_index and song.patterns[pattern_index] then
pattern_length = song.patterns[pattern_index].number_of_lines
else
pattern_length = current_pattern_length(song)
end
local target_line
if follow_last_row then
target_line = pattern_length
else
if desired_line == nil then
desired_line = song.selected_line_index or 1
end
target_line = clamp_line(desired_line, pattern_length)
end
internal_update = true
if song.selected_sequence_index ~= sequence_index then
song.selected_sequence_index = sequence_index
end
if song.selected_line_index ~= target_line then
if desired_line ~= nil and target_line ~= desired_line then
sequence_change_pending = true
sequence_change_expected_line = target_line
end
song.selected_line_index = target_line
end
internal_update = false
end
local function on_playback_pos_changed()
if not follow_page_enabled then
return
end
local song = renoise.song()
if not song then
return
end
if not song.transport.playing then
return
end
local pos = song.transport.playback_pos
if not pos then
return
end
local sequence_index = pos.sequence
if not sequence_index or sequence_index < 1 then
return
end
if last_sequence_index == sequence_index then
return
end
last_sequence_index = sequence_index
if desired_line == nil then
init_desired_line(song)
end
follow_to_sequence(song, sequence_index)
end
local function follow_page_idle_notifier()
local song = renoise.song()
if not song then
return
end
on_playback_pos_changed()
if internal_update then
return
end
local current_sequence = song.selected_sequence_index
if last_seen_sequence_index ~= current_sequence then
if desired_line == nil then
init_desired_line(song)
else
local pattern_length = current_pattern_length(song)
if not follow_last_row and desired_line > pattern_length then
sequence_change_pending = true
sequence_change_expected_line = pattern_length
end
end
last_seen_sequence_index = current_sequence
end
local current_line = song.selected_line_index
if last_seen_line_index ~= current_line then
last_seen_line_index = current_line
if song.transport.playing and song.transport.follow_player then
return
end
if sequence_change_pending and sequence_change_expected_line == current_line then
sequence_change_pending = false
sequence_change_expected_line = nil
return
end
sequence_change_pending = false
sequence_change_expected_line = nil
desired_line = current_line
follow_last_row = (desired_line == current_pattern_length(song))
end
end
local function attach_song(song)
if not song then
return
end
attached_song = song
last_sequence_index = nil
sequence_change_pending = false
sequence_change_expected_line = nil
last_seen_line_index = song.selected_line_index
last_seen_sequence_index = song.selected_sequence_index
init_desired_line(song)
local playback_obs = get_playback_pos_observable(song.transport)
if playback_obs then
use_idle_notifier = false
if not playback_obs:has_notifier(on_playback_pos_changed) then
playback_obs:add_notifier(on_playback_pos_changed)
end
else
use_idle_notifier = true
if not renoise.tool().app_idle_observable:has_notifier(follow_page_idle_notifier) then
renoise.tool().app_idle_observable:add_notifier(follow_page_idle_notifier)
end
end
end
local function detach_song(song)
if not song then
return
end
local playback_obs = get_playback_pos_observable(song.transport)
if playback_obs and playback_obs:has_notifier(on_playback_pos_changed) then
playback_obs:remove_notifier(on_playback_pos_changed)
end
if use_idle_notifier and renoise.tool().app_idle_observable:has_notifier(follow_page_idle_notifier) then
renoise.tool().app_idle_observable:remove_notifier(follow_page_idle_notifier)
end
attached_song = nil
last_sequence_index = nil
last_seen_line_index = nil
last_seen_sequence_index = nil
end
function PakettiFollowPagePatternSetEnabled(enabled)
follow_page_enabled = enabled and true or false
local song = renoise.song()
if not song then
return
end
if follow_page_enabled then
attach_song(song)
else
detach_song(song)
end
end
function PakettiFollowPagePatternOnNewDocument()
local song = renoise.song()
if not song then
return
end
if attached_song and attached_song ~= song then
detach_song(attached_song)
end
follow_page_enabled = preferences.pakettiFollowPagePattern.value
if follow_page_enabled then
attach_song(song)
else
detach_song(song)
end
end
function PakettiToggleFollowPagePattern()
local enabled = not preferences.pakettiFollowPagePattern.value
preferences.pakettiFollowPagePattern.value = enabled
preferences:save_as("preferences.xml")
PakettiFollowPagePatternSetEnabled(enabled)
renoise.app():show_status("Follow Page Pattern: " .. (enabled and "ON" or "OFF"))
end