Redesign Session+Route Template Meta Script API

Remove special-cased script types. Allow Action-Scripts to be re-used
for session-setup or route-templates.
This commit is contained in:
Robin Gareus 2017-08-18 20:41:35 +02:00
parent e951e68780
commit e0a83a758e
14 changed files with 344 additions and 77 deletions

View file

@ -41,9 +41,13 @@
#include "ardour/session.h"
#include "ardour/vca.h"
#include "utils.h"
#include "LuaBridge/LuaBridge.h"
#include "add_route_dialog.h"
#include "ardour_ui.h"
#include "route_group_dialog.h"
#include "utils.h"
#include "pbd/i18n.h"
using namespace Gtk;
@ -139,6 +143,7 @@ AddRouteDialog::AddRouteDialog ()
/* template_desc is the textview that displays the currently selected template's description */
trk_template_desc.set_editable (false);
trk_template_desc.set_can_focus (false);
trk_template_desc.set_wrap_mode (Gtk::WRAP_WORD);
trk_template_desc.set_size_request(400,200);
trk_template_desc.set_name (X_("TextOnBackground"));
@ -288,25 +293,94 @@ AddRouteDialog::trk_template_row_selected ()
trk_template_desc.get_buffer ()->set_text (d);
const string n = (*iter)[track_template_columns.name];
if ( n != _("Manual Configuration") ) {
/* template or meta-template */
const string p = (*iter)[track_template_columns.path];
bool meta_template = p.substr (0, 11) == "urn:ardour:";
const string p = (*iter)[track_template_columns.path];
if ( n != _("Manual Configuration") && p.substr (0, 11) == "urn:ardour:") {
/* lua script - meta-template */
const std::map<std::string, std::string> rs (ARDOUR_UI::instance()->route_setup_info (p.substr (11)));
trk_template_desc.set_sensitive (true);
manual_label.set_sensitive (false);
add_label.set_sensitive (false);
type_label.set_sensitive (false);
name_label.set_sensitive (!meta_template);
name_label.set_sensitive (rs.find ("name") != rs.end());
group_label.set_sensitive (rs.find ("group") != rs.end());
strict_io_label.set_sensitive (rs.find ("strict_io") != rs.end());
configuration_label.set_sensitive (rs.find ("channels") != rs.end ());
mode_label.set_sensitive (rs.find ("track_mode") != rs.end ());
instrument_label.set_sensitive (rs.find ("instrument") != rs.end ());
strict_io_label.set_sensitive (rs.find ("strict_io") != rs.end());
track_bus_combo.set_sensitive (false);
routes_spinner.set_sensitive (rs.find ("how_many") != rs.end ());
name_template_entry.set_sensitive (rs.find ("name") != rs.end ());
route_group_combo.set_sensitive (rs.find ("group") != rs.end());
channel_combo.set_sensitive (rs.find ("channels") != rs.end ());
mode_combo.set_sensitive (rs.find ("track_mode") != rs.end ());
instrument_combo.set_sensitive (rs.find ("instrument") != rs.end ());
strict_io_combo.set_sensitive (rs.find ("strict_io") != rs.end());
std::map<string,string>::const_iterator it;
if ((it = rs.find ("name")) != rs.end()) {
name_template_entry.set_text (it->second);
}
if ((it = rs.find ("how_many")) != rs.end()) {
routes_adjustment.set_value (atoi (it->second.c_str()));
}
if ((it = rs.find ("track_mode")) != rs.end()) {
switch ((ARDOUR::TrackMode) atoi (it->second.c_str())) {
case ARDOUR::Normal:
mode_combo.set_active_text (_("Normal"));
break;
case ARDOUR::NonLayered:
mode_combo.set_active_text (_("Nn Layered"));
break;
case ARDOUR::Destructive:
mode_combo.set_active_text (_("Tape"));
break;
}
}
if ((it = rs.find ("strict_io")) != rs.end()) {
if (it->second == X_("true")) {
strict_io_combo.set_active (1);
} else if (it->second == X_("false")) {
strict_io_combo.set_active (0);
}
}
if ((it = rs.find ("channels")) != rs.end()) {
uint32_t channels = atoi (it->second.c_str());
for (ChannelSetups::iterator i = channel_setups.begin(); i != channel_setups.end(); ++i) {
if ((*i).channels == channels) {
channel_combo.set_active_text ((*i).name);
break;
}
}
}
} else if ( n != _("Manual Configuration")) {
/* user-template */
trk_template_desc.set_sensitive (true);
manual_label.set_sensitive (false);
add_label.set_sensitive (false);
type_label.set_sensitive (false);
name_label.set_sensitive (true);
group_label.set_sensitive (false);
strict_io_label.set_sensitive (false);
configuration_label.set_sensitive (false);
mode_label.set_sensitive (false);
instrument_label.set_sensitive (false);
routes_spinner.set_sensitive (!meta_template);
routes_spinner.set_sensitive (true);
track_bus_combo.set_sensitive (false);
name_template_entry.set_sensitive (!meta_template);
name_template_entry.set_sensitive (true);
channel_combo.set_sensitive (false);
mode_combo.set_sensitive (false);
instrument_combo.set_sensitive (false);
@ -314,7 +388,7 @@ AddRouteDialog::trk_template_row_selected ()
route_group_combo.set_sensitive (false);
} else {
/* all manual mode */
trk_template_desc.set_sensitive (false);
manual_label.set_sensitive (true);
@ -564,21 +638,26 @@ AddRouteDialog::mode ()
return ARDOUR::Normal;
}
uint32_t
AddRouteDialog::channel_count ()
{
string str = channel_combo.get_active_text();
for (ChannelSetups::iterator i = channel_setups.begin(); i != channel_setups.end(); ++i) {
if (str == (*i).name) {
return (*i).channels;
}
}
return 0;
}
ChanCount
AddRouteDialog::channels ()
{
ChanCount ret;
string str;
switch (type_wanted()) {
case AudioTrack:
case AudioBus:
str = channel_combo.get_active_text();
for (ChannelSetups::iterator i = channel_setups.begin(); i != channel_setups.end(); ++i) {
if (str == (*i).name) {
ret.set (DataType::AUDIO, (*i).channels);
break;
}
}
ret.set (DataType::MIDI, channel_count ());
ret.set (DataType::MIDI, 0);
break;
@ -589,13 +668,7 @@ AddRouteDialog::channels ()
break;
case MixedTrack:
str = channel_combo.get_active_text();
for (ChannelSetups::iterator i = channel_setups.begin(); i != channel_setups.end(); ++i) {
if (str == (*i).name) {
ret.set (DataType::AUDIO, (*i).channels);
break;
}
}
ret.set (DataType::MIDI, channel_count ());
ret.set (DataType::MIDI, 1);
break;
default:
@ -676,8 +749,11 @@ AddRouteDialog::refill_channel_setups ()
trk_template_model->clear();
/* Add any Lua scripts (factory templates) found in the scripts folder */
LuaScriptList& ms (LuaScripting::instance ().scripts (LuaScriptInfo::TrackSetup));
LuaScriptList& ms (LuaScripting::instance ().scripts (LuaScriptInfo::EditorAction));
for (LuaScriptList::const_iterator s = ms.begin(); s != ms.end(); ++s) {
if (!((*s)->subtype & LuaScriptInfo::RouteSetup)) {
continue;
}
TreeModel::Row row;
if ( (*s)->name == "Add tracks") { //somewhat-special, most-used template
row = *(trk_template_model->prepend ());

View file

@ -71,6 +71,7 @@ public:
TypeWanted type_wanted() const;
ARDOUR::ChanCount channels ();
uint32_t channel_count ();
int count ();
std::string name_template () const;

View file

@ -3885,6 +3885,100 @@ static void _lua_print (std::string s) {
PBD::info << "LuaInstance: " << s << endmsg;
}
std::map<std::string, std::string>
ARDOUR_UI::route_setup_info (const std::string& script_path)
{
std::map<std::string, std::string> rv;
if (!Glib::file_test (script_path, Glib::FILE_TEST_EXISTS | Glib::FILE_TEST_IS_REGULAR)) {
return rv;
}
LuaState lua;
lua.Print.connect (&_lua_print);
lua.sandbox (true);
lua_State* L = lua.getState();
LuaInstance::register_classes (L);
LuaBindings::set_session (L, _session);
luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
lua_setglobal (L, "Editor");
lua.do_command ("function ardour () end");
lua.do_file (script_path);
try {
luabridge::LuaRef fn = luabridge::getGlobal (L, "route_setup");
if (!fn.isFunction ()) {
return rv;
}
luabridge::LuaRef rs = fn ();
if (!rs.isTable ()) {
return rv;
}
for (luabridge::Iterator i(rs); !i.isNil (); ++i) {
if (!i.key().isString()) {
continue;
}
std::string key = i.key().tostring();
if (i.value().isString() || i.value().isNumber() || i.value().isBoolean()) {
rv[key] = i.value().tostring();
}
}
} catch (luabridge::LuaException const& e) {
cerr << "LuaException:" << e.what () << endl;
return rv;
}
return rv;
}
void
ARDOUR_UI::meta_route_setup (const std::string& script_path)
{
if (!Glib::file_test (script_path, Glib::FILE_TEST_EXISTS | Glib::FILE_TEST_IS_REGULAR)) {
return;
}
assert (add_route_dialog);
int count;
if ((count = add_route_dialog->count()) <= 0) {
return;
}
LuaState lua;
lua.Print.connect (&_lua_print);
lua.sandbox (true);
lua_State* L = lua.getState();
LuaInstance::register_classes (L);
LuaBindings::set_session (L, _session);
luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
lua_setglobal (L, "Editor");
lua.do_command ("function ardour () end");
lua.do_file (script_path);
luabridge::LuaRef args (luabridge::newTable (L));
args["name"] = add_route_dialog->name_template ();
args["insert_at"] = translate_order (add_route_dialog->insert_at());
args["group"] = add_route_dialog->route_group ();
args["strict_io"] = add_route_dialog->use_strict_io ();
args["instrument"] = add_route_dialog->requested_instrument ();
args["track_mode"] = add_route_dialog->mode ();
args["channels"] = add_route_dialog->channel_count ();
args["how_many"] = count;
try {
luabridge::LuaRef fn = luabridge::getGlobal (L, "factory");
if (fn.isFunction()) {
fn (args)();
}
} catch (luabridge::LuaException const& e) {
cerr << "LuaException:" << e.what () << endl;
}
}
void
ARDOUR_UI::meta_session_setup (const std::string& script_path)
{
@ -3906,9 +4000,9 @@ ARDOUR_UI::meta_session_setup (const std::string& script_path)
lua.do_file (script_path);
try {
luabridge::LuaRef fn = luabridge::getGlobal (L, "session_setup");
luabridge::LuaRef fn = luabridge::getGlobal (L, "factory");
if (fn.isFunction()) {
fn ();
fn ()();
}
} catch (luabridge::LuaException const& e) {
cerr << "LuaException:" << e.what () << endl;
@ -4379,7 +4473,7 @@ ARDOUR_UI::add_route_dialog_response (int r)
std::string template_path = add_route_dialog->get_template_path();
if (!template_path.empty() && template_path.substr (0, 11) == "urn:ardour:") {
meta_session_setup (template_path.substr (11));
meta_route_setup (template_path.substr (11));
return;
}

View file

@ -67,6 +67,8 @@
#include "ardour/session_handle.h"
#include "ardour/system_exec.h"
#include "LuaBridge/LuaBridge.h"
#include "video_timeline.h"
#include "widgets/ardour_button.h"
@ -347,6 +349,8 @@ public:
ARDOUR::PresentationInfo::order_t translate_order (RouteDialogs::InsertAt);
std::map<std::string, std::string> route_setup_info (const std::string& script_path);
protected:
friend class PublicEditor;
@ -624,6 +628,7 @@ private:
void manage_templates ();
void meta_session_setup (const std::string& script_path);
void meta_route_setup (const std::string& script_path);
void edit_metadata ();
void import_metadata ();

View file

@ -237,7 +237,7 @@ SessionDialog::meta_master_bus_profile (std::string script_path) const
}
LuaScriptInfo::ScriptType type = LuaScriptInfo::str2type (nfo["type"].cast<std::string>());
if (type != LuaScriptInfo::SessionSetup) {
if (type != LuaScriptInfo::SessionInit) {
return UINT32_MAX;
}
@ -562,10 +562,22 @@ SessionDialog::populate_session_templates ()
template_model->clear ();
//Add any Lua scripts (factory templates) found in the scripts folder
LuaScriptList& ms (LuaScripting::instance ().scripts (LuaScriptInfo::SessionSetup));
LuaScriptList& ms (LuaScripting::instance ().scripts (LuaScriptInfo::SessionInit));
for (LuaScriptList::const_iterator s = ms.begin(); s != ms.end(); ++s) {
TreeModel::Row row;
row = *(template_model->append ());
TreeModel::Row row = *(template_model->append ());
row[session_template_columns.name] = "Meta: " + (*s)->name;
row[session_template_columns.path] = "urn:ardour:" + (*s)->path;
row[session_template_columns.description] = (*s)->description;
row[session_template_columns.created_with_short] = _("{Factory Template}");
row[session_template_columns.created_with_long] = _("{Factory Template}");
}
LuaScriptList& as (LuaScripting::instance ().scripts (LuaScriptInfo::EditorAction));
for (LuaScriptList::const_iterator s = as.begin(); s != as.end(); ++s) {
if (!((*s)->subtype & LuaScriptInfo::SessionSetup)) {
continue;
}
TreeModel::Row row = *(template_model->append ());
row[session_template_columns.name] = "Meta: " + (*s)->name;
row[session_template_columns.path] = "urn:ardour:" + (*s)->path;
row[session_template_columns.description] = (*s)->description;

View file

@ -38,8 +38,14 @@ class LIBARDOUR_API LuaScriptInfo {
EditorHook,
EditorAction,
Snippet,
SessionSetup,
TrackSetup,
SessionInit,
};
/* binary flags, valid for ActionScripts */
enum ScriptSubType {
None = 0x00,
RouteSetup = 0x01,
SessionSetup = 0x02,
};
static std::string type2str (const ScriptType t);
@ -47,6 +53,7 @@ class LIBARDOUR_API LuaScriptInfo {
LuaScriptInfo (ScriptType t, const std::string &n, const std::string &p, const std::string &uid)
: type (t)
, subtype (0)
, name (n)
, path (p)
, unique_id (uid)
@ -55,6 +62,8 @@ class LIBARDOUR_API LuaScriptInfo {
virtual ~LuaScriptInfo () { }
ScriptType type;
uint32_t subtype;
std::string name;
std::string path;
std::string unique_id;

View file

@ -158,12 +158,9 @@ LuaScripting::scan ()
case LuaScriptInfo::Snippet:
_sl_snippet->push_back(lsi);
break;
case LuaScriptInfo::SessionSetup:
case LuaScriptInfo::SessionInit:
_sl_setup->push_back(lsi);
break;
case LuaScriptInfo::TrackSetup:
_sl_tracks->push_back(lsi);
break;
default:
break;
}
@ -286,6 +283,24 @@ LuaScripting::scan_script (const std::string &fn, const std::string &sc)
if (key == "description") { lsi->description = val; }
if (key == "category") { lsi->category = val; }
}
if (type == LuaScriptInfo::EditorAction) {
luabridge::LuaRef lua_rs = luabridge::getGlobal (L, "route_setup");
if (lua_rs.isFunction ()) {
lsi->subtype |= LuaScriptInfo::RouteSetup;
}
luabridge::LuaRef lua_ss = luabridge::getGlobal (L, "session_setup");
if (lua_ss.isFunction ()) {
if (lua_ss () == true) {
lsi->subtype |= LuaScriptInfo::SessionSetup;
}
}
}
return lsi;
}
@ -312,12 +327,9 @@ LuaScripting::scripts (LuaScriptInfo::ScriptType type) {
case LuaScriptInfo::Snippet:
return *_sl_snippet;
break;
case LuaScriptInfo::SessionSetup:
case LuaScriptInfo::SessionInit:
return *_sl_setup;
break;
case LuaScriptInfo::TrackSetup:
return *_sl_tracks;
break;
default:
break;
}
@ -333,8 +345,7 @@ LuaScriptInfo::type2str (const ScriptType t) {
case LuaScriptInfo::EditorHook: return "EditorHook";
case LuaScriptInfo::EditorAction: return "EditorAction";
case LuaScriptInfo::Snippet: return "Snippet";
case LuaScriptInfo::SessionSetup: return "SessionSetup";
case LuaScriptInfo::TrackSetup: return "TrackSetup";
case LuaScriptInfo::SessionInit: return "SessionInit";
default: return "Invalid";
}
}
@ -347,8 +358,7 @@ LuaScriptInfo::str2type (const std::string& str) {
if (!strcasecmp (type, "EditorHook")) {return LuaScriptInfo::EditorHook;}
if (!strcasecmp (type, "EditorAction")) {return LuaScriptInfo::EditorAction;}
if (!strcasecmp (type, "Snippet")) {return LuaScriptInfo::Snippet;}
if (!strcasecmp (type, "SessionSetup")) {return LuaScriptInfo::SessionSetup;}
if (!strcasecmp (type, "TrackSetup")) {return LuaScriptInfo::TrackSetup;}
if (!strcasecmp (type, "SessionInit")) {return LuaScriptInfo::SessionInit;}
return LuaScriptInfo::Invalid;
}

View file

@ -338,7 +338,7 @@ CoreAudioBackend::set_buffer_size (uint32_t bs)
for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i) {
coreaudio_set_realtime_policy (*i);
}
engine.buffer_size_change (bs);
//engine.buffer_size_change (bs);
return 0;
}

View file

@ -1,11 +1,52 @@
ardour {
["type"] = "TrackSetup",
name = "Route Test",
description = [[ FOR TESTING AND PROTOTYING ONLY ]]
["type"] = "EditorAction",
name = "Generic Audio Track",
description = [[Example ]]
}
-- DON'T COUNT ON THIS TO REMAIN AS IS.
-- This may turn into a factory method, re-usable as ActionScript.
function session_setup ()
Session:new_audio_track (1, 1, nil, 1, "Hello", ARDOUR.PresentationInfo.max_order, ARDOUR.TrackMode.Normal)
-- If a route_setup function is present in an Editor Action Script
-- the script is also listed in the "add track/bus" dialog as meta-template
--
-- The function is expected to return a Lua table. The table may be empty.
function route_setup ()
local e = Session:engine()
local _, t = e:get_backend_ports ("", ARDOUR.DataType("audio"), ARDOUR.PortFlags.IsOutput | ARDOUR.PortFlags.IsPhysical, C.StringVector())
return
{
-- keys control which AddRouteDialog controls are made sensitive.
-- The following keys accept a default value to pre-seed the dialog
['how_many'] = t[4]:size(),
['name'] = 'Audio',
['channels'] = 2,
-- these keys just need to be set (to something other than nil)
['insert_at'] = ARDOUR.PresentationInfo.max_order,
['group'] = false,
--[[
['track_mode'] = ARDOUR.TrackMode.Normal,
['strict_io'] = true,
--]]
}
end
-- The Script can be used as EditorAction in which case it can
-- optionally provide instantiation parmaters
function action_params ()
return
{
['how_many'] = { title = "How Many tracks to add", default = "1" },
["name"] = { title = "Track Name Prefix", default = "Audio" },
}
end
function factory (params) return function ()
local p = params or route_setup ()
local name = p["name"] or 'Audio'
local how_many = p["how_many"] or 1
local channels = p["channels"] or 1
local insert_at = p["insert_at"] or ARDOUR.PresentationInfo.max_order;
local group = p["group"] or nil
Session:new_audio_track (channels, channels, group, how_many, name, insert_at, ARDOUR.TrackMode.Normal)
end end

View file

@ -1,5 +1,5 @@
ardour {
["type"] = "SessionSetup",
["type"] = "SessionInit",
name = "Live Band Recording Session",
description = [[
This template helps create the tracks for a typical pop/rock band.
@ -10,7 +10,7 @@ Each track comes with its pre-assigned grouping, routing, EQ and plugins.
]]
}
function session_setup ()
function factory () return function ()
--prompt the user for the tracks they'd like to instantiate
local dialog_options = {
@ -92,4 +92,4 @@ function session_setup ()
Editor:access_action("Editor","fit_all_tracks")
Session:save_state("");
end
end end

View file

@ -1,5 +1,5 @@
ardour {
["type"] = "SessionSetup",
["type"] = "SessionInit",
name = "Recording Session",
description = [[Add as many mono tracks to the new session as there are physical audio inputs and optionally record-arm them.]]
}
@ -7,21 +7,28 @@ ardour {
---- For use with templates: Session Template setup-hook
--
-- If a script named 'template.lua' exists in a session-template folder
-- the `session_setup` function of the script is called after
-- creating the session from the template.
-- the function produced by the 'factory' function of the script is called
-- once after creating the session from the template.
--
-- (e.g. ~/.config/ardour5/templates/Template-Name/template.lua)
--
--
---- For use as meta-session
---- For use as meta-session (specic session-setup scripts)
--
-- Every Lua script in the script-folder of type "SessionSetup"
-- Every Lua script in the script-folder of type "SessionInit"
-- is listed as implicit template in the new-session dialog.
-- The scripts 'session_setup' function is called once after
-- creating a new, empty session.
-- The function produced by the scripts `factory` function is called
-- once after creating a new, empty session.
--
---- For use as meta-session (general purpose Actions)
--
-- In some cases normal action scripts can also serve as session-setup
-- To include those ActionScripts in the template-list the script needs
-- to implement an additional function
-- function session_setup () return true end;
-- The script's factory will be called without any parameters
function session_setup ()
function factory () return function ()
local e = Session:engine()
-- from the engine's POV readable/capture ports are "outputs"
local _, t = e:get_backend_ports ("", ARDOUR.DataType("audio"), ARDOUR.PortFlags.IsOutput | ARDOUR.PortFlags.IsPhysical, C.StringVector())
@ -50,4 +57,4 @@ function session_setup ()
end
Session:save_state("");
end
end end

View file

@ -1,5 +1,5 @@
ardour {
["type"] = "TrackSetup",
["type"] = "EditorAction",
name = "Live Band Recording Session",
description = [[
This template helps create the tracks for a typical pop/rock band.
@ -11,10 +11,12 @@ Each track will be pre-assigned with a color.
Optionally, tracks may be assigned to sensible Groups ( vocals, guitars, drums )
Optionally, tracks may be assigned Gates and other plugins.
]]
]]
}
function session_setup ()
function route_setup () return {} end
function factory () return function ()
--prompt the user for the tracks they'd like to instantiate
local dialog_options = {
@ -334,4 +336,4 @@ function session_setup ()
Editor:access_action("Editor","fit_all_tracks")
Session:save_state("");
end
end end

View file

@ -1,5 +1,5 @@
ardour {
["type"] = "TrackSetup",
["type"] = "EditorAction",
name = "Add tracks",
description = [[
This template creates audio tracks.
@ -12,7 +12,17 @@ You will be prompted for:
]]
}
function session_setup ()
function route_setup ()
return
{
['Insert_at'] = ARDOUR.PresentationInfo.max_order;
}
end
function factory (params) return function ()
local p = params or route_setup ()
local insert_at = p["insert_at"] or ARDOUR.PresentationInfo.max_order;
local e = Session:engine()
-- from the engine's POV readable/capture ports are "outputs"
local _, t = e:get_backend_ports ("", ARDOUR.DataType("audio"), ARDOUR.PortFlags.IsOutput | ARDOUR.PortFlags.IsPhysical, C.StringVector())
@ -31,11 +41,11 @@ function session_setup ()
end
-- create tracks
local tl = Session:new_audio_track (1, 1, nil, rv['tracks'], "", ARDOUR.PresentationInfo.max_order, ARDOUR.TrackMode.Normal)
local tl = Session:new_audio_track (1, 1, nil, rv['tracks'], "", insert_at, ARDOUR.TrackMode.Normal)
-- and optionally record-arm them
if rv['recarm'] then
for track in tl:iter() do
track:rec_enable_control ():set_value (1, PBD.GroupControlDisposition.NoGroup)
end
end
end
end end

View file

@ -1,11 +1,11 @@
ardour {
["type"] = "SessionSetup",
["type"] = "SessionInit",
name = "Advanced Session",
description = [[Allows to configure master-bus and autoconnect]],
master_bus = 0
}
function session_setup ()
function factory () return function ()
local auto_connect_in = {
[0] = "Manually",
@ -56,4 +56,4 @@ function session_setup ()
ARDOUR.config():set_output_auto_connect (rv['ac_output'])
Session:save_state("");
end
end end