Compare commits
3 Commits
master
...
tempo-stre
Author | SHA1 | Date |
---|---|---|
Robin Gareus | d77ab7eefa | |
Robin Gareus | fbcf281425 | |
Robin Gareus | e45fcad607 |
|
@ -94,6 +94,9 @@ STATIC(SetSession, &LuaInstance::SetSession, 0)
|
|||
// Editor Selection Changed
|
||||
STATIC(SelectionChanged, &LuaInstance::SelectionChanged, 0)
|
||||
|
||||
// Tempo Map Changed (old_map, new_map, from_undo)
|
||||
STATIC(TempoMapChanged, &Temporal::TempoMap::MapChanged, 3)
|
||||
|
||||
// TODO per track/route signals,
|
||||
// TODO per plugin actions / controllables
|
||||
// TODO per region actions
|
||||
|
|
|
@ -234,7 +234,7 @@ CLASSINFO(UIConfiguration);
|
|||
|
||||
|
||||
/* this needs to match gtk2_ardour/luasignal.h */
|
||||
CLASSKEYS(std::bitset<50ul>); // LuaSignal::LAST_SIGNAL
|
||||
CLASSKEYS(std::bitset<51ul>); // LuaSignal::LAST_SIGNAL
|
||||
|
||||
CLASSKEYS(void);
|
||||
CLASSKEYS(float);
|
||||
|
|
|
@ -49,7 +49,8 @@ std::string Meter::xml_node_name = X_("Meter");
|
|||
|
||||
SerializedRCUManager<TempoMap> TempoMap::_map_mgr (0);
|
||||
thread_local TempoMap::SharedPtr TempoMap::_tempo_map_p;
|
||||
PBD::Signal0<void> TempoMap::MapChanged;
|
||||
|
||||
PBD::Signal3<void, TempoMap::SharedPtr, TempoMap::SharedPtr, bool> TempoMap::MapChanged;
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define TEMPO_MAP_ASSERT(expr) TempoMap::map_assert(expr, #expr, __FILE__, __LINE__)
|
||||
|
@ -4357,8 +4358,10 @@ TempoMap::write_copy()
|
|||
}
|
||||
|
||||
int
|
||||
TempoMap::update (TempoMap::WritableSharedPtr m)
|
||||
TempoMap::update (TempoMap::WritableSharedPtr m, bool from_undo)
|
||||
{
|
||||
SharedPtr old_map = read ();
|
||||
|
||||
if (!_map_mgr.update (m)) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -4372,7 +4375,7 @@ TempoMap::update (TempoMap::WritableSharedPtr m)
|
|||
}
|
||||
#endif
|
||||
|
||||
MapChanged (); /* EMIT SIGNAL */
|
||||
MapChanged (old_map, read (), from_undo); /* EMIT SIGNAL */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -5005,7 +5008,7 @@ TempoCommand::undo ()
|
|||
|
||||
TempoMap::WritableSharedPtr map (TempoMap::write_copy());
|
||||
map->set_state (*_before, Stateful::current_state_version);
|
||||
TempoMap::update (map);
|
||||
TempoMap::update (map, true);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -791,7 +791,7 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
|
|||
/* API for typical tempo map changes */
|
||||
|
||||
LIBTEMPORAL_API static WritableSharedPtr write_copy();
|
||||
LIBTEMPORAL_API static int update (WritableSharedPtr m);
|
||||
LIBTEMPORAL_API static int update (WritableSharedPtr m, bool from_undo = false);
|
||||
LIBTEMPORAL_API static void abort_update ();
|
||||
|
||||
/* not part of public API */
|
||||
|
@ -1010,7 +1010,7 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
|
|||
|
||||
LIBTEMPORAL_API void dump (std::ostream&) const;
|
||||
|
||||
LIBTEMPORAL_API static PBD::Signal0<void> MapChanged;
|
||||
LIBTEMPORAL_API static PBD::Signal3<void, SharedPtr, SharedPtr, bool> MapChanged;
|
||||
|
||||
LIBTEMPORAL_API XMLNode& get_state() const;
|
||||
|
||||
|
|
|
@ -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