From 2e860cd10d6fc79aa017797f600c8fe64e7e1759 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Fri, 21 Oct 2022 19:50:26 +0200 Subject: [PATCH] Move SimpleExport to libardour, add Lua bindings This allows to export a session from the commandline tool ```Lua AudioEngine:set_backend("None (Dummy)", "", "") s = load_session ("/path/to/session", "snapshot") assert (s); e = s:simple_export() assert (e:check_outputs ()) e:run_export () e = nil ``` --- libs/ardour/ardour/lua_api.h | 5 + libs/ardour/ardour/simple_export.h | 70 ++++++++++ libs/ardour/lua_api.cc | 11 ++ libs/ardour/luabindings.cc | 11 ++ libs/ardour/simple_export.cc | 214 +++++++++++++++++++++++++++++ libs/ardour/wscript | 1 + 6 files changed, 312 insertions(+) create mode 100644 libs/ardour/ardour/simple_export.h create mode 100644 libs/ardour/simple_export.cc diff --git a/libs/ardour/ardour/lua_api.h b/libs/ardour/ardour/lua_api.h index 4f42e12c15..61415400d7 100644 --- a/libs/ardour/ardour/lua_api.h +++ b/libs/ardour/ardour/lua_api.h @@ -256,6 +256,11 @@ namespace ARDOUR { namespace LuaAPI { */ int timecode_to_sample_lua (lua_State *L); + /** create a \ref SimpleExport Object + * for the current session + */ + int simple_export (lua_State* L); + /** * Delay execution until next prcess cycle starts. * @param n_cycles process-cycles to wait for. diff --git a/libs/ardour/ardour/simple_export.h b/libs/ardour/ardour/simple_export.h new file mode 100644 index 0000000000..4b8f64ff89 --- /dev/null +++ b/libs/ardour/ardour/simple_export.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2022 Robin Gareus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _ardour_simple_export_h_ +#define _ardour_simple_export_h_ + +#include "ardour/libardour_visibility.h" +#include "ardour/session_handle.h" +#include "ardour/types.h" + +namespace ARDOUR +{ +class ExportHandler; +class ExportStatus; +class ExportProfileManager; + +/** Base class for audio export + * + * This allows one to export audio from the session's + * master bus using a given export-preset. + */ +class LIBARDOUR_API SimpleExport : public ARDOUR::SessionHandlePtr +{ +public: + SimpleExport (); + virtual ~SimpleExport () {} + + void set_session (ARDOUR::Session*); + bool run_export (); + + void set_name (std::string const&); + void set_folder (std::string const&); + void set_range (samplepos_t, samplepos_t); + bool set_preset (std::string const&); + + std::string preset_uuid () const; + std::string folder () const; + bool check_outputs () const; + +protected: + boost::shared_ptr _handler; + boost::shared_ptr _status; + boost::shared_ptr _manager; + +private: + std::string _name; + std::string _folder; + std::string _pset_id; + samplepos_t _start; + samplepos_t _end; +}; + +} + +#endif diff --git a/libs/ardour/lua_api.cc b/libs/ardour/lua_api.cc index d842fe5d90..e1a7e3c8c6 100644 --- a/libs/ardour/lua_api.cc +++ b/libs/ardour/lua_api.cc @@ -37,6 +37,7 @@ #include "ardour/plugin_manager.h" #include "ardour/readable.h" #include "ardour/region_factory.h" +#include "ardour/simple_export.h" #include "ardour/source_factory.h" #include "LuaBridge/LuaBridge.h" @@ -534,6 +535,16 @@ ARDOUR::LuaAPI::wait_for_process_callback (size_t n_cycles, int64_t timeout_ms) return true; } +int +ARDOUR::LuaAPI::simple_export (lua_State* L) +{ + Session* const s = luabridge::Userdata::get (L, 1, false); + void* ptr = luabridge::UserdataValue::place (L); + SimpleExport* se = new (ptr) SimpleExport (); + se->set_session (s); + return 1; +} + void ARDOUR::LuaAPI::segfault () { diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc index da31b9837d..b306bf82fa 100644 --- a/libs/ardour/luabindings.cc +++ b/libs/ardour/luabindings.cc @@ -92,6 +92,7 @@ #include "ardour/session_object.h" #include "ardour/session_playlists.h" #include "ardour/sidechain.h" +#include "ardour/simple_export.h" #include "ardour/solo_isolate_control.h" #include "ardour/solo_safe_control.h" #include "ardour/stripable.h" @@ -1707,6 +1708,15 @@ LuaBindings::common (lua_State* L) .addStaticFunction ("force_zero_latency", &Latent::force_zero_latency) .endClass () + .beginClass ("SimpleExport") + .addFunction ("run_export", &SimpleExport::run_export) + .addFunction ("set_name", &SimpleExport::set_name) + .addFunction ("set_folder", &SimpleExport::set_folder) + .addFunction ("set_range", &SimpleExport::set_range) + .addFunction ("set_preset", &SimpleExport::set_preset) + .addFunction ("check_outputs", &SimpleExport::check_outputs) + .endClass () + .deriveWSPtrClass ("Automatable") .addCast ("to_slavable") .addFunction ("automation_control", (boost::shared_ptr(Automatable::*)(const Evoral::Parameter&, bool))&Automatable::automation_control) @@ -2878,6 +2888,7 @@ LuaBindings::common (lua_State* L) .addFunction ("vca_manager", &Session::vca_manager_ptr) .addExtCFunction ("timecode_to_sample_lua", ARDOUR::LuaAPI::timecode_to_sample_lua) .addExtCFunction ("sample_to_timecode_lua", ARDOUR::LuaAPI::sample_to_timecode_lua) + .addExtCFunction ("simple_export", &ARDOUR::LuaAPI::simple_export) .endClass () .beginClass ("RegionFactory") diff --git a/libs/ardour/simple_export.cc b/libs/ardour/simple_export.cc new file mode 100644 index 0000000000..1913c9b8f5 --- /dev/null +++ b/libs/ardour/simple_export.cc @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2022 Robin Gareus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include "ardour/export_channel_configuration.h" +#include "ardour/export_filename.h" +#include "ardour/export_preset.h" +#include "ardour/export_profile_manager.h" +#include "ardour/export_status.h" +#include "ardour/export_timespan.h" +#include "ardour/profile.h" +#include "ardour/session_directory.h" +#include "ardour/simple_export.h" + +#include "pbd/i18n.h" + +using namespace std; +using namespace ARDOUR; +using namespace PBD; + +SimpleExport::SimpleExport () + : _pset_id ("df340c53-88b5-4342-a1c8-58e0704872ea" /* CD */) + , _start (0) + , _end (0) +{ +} + +void +SimpleExport::set_session (ARDOUR::Session* s) +{ + SessionHandlePtr::set_session (s); + if (!s) { + _manager.reset (); + return; + } + + _handler = _session->get_export_handler (); + _status = _session->get_export_status (); + + /* create manager, by default it is preconfigured to + * - one Timespan (session-range, if set, otherwise empty) + * - one ChannelConfig (master-bus, IFF the session as a master) + */ + _manager.reset (new ExportProfileManager (*_session, ExportProfileManager::RangeExport)); + + /* set formats(s) and export-filename */ + set_preset (_pset_id); +} + +void +SimpleExport::set_name (std::string const& name) +{ + _name = name; +} + +void +SimpleExport::set_folder (std::string const& folder) +{ + _folder = folder; + if (!_folder.empty ()) { + g_mkdir_with_parents (_folder.c_str (), 0755); + } +} + +void +SimpleExport::set_range (samplepos_t start, samplepos_t end) +{ + _start = start; + _end = end; +} + +bool +SimpleExport::set_preset (std::string const& pset_uuid) +{ + if (!_manager) { + return false; + } + + ExportProfileManager::PresetList const& psets (_manager->get_presets ()); + assert (psets.size () > 0); + bool rv = false; + + ExportPresetPtr epp = psets.front (); + for (auto const& pset : psets) { + if (pset->id ().to_s () == pset_uuid) { + epp = pset; + rv = true; + break; + } + } + + _pset_id = epp->id ().to_s (); + /* Load preset(s) - this sets formats(s) and export-filename */ + _manager->load_preset (epp); + return rv; +} + +std::string +SimpleExport::preset_uuid () const +{ + if (!_manager) { + return _pset_id; + } + return _manager->preset ()->id ().to_s (); +} + +std::string +SimpleExport::folder () const +{ + return _folder; +} + +bool +SimpleExport::check_outputs () const +{ + if (!_manager) { + return false; + } + /* check that master-bus was added */ + auto cc (_manager->get_channel_configs ()); + assert (cc.size () == 1); + if (cc.front ()->config->get_n_chans () == 0) { + return false; + } + return true; +} + +bool +SimpleExport::run_export () +{ + if (!_session || !check_outputs ()) { + return false; + } + + if (_name.empty ()) { + _name = _session->snap_name (); + } + + if (_folder.empty ()) { + _folder = _session->session_directory ().export_path (); + } + + Location* srl; + if (_start != _end) { + ; // range already set + } else if (NULL != (srl = _session->locations ()->session_range_location ())) { + _start = srl->start_sample (); + _end = srl->end_sample (); + } + + if (_start >= _end) { + return false; + } + + /* Setup timespan */ + auto ts = _manager->get_timespans (); + assert (ts.size () == 1); + assert (ts.front ()->timespans->size () == 1); + + ts.front ()->timespans->front ()->set_name (_name); + ts.front ()->timespans->front ()->set_realtime (false); + ts.front ()->timespans->front ()->set_range (_start, _end); + + /* Now update filename(s) for each format */ + auto fns = _manager->get_filenames (); + assert (!fns.empty ()); + + auto fms = _manager->get_formats (); + for (auto const& fm : fms) { + for (auto const& fn : fns) { + fn->filename->set_folder (_folder); + fn->filename->set_timespan (ts.front ()->timespans->front ()); + info << string_compose (_("Exporting: '%1'"), fn->filename->get_path (fm->format)) << endmsg; + } + } + + /* All done, configure the handler */ + _manager->prepare_for_export (); + + try { + if (0 != _handler->do_export ()) { + return false; + } + } catch (std::exception& e) { + error << string_compose (_("Export initialization failed: %1"), e.what ()) << endmsg; + return false; + } + + while (_status->running ()) { + GUIIdle (); + // TODO only sleep if GUI did not process any events + Glib::usleep (10000); + } + + _status->finish (TRS_UI); + + return !_status->aborted (); +} diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 4b4e9a68a6..7f399ba317 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -240,6 +240,7 @@ libardour_sources = [ 'session_time.cc', 'session_transport.cc', 'sidechain.cc', + 'simple_export.cc', 'slavable.cc', 'slavable_automation_control.cc', 'smf_source.cc',