Add Lua script to timestretch audio when tempo-map changes
This commit is contained in:
parent
fbcf281425
commit
d77ab7eefa
121
share/scripts/_tempo_map.lua
Normal file
121
share/scripts/_tempo_map.lua
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
ardour {
|
||||||
|
["type"] = "EditorHook",
|
||||||
|
name = "Tempo Map Audio",
|
||||||
|
author = "Ardour Team",
|
||||||
|
description = "Time Stretch Audio when Tempo Map changed",
|
||||||
|
}
|
||||||
|
|
||||||
|
-- subscribe to signals
|
||||||
|
-- http://manual.ardour.org/lua-scripting/class_reference/#LuaSignal.LuaSignal
|
||||||
|
function signals ()
|
||||||
|
s = LuaSignal.Set()
|
||||||
|
s:add ({[LuaSignal.TempoMapChanged] = true})
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
-- create callback functions
|
||||||
|
function factory ()
|
||||||
|
-- callback function which invoked when signal is emitted
|
||||||
|
return function (signal, ref, ...)
|
||||||
|
-- 'TempoMapChanged' passes 3 aruguments: old and current tempo-map and a boolean from_undo
|
||||||
|
local tmo, tmn, from_undo = ...
|
||||||
|
|
||||||
|
if from_undo or not Session:collected_undo_commands () then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
function find_track_for_region (region_id)
|
||||||
|
for route in Session:get_tracks ():iter () do
|
||||||
|
local track = route:to_track ()
|
||||||
|
local pl = track:playlist ()
|
||||||
|
if not pl:region_by_id (region_id):isnil () then
|
||||||
|
return track
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local all_regions = ARDOUR.RegionFactory.regions()
|
||||||
|
|
||||||
|
-- copy region-map
|
||||||
|
local regions = {}
|
||||||
|
for _, r in all_regions:iter() do
|
||||||
|
local ar = r:to_audioregion ()
|
||||||
|
if ar:isnil () then goto next end
|
||||||
|
local track = find_track_for_region (r:to_stateful ():id ())
|
||||||
|
if not track then goto next end
|
||||||
|
regions[ar] = track
|
||||||
|
::next::
|
||||||
|
end
|
||||||
|
|
||||||
|
for ar, track in pairs (regions) do
|
||||||
|
print ("Processing Region: ", ar:name (), track:name ())
|
||||||
|
|
||||||
|
-- create Rubberband stretcher
|
||||||
|
local rb = ARDOUR.LuaAPI.Rubberband (ar, false)
|
||||||
|
|
||||||
|
-- the rubberband-filter also implements the readable API.
|
||||||
|
-- https://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:AudioReadable
|
||||||
|
-- This allows to read from the master-source of the given audio-region.
|
||||||
|
-- XXX but, it will not work for incremental tempo-map changes :(
|
||||||
|
local max_pos = rb:readable ():readable_length ()
|
||||||
|
|
||||||
|
-- the beat-map is a table holding audio-sample positions:
|
||||||
|
-- [from] = to
|
||||||
|
local beat_map = {}
|
||||||
|
beat_map[0] = 0
|
||||||
|
|
||||||
|
-- get position of region
|
||||||
|
local region_pos = ar:position()
|
||||||
|
|
||||||
|
local beats = tmo:quarters_at (region_pos)
|
||||||
|
local sample = 0
|
||||||
|
local beat = 0
|
||||||
|
|
||||||
|
if beats:get_ticks () > 0 then
|
||||||
|
beat = beats:get_beats () + 1
|
||||||
|
else
|
||||||
|
beat = beats:get_beats ()
|
||||||
|
end
|
||||||
|
|
||||||
|
local old_pos = tmo:sample_at_beats (beats)
|
||||||
|
local new_pos = tmn:sample_at_beats (beats)
|
||||||
|
print ("start", old_pos, new_pos)
|
||||||
|
|
||||||
|
while sample < max_pos do
|
||||||
|
local b = Temporal.Beats (beat, 0)
|
||||||
|
sample = tmo:sample_at_beats (b) - old_pos
|
||||||
|
local to = tmn:sample_at_beats (b) - new_pos
|
||||||
|
beat_map[sample] = to
|
||||||
|
print ("map ", sample, "to", to)
|
||||||
|
beat = beat + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local old_end_beats = tmo:quarters_at (Temporal.timepos_t (region_pos:samples () + max_pos))
|
||||||
|
local new_end = tmn:sample_at_beats (old_end_beats)
|
||||||
|
local stretch = (new_end - new_pos) / max_pos
|
||||||
|
print ("stretch factor", stretch, new_pos, new_end, max_pos)
|
||||||
|
|
||||||
|
-- configure rubberband stretch tool
|
||||||
|
rb:set_strech_and_pitch (stretch, 1) -- no overall stretching, no pitch-shift
|
||||||
|
rb:set_mapping (beat_map) -- apply beat-map from/to
|
||||||
|
|
||||||
|
-- now stretch the region
|
||||||
|
function rb_progress (_, pos)
|
||||||
|
end
|
||||||
|
|
||||||
|
local nar = rb:process (rb_progress)
|
||||||
|
|
||||||
|
-- replace region
|
||||||
|
if not nar:isnil () then
|
||||||
|
print ("new audio region: ", nar:name (), nar:length ())
|
||||||
|
local playlist = track:playlist ()
|
||||||
|
playlist:to_stateful ():clear_changes () -- prepare undo
|
||||||
|
playlist:remove_region (ar)
|
||||||
|
playlist:add_region (nar, Temporal.timepos_t(new_pos), 1, false)
|
||||||
|
Session:add_stateful_diff_command (playlist:to_statefuldestructible ())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user