Compare commits

...

5 commits

Author SHA1 Message Date
Robin Gareus
2aadc53a44
Strip Import: add option to show all local tracks 2025-12-01 20:34:33 +01:00
Robin Gareus
5ef94b0511
Strip Import: use new API, and meta-data 2025-12-01 18:38:45 +01:00
Robin Gareus
00af254b04
Extend strip import API, include additional information 2025-12-01 16:51:10 +01:00
Robin Gareus
9089151f68
Prepare for Strip Import API overhaul 2025-12-01 15:11:07 +01:00
Robin Gareus
52af610937
Clarify MIDI randomize script description 2025-12-01 14:15:23 +01:00
5 changed files with 180 additions and 72 deletions

View file

@ -425,30 +425,76 @@ StripImportDialog::refill_import_table ()
_strip_table.attach (*manage (new ArdourHSpacer (1.0)), 0, 4, 1, 2, EXPAND | FILL, SHRINK, 4, 8);
/* clang-format on */
const bool show_all_local_tracks = _show_all_toggle->get_active ();
std::vector<std::pair<PBD::ID, PBD::ID>> sorted_map;
if (show_all_local_tracks) {
for (auto const& r : _route_map) {
PBD::ID d (0);
try {
d = _import_map.at (r.first);
} catch (...) {}
sorted_map.push_back (make_pair (r.first, d));
}
for (auto const& i : _import_map) {
if (_route_map.find (i.first) == _route_map.end ()) {
sorted_map.push_back (i);
}
}
} else {
for (auto const& i : _import_map) {
sorted_map.push_back (i);
}
}
std::sort (sorted_map.begin (), sorted_map.end (), [=] (auto& a, auto& b) {
try {
return _route_map.at (a.first).pi.order () < _route_map.at (b.first).pi.order ();
} catch (...) {
}
return a.second < b.second;
});
/* Refill table */
int r = 1;
for (auto& [rid, eid] : _import_map) {
++r;
if (_route_map.find (rid) != _route_map.end ()) {
l = manage (new Label (_route_map[rid], 0, 0.5));
for (auto& [rid, eid] : sorted_map /*_import_map*/) {
bool is_new = _route_map.find (rid) == _route_map.end ();
if (!is_new) {
l = manage (new Label (_route_map.at (rid).name, 0, 0.5));
} else {
l = manage (new Label (_("<i>New Track</i>"), 0, 0.5));
l->set_use_markup ();
}
++r;
_strip_table.attach (*l, 0, 1, r, r + 1, EXPAND | FILL, SHRINK);
#if 0
l = manage (new Label (_extern_map[eid], 1.0, 0.5));
_strip_table.attach (*l, 2, 3, r, r + 1, EXPAND | FILL, SHRINK);
#else
using namespace Menu_Helpers;
ArdourDropdown* dd = manage (new ArdourDropdown ());
for (auto& [eid, ename] : _extern_map) {
dd->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (ename), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::change_mapping), dd, rid, eid, ename)));
if (show_all_local_tracks) {
dd->add_menu_elem (MenuElem ("---", sigc::bind (sigc::mem_fun (*this, &StripImportDialog::change_mapping), dd, rid, PBD::ID (0), "---")));
}
for (auto& [eid, einfo] : _extern_map) {
dd->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (einfo.name), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::change_mapping), dd, rid, eid, einfo.name)));
}
assert (show_all_local_tracks || _extern_map.find (eid) != _extern_map.end ());
try {
dd->set_text (_extern_map.at (eid).name);
} catch (std::out_of_range const&) {
dd->set_text ("---");
}
dd->set_text (_extern_map[eid]);
_strip_table.attach (*dd, 2, 3, r, r + 1, EXPAND | FILL, SHRINK);
#endif
if (show_all_local_tracks && !is_new) {
continue;
}
ArdourButton* rm = manage (new ArdourButton ());
rm->set_icon (ArdourIcon::CloseCross);
rm->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &StripImportDialog::remove_mapping), rid));
@ -485,18 +531,20 @@ StripImportDialog::refill_import_table ()
_add_rid_dropdown->add_menu_elem (MenuElem (_(" -- New Track -- "), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), false, next_new, _("New Track"))));
sizing_texts.push_back (_(" -- New Track -- "));
for (auto& [rid, rname] : _route_map) {
if (_import_map.find (rid) != _import_map.end ()) {
continue;
if (!show_all_local_tracks) {
for (auto& [rid, rinfo] : _route_map) {
if (_import_map.find (rid) != _import_map.end ()) {
continue;
}
_add_rid_dropdown->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (rinfo.name), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), false, rid, rinfo.name)));
sizing_texts.push_back (rinfo.name);
}
_add_rid_dropdown->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (rname), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), false, rid, rname)));
sizing_texts.push_back (rname);
}
_add_eid_dropdown = manage (new ArdourWidgets::ArdourDropdown ());
for (auto& [eid, ename] : _extern_map) {
_add_eid_dropdown->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (ename), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), true, eid, ename)));
sizing_texts.push_back (ename);
for (auto& [eid, einfo] : _extern_map) {
_add_eid_dropdown->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (einfo.name), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), true, eid, einfo.name)));
sizing_texts.push_back (einfo.name);
}
_add_rid_dropdown->set_sizing_texts (sizing_texts);
@ -534,8 +582,16 @@ StripImportDialog::idle_refill_import_table ()
void
StripImportDialog::change_mapping (ArdourDropdown* dd, PBD::ID const& rid, PBD::ID const& eid, std::string const& name)
{
if (eid == PBD::ID (0)) {
_import_map.erase (rid);
} else {
_import_map[rid] = eid;
}
dd->set_text (name);
_import_map[rid] = eid;
if (_show_all_toggle->get_active ()) {
idle_refill_import_table ();
}
}
void
@ -558,7 +614,6 @@ StripImportDialog::add_mapping ()
assert (_add_rid != PBD::ID (0));
assert (_add_eid != PBD::ID (0));
_default_mapping = false;
_import_map[_add_rid] = _add_eid;
idle_refill_import_table ();
@ -568,7 +623,6 @@ void
StripImportDialog::remove_mapping (PBD::ID const& id)
{
if (1 == _import_map.erase (id)) {
_default_mapping = false;
idle_refill_import_table ();
}
}
@ -576,7 +630,6 @@ StripImportDialog::remove_mapping (PBD::ID const& id)
void
StripImportDialog::clear_mapping ()
{
_default_mapping = false;
_import_map.clear ();
idle_refill_import_table ();
}
@ -584,11 +637,18 @@ StripImportDialog::clear_mapping ()
void
StripImportDialog::import_all_strips ()
{
_default_mapping = false;
_import_map.clear ();
int64_t next_id = std::numeric_limits<uint64_t>::max () - 1 - _extern_map.size ();
for (auto& [eid, ename] : _extern_map) {
for (auto& [eid, einfo] : _extern_map) {
if (einfo.pi.special () || einfo.pi.hidden ()) {
continue;
}
#ifdef MIXBUS
if (einfo.mixbus > 0) {
continue;
}
#endif
PBD::ID next_new = PBD::ID (next_id++);
_import_map[next_new] = eid;
}
@ -600,21 +660,26 @@ void
StripImportDialog::set_default_mapping (bool and_idle_update)
{
_import_map.clear ();
_default_mapping = true;
if (_match_pbd_id) {
/* try a 1:1 mapping */
for (auto& [eid, ename] : _extern_map) {
for (auto& [eid, einfo] : _extern_map) {
if (_route_map.find (eid) != _route_map.end ()) {
_import_map[eid] = eid;
}
}
} else {
/* match by name */
for (auto& [eid, ename] : _extern_map) {
for (auto& [eid, einfo] : _extern_map) {
// TODO consider building a reverse [pointer] map
for (auto& [rid, rname] : _route_map) {
if (ename == rname) {
for (auto& [rid, rinfo] : _route_map) {
#ifdef MIXBUS
if (einfo.mixbus > 0 && einfo.mixbus == rinfo.mixbus) {
_import_map[rid] = eid;
break;
}
#endif
if (einfo == rinfo) {
_import_map[rid] = eid;
break;
}
@ -632,26 +697,29 @@ StripImportDialog::setup_strip_import_page ()
_route_map.clear ();
for (auto const& r : *_session->get_routes ()) {
_route_map[r->id ()] = r->name ();
#ifdef MIXBUS
_route_map.emplace (r->id (), Session::RouteImportInfo (r->name (), r->presentation_info (), c->mixbus ()));
#else
_route_map.emplace (r->id (), Session::RouteImportInfo (r->name (), r->presentation_info (), 0));
#endif
}
set_default_mapping (false);
using namespace Menu_Helpers;
_action = manage (new ArdourWidgets::ArdourDropdown ());
_action->add_menu_elem (MenuElem (_("Clear Mapping"), sigc::mem_fun (*this, &StripImportDialog::clear_mapping)));
_action->add_menu_elem (MenuElem (_("Import all as new tracks"), sigc::mem_fun (*this, &StripImportDialog::import_all_strips)));
_action->add_menu_elem (MenuElem (_match_pbd_id ? _("Reset - auto-map by ID") : _("Reset - auto-map by name"), sigc::bind (mem_fun (*this, &StripImportDialog::set_default_mapping), true)));
_action->set_text (_("Actions"));
refill_import_table ();
_clear_mapping = new ArdourButton (_("Clear Mapping"));
_reset_mapping = new ArdourButton (_match_pbd_id ? _("Reset - auto-map by ID") : _("Reset - auto-map by name"));
_import_strips = new ArdourButton (_("Import all as new tracks"));
_clear_mapping->signal_clicked.connect (mem_fun (*this, &StripImportDialog::clear_mapping));
_import_strips->signal_clicked.connect (mem_fun (*this, &StripImportDialog::import_all_strips));
_reset_mapping->signal_clicked.connect (sigc::bind (mem_fun (*this, &StripImportDialog::set_default_mapping), true));
_show_all_toggle = new ArdourButton (_("Show all local tracks"), ArdourButton::led_default_elements, true);
_show_all_toggle->set_led_left (true);
_show_all_toggle->set_can_focus (true);
_show_all_toggle->signal_clicked.connect (mem_fun (*this, &StripImportDialog::refill_import_table));
HBox* hbox = manage (new HBox ());
hbox->set_spacing (4);
hbox->pack_start (*_clear_mapping, true, false);
hbox->pack_start (*_import_strips, true, false);
hbox->pack_start (*_reset_mapping, true, false);
hbox->pack_start (*_action, true, false);
hbox->pack_start (*_show_all_toggle, true, false);
VBox* vbox = manage (new VBox ());
vbox->pack_start (_strip_table, false, false, 4);
@ -664,7 +732,10 @@ StripImportDialog::setup_strip_import_page ()
_page_strip.pack_end (*hbox, false, false, 4);
_page_strip.show_all ();
_ok_button->set_sensitive (true); // XXX
_ok_button->set_sensitive (true);
set_default_mapping (false);
refill_import_table ();
}
void

View file

@ -28,17 +28,14 @@
#include <ytkmm/treestore.h>
#include <ytkmm/treeview.h>
#include "ardour/search_paths.h"
#include "pbd/id.h"
#include "ardour/search_paths.h"
#include "ardour/session.h"
#include "ardour/template_utils.h"
#include "ardour_dialog.h"
namespace ARDOUR
{
class Session;
}
namespace ArdourWidgets
{
class ArdourButton;
@ -122,19 +119,18 @@ private:
ArdourWidgets::ArdourDropdown* _add_rid_dropdown;
ArdourWidgets::ArdourDropdown* _add_eid_dropdown;
ArdourWidgets::ArdourButton* _add_new_mapping;
ArdourWidgets::ArdourButton* _clear_mapping;
ArdourWidgets::ArdourButton* _reset_mapping;
ArdourWidgets::ArdourButton* _import_strips;
ArdourWidgets::ArdourDropdown* _action;
ArdourWidgets::ArdourButton* _show_all_toggle;
bool _match_pbd_id;
std::string _path;
std::map<PBD::ID, std::string> _extern_map;
std::map<PBD::ID, std::string> _route_map;
std::map<PBD::ID, PBD::ID> _import_map;
bool _match_pbd_id;
std::string _path;
std::map<PBD::ID, PBD::ID> _import_map;
std::map<PBD::ID, ARDOUR::Session::RouteImportInfo> _extern_map;
std::map<PBD::ID, ARDOUR::Session::RouteImportInfo> _route_map;
PBD::ID _add_rid;
PBD::ID _add_eid;
bool _default_mapping;
sigc::connection _notebook_connection;
sigc::connection _chooser_connection;

View file

@ -664,17 +664,39 @@ public:
std::vector<std::string> possible_states() const;
static std::vector<std::string> possible_states (std::string path);
enum RouteGroupImportMode {
IgnoreRouteGroup,
UseRouteGroup,
CreateRouteGroup
};
struct RouteImportInfo {
RouteImportInfo (std::string const& n, PresentationInfo const& p, int mb)
: name (n)
, pi (p)
, mixbus (mb)
{}
std::string name;
PresentationInfo pi;
int mixbus;
bool operator< (RouteImportInfo const& o) {
if (mixbus != o.mixbus) {
return mixbus < o.mixbus;
}
return name < o.name;
}
bool operator== (RouteImportInfo const& o) {
return mixbus == o.mixbus && name == o.name;
}
};
bool export_route_state (std::shared_ptr<RouteList> rl, const std::string& path, bool with_sources);
int import_route_state (const std::string& path, std::map<PBD::ID, PBD::ID> const&, RouteGroupImportMode rgim = CreateRouteGroup);
std::map<PBD::ID, std::string> parse_route_state (const std::string& path, bool& match_pbd_id);
std::map<PBD::ID, RouteImportInfo> parse_route_state (const std::string& path, bool& match_pbd_id);
/// The instant xml file is written to the session directory
void add_instant_xml (XMLNode&, bool write_to_config = true);

View file

@ -1198,16 +1198,8 @@ Session::export_route_state (std::shared_ptr<RouteList> rl, const string& path,
}
static bool
allow_import_route_state (XMLNode const& node, int version)
allow_import_route_state (PresentationInfo const& pi)
{
XMLNode* pnode = node.child (PresentationInfo::state_node_name.c_str ());
if (!pnode) {
return false;
}
PresentationInfo pi (PresentationInfo::Flag (0));
pi.set_state (*pnode, version);
if (pi.special (false)) { // |SurroundMaster|MonitorOut|Auditioner
return false;
}
@ -1218,10 +1210,24 @@ allow_import_route_state (XMLNode const& node, int version)
return true;
}
std::map<PBD::ID, std::string>
static bool
allow_import_route_state (XMLNode const& node, int version)
{
XMLNode* pnode = node.child (PresentationInfo::state_node_name.c_str ());
if (!pnode) {
return false;
}
PresentationInfo pi (PresentationInfo::Flag (0));
pi.set_state (*pnode, version);
return allow_import_route_state (pi);
}
std::map<PBD::ID, Session::RouteImportInfo>
Session::parse_route_state (const string& path, bool& match_pbd_id)
{
std::map<PBD::ID, std::string> rv;
std::map<PBD::ID, RouteImportInfo> rv;
XMLTree tree;
if (!tree.read (path)) {
@ -1256,11 +1262,24 @@ Session::parse_route_state (const string& path, bool& match_pbd_id)
continue;
}
if (!allow_import_route_state (*rxml, version)) {
XMLNode* pnode = rxml->child (PresentationInfo::state_node_name.c_str ());
if (!pnode) {
continue;
}
rv[id] = name;
PresentationInfo pi (PresentationInfo::Flag (0));
pi.set_state (*pnode, version);
if (!allow_import_route_state (pi)) {
continue;
}
int mixbus = 0;
#ifdef MIXBUS
rxml->get_property (X_("mixbus-num"), mixbus)
#endif
rv.emplace (id, RouteImportInfo (name, pi, mixbus));
}
}
return rv;

View file

@ -1,7 +1,7 @@
ardour { ["type"] = "EditorAction", name = "Brutalize MIDI",
license = "MIT",
author = "Ardour Team",
description = [[Randomize MIDI Note position (de-quantize).]]
description = [[Randomize MIDI Note position (de-quantize) of selected MIDI regions.]]
}
function factory () return function ()