Repository navigation
Expand file tree
/
Copy pathfadeOutRegion.lua
More file actions
288 lines (249 loc) · 9.01 KB
/
Copy pathfadeOutRegion.lua
File metadata and controls
288 lines (249 loc) · 9.01 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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
local END_REGION_NAME = "END"
local FADE_DURATION_SECONDS = 8.0
local END_THRESHOLD_SECONDS = 0.25
local END_MUTE_RELEASE_SECONDS = 0.08
local END_STOP_EARLY_SECONDS = 0.03
local END_CLEAR_FADE_DELAY_SECONDS = 0.12
local END_CLEAR_FADE_AFTER_JUMP_SECONDS = 0.2
local END_MUTE_RELEASE_AFTER_JUMP_SECONDS = 0.12
local END_UNMUTE_PLAY_ADVANCE_SECONDS = 0.06
local END_UNMUTE_FAILSAFE_SECONDS = 0.6
local NOTICE_WINDOW_W = 420
local NOTICE_WINDOW_H = 110
local NOTICE_FONT_SIZE = 32
local function setToggleState(isActive)
local _, _, sectionId, commandId = reaper.get_action_context()
if commandId == 0 then
return
end
reaper.SetToggleCommandState(sectionId, commandId, isActive and 1 or 0)
reaper.RefreshToolbar2(sectionId, commandId)
end
local function setFadeIndicator(isActive)
setToggleState(isActive)
if isActive then
gfx.init("Fadeout ativo", NOTICE_WINDOW_W, NOTICE_WINDOW_H, 0)
gfx.setfont(1, "Arial", NOTICE_FONT_SIZE)
gfx.set(1, 1, 1, 1)
gfx.x = 20
gfx.y = (NOTICE_WINDOW_H - NOTICE_FONT_SIZE) / 2
gfx.drawstr("FADEOUT ATIVO")
gfx.update()
else
if gfx.getchar() >= 0 then
gfx.quit()
end
end
end
local function getActivePosition()
local playState = reaper.GetPlayState()
if playState & 1 == 1 then
return reaper.GetPlayPosition()
end
return reaper.GetCursorPosition()
end
local function findRegionAtPosition(position)
local numMarkers, numRegions = reaper.CountProjectMarkers(0)
for i = 0, numMarkers + numRegions - 1 do
local _, isrgn, pos, rgnend, name, markrgnindexnumber = reaper.EnumProjectMarkers(i)
if isrgn and position >= pos and position <= rgnend then
return {
pos = pos,
rgnend = rgnend,
name = name,
index = markrgnindexnumber
}
end
end
return nil
end
local function findRegionByName(regionName)
local numMarkers, numRegions = reaper.CountProjectMarkers(0)
for i = 0, numMarkers + numRegions - 1 do
local _, isrgn, pos, rgnend, name, markrgnindexnumber = reaper.EnumProjectMarkers(i)
if isrgn and name == regionName then
return {
pos = pos,
rgnend = rgnend,
name = name,
index = markrgnindexnumber
}
end
end
return nil
end
local function applyFadeOutToItemsInRegion(regionStart, regionEnd)
local fadeStart = math.max(regionEnd - FADE_DURATION_SECONDS, regionStart)
local totalTracks = reaper.CountTracks(0)
local itemsAffected = 0
local affectedItems = {}
for t = 0, totalTracks - 1 do
local track = reaper.GetTrack(0, t)
local itemCount = reaper.CountTrackMediaItems(track)
for i = 0, itemCount - 1 do
local item = reaper.GetTrackMediaItem(track, i)
local itemStart = reaper.GetMediaItemInfo_Value(item, "D_POSITION")
local itemLen = reaper.GetMediaItemInfo_Value(item, "D_LENGTH")
local itemEnd = itemStart + itemLen
if itemStart < regionEnd and itemEnd > regionEnd then
reaper.SplitMediaItem(item, regionEnd)
itemLen = reaper.GetMediaItemInfo_Value(item, "D_LENGTH")
itemEnd = itemStart + itemLen
end
if itemEnd > fadeStart and itemEnd <= regionEnd then
local available = itemEnd - fadeStart
local fadeLen = math.min(FADE_DURATION_SECONDS, available)
if fadeLen > 0 then
reaper.SetMediaItemInfo_Value(item, "D_FADEOUTLEN", fadeLen)
itemsAffected = itemsAffected + 1
affectedItems[#affectedItems + 1] = item
end
end
end
end
return itemsAffected, affectedItems
end
local function clearFadeOutOnItems(items)
for i = 1, #items do
local item = items[i]
reaper.SetMediaItemInfo_Value(item, "D_FADEOUTLEN", 0.0)
end
end
local function disableLoopAndTimeSelection()
if reaper.GetToggleCommandState(1068) == 1 then
reaper.Main_OnCommand(1068, 0)
end
reaper.GetSet_LoopTimeRange(true, false, 0, 0, false)
reaper.Main_OnCommand(40635, 0)
end
local function setMasterMute(isMuted)
local master = reaper.GetMasterTrack(0)
if master then
reaper.SetMediaTrackInfo_Value(master, "B_MUTE", isMuted and 1 or 0)
end
end
local function unmuteMasterLater(delaySeconds)
local startTime = reaper.time_precise()
local function wait()
if reaper.time_precise() - startTime >= delaySeconds then
setMasterMute(false)
return
end
reaper.defer(wait)
end
reaper.defer(wait)
end
local function startEndRegionMonitor(targetRegionEnd, endRegionStart, affectedItems)
local lastPlayPos = reaper.GetPlayPosition()
local didFinish = false
local function clearFadesWhenStopped(delaySeconds)
local startTime = reaper.time_precise()
local function wait()
if reaper.GetPlayState() == 0 and (reaper.time_precise() - startTime) >= delaySeconds then
clearFadeOutOnItems(affectedItems)
return
end
reaper.defer(wait)
end
reaper.defer(wait)
end
local function clearFadesAfterDelay(delaySeconds)
local startTime = reaper.time_precise()
local function wait()
if reaper.time_precise() - startTime >= delaySeconds then
clearFadeOutOnItems(affectedItems)
return
end
reaper.defer(wait)
end
reaper.defer(wait)
end
local function unmuteAndClearAfterJump(jumpStartPos)
local startTime = reaper.time_precise()
local function wait()
local playState = reaper.GetPlayState()
local playPos = reaper.GetPlayPosition()
if playState & 1 == 1 and playPos >= (jumpStartPos + END_UNMUTE_PLAY_ADVANCE_SECONDS) then
setMasterMute(false)
clearFadeOutOnItems(affectedItems)
return
end
if reaper.time_precise() - startTime >= END_UNMUTE_FAILSAFE_SECONDS then
setMasterMute(false)
clearFadeOutOnItems(affectedItems)
return
end
reaper.defer(wait)
end
reaper.defer(wait)
end
local function handleEnd()
if didFinish then
return
end
didFinish = true
setMasterMute(true)
disableLoopAndTimeSelection()
setFadeIndicator(false)
if endRegionStart then
reaper.SetEditCurPos2(0, endRegionStart, true, true)
reaper.Main_OnCommand(1007, 0)
-- Jump and keep playing; only unmute/clear after play advances a bit.
unmuteAndClearAfterJump(endRegionStart)
else
reaper.SetEditCurPos2(0, targetRegionEnd, true, false)
reaper.Main_OnCommand(1016, 0)
-- Clear fades after a short stopped delay to avoid the pop.
clearFadesWhenStopped(END_CLEAR_FADE_DELAY_SECONDS)
unmuteMasterLater(END_MUTE_RELEASE_SECONDS)
end
end
local function monitor()
local playState = reaper.GetPlayState()
local playPos = reaper.GetPlayPosition()
local endCheck = targetRegionEnd
local nearEnd = playPos >= endCheck
or lastPlayPos >= (endCheck - END_THRESHOLD_SECONDS)
if playState == 0 then
if nearEnd then
handleEnd()
else
setFadeIndicator(false)
end
return
end
local endEarly = (not endRegionStart) and playPos >= (endCheck - END_STOP_EARLY_SECONDS)
local reachedEnd = playPos >= endCheck
or (playPos < lastPlayPos and lastPlayPos >= (endCheck - END_THRESHOLD_SECONDS))
if endEarly or reachedEnd then
handleEnd()
return
end
lastPlayPos = playPos
reaper.defer(monitor)
end
reaper.defer(monitor)
end
local function main()
setFadeIndicator(true)
local position = getActivePosition()
local currentRegion = findRegionAtPosition(position)
if not currentRegion then
setFadeIndicator(false)
reaper.MB("Nenhuma região encontrada na posição atual.", "Erro", 0)
return
end
reaper.Undo_BeginBlock()
local itemsAffected, affectedItems = applyFadeOutToItemsInRegion(currentRegion.pos, currentRegion.rgnend)
reaper.Undo_EndBlock("Fade out da região atual (itens)", -1)
if itemsAffected == 0 then
reaper.MB("Nenhum item encontrado para aplicar fade out.", "Aviso", 0)
end
local endRegion = findRegionByName(END_REGION_NAME)
if endRegion and endRegion.pos ~= currentRegion.pos then
startEndRegionMonitor(currentRegion.rgnend, endRegion.pos, affectedItems)
else
startEndRegionMonitor(currentRegion.rgnend, nil, affectedItems)
end
end
main()