From 3986f5d0dab04524f991fbd67dfc52705b805d23 Mon Sep 17 00:00:00 2001 From: houston Date: Wed, 27 Jan 2021 12:24:21 +0100 Subject: [PATCH 1/4] add fade_in_length() and fade_out_length() methods to audioregion to give access to theses values from Lua script. Give a script example that makes a new playlist in selected tracks with only audible regions. --- libs/ardour/ardour/audioregion.h | 2 + libs/ardour/audioregion.cc | 12 ++ libs/ardour/luabindings.cc | 2 + share/scripts/new_clean_playlists.lua | 249 ++++++++++++++++++++++++++ 4 files changed, 265 insertions(+) create mode 100644 share/scripts/new_clean_playlists.lua diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index 6eaa37f1f4d..40ebdc020c2 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -132,6 +132,8 @@ class LIBARDOUR_API AudioRegion : public Region bool fade_in_is_default () const; bool fade_out_is_default () const; + double fade_in_length (); + double fade_out_length (); void set_fade_in_active (bool yn); void set_fade_in_shape (FadeShape); diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index aa1f86851d2..8e95b260707 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -1244,6 +1244,18 @@ AudioRegion::fade_out_is_default () const return _fade_out->size() == 2 && _fade_out->when(true) == 0 && _fade_out->when(false) == 64; } +double +AudioRegion::fade_in_length () +{ + return _fade_in->when(false); +} + +double +AudioRegion::fade_out_length() +{ + return _fade_out->when(false); +} + void AudioRegion::set_default_fade_in () { diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc index f5f33c14e2d..8bbe8d4a303 100644 --- a/libs/ardour/luabindings.cc +++ b/libs/ardour/luabindings.cc @@ -1306,6 +1306,8 @@ LuaBindings::common (lua_State* L) .addFunction ("rms", &AudioRegion::rms) .addFunction ("fade_in_active", &AudioRegion::fade_in_active) .addFunction ("fade_out_active", &AudioRegion::fade_out_active) + .addFunction ("fade_in_length", &AudioRegion::fade_in_length) + .addFunction ("fade_out_length", &AudioRegion::fade_out_length) .addFunction ("set_fade_in_active", &AudioRegion::set_fade_in_active) .addFunction ("set_fade_in_shape", &AudioRegion::set_fade_in_shape) .addFunction ("set_fade_in_length", &AudioRegion::set_fade_in_length) diff --git a/share/scripts/new_clean_playlists.lua b/share/scripts/new_clean_playlists.lua new file mode 100644 index 00000000000..9c0dd3e390b --- /dev/null +++ b/share/scripts/new_clean_playlists.lua @@ -0,0 +1,249 @@ +ardour { ["type"] = "EditorAction", name = "New clean playlists", + license = "MIT", + author = "Mathieu Picot (Houston4444)", + description = [[Copy the current playlist of selected tracks to a new playlist with audible regions only.]] +} + +function factory (params) return function () + + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection + local sel = Editor:get_selection () + + -- Track/Bus Selection -- iterate over all Editor-GUI selected tracks + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:TrackSelection + for route in sel.tracks:routelist():iter() do + + -- each of the items 'route' is-a + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route + local track = route:to_track() -- see if it's a track + if track:isnil() then + -- if not, skip it + goto next_route + end + + local is_audio = false + if track:data_type():to_string() == "audio" then + is_audio = true + end + + -- copy current playlist, get new playlist and rename it + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Playlist + track:use_copy_playlist() + local playlist = track:playlist() + playlist:set_name(track:name() .. "._MAIN_") + + -- we will stock here the values needed to modify/remove regions + local regions_to_modify = {} + + for r in playlist:region_list():iter() do + local r_front = r:position() + local r_end = r_front + r:length() + + -- region can be finally splited into many segments + -- 'segments' is a table containing segments with the form + -- {{seg1_start, seg1_end}, {seg2_start, seg2_finish}...} + -- segments will finally be regions + local segments = {{r_front, r_end}} + + -- values used for audio tracks to prevent cut during an audible fade + local r_no_cut_before = r_front + local r_no_cut_after = r_end + + if is_audio then + local ra = r:to_audioregion() + + if ra:fade_in_active() then + r_no_cut_before = r_front + math.floor(ra:fade_in_length()) + 64 + end + + if ra:fade_out_active() then + r_no_cut_after = r_end - math.floor(ra:fade_out_length()) - 64 + end + end + + for rg in playlist:region_list():iter() do + -- ignore regions equal or above this one + if rg:layer() <= r:layer() then + goto next_rg + end + + -- get points between which lower regions can be cut + local cut_point_left = rg:position() + local cut_point_right = cut_point_left + rg:length() + + if is_audio then + rga = rg:to_audioregion() + + cut_point_left = cut_point_left + 64 + cut_point_right = cut_point_right - 64 + + if rga:fade_in_active() then + cut_point_left = cut_point_left + math.floor(rga:fade_in_length()) + end + + if rga:fade_out_active() then + cut_point_right = cut_point_right - math.floor(rga:fade_out_length()) + end + end + + -- ignore regions with no overlap with this one + if cut_point_left >= segments[#segments][2] + or cut_point_right <= segments[1][1] then + goto next_rg + end + + -- stock here the segments ids we should remove + local remove_id_list = {} + + -- iterate segments + for i, s in pairs(segments) do + if cut_point_right <= s[1] or cut_point_left >= s[2] then + -- rg: ___ or ____ + -- s : ___ ____ + + -- compared region doesn't overlap this segment + goto next_segment + + elseif cut_point_left <= s[1] and cut_point_right >= s[2] then + -- rg: ________ + -- s : ____ + + -- compared region exceed both front and end + -- this segment will be removed + table.insert(remove_id_list, i) + goto next_segment + + elseif cut_point_left <= s[1] then + -- rg: ____ + -- s : _____ + + if is_audio then + -- audio segment is cut 64 samples before compared region fade out + -- in the limit of the original segment + -- it also do not cut during the region fades + if cut_point_right > r_no_cut_before then + s[1] = math.min(cut_point_right, + r_no_cut_after) + end + else + s[1] = cut_point_right + end + + elseif cut_point_right >= s[2] then + -- rg: _____ + -- s : _____ + + if is_audio then + -- audio segment is cut 64 samples after compared region fade in + -- in the limit of the original segment + if cut_point_left < r_no_cut_after then + s[2] = math.max(cut_point_left, + r_no_cut_before) + end + else + s[2] = cut_point_left + end + + else + -- rg: B___C + -- s : A___________D + + -- worst case, compared region rg is above r, + -- rg starts after and finish before r. + + point_b = cut_point_left + point_c = cut_point_right + + if is_audio then + point_b = math.max(point_b, r_no_cut_before) + point_c = math.min(point_c, r_no_cut_after) + + if point_b > r_no_cut_after or point_c < r_no_cut_before then + -- we must keep the fade integrity + goto next_segment + end + end + + -- we can modify the table now because + -- we break the iteration loop just after. + -- add the new segment C->D + table.insert(segments, i + 1, {point_c, s[2]}) + + -- reduce the original segment (from A->D to A->B) + s[2] = point_b + break + end + ::next_segment:: + end + + -- remove segments that need to be removed + for i=1, #remove_id_list do + table.remove( + segments, remove_id_list[#remove_id_list +1 -i]) + end + + -- if all segments are removed, region will be deleted + if segments[1] == nil then break end + + ::next_rg:: + end + + -- security check + -- prevent segments and gaps shorter than 128 + -- and overlap segments from the same region + local last_end = -1 + local new_segments = {} + + for i, s in pairs(segments) do + if s[2] - s[1] > 128 then + if s[1] > last_end + 128 then + table.insert(new_segments, s) + else + new_segments[#new_segments][2] = s[2] + end + end + last_end = s[2] + end + + -- remember the regions we have to delete or modify + table.insert(regions_to_modify, {r, new_segments}) + end + + -- Remove, split or cut needed regions + for i=1, #regions_to_modify do + r = regions_to_modify[i][1] + segments = regions_to_modify[i][2] + + if #segments == 0 then + playlist:remove_region(r) + else + for j=1, #segments do + if j == #segments then + -- last segment, cut directly this region + rg = r + else + -- 2 segments or more for this region + -- So we need to clone the region before to cut it + rg = ARDOUR.RegionFactory.clone_region(r, true, false) + playlist:add_region(rg, rg:position(), 1, false, 0, 0, false) + + while rg:layer() > r:layer() + do + rg:lower() + end + end + + -- finally cut the region if needed + if segments[j][1] ~= rg:position() then + rg:cut_front(segments[j][1], 0) + end + + if segments[j][2] ~= rg:position() + rg:length() then + rg:cut_end(segments[j][2], 0) + end + end + end + end + ::next_route:: + end +end end From fb9e890def7f5f6e9705f89c1a8a77ec73fc4649 Mon Sep 17 00:00:00 2001 From: houston Date: Wed, 27 Jan 2021 12:47:42 +0100 Subject: [PATCH 2/4] fix indentation problems with spaces and tabs, use tabs --- share/scripts/new_clean_playlists.lua | 98 +++++++++++++-------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/share/scripts/new_clean_playlists.lua b/share/scripts/new_clean_playlists.lua index 9c0dd3e390b..04e784c4f5d 100644 --- a/share/scripts/new_clean_playlists.lua +++ b/share/scripts/new_clean_playlists.lua @@ -33,25 +33,25 @@ function factory (params) return function () playlist:set_name(track:name() .. "._MAIN_") -- we will stock here the values needed to modify/remove regions - local regions_to_modify = {} + local regions_to_modify = {} - for r in playlist:region_list():iter() do - local r_front = r:position() - local r_end = r_front + r:length() + for r in playlist:region_list():iter() do + local r_front = r:position() + local r_end = r_front + r:length() - -- region can be finally splited into many segments - -- 'segments' is a table containing segments with the form - -- {{seg1_start, seg1_end}, {seg2_start, seg2_finish}...} + -- region can be finally splited into many segments + -- 'segments' is a table containing segments with the form + -- {{seg1_start, seg1_end}, {seg2_start, seg2_finish}...} -- segments will finally be regions - local segments = {{r_front, r_end}} - + local segments = {{r_front, r_end}} + -- values used for audio tracks to prevent cut during an audible fade local r_no_cut_before = r_front local r_no_cut_after = r_end - + if is_audio then local ra = r:to_audioregion() - + if ra:fade_in_active() then r_no_cut_before = r_front + math.floor(ra:fade_in_length()) + 64 end @@ -61,11 +61,11 @@ function factory (params) return function () end end - for rg in playlist:region_list():iter() do - -- ignore regions equal or above this one - if rg:layer() <= r:layer() then - goto next_rg - end + for rg in playlist:region_list():iter() do + -- ignore regions equal or above this one + if rg:layer() <= r:layer() then + goto next_rg + end -- get points between which lower regions can be cut local cut_point_left = rg:position() @@ -86,17 +86,17 @@ function factory (params) return function () end end - -- ignore regions with no overlap with this one - if cut_point_left >= segments[#segments][2] + -- ignore regions with no overlap with this one + if cut_point_left >= segments[#segments][2] or cut_point_right <= segments[1][1] then - goto next_rg - end + goto next_rg + end - -- stock here the segments ids we should remove - local remove_id_list = {} + -- stock here the segments ids we should remove + local remove_id_list = {} - -- iterate segments - for i, s in pairs(segments) do + -- iterate segments + for i, s in pairs(segments) do if cut_point_right <= s[1] or cut_point_left >= s[2] then -- rg: ___ or ____ -- s : ___ ____ @@ -116,8 +116,8 @@ function factory (params) return function () elseif cut_point_left <= s[1] then -- rg: ____ -- s : _____ - - if is_audio then + + if is_audio then -- audio segment is cut 64 samples before compared region fade out -- in the limit of the original segment -- it also do not cut during the region fades @@ -129,11 +129,11 @@ function factory (params) return function () s[1] = cut_point_right end - elseif cut_point_right >= s[2] then + elseif cut_point_right >= s[2] then -- rg: _____ -- s : _____ - if is_audio then + if is_audio then -- audio segment is cut 64 samples after compared region fade in -- in the limit of the original segment if cut_point_left < r_no_cut_after then @@ -144,10 +144,10 @@ function factory (params) return function () s[2] = cut_point_left end - else + else -- rg: B___C -- s : A___________D - + -- worst case, compared region rg is above r, -- rg starts after and finish before r. @@ -157,7 +157,7 @@ function factory (params) return function () if is_audio then point_b = math.max(point_b, r_no_cut_before) point_c = math.min(point_c, r_no_cut_after) - + if point_b > r_no_cut_after or point_c < r_no_cut_before then -- we must keep the fade integrity goto next_segment @@ -167,26 +167,26 @@ function factory (params) return function () -- we can modify the table now because -- we break the iteration loop just after. -- add the new segment C->D - table.insert(segments, i + 1, {point_c, s[2]}) + table.insert(segments, i + 1, {point_c, s[2]}) -- reduce the original segment (from A->D to A->B) - s[2] = point_b - break - end - ::next_segment:: - end - - -- remove segments that need to be removed - for i=1, #remove_id_list do - table.remove( + s[2] = point_b + break + end + ::next_segment:: + end + + -- remove segments that need to be removed + for i=1, #remove_id_list do + table.remove( segments, remove_id_list[#remove_id_list +1 -i]) - end + end -- if all segments are removed, region will be deleted - if segments[1] == nil then break end + if segments[1] == nil then break end - ::next_rg:: - end + ::next_rg:: + end -- security check -- prevent segments and gaps shorter than 128 @@ -210,9 +210,9 @@ function factory (params) return function () end -- Remove, split or cut needed regions - for i=1, #regions_to_modify do - r = regions_to_modify[i][1] - segments = regions_to_modify[i][2] + for i=1, #regions_to_modify do + r = regions_to_modify[i][1] + segments = regions_to_modify[i][2] if #segments == 0 then playlist:remove_region(r) @@ -243,7 +243,7 @@ function factory (params) return function () end end end - end + end ::next_route:: end end end From f71043f6c8b15a2cd66d2171e20cd27609ea1c25 Mon Sep 17 00:00:00 2001 From: houston Date: Sat, 30 Jan 2021 21:41:02 +0100 Subject: [PATCH 3/4] fade_in_length() and fade_out_length() now returns samplecnt_t, not double --- libs/ardour/audioregion.cc | 12 +++++++----- share/scripts/new_clean_playlists.lua | 19 +++++++++++++++---- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 8e95b260707..55df68cabfa 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -1244,16 +1244,18 @@ AudioRegion::fade_out_is_default () const return _fade_out->size() == 2 && _fade_out->when(true) == 0 && _fade_out->when(false) == 64; } -double +samplecnt_t AudioRegion::fade_in_length () { - return _fade_in->when(false); + samplecnt_t fade_in_length = (samplecnt_t) _fade_in->when(false); + return fade_in_length; } -double -AudioRegion::fade_out_length() +samplecnt_t +AudioRegion::fade_out_length () { - return _fade_out->when(false); + samplecnt_t fade_out_length = (samplecnt_t) _fade_out->when(false); + return fade_out_length; } void diff --git a/share/scripts/new_clean_playlists.lua b/share/scripts/new_clean_playlists.lua index 04e784c4f5d..5ad118959b1 100644 --- a/share/scripts/new_clean_playlists.lua +++ b/share/scripts/new_clean_playlists.lua @@ -26,6 +26,17 @@ function factory (params) return function () is_audio = true end + local main_pl_name = track:name() .. "._MAIN_" + local main_pl_ex_name = track:name() .. "_ExMAIN_" + + -- find the playlist name track_name._MAIN_ + for pl in Session:playlists():playlists_for_track (track):iter() do + if pl:name() == main_pl_name then + pl:set_name(main_pl_ex_name) + break + end + end + -- copy current playlist, get new playlist and rename it -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Playlist track:use_copy_playlist() @@ -53,11 +64,11 @@ function factory (params) return function () local ra = r:to_audioregion() if ra:fade_in_active() then - r_no_cut_before = r_front + math.floor(ra:fade_in_length()) + 64 + r_no_cut_before = r_front + ra:fade_in_length() + 64 end if ra:fade_out_active() then - r_no_cut_after = r_end - math.floor(ra:fade_out_length()) - 64 + r_no_cut_after = r_end - ra:fade_out_length() - 64 end end @@ -78,11 +89,11 @@ function factory (params) return function () cut_point_right = cut_point_right - 64 if rga:fade_in_active() then - cut_point_left = cut_point_left + math.floor(rga:fade_in_length()) + cut_point_left = cut_point_left + rga:fade_in_length() end if rga:fade_out_active() then - cut_point_right = cut_point_right - math.floor(rga:fade_out_length()) + cut_point_right = cut_point_right - rga:fade_out_length() end end From a39fe317ce9701bed2b1faa5ceae5bb65257a07c Mon Sep 17 00:00:00 2001 From: houston Date: Sun, 31 Jan 2021 20:44:40 +0100 Subject: [PATCH 4/4] mend --- libs/ardour/ardour/audioregion.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index 40ebdc020c2..4b228793239 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -132,8 +132,8 @@ class LIBARDOUR_API AudioRegion : public Region bool fade_in_is_default () const; bool fade_out_is_default () const; - double fade_in_length (); - double fade_out_length (); + samplecnt_t fade_in_length (); + samplecnt_t fade_out_length (); void set_fade_in_active (bool yn); void set_fade_in_shape (FadeShape);