Merge branch 'master' into mixer-snapshots
This commit is contained in:
parent
db09fe1eb8
commit
ad46ad409a
|
@ -90,7 +90,7 @@ ActionManager::load_menus (const string& menus_file)
|
|||
if (!loaded) {
|
||||
cerr << string_compose (_("%1 will not work without a valid menu definition file"), PROGRAM_NAME) << endl;
|
||||
error << string_compose (_("%1 will not work without a valid menu definition file"), PROGRAM_NAME) << endmsg;
|
||||
exit(1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ export ARDOUR_DATA_PATH=$TOP:$TOP/build:$TOP/gtk2_ardour:$TOP/build/gtk2_ardour:
|
|||
export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:.
|
||||
export ARDOUR_EXPORT_FORMATS_PATH=$TOP/export:.
|
||||
export ARDOUR_THEMES_PATH=$TOP/gtk2_ardour/themes:.
|
||||
export ARDOUR_BACKEND_PATH=$libs/backends/jack:$libs/backends/wavesaudio:$libs/backends/dummy:$libs/backends/alsa:$libs/backends/coreaudio:$libs/backends/portaudio:$libs/backends/asio
|
||||
export ARDOUR_BACKEND_PATH=$libs/backends/jack:$libs/backends/dummy:$libs/backends/alsa:$libs/backends/coreaudio:$libs/backends/portaudio
|
||||
export ARDOUR_TEST_PATH=$TOP/libs/ardour/test/data
|
||||
export PBD_TEST_PATH=$TOP/libs/pbd/test
|
||||
export EVORAL_TEST_PATH=$TOP/libs/evoral/test/testdata
|
||||
|
|
|
@ -353,7 +353,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
|
|||
MessageDialog msg (string_compose (_("Your configuration files were copied. You can now restart %1."), PROGRAM_NAME), true);
|
||||
msg.run ();
|
||||
/* configuration was modified, exit immediately */
|
||||
_exit (0);
|
||||
_exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
|
@ -742,7 +742,7 @@ ARDOUR_UI::post_engine ()
|
|||
|
||||
halt_connection.disconnect ();
|
||||
AudioEngine::instance()->stop ();
|
||||
exit (0);
|
||||
exit (EXIT_SUCCESS);
|
||||
|
||||
}
|
||||
|
||||
|
@ -819,7 +819,7 @@ ARDOUR_UI::post_engine ()
|
|||
|
||||
halt_connection.disconnect ();
|
||||
AudioEngine::instance()->stop ();
|
||||
exit (0);
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* this being a GUI and all, we want peakfiles */
|
||||
|
@ -1264,7 +1264,7 @@ ARDOUR_UI::starting ()
|
|||
c.signal_toggled().connect (sigc::hide_return (sigc::bind (sigc::ptr_fun (toggle_file_existence), path)));
|
||||
|
||||
if (d.run () != RESPONSE_OK) {
|
||||
_exit (0);
|
||||
_exit (EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -1881,7 +1881,7 @@ ARDOUR_UI::open_recent_session ()
|
|||
recent_session_dialog.hide();
|
||||
return;
|
||||
} else {
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3245,7 +3245,7 @@ ARDOUR_UI::load_from_application_api (const std::string& path)
|
|||
ARDOUR_COMMAND_LINE::session_name = "";
|
||||
|
||||
if (get_session_parameters (true, false)) {
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3288,15 +3288,33 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
|
|||
template_name = load_template;
|
||||
}
|
||||
|
||||
session_name = basename_nosuffix (ARDOUR_COMMAND_LINE::session_name);
|
||||
session_path = ARDOUR_COMMAND_LINE::session_name;
|
||||
|
||||
if (!session_path.empty()) {
|
||||
|
||||
if (Glib::file_test (session_path.c_str(), Glib::FILE_TEST_EXISTS)) {
|
||||
|
||||
session_name = basename_nosuffix (ARDOUR_COMMAND_LINE::session_name);
|
||||
|
||||
if (Glib::file_test (session_path.c_str(), Glib::FILE_TEST_IS_REGULAR)) {
|
||||
/* session/snapshot file, change path to be dir */
|
||||
session_path = Glib::path_get_dirname (session_path);
|
||||
}
|
||||
} else {
|
||||
|
||||
/* session (file or folder) does not exist ... did the
|
||||
* user give us a path or just a name?
|
||||
*/
|
||||
|
||||
if (session_path.find (G_DIR_SEPARATOR) == string::npos) {
|
||||
/* user gave session name with no path info, use
|
||||
default session folder.
|
||||
*/
|
||||
session_name = ARDOUR_COMMAND_LINE::session_name;
|
||||
session_path = Glib::build_filename (Config->get_default_session_parent_dir (), session_name);
|
||||
} else {
|
||||
session_name = basename_nosuffix (ARDOUR_COMMAND_LINE::session_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3328,11 +3346,8 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
|
|||
session_dialog.clear_given ();
|
||||
}
|
||||
|
||||
if (should_be_new || session_name.empty()) {
|
||||
/* need the dialog to get info from user */
|
||||
|
||||
cerr << "run dialog\n";
|
||||
|
||||
if (session_name.empty()) {
|
||||
/* need the dialog to get the name (at least) from the user */
|
||||
switch (session_dialog.run()) {
|
||||
case RESPONSE_ACCEPT:
|
||||
break;
|
||||
|
@ -3486,12 +3501,7 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
|
|||
|
||||
if (ret == -2) {
|
||||
/* not connected to the AudioEngine, so quit to avoid an infinite loop */
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (!ARDOUR_COMMAND_LINE::immediate_save.empty()) {
|
||||
_session->save_state (ARDOUR_COMMAND_LINE::immediate_save, false);
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* clear this to avoid endless attempts to load the
|
||||
|
@ -3521,7 +3531,7 @@ ARDOUR_UI::close_session()
|
|||
ARDOUR_COMMAND_LINE::session_name = "";
|
||||
|
||||
if (get_session_parameters (true, false)) {
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3585,7 +3595,7 @@ ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name,
|
|||
|
||||
switch (response) {
|
||||
case RESPONSE_CANCEL:
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -5580,7 +5590,7 @@ ARDOUR_UI::audioengine_became_silent ()
|
|||
case Gtk::RESPONSE_NO:
|
||||
/* save and quit */
|
||||
save_state_canfail ("");
|
||||
exit (0);
|
||||
exit (EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
case Gtk::RESPONSE_CANCEL:
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
#include "selection_memento.h"
|
||||
#include "tempo_curve.h"
|
||||
|
||||
#include "ptformat/ptfformat.h"
|
||||
#include "ptformat/ptformat.h"
|
||||
|
||||
namespace Gtkmm2ext {
|
||||
class Bindings;
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
#include "ardour/session.h"
|
||||
#include "pbd/memento_command.h"
|
||||
|
||||
#include "ptformat/ptfformat.h"
|
||||
#include "ptformat/ptformat.h"
|
||||
|
||||
#include "ardour_ui.h"
|
||||
#include "cursor_context.h"
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include <gtkmm/menu.h>
|
||||
|
||||
#include "pbd/convert.h"
|
||||
|
||||
|
@ -29,8 +32,6 @@
|
|||
#include "ardour/route.h"
|
||||
#include "ardour/session.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "export_channel_selector.h"
|
||||
#include "route_sorter.h"
|
||||
|
||||
|
@ -547,18 +548,37 @@ RegionExportChannelSelector::handle_selection ()
|
|||
TrackExportChannelSelector::TrackExportChannelSelector (ARDOUR::Session * session, ProfileManagerPtr manager)
|
||||
: ExportChannelSelector(session, manager)
|
||||
, track_output_button(_("Apply track/bus processing"))
|
||||
, select_tracks_button (_("Select all tracks"))
|
||||
, select_busses_button (_("Select all busses"))
|
||||
, select_none_button (_("Deselect all"))
|
||||
{
|
||||
pack_start(main_layout);
|
||||
|
||||
// Populate Selection Menu
|
||||
{
|
||||
using namespace Gtk::Menu_Helpers;
|
||||
|
||||
select_menu.set_text (_("Selection Actions"));
|
||||
select_menu.disable_scrolling ();
|
||||
|
||||
select_menu.AddMenuElem (MenuElem (_("Select tracks"), sigc::mem_fun (*this, &TrackExportChannelSelector::select_tracks)));
|
||||
select_menu.AddMenuElem (MenuElem (_("Select busses"), sigc::mem_fun (*this, &TrackExportChannelSelector::select_busses)));
|
||||
select_menu.AddMenuElem (MenuElem (_("Deselect all"), sigc::mem_fun (*this, &TrackExportChannelSelector::select_none)));
|
||||
select_menu.AddMenuElem (SeparatorElem ());
|
||||
|
||||
exclude_hidden = new Gtk::CheckMenuItem (_("Exclude Hidden"));
|
||||
exclude_hidden->set_active (false);
|
||||
exclude_hidden->show();
|
||||
select_menu.AddMenuElem (*exclude_hidden);
|
||||
|
||||
exclude_muted = new Gtk::CheckMenuItem (_("Exclude Muted"));
|
||||
exclude_muted->set_active (true);
|
||||
exclude_muted->show();
|
||||
select_menu.AddMenuElem (*exclude_muted);
|
||||
}
|
||||
|
||||
// Options
|
||||
options_box.pack_start(track_output_button);
|
||||
options_box.pack_start (select_tracks_button);
|
||||
options_box.pack_start (select_busses_button);
|
||||
options_box.pack_start (select_none_button);
|
||||
main_layout.pack_start(options_box, false, false);
|
||||
options_box.set_spacing (8);
|
||||
options_box.pack_start (track_output_button, false, false);
|
||||
options_box.pack_start (select_menu, false, false);
|
||||
main_layout.pack_start (options_box, false, false);
|
||||
|
||||
// Track scroller
|
||||
track_scroller.add (track_view);
|
||||
|
@ -589,10 +609,6 @@ TrackExportChannelSelector::TrackExportChannelSelector (ARDOUR::Session * sessio
|
|||
column->pack_start (*text_renderer, false);
|
||||
column->add_attribute (text_renderer->property_text(), track_cols.label);
|
||||
|
||||
select_tracks_button.signal_clicked().connect (sigc::mem_fun (*this, &TrackExportChannelSelector::select_tracks));
|
||||
select_busses_button.signal_clicked().connect (sigc::mem_fun (*this, &TrackExportChannelSelector::select_busses));
|
||||
select_none_button.signal_clicked().connect (sigc::mem_fun (*this, &TrackExportChannelSelector::select_none));
|
||||
|
||||
track_output_button.signal_clicked().connect (sigc::mem_fun (*this, &TrackExportChannelSelector::track_outputs_selected));
|
||||
|
||||
fill_list();
|
||||
|
@ -600,6 +616,12 @@ TrackExportChannelSelector::TrackExportChannelSelector (ARDOUR::Session * sessio
|
|||
show_all_children ();
|
||||
}
|
||||
|
||||
TrackExportChannelSelector::~TrackExportChannelSelector ()
|
||||
{
|
||||
delete exclude_hidden;
|
||||
delete exclude_muted;
|
||||
}
|
||||
|
||||
void
|
||||
TrackExportChannelSelector::sync_with_manager ()
|
||||
{
|
||||
|
@ -610,11 +632,19 @@ TrackExportChannelSelector::sync_with_manager ()
|
|||
void
|
||||
TrackExportChannelSelector::select_tracks ()
|
||||
{
|
||||
bool excl_hidden = exclude_hidden->get_active ();
|
||||
bool excl_muted = exclude_muted->get_active ();
|
||||
|
||||
for (Gtk::ListStore::Children::iterator it = track_list->children().begin(); it != track_list->children().end(); ++it) {
|
||||
Gtk::TreeModel::Row row = *it;
|
||||
boost::shared_ptr<Route> route = row[track_cols.route];
|
||||
if (boost::dynamic_pointer_cast<Track> (route)) {
|
||||
// it's a track
|
||||
if (excl_muted && route->muted ()) {
|
||||
continue;
|
||||
}
|
||||
if (excl_hidden && route->is_hidden ()) {
|
||||
continue;
|
||||
}
|
||||
row[track_cols.selected] = true;
|
||||
}
|
||||
}
|
||||
|
@ -624,11 +654,19 @@ TrackExportChannelSelector::select_tracks ()
|
|||
void
|
||||
TrackExportChannelSelector::select_busses ()
|
||||
{
|
||||
bool excl_hidden = exclude_hidden->get_active ();
|
||||
bool excl_muted = exclude_muted->get_active ();
|
||||
|
||||
for (Gtk::ListStore::Children::iterator it = track_list->children().begin(); it != track_list->children().end(); ++it) {
|
||||
Gtk::TreeModel::Row row = *it;
|
||||
boost::shared_ptr<Route> route = row[track_cols.route];
|
||||
if (!boost::dynamic_pointer_cast<Track> (route)) {
|
||||
// it's not a track, must be a bus
|
||||
if (excl_muted && route->muted ()) {
|
||||
continue;
|
||||
}
|
||||
if (excl_hidden && route->is_hidden ()) {
|
||||
continue;
|
||||
}
|
||||
row[track_cols.selected] = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@
|
|||
#include <gtkmm/treemodel.h>
|
||||
#include <gtkmm/treeview.h>
|
||||
|
||||
#include "widgets/ardour_dropdown.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
class Session;
|
||||
class ExportChannelConfiguration;
|
||||
|
@ -243,6 +245,7 @@ class TrackExportChannelSelector : public ExportChannelSelector
|
|||
{
|
||||
public:
|
||||
TrackExportChannelSelector (ARDOUR::Session * session, ProfileManagerPtr manager);
|
||||
~TrackExportChannelSelector ();
|
||||
|
||||
virtual void sync_with_manager ();
|
||||
|
||||
|
@ -274,11 +277,11 @@ class TrackExportChannelSelector : public ExportChannelSelector
|
|||
|
||||
Gtk::ScrolledWindow track_scroller;
|
||||
|
||||
Gtk::HBox options_box;
|
||||
Gtk::CheckButton track_output_button;
|
||||
Gtk::Button select_tracks_button;
|
||||
Gtk::Button select_busses_button;
|
||||
Gtk::Button select_none_button;
|
||||
Gtk::HBox options_box;
|
||||
Gtk::CheckButton track_output_button;
|
||||
ArdourWidgets::ArdourDropdown select_menu;
|
||||
Gtk::CheckMenuItem* exclude_hidden;
|
||||
Gtk::CheckMenuItem* exclude_muted;
|
||||
void select_tracks ();
|
||||
void select_busses ();
|
||||
void select_none ();
|
||||
|
|
|
@ -1117,3 +1117,9 @@ GainMeter::route_active_changed ()
|
|||
meter_configuration_changed (_meter->input_streams ());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GainMeter::redraw_metrics ()
|
||||
{
|
||||
GainMeterBase::redraw_metrics ();
|
||||
}
|
||||
|
|
|
@ -225,6 +225,7 @@ class GainMeter : public GainMeterBase, public Gtk::VBox
|
|||
gint meter_ticks1_expose (GdkEventExpose *);
|
||||
gint meter_ticks2_expose (GdkEventExpose *);
|
||||
void on_style_changed (const Glib::RefPtr<Gtk::Style>&);
|
||||
void redraw_metrics ();
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
#include "pbd/convert.h"
|
||||
#include "pbd/error.h"
|
||||
#include "pbd/unwind.h"
|
||||
|
||||
#include "ardour/latent.h"
|
||||
|
||||
#include "gtkmm2ext/utils.h"
|
||||
|
@ -64,15 +66,14 @@ LatencyBarController::get_label (double&)
|
|||
}
|
||||
|
||||
LatencyGUI::LatencyGUI (Latent& l, samplepos_t sr, samplepos_t psz)
|
||||
: _latent (l),
|
||||
initial_value (_latent.effective_latency ()),
|
||||
sample_rate (sr),
|
||||
period_size (psz),
|
||||
ignored (new PBD::IgnorableControllable()),
|
||||
/* max 1 second, step by samples, page by msecs */
|
||||
adjustment (initial_value, 0.0, sample_rate, 1.0, sample_rate / 1000.0f),
|
||||
bc (adjustment, this),
|
||||
reset_button (_("Reset"))
|
||||
: _latent (l)
|
||||
, sample_rate (sr)
|
||||
, period_size (psz)
|
||||
, ignored (new PBD::IgnorableControllable())
|
||||
, _ignore_change (false)
|
||||
, adjustment (0, 0.0, sample_rate, 1.0, sample_rate / 1000.0f) /* max 1 second, step by samples, page by msecs */
|
||||
, bc (adjustment, this)
|
||||
, reset_button (_("Reset"))
|
||||
{
|
||||
Widget* w;
|
||||
|
||||
|
@ -103,6 +104,12 @@ LatencyGUI::LatencyGUI (Latent& l, samplepos_t sr, samplepos_t psz)
|
|||
plus_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &LatencyGUI::change_latency_from_button), 1));
|
||||
reset_button.signal_clicked().connect (sigc::mem_fun (*this, &LatencyGUI::reset));
|
||||
|
||||
/* Limit value to adjustment range (max = sample_rate).
|
||||
* Otherwise if the signal_latency() is larger than the adjustment's max,
|
||||
* LatencyGUI::finish() would set the adjustment's max value as custom-latency.
|
||||
*/
|
||||
adjustment.set_value (std::min (sample_rate, _latent.signal_latency ()));
|
||||
|
||||
adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &LatencyGUI::finish));
|
||||
|
||||
bc.set_size_request (-1, 25);
|
||||
|
@ -116,28 +123,26 @@ LatencyGUI::LatencyGUI (Latent& l, samplepos_t sr, samplepos_t psz)
|
|||
void
|
||||
LatencyGUI::finish ()
|
||||
{
|
||||
samplepos_t new_value = (samplepos_t) adjustment.get_value();
|
||||
if (new_value != initial_value) {
|
||||
_latent.set_user_latency (new_value);
|
||||
if (_ignore_change) {
|
||||
return;
|
||||
}
|
||||
samplepos_t new_value = (samplepos_t) adjustment.get_value();
|
||||
_latent.set_user_latency (new_value);
|
||||
}
|
||||
|
||||
void
|
||||
LatencyGUI::reset ()
|
||||
{
|
||||
_latent.unset_user_latency ();
|
||||
initial_value = std::min (sample_rate, _latent.signal_latency ());
|
||||
adjustment.set_value (initial_value);
|
||||
PBD::Unwinder<bool> uw (_ignore_change, true);
|
||||
adjustment.set_value (std::min (sample_rate, _latent.signal_latency ()));
|
||||
}
|
||||
|
||||
void
|
||||
LatencyGUI::refresh ()
|
||||
{
|
||||
/* limit to adjustment range, otherwise LatencyGUI::finish() would
|
||||
* set the adjustment's value as custom-latency
|
||||
*/
|
||||
initial_value = std::min (sample_rate, _latent.effective_latency ());
|
||||
adjustment.set_value (initial_value);
|
||||
PBD::Unwinder<bool> uw (_ignore_change, true);
|
||||
adjustment.set_value (std::min (sample_rate, _latent.effective_latency ()));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -175,5 +180,3 @@ LatencyDialog::LatencyDialog (const std::string& title, Latent& l, samplepos_t s
|
|||
show_all ();
|
||||
run ();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -60,17 +60,19 @@ public:
|
|||
LatencyGUI (ARDOUR::Latent&, samplepos_t sample_rate, samplepos_t period_size);
|
||||
~LatencyGUI() { }
|
||||
|
||||
void finish ();
|
||||
void reset ();
|
||||
void refresh ();
|
||||
|
||||
private:
|
||||
void reset ();
|
||||
void finish ();
|
||||
|
||||
ARDOUR::Latent& _latent;
|
||||
samplepos_t initial_value;
|
||||
samplepos_t sample_rate;
|
||||
samplepos_t period_size;
|
||||
|
||||
boost::shared_ptr<PBD::IgnorableControllable> ignored;
|
||||
|
||||
bool _ignore_change;
|
||||
Gtk::Adjustment adjustment;
|
||||
LatencyBarController bc;
|
||||
Gtk::HBox hbox1;
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "luainstance.h"
|
||||
#include "luasignal.h"
|
||||
#include "marker.h"
|
||||
#include "mixer_ui.h"
|
||||
#include "region_view.h"
|
||||
#include "processor_box.h"
|
||||
#include "time_axis_view.h"
|
||||
|
@ -379,6 +380,10 @@ namespace LuaMixer {
|
|||
|
||||
};
|
||||
|
||||
static void mixer_screenshot (const std::string& fn) {
|
||||
Mixer_UI::instance()->screenshot (fn);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static PBD::ScopedConnectionList _luaexecs;
|
||||
|
@ -747,6 +752,8 @@ LuaInstance::register_classes (lua_State* L)
|
|||
|
||||
.addFunction ("http_get", &http_get_unlogged)
|
||||
|
||||
.addFunction ("mixer_screenshot", &mixer_screenshot)
|
||||
|
||||
.addFunction ("processor_selection", &LuaMixer::processor_selection)
|
||||
|
||||
.beginStdCPtrList <ArdourMarker> ("ArdourMarkerList")
|
||||
|
|
|
@ -149,7 +149,7 @@ This could be due to misconfiguration or to an error inside %2.\n\
|
|||
Click OK to exit %1."), PROGRAM_NAME, AudioEngine::instance()->current_backend_name()));
|
||||
|
||||
msg.run ();
|
||||
_exit (0);
|
||||
_exit (EXIT_SUCCESS);
|
||||
|
||||
} else {
|
||||
|
||||
|
@ -355,7 +355,7 @@ int main (int argc, char *argv[])
|
|||
|
||||
if (parse_opts (argc, argv)) {
|
||||
command_line_parse_error (&argc, &argv);
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
cout << PROGRAM_NAME
|
||||
|
@ -369,7 +369,7 @@ int main (int argc, char *argv[])
|
|||
<< endl;
|
||||
|
||||
if (just_version) {
|
||||
exit (0);
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
if (no_splash) {
|
||||
|
@ -390,11 +390,7 @@ int main (int argc, char *argv[])
|
|||
"Run %1 from a commandline for more information."), PROGRAM_NAME),
|
||||
false, Gtk::MESSAGE_ERROR , Gtk::BUTTONS_OK, true);
|
||||
msg.run ();
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (curvetest_file) {
|
||||
return curvetest (curvetest_file);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#ifndef PLATFORM_WINDOWS
|
||||
|
@ -407,14 +403,14 @@ int main (int argc, char *argv[])
|
|||
|
||||
if (UIConfiguration::instance().pre_gui_init ()) {
|
||||
error << _("Could not complete pre-GUI initialization") << endmsg;
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
try {
|
||||
ui = new ARDOUR_UI (&argc, &argv, localedir.c_str());
|
||||
} catch (failed_constructor& err) {
|
||||
error << string_compose (_("could not create %1 GUI"), PROGRAM_NAME) << endmsg;
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
|
|
@ -212,7 +212,7 @@ MidiStreamView::display_track (boost::shared_ptr<Track> tr)
|
|||
|
||||
draw_note_lines();
|
||||
|
||||
NoteRangeChanged();
|
||||
NoteRangeChanged(); /* EMIT SIGNAL*/
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -424,7 +424,7 @@ MidiStreamView::apply_note_range(uint8_t lowest, uint8_t highest, bool to_region
|
|||
apply_note_range_to_regions ();
|
||||
}
|
||||
|
||||
NoteRangeChanged();
|
||||
NoteRangeChanged(); /* EMIT SIGNAL*/
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -158,6 +158,8 @@ MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
|
|||
|
||||
subplugin_menu.set_name ("ArdourContextMenu");
|
||||
|
||||
_note_range_changed_connection.disconnect();
|
||||
|
||||
if (!gui_property ("note-range-min").empty ()) {
|
||||
midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()),
|
||||
atoi (gui_property ("note-range-max").c_str()),
|
||||
|
@ -202,13 +204,6 @@ MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
|
|||
_piano_roll_header->ToggleNoteSelection.connect (
|
||||
sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
|
||||
|
||||
/* Update StreamView during scroomer drags.*/
|
||||
|
||||
_range_scroomer->DragStarting.connect (
|
||||
sigc::mem_fun (*this, &MidiTimeAxisView::start_scroomer_update));
|
||||
_range_scroomer->DragFinishing.connect (
|
||||
sigc::mem_fun (*this, &MidiTimeAxisView::stop_scroomer_update));
|
||||
|
||||
/* Put the scroomer and the keyboard in a VBox with a padding
|
||||
label so that they can be reduced in height for stacked-view
|
||||
tracks.
|
||||
|
@ -231,8 +226,13 @@ MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
|
|||
time_axis_hbox.pack_end(*v, false, false, 0);
|
||||
midi_scroomer_size_group->add_widget (*v);
|
||||
|
||||
midi_view()->NoteRangeChanged.connect (
|
||||
sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
|
||||
/* callback from StreamView scroomer drags, as well as
|
||||
* automatic changes of note-range (e.g. at rec-stop).
|
||||
* This callback is used to save the note-range-min/max
|
||||
* GUI Object property
|
||||
*/
|
||||
_note_range_changed_connection = midi_view()->NoteRangeChanged.connect (
|
||||
sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
|
||||
|
||||
/* ask for notifications of any new RegionViews */
|
||||
_view->RegionViewAdded.connect (
|
||||
|
@ -383,19 +383,6 @@ MidiTimeAxisView::setup_midnam_patches ()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiTimeAxisView::start_scroomer_update ()
|
||||
{
|
||||
_note_range_changed_connection.disconnect();
|
||||
_note_range_changed_connection = midi_view()->NoteRangeChanged.connect (
|
||||
sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
|
||||
}
|
||||
void
|
||||
MidiTimeAxisView::stop_scroomer_update ()
|
||||
{
|
||||
_note_range_changed_connection.disconnect();
|
||||
}
|
||||
|
||||
void
|
||||
MidiTimeAxisView::update_patch_selector ()
|
||||
{
|
||||
|
@ -1147,11 +1134,6 @@ MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool a
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiTimeAxisView::update_range()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
MidiTimeAxisView::show_all_automation (bool apply_to_selection)
|
||||
{
|
||||
|
|
|
@ -96,8 +96,6 @@ public:
|
|||
boost::shared_ptr<MIDI::Name::MasterDeviceNames> get_device_names();
|
||||
boost::shared_ptr<MIDI::Name::CustomDeviceMode> get_device_mode();
|
||||
|
||||
void update_range();
|
||||
|
||||
Gtk::CheckMenuItem* automation_child_menu_item (Evoral::Parameter);
|
||||
|
||||
StepEditor* step_editor() { return _step_editor; }
|
||||
|
@ -121,8 +119,6 @@ private:
|
|||
void setup_midnam_patches ();
|
||||
void update_patch_selector ();
|
||||
|
||||
void start_scroomer_update ();
|
||||
void stop_scroomer_update ();
|
||||
sigc::connection _note_range_changed_connection;
|
||||
|
||||
void model_changed(const std::string& model);
|
||||
|
|
|
@ -106,6 +106,7 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
|
|||
, _comment_button (_("Comments"))
|
||||
, trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
|
||||
, _visibility (X_("mixer-element-visibility"))
|
||||
, _suspend_menu_callbacks (false)
|
||||
, control_slave_ui (sess)
|
||||
{
|
||||
init ();
|
||||
|
@ -138,6 +139,7 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt
|
|||
, _comment_button (_("Comments"))
|
||||
, trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
|
||||
, _visibility (X_("mixer-element-visibility"))
|
||||
, _suspend_menu_callbacks (false)
|
||||
, control_slave_ui (sess)
|
||||
{
|
||||
init ();
|
||||
|
@ -605,11 +607,7 @@ MixerStrip::set_route (boost::shared_ptr<Route> rt)
|
|||
control_slave_ui.set_sensitive(true);
|
||||
}
|
||||
|
||||
if (_mixer_owned && route()->is_master() ) {
|
||||
spacer.show();
|
||||
} else {
|
||||
spacer.hide();
|
||||
}
|
||||
hide_master_spacer (false);
|
||||
|
||||
if (is_track()) {
|
||||
monitor_input_button->show ();
|
||||
|
@ -934,10 +932,15 @@ MixerStrip::output_press (GdkEventButton *ev)
|
|||
citems.pop_back ();
|
||||
}
|
||||
|
||||
if (!ARDOUR::Profile->get_mixbus()) {
|
||||
citems.push_back (SeparatorElem());
|
||||
citems.push_back (SeparatorElem());
|
||||
|
||||
if (!ARDOUR::Profile->get_mixbus()) {
|
||||
bool need_separator = false;
|
||||
for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
|
||||
if (!_route->output()->can_add_port (*i)) {
|
||||
continue;
|
||||
}
|
||||
need_separator = true;
|
||||
citems.push_back (
|
||||
MenuElem (
|
||||
string_compose (_("Add %1 port"), (*i).to_i18n_string()),
|
||||
|
@ -945,9 +948,11 @@ MixerStrip::output_press (GdkEventButton *ev)
|
|||
)
|
||||
);
|
||||
}
|
||||
if (need_separator) {
|
||||
citems.push_back (SeparatorElem());
|
||||
}
|
||||
}
|
||||
|
||||
citems.push_back (SeparatorElem());
|
||||
citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
|
||||
|
||||
Gtkmm2ext::anchored_menu_popup(&output_menu, &output_button, "",
|
||||
|
@ -1040,7 +1045,13 @@ MixerStrip::input_press (GdkEventButton *ev)
|
|||
}
|
||||
|
||||
citems.push_back (SeparatorElem());
|
||||
|
||||
bool need_separator = false;
|
||||
for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
|
||||
if (!_route->input()->can_add_port (*i)) {
|
||||
continue;
|
||||
}
|
||||
need_separator = true;
|
||||
citems.push_back (
|
||||
MenuElem (
|
||||
string_compose (_("Add %1 port"), (*i).to_i18n_string()),
|
||||
|
@ -1048,8 +1059,10 @@ MixerStrip::input_press (GdkEventButton *ev)
|
|||
)
|
||||
);
|
||||
}
|
||||
if (need_separator) {
|
||||
citems.push_back (SeparatorElem());
|
||||
}
|
||||
|
||||
citems.push_back (SeparatorElem());
|
||||
citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
|
||||
|
||||
Gtkmm2ext::anchored_menu_popup(&input_menu, &input_button, "",
|
||||
|
@ -2524,7 +2537,7 @@ MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
|
|||
|
||||
RadioMenuItem::Group group;
|
||||
|
||||
PBD::Unwinder<bool> (_suspend_menu_callbacks, true);
|
||||
PBD::Unwinder<bool> uw (_suspend_menu_callbacks, true);
|
||||
add_level_meter_item_point (items, group, _("Input"), MeterInput);
|
||||
add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
|
||||
add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
|
||||
|
@ -2658,3 +2671,13 @@ MixerStrip::set_marked_for_display (bool yn)
|
|||
{
|
||||
return RouteUI::mark_hidden (!yn);
|
||||
}
|
||||
|
||||
void
|
||||
MixerStrip::hide_master_spacer (bool yn)
|
||||
{
|
||||
if (_mixer_owned && route()->is_master() && !yn) {
|
||||
spacer.show();
|
||||
} else {
|
||||
spacer.hide();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,6 +114,9 @@ public:
|
|||
return _mixer_owned;
|
||||
}
|
||||
|
||||
/* used for screenshots */
|
||||
void hide_master_spacer (bool);
|
||||
|
||||
void hide_things ();
|
||||
|
||||
sigc::signal<void> WidthChanged;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <glibmm/threads.h>
|
||||
|
||||
#include <gtkmm/accelmap.h>
|
||||
#include <gtkmm/offscreenwindow.h>
|
||||
#include <gtkmm/stock.h>
|
||||
|
||||
#include "pbd/convert.h"
|
||||
|
@ -160,15 +161,14 @@ Mixer_UI::Mixer_UI ()
|
|||
#endif
|
||||
|
||||
_group_tabs = new MixerGroupTabs (this);
|
||||
VBox* b = manage (new VBox);
|
||||
b->set_spacing (0);
|
||||
b->set_border_width (0);
|
||||
b->pack_start (*_group_tabs, PACK_SHRINK);
|
||||
b->pack_start (strip_packer);
|
||||
b->show_all ();
|
||||
b->signal_scroll_event().connect (sigc::mem_fun (*this, &Mixer_UI::on_scroll_event), false);
|
||||
strip_group_box.set_spacing (0);
|
||||
strip_group_box.set_border_width (0);
|
||||
strip_group_box.pack_start (*_group_tabs, PACK_SHRINK);
|
||||
strip_group_box.pack_start (strip_packer);
|
||||
strip_group_box.show_all ();
|
||||
strip_group_box.signal_scroll_event().connect (sigc::mem_fun (*this, &Mixer_UI::on_scroll_event), false);
|
||||
|
||||
scroller.add (*b);
|
||||
scroller.add (strip_group_box);
|
||||
scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
|
||||
|
||||
setup_track_display ();
|
||||
|
@ -3502,3 +3502,83 @@ Mixer_UI::vca_unassign (boost::shared_ptr<VCA> vca)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Mixer_UI::screenshot (std::string const& filename)
|
||||
{
|
||||
if (!_session) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int height = strip_packer.get_height();
|
||||
bool with_vca = vca_vpacker.is_visible ();
|
||||
MixerStrip* master = strip_by_route (_session->master_out ());
|
||||
|
||||
Gtk::OffscreenWindow osw;
|
||||
Gtk::HBox b;
|
||||
osw.add (b);
|
||||
b.show ();
|
||||
|
||||
/* unpack widgets, add to OffscreenWindow */
|
||||
|
||||
strip_group_box.remove (strip_packer);
|
||||
b.pack_start (strip_packer, false, false);
|
||||
/* hide extra elements inside strip_packer */
|
||||
add_button.hide ();
|
||||
scroller_base.hide ();
|
||||
#ifdef MIXBUS
|
||||
mb_shadow.hide();
|
||||
#endif
|
||||
|
||||
if (with_vca) {
|
||||
/* work around Gtk::ScrolledWindow */
|
||||
Gtk::Viewport* viewport = (Gtk::Viewport*) vca_scroller.get_child();
|
||||
viewport->remove (); // << vca_hpacker
|
||||
b.pack_start (vca_hpacker, false, false);
|
||||
/* hide some growing widgets */
|
||||
add_vca_button.hide ();
|
||||
vca_scroller_base.hide();
|
||||
}
|
||||
|
||||
if (master) {
|
||||
out_packer.remove (*master);
|
||||
b.pack_start (*master, false, false);
|
||||
master->hide_master_spacer (true);
|
||||
}
|
||||
|
||||
/* prepare the OffscreenWindow for rendering */
|
||||
osw.set_size_request (-1, height);
|
||||
osw.show ();
|
||||
osw.queue_resize ();
|
||||
osw.queue_draw ();
|
||||
osw.get_window()->process_updates (true);
|
||||
|
||||
/* create screenshot */
|
||||
Glib::RefPtr<Gdk::Pixbuf> pb = osw.get_pixbuf ();
|
||||
pb->save (filename, "png");
|
||||
|
||||
/* unpack elements before destorying the Box & OffscreenWindow */
|
||||
list<Gtk::Widget*> children = b.get_children();
|
||||
for (list<Gtk::Widget*>::iterator child = children.begin(); child != children.end(); ++child) {
|
||||
b.remove (**child);
|
||||
}
|
||||
osw.remove ();
|
||||
|
||||
/* now re-pack the widgets into the main mixer window */
|
||||
add_button.show ();
|
||||
scroller_base.show ();
|
||||
#ifdef MIXBUS
|
||||
mb_shadow.show();
|
||||
#endif
|
||||
strip_group_box.pack_start (strip_packer);
|
||||
if (with_vca) {
|
||||
add_vca_button.show ();
|
||||
vca_scroller_base.show();
|
||||
vca_scroller.add (vca_hpacker);
|
||||
}
|
||||
if (master) {
|
||||
master->hide_master_spacer (false);
|
||||
out_packer.pack_start (*master, false, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -147,6 +147,8 @@ public:
|
|||
void showhide_mixbusses (bool on);
|
||||
#endif
|
||||
|
||||
bool screenshot (std::string const&);
|
||||
|
||||
protected:
|
||||
void set_axis_targets_for_operation ();
|
||||
ARDOUR::AutomationControlSet selected_gaincontrols ();
|
||||
|
@ -175,6 +177,7 @@ private:
|
|||
ArdourWidgets::VPane rhs_pane1;
|
||||
ArdourWidgets::VPane rhs_pane2;
|
||||
ArdourWidgets::HPane inner_pane;
|
||||
Gtk::VBox strip_group_box;
|
||||
Gtk::HBox strip_packer;
|
||||
Gtk::ScrolledWindow vca_scroller;
|
||||
Gtk::HBox vca_hpacker;
|
||||
|
|
|
@ -37,20 +37,17 @@ using namespace std;
|
|||
|
||||
string ARDOUR_COMMAND_LINE::session_name = "";
|
||||
string ARDOUR_COMMAND_LINE::backend_client_name = "ardour";
|
||||
string ARDOUR_COMMAND_LINE::backend_session_uuid;
|
||||
bool ARDOUR_COMMAND_LINE::show_key_actions = false;
|
||||
bool ARDOUR_COMMAND_LINE::show_actions = false;
|
||||
bool ARDOUR_COMMAND_LINE::no_splash = false;
|
||||
bool ARDOUR_COMMAND_LINE::just_version = false;
|
||||
bool ARDOUR_COMMAND_LINE::use_vst = true;
|
||||
bool ARDOUR_COMMAND_LINE::new_session = false;
|
||||
char* ARDOUR_COMMAND_LINE::curvetest_file = 0;
|
||||
bool ARDOUR_COMMAND_LINE::try_hw_optimization = true;
|
||||
bool ARDOUR_COMMAND_LINE::no_connect_ports = false;
|
||||
string ARDOUR_COMMAND_LINE::keybindings_path = ""; /* empty means use builtin default */
|
||||
std::string ARDOUR_COMMAND_LINE::menus_file = "ardour.menus";
|
||||
bool ARDOUR_COMMAND_LINE::finder_invoked_ardour = false;
|
||||
string ARDOUR_COMMAND_LINE::immediate_save;
|
||||
string ARDOUR_COMMAND_LINE::load_template;
|
||||
bool ARDOUR_COMMAND_LINE::check_announcements = true;
|
||||
|
||||
|
@ -72,14 +69,10 @@ print_help (const char *execname)
|
|||
<< _(" -b, --bindings Display all current key bindings\n")
|
||||
<< _(" -B, --bypass-plugins Bypass all plugins in an existing session\n")
|
||||
<< _(" -c, --name <name> Use a specific backend client name, default is ardour\n")
|
||||
#ifndef NDEBUG
|
||||
<< _(" -C, --curvetest filename Curve algorithm debugger\n")
|
||||
#endif
|
||||
<< _(" -d, --disable-plugins Disable all plugins (safe mode)\n")
|
||||
#ifndef NDEBUG
|
||||
<< _(" -D, --debug <options> Set debug flags. Use \"-D list\" to see available options\n")
|
||||
#endif
|
||||
<< _(" -E, --save <file> Load the specified session, save it to <file> and then quit\n")
|
||||
<< _(" -h, --help Print this message\n")
|
||||
<< _(" -k, --keybindings <file> Name of key bindings to load\n")
|
||||
<< _(" -m, --menus file Use \"file\" to define menus\n")
|
||||
|
@ -89,7 +82,6 @@ print_help (const char *execname)
|
|||
<< _(" -P, --no-connect-ports Do not connect any ports at startup\n")
|
||||
<< _(" -S, --sync Draw the GUI synchronously\n")
|
||||
<< _(" -T, --template <name> Use given template for new session\n")
|
||||
<< _(" -U, --uuid <uuid> Set (jack) backend UUID\n")
|
||||
<< _(" -v, --version Print version and exit\n")
|
||||
#ifdef WINDOWS_VST_SUPPORT
|
||||
<< _(" -V, --novst Disable WindowsVST support\n")
|
||||
|
@ -130,9 +122,6 @@ ARDOUR_COMMAND_LINE::parse_opts (int argc, char *argv[])
|
|||
{ "new", 1, 0, 'N' },
|
||||
{ "no-hw-optimizations", 0, 0, 'O' },
|
||||
{ "sync", 0, 0, 'S' },
|
||||
{ "curvetest", 1, 0, 'C' },
|
||||
{ "save", 1, 0, 'E' },
|
||||
{ "uuid", 1, 0, 'U' },
|
||||
{ "template", 1, 0, 'T' },
|
||||
{ "no-connect-ports", 0, 0, 'P' },
|
||||
{ 0, 0, 0, 0 }
|
||||
|
@ -158,7 +147,7 @@ ARDOUR_COMMAND_LINE::parse_opts (int argc, char *argv[])
|
|||
|
||||
case 'h':
|
||||
print_help (execname);
|
||||
exit (0);
|
||||
exit (EXIT_SUCCESS);
|
||||
break;
|
||||
case 'H':
|
||||
#ifndef NDEBUG
|
||||
|
@ -187,7 +176,7 @@ ARDOUR_COMMAND_LINE::parse_opts (int argc, char *argv[])
|
|||
|
||||
case 'D':
|
||||
if (PBD::parse_debug_options (optarg)) {
|
||||
exit (0);
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -234,22 +223,10 @@ ARDOUR_COMMAND_LINE::parse_opts (int argc, char *argv[])
|
|||
backend_client_name = optarg;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
curvetest_file = optarg;
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
keybindings_path = optarg;
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
immediate_save = optarg;
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
backend_session_uuid = optarg;
|
||||
break;
|
||||
|
||||
default:
|
||||
return print_help(execname);
|
||||
}
|
||||
|
|
|
@ -30,17 +30,14 @@ extern bool show_actions;
|
|||
extern bool no_splash;
|
||||
extern bool just_version;
|
||||
extern std::string backend_client_name;
|
||||
extern std::string backend_session_uuid;
|
||||
extern bool use_vst;
|
||||
extern bool new_session;
|
||||
extern char* curvetest_file;
|
||||
extern bool try_hw_optimization;
|
||||
extern bool no_connect_ports;
|
||||
extern bool use_gtk_theme;
|
||||
extern std::string keybindings_path;
|
||||
extern std::string menus_file;
|
||||
extern bool finder_invoked_ardour;
|
||||
extern std::string immediate_save;
|
||||
extern std::string load_template;
|
||||
extern bool check_announcements;
|
||||
|
||||
|
|
|
@ -229,7 +229,7 @@ PatchChangeWidget::refill_banks ()
|
|||
const int b = bank (_channel);
|
||||
|
||||
{
|
||||
PBD::Unwinder<bool> (_ignore_spin_btn_signals, true);
|
||||
PBD::Unwinder<bool> uw (_ignore_spin_btn_signals, true);
|
||||
_bank_msb_spin.set_value (b >> 7);
|
||||
_bank_lsb_spin.set_value (b & 127);
|
||||
}
|
||||
|
|
|
@ -407,9 +407,15 @@ PluginEqGui::run_impulse_analysis ()
|
|||
}
|
||||
|
||||
samplepos_t sample_pos = 0;
|
||||
samplecnt_t latency = _plugin->signal_latency ();
|
||||
samplecnt_t latency = _plugin_insert->effective_latency ();
|
||||
samplecnt_t samples_remain = _buffer_size + latency;
|
||||
|
||||
/* Note: https://discourse.ardour.org/t/plugins-ladspa-questions/101292/15
|
||||
* Capture the complete response from the beginning, and more than "latency" samples,
|
||||
* Then unwrap the phase-response corresponding to reported latency, leaving the
|
||||
* magnitude unchanged.
|
||||
*/
|
||||
|
||||
_impulse_fft->reset ();
|
||||
|
||||
while (samples_remain > 0) {
|
||||
|
|
|
@ -446,7 +446,7 @@ PortMatrix::popup_menu (BundleChannel column, BundleChannel row, uint32_t t)
|
|||
if (can_add_channels (bc[dim].bundle)) {
|
||||
/* Start off with options for the `natural' port type */
|
||||
for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
|
||||
if (should_show (*i)) {
|
||||
if (should_show (*i) && can_add_channel_proxy (w, *i)) {
|
||||
snprintf (buf, sizeof (buf), _("Add %s %s"), (*i).to_i18n_string(), channel_noun().c_str());
|
||||
sub.push_back (MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::add_channel_proxy), w, *i)));
|
||||
}
|
||||
|
@ -454,7 +454,7 @@ PortMatrix::popup_menu (BundleChannel column, BundleChannel row, uint32_t t)
|
|||
|
||||
/* Now add other ones */
|
||||
for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
|
||||
if (!should_show (*i)) {
|
||||
if (!should_show (*i) && can_add_channel_proxy (w, *i)) {
|
||||
snprintf (buf, sizeof (buf), _("Add %s %s"), (*i).to_i18n_string(), channel_noun().c_str());
|
||||
sub.push_back (MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::add_channel_proxy), w, *i)));
|
||||
}
|
||||
|
@ -790,6 +790,17 @@ PortMatrix::remove_all_channels (boost::weak_ptr<Bundle> w)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PortMatrix::can_add_channel_proxy (boost::weak_ptr<Bundle> w, DataType t) const
|
||||
{
|
||||
boost::shared_ptr<Bundle> b = w.lock ();
|
||||
if (!b) {
|
||||
return false;
|
||||
}
|
||||
boost::shared_ptr<IO> io = io_from_bundle (b);
|
||||
return io->can_add_port (t);
|
||||
}
|
||||
|
||||
void
|
||||
PortMatrix::add_channel_proxy (boost::weak_ptr<Bundle> w, DataType t)
|
||||
{
|
||||
|
|
|
@ -182,6 +182,7 @@ private:
|
|||
void routes_changed ();
|
||||
void reconnect_to_routes ();
|
||||
void select_arrangement ();
|
||||
bool can_add_channel_proxy (boost::weak_ptr<ARDOUR::Bundle>, ARDOUR::DataType) const;
|
||||
void add_channel_proxy (boost::weak_ptr<ARDOUR::Bundle>, ARDOUR::DataType);
|
||||
void remove_channel_proxy (boost::weak_ptr<ARDOUR::Bundle>, uint32_t);
|
||||
void rename_channel_proxy (boost::weak_ptr<ARDOUR::Bundle>, uint32_t);
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "pbd/i18n.h"
|
||||
#include "pbd/file_utils.h"
|
||||
|
||||
#include "ptformat/ptfformat.h"
|
||||
#include "ptformat/ptformat.h"
|
||||
|
||||
#include "ardour/session_handle.h"
|
||||
|
||||
|
@ -111,24 +111,35 @@ void
|
|||
PTImportSelector::update_ptf()
|
||||
{
|
||||
if (ptimport_ptf_chooser.get_filename ().size () > 0) {
|
||||
int err = 0;
|
||||
std::string path = ptimport_ptf_chooser.get_filename ();
|
||||
bool ok = Glib::file_test(path.c_str(), Glib::FILE_TEST_IS_REGULAR | Glib::FILE_TEST_IS_SYMLINK)
|
||||
&& !Glib::file_test(path.c_str(), Glib::FILE_TEST_IS_DIR);
|
||||
if (ok) {
|
||||
if (_ptf->load (path, _session_rate) == -1) {
|
||||
err = _ptf->load (path, _session_rate);
|
||||
if (err == -1) {
|
||||
ptimport_info_text.get_buffer ()->set_text ("Cannot decrypt PT session\n");
|
||||
ptimport_import_button.set_sensitive(false);
|
||||
} else if (err == -2) {
|
||||
ptimport_info_text.get_buffer ()->set_text ("Cannot detect PT session\n");
|
||||
ptimport_import_button.set_sensitive(false);
|
||||
} else if (err == -3) {
|
||||
ptimport_info_text.get_buffer ()->set_text ("Incompatible PT version\n");
|
||||
ptimport_import_button.set_sensitive(false);
|
||||
} else if (err == -4) {
|
||||
ptimport_info_text.get_buffer ()->set_text ("Cannot parse PT session\n");
|
||||
ptimport_import_button.set_sensitive(false);
|
||||
} else {
|
||||
std::string ptinfo = string_compose (_("PT Session [ VALID ]\n\nSession Info:\n\n\nPT v%1 Session @ %2Hz\n\n%3 audio files\n%4 audio regions\n%5 active audio regions\n%6 midi regions\n%7 active midi regions\n\n"),
|
||||
(int)_ptf->version,
|
||||
_ptf->sessionrate,
|
||||
_ptf->audiofiles.size (),
|
||||
_ptf->regions.size (),
|
||||
_ptf->tracks.size (),
|
||||
_ptf->midiregions.size (),
|
||||
_ptf->miditracks.size ()
|
||||
(int)_ptf->version (),
|
||||
_ptf->sessionrate (),
|
||||
_ptf->audiofiles ().size (),
|
||||
_ptf->regions ().size (),
|
||||
_ptf->tracks ().size (),
|
||||
_ptf->midiregions ().size (),
|
||||
_ptf->miditracks ().size ()
|
||||
);
|
||||
if (_session_rate != _ptf->sessionrate) {
|
||||
if (_session_rate != _ptf->sessionrate ()) {
|
||||
ptinfo = string_compose (_("%1WARNING:\n\nSample rate mismatch,\nwill be resampling\n"), ptinfo);
|
||||
}
|
||||
ptimport_info_text.get_buffer ()->set_text (ptinfo);
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "ptformat/ptfformat.h"
|
||||
#include "ptformat/ptformat.h"
|
||||
|
||||
#include "ardour_dialog.h"
|
||||
#include "ardour/session.h"
|
||||
|
|
|
@ -60,7 +60,6 @@ using namespace Gtkmm2ext;
|
|||
|
||||
RouteParams_UI::RouteParams_UI ()
|
||||
: ArdourWindow (_("Tracks and Busses"))
|
||||
, latency_apply_button (Stock::APPLY)
|
||||
, track_menu(0)
|
||||
{
|
||||
insert_box = 0;
|
||||
|
@ -114,7 +113,6 @@ RouteParams_UI::RouteParams_UI ()
|
|||
update_title();
|
||||
|
||||
latency_packer.set_spacing (18);
|
||||
latency_button_box.pack_start (latency_apply_button);
|
||||
delay_label.set_alignment (0, 0.5);
|
||||
|
||||
// changeable area
|
||||
|
@ -286,7 +284,6 @@ RouteParams_UI::cleanup_latency_frame ()
|
|||
if (latency_widget) {
|
||||
latency_frame.remove ();
|
||||
latency_packer.remove (*latency_widget);
|
||||
latency_packer.remove (latency_button_box);
|
||||
latency_packer.remove (delay_label);
|
||||
latency_connections.drop_connections ();
|
||||
latency_click_connection.disconnect ();
|
||||
|
@ -302,15 +299,11 @@ RouteParams_UI::setup_latency_frame ()
|
|||
{
|
||||
latency_widget = new LatencyGUI (*(_route->output()), _session->sample_rate(), AudioEngine::instance()->samples_per_cycle());
|
||||
|
||||
char buf[128];
|
||||
snprintf (buf, sizeof (buf), _("Latency: %" PRId64 " samples"), _route->signal_latency());
|
||||
delay_label.set_text (buf);
|
||||
refresh_latency ();
|
||||
|
||||
latency_packer.pack_start (*latency_widget, false, false);
|
||||
latency_packer.pack_start (latency_button_box, false, false);
|
||||
latency_packer.pack_start (delay_label);
|
||||
latency_packer.pack_start (delay_label, false, false);
|
||||
|
||||
latency_click_connection = latency_apply_button.signal_clicked().connect (sigc::mem_fun (*latency_widget, &LatencyGUI::finish));
|
||||
_route->signal_latency_updated.connect (latency_connections, invalidator (*this), boost::bind (&RouteParams_UI::refresh_latency, this), gui_context());
|
||||
|
||||
latency_frame.add (latency_packer);
|
||||
|
|
|
@ -92,8 +92,6 @@ private:
|
|||
|
||||
Gtk::Frame latency_frame;
|
||||
Gtk::VBox latency_packer;
|
||||
Gtk::HButtonBox latency_button_box;
|
||||
Gtk::Button latency_apply_button;
|
||||
LatencyGUI* latency_widget;
|
||||
Gtk::Label delay_label;
|
||||
|
||||
|
|
|
@ -104,19 +104,19 @@ SessionDialog::SessionDialog (bool require_new, const std::string& session_name,
|
|||
info_frame.set_border_width (12);
|
||||
get_vbox()->pack_start (info_frame, false, false);
|
||||
|
||||
if (!template_name.empty()) {
|
||||
load_template_override = template_name;
|
||||
}
|
||||
|
||||
setup_new_session_page ();
|
||||
|
||||
if (!new_only) {
|
||||
if (!require_new) {
|
||||
setup_initial_choice_box ();
|
||||
get_vbox()->pack_start (ic_vbox, true, true);
|
||||
} else {
|
||||
get_vbox()->pack_start (session_new_vbox, true, true);
|
||||
}
|
||||
|
||||
if (!template_name.empty()) {
|
||||
load_template_override = template_name;
|
||||
}
|
||||
|
||||
get_vbox()->show_all ();
|
||||
|
||||
/* fill data models and show/hide accordingly */
|
||||
|
@ -139,19 +139,6 @@ SessionDialog::SessionDialog (bool require_new, const std::string& session_name,
|
|||
recent_label.hide ();
|
||||
}
|
||||
}
|
||||
|
||||
/* possibly get out of here immediately if everything is ready to go.
|
||||
We still need to set up the whole dialog because of the way
|
||||
ARDOUR_UI::get_session_parameters() might skip it on a first
|
||||
pass then require it for a second pass (e.g. when there
|
||||
is an error with session loading and we have to ask the user
|
||||
what to do next).
|
||||
*/
|
||||
|
||||
if (!session_name.empty() && !require_new) {
|
||||
response (RESPONSE_OK);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SessionDialog::SessionDialog ()
|
||||
|
@ -183,8 +170,6 @@ SessionDialog::SessionDialog ()
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
SessionDialog::~SessionDialog()
|
||||
{
|
||||
}
|
||||
|
@ -285,8 +270,36 @@ std::string
|
|||
SessionDialog::session_template_name ()
|
||||
{
|
||||
if (!load_template_override.empty()) {
|
||||
string the_path (ARDOUR::user_template_directory());
|
||||
return Glib::build_filename (the_path, load_template_override + ARDOUR::template_suffix);
|
||||
/* compare to SessionDialog::populate_session_templates */
|
||||
|
||||
/* compare by name (path may or may not be UTF-8) */
|
||||
vector<TemplateInfo> templates;
|
||||
find_session_templates (templates, false);
|
||||
for (vector<TemplateInfo>::iterator x = templates.begin(); x != templates.end(); ++x) {
|
||||
if ((*x).name == load_template_override) {
|
||||
return (*x).path;
|
||||
}
|
||||
}
|
||||
|
||||
/* look up script by name */
|
||||
LuaScriptList scripts (LuaScripting::instance ().scripts (LuaScriptInfo::SessionInit));
|
||||
LuaScriptList& as (LuaScripting::instance ().scripts (LuaScriptInfo::EditorAction));
|
||||
for (LuaScriptList::const_iterator s = as.begin(); s != as.end(); ++s) {
|
||||
if ((*s)->subtype & LuaScriptInfo::SessionSetup) {
|
||||
scripts.push_back (*s);
|
||||
}
|
||||
}
|
||||
std::sort (scripts.begin(), scripts.end(), LuaScripting::Sorter());
|
||||
for (LuaScriptList::const_iterator s = scripts.begin(); s != scripts.end(); ++s) {
|
||||
if ((*s)->name == load_template_override) {
|
||||
return "urn:ardour:" + (*s)->path;
|
||||
}
|
||||
}
|
||||
|
||||
/* this will produce a more or less meaninful error later:
|
||||
* "ERROR: Could not open session template [abs-path to user-config dir]"
|
||||
*/
|
||||
return Glib::build_filename (ARDOUR::user_template_directory (), load_template_override);
|
||||
}
|
||||
|
||||
if (template_chooser.get_selection()->count_selected_rows() > 0) {
|
||||
|
@ -305,8 +318,11 @@ SessionDialog::session_template_name ()
|
|||
std::string
|
||||
SessionDialog::session_name (bool& should_be_new)
|
||||
{
|
||||
if (!_provided_session_name.empty() && !new_only) {
|
||||
should_be_new = false;
|
||||
if (!_provided_session_name.empty()) {
|
||||
/* user gave name on cmdline/invocation. Did they also specify
|
||||
that it must be a new session?
|
||||
*/
|
||||
should_be_new = new_only;
|
||||
return _provided_session_name;
|
||||
}
|
||||
|
||||
|
@ -338,7 +354,7 @@ SessionDialog::session_name (bool& should_be_new)
|
|||
std::string
|
||||
SessionDialog::session_folder ()
|
||||
{
|
||||
if (!_provided_session_path.empty() && !new_only) {
|
||||
if (!_provided_session_path.empty()) {
|
||||
return _provided_session_path;
|
||||
}
|
||||
|
||||
|
@ -564,14 +580,6 @@ SessionDialog::open_button_pressed (GdkEventButton* ev)
|
|||
return true;
|
||||
}
|
||||
|
||||
struct LuaScriptListSorter
|
||||
{
|
||||
bool operator() (LuaScriptInfoPtr const a, LuaScriptInfoPtr const b) const {
|
||||
return ARDOUR::cmp_nocase_utf8 (a->name, b->name) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
SessionDialog::populate_session_templates ()
|
||||
{
|
||||
|
@ -592,8 +600,7 @@ SessionDialog::populate_session_templates ()
|
|||
}
|
||||
}
|
||||
|
||||
LuaScriptListSorter cmp;
|
||||
std::sort (scripts.begin(), scripts.end(), cmp);
|
||||
std::sort (scripts.begin(), scripts.end(), LuaScripting::Sorter());
|
||||
|
||||
for (LuaScriptList::const_iterator s = scripts.begin(); s != scripts.end(); ++s) {
|
||||
TreeModel::Row row = *(template_model->append ());
|
||||
|
@ -690,7 +697,7 @@ SessionDialog::setup_new_session_page ()
|
|||
HBox* template_hbox = manage (new HBox);
|
||||
|
||||
//if the "template override" is provided, don't give the user any template selections (?)
|
||||
if ( load_template_override.empty() ) {
|
||||
if (load_template_override.empty()) {
|
||||
template_hbox->set_spacing (8);
|
||||
|
||||
Gtk::ScrolledWindow *template_scroller = manage (new Gtk::ScrolledWindow());
|
||||
|
|
|
@ -107,7 +107,7 @@ UI_CONFIG_VARIABLE (bool, open_gui_after_adding_plugin, "open-gui-after-adding-p
|
|||
UI_CONFIG_VARIABLE (bool, show_inline_display_by_default, "show-inline-display-by-default", true)
|
||||
UI_CONFIG_VARIABLE (bool, prefer_inline_over_gui, "prefer-inline-over-gui", true)
|
||||
UI_CONFIG_VARIABLE (uint32_t, max_inline_controls, "max-inline-controls", 32) /* per processor */
|
||||
UI_CONFIG_VARIABLE (uint32_t, action_table_columns, "action-table-columns", 0)
|
||||
UI_CONFIG_VARIABLE (uint32_t, action_table_columns, "action-table-columns", 3)
|
||||
UI_CONFIG_VARIABLE (bool, use_wm_visibility, "use-wm-visibility", true)
|
||||
UI_CONFIG_VARIABLE (std::string, stripable_color_palette, "stripable-color-palette", "#AA3939:#FFAAAA:#D46A6A:#801515:#550000:#AA8E39:#FFEAAA:#D4BA6A:#806515:#554000:#343477:#8080B3:#565695:#1A1A59:#09093B:#2D882D:#88CC88:#55AA55:#116611:#004400") /* Gtk::ColorSelection::palette_to_string */
|
||||
UI_CONFIG_VARIABLE (bool, use_note_bars_for_velocity, "use-note-bars-for-velocity", true)
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
#!/bin/sh
|
||||
. `dirname "$0"`/../build/headless/hardev_common_waf.sh
|
||||
TOP=`dirname "$0"`/..
|
||||
. $TOP/build/gtk2_ardour/ardev_common_waf.sh
|
||||
LD_LIBRARY_PATH=$LD_LIBRARY_PATH
|
||||
export ARDOUR_INSIDE_GDB=1
|
||||
exec gdb --args $TOP/$EXECUTABLE $@
|
||||
if test -n "`which gdb`"; then
|
||||
exec gdb --args $TOP/build/headless/hardour-$ARDOURVERSION "$@"
|
||||
fi
|
||||
if test -n "`which lldb`"; then
|
||||
exec lldb -- $TOP/build/headless/hardour-$ARDOURVERSION "$@"
|
||||
fi
|
||||
echo "neither gdb nor lldb was found."
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/bin/sh
|
||||
. `dirname "$0"`/../build/headless/hardev_common_waf.sh
|
||||
TOP=`dirname "$0"`/..
|
||||
. $TOP/build/gtk2_ardour/ardev_common_waf.sh
|
||||
export UBUNTU_MENUPROXY=""
|
||||
exec $TOP/$EXECUTABLE "$@"
|
||||
exec $TOP/build/headless/hardour-$ARDOURVERSION "$@"
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
TOP=`dirname "$0"`/..
|
||||
|
||||
#export G_DEBUG=fatal_criticals
|
||||
|
||||
libs=$TOP/@LIBS@
|
||||
|
||||
#
|
||||
# when running ardev, the various parts of Ardour have not been consolidated into the locations that they
|
||||
# would normally end up after an install. We therefore need to set up environment variables so that we
|
||||
# can find all the components.
|
||||
#
|
||||
|
||||
export ARDOUR_PATH=$TOP/gtk2_ardour/icons:$TOP/gtk2_ardour/pixmaps:$TOP/build/gtk2_ardour:$TOP/gtk2_ardour:.
|
||||
export ARDOUR_SURFACES_PATH=$libs/surfaces/osc:$libs/surfaces/generic_midi:$libs/surfaces/tranzport:$libs/surfaces/powermate:$libs/surfaces/mackie:$libs/surfaces/wiimote
|
||||
export ARDOUR_PANNER_PATH=$libs/panners
|
||||
export ARDOUR_DATA_PATH=$TOP:$TOP/build:$TOP/gtk2_ardour:$TOP/build/gtk2_ardour:.
|
||||
export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:.
|
||||
export ARDOUR_MCP_PATH=$TOP/mcp:.
|
||||
export ARDOUR_EXPORT_FORMATS_PATH=$TOP/export:.
|
||||
export ARDOUR_BACKEND_PATH=$libs/backends/jack
|
||||
|
||||
#
|
||||
# even though we set the above variables, ardour requires that these
|
||||
# two also be set. the above settings will override them.
|
||||
#
|
||||
|
||||
export ARDOUR_CONFIG_PATH=$TOP:$TOP/gtk2_ardour:$TOP/build:$TOP/build/gtk2_ardour
|
||||
export ARDOUR_DLL_PATH=$libs
|
||||
|
||||
export GTK_PATH=~/.ardour3:$libs/clearlooks-newer
|
||||
export VAMP_PATH=$libs/vamp-plugins${VAMP_PATH:+:$VAMP_PATH}
|
||||
|
||||
export LD_LIBRARY_PATH=$libs/qm-dsp:$libs/vamp-sdk:$libs/surfaces:$libs/surfaces/control_protocol:$libs/ardour:$libs/midi++2:$libs/pbd:$libs/rubberband:$libs/soundtouch:$libs/gtkmm2ext:$libs/gnomecanvas:$libs/libsndfile:$libs/appleutility:$libs/taglib:$libs/evoral:$libs/evoral/src/libsmf:$libs/audiographer:$libs/temporal:$libs/libltc:$libs/canvas:$libs/ardouralsautil${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
|
||||
|
||||
# DYLD_LIBRARY_PATH is for darwin.
|
||||
export DYLD_FALLBACK_LIBRARY_PATH=$LD_LIBRARY_PATH
|
||||
|
||||
ARDOURVERSION=@VERSION@
|
||||
EXECUTABLE=@EXECUTABLE@
|
|
@ -1,8 +1,12 @@
|
|||
#!/bin/sh
|
||||
. `dirname "$0"`/../build/headless/hardev_common_waf.sh
|
||||
LD_LIBRARY_PATH=$LD_LIBRARY_PATH
|
||||
VALGRIND_OPTIONS="$VALGRIND_OPTIONS --num-callers=50"
|
||||
VALGRIND_OPTIONS="$VALGRIND_OPTIONS --error-limit=no"
|
||||
#VALGRIND_OPTIONS="$VALGRIND_OPTIONS --leak-check=full --leak-resolution=high"
|
||||
#VALGRIND_OPTIONS="$VALGRIND_OPTIONS --show-leak-kinds=all -v"
|
||||
#VALGRIND_OPTIONS="$VALGRIND_OPTIONS --log-file=/tmp/hardour-%p.log"
|
||||
|
||||
TOP=`dirname "$0"`/..
|
||||
. $TOP/build/gtk2_ardour/ardev_common_waf.sh
|
||||
if uname | grep -q arwin; then
|
||||
OBJSUPP="--suppressions=${TOP}/tools/objc.supp"
|
||||
fi
|
||||
|
@ -12,4 +16,4 @@ exec valgrind --tool=memcheck \
|
|||
--track-origins=yes \
|
||||
--suppressions=${TOP}/tools/valgrind.supp \
|
||||
$OBJSUPP \
|
||||
$TOP/$EXECUTABLE --novst "$@"
|
||||
$TOP/build/headless/hardour-$ARDOURVERSION --novst "$@"
|
||||
|
|
|
@ -1,34 +1,43 @@
|
|||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <getopt.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "pbd/failed_constructor.h"
|
||||
#include "pbd/error.h"
|
||||
#ifndef PLATFORM_WINDOWS
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <glibmm.h>
|
||||
|
||||
#include "pbd/convert.h"
|
||||
#include "pbd/crossthread.h"
|
||||
#include "pbd/debug.h"
|
||||
#include "pbd/error.h"
|
||||
#include "pbd/failed_constructor.h"
|
||||
|
||||
#include "ardour/ardour.h"
|
||||
#include "ardour/audioengine.h"
|
||||
#include "ardour/revision.h"
|
||||
#include "ardour/session.h"
|
||||
|
||||
#include "control_protocol/control_protocol.h"
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
#include <windows.h>
|
||||
#define sleep(X) Sleep((X) * 1000)
|
||||
#endif
|
||||
|
||||
static const char* localedir = LOCALEDIR;
|
||||
|
||||
TestReceiver test_receiver;
|
||||
static string backend_client_name;
|
||||
static string backend_name = "JACK";
|
||||
static CrossThreadChannel xthread (true);
|
||||
static TestReceiver test_receiver;
|
||||
|
||||
/** @param dir Session directory.
|
||||
* @param state Session state file, without .ardour suffix.
|
||||
*/
|
||||
Session *
|
||||
static Session*
|
||||
load_session (string dir, string state)
|
||||
{
|
||||
SessionEvent::create_per_thread_pool ("test", 512);
|
||||
|
@ -40,14 +49,14 @@ load_session (string dir, string state)
|
|||
|
||||
AudioEngine* engine = AudioEngine::create ();
|
||||
|
||||
if (!engine->set_default_backend ()) {
|
||||
std::cerr << "Cannot create Audio/MIDI engine\n";
|
||||
::exit (1);
|
||||
if (!engine->set_backend (backend_name, backend_client_name, "")) {
|
||||
std::cerr << "Cannot set Audio/MIDI engine backend\n";
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (engine->start () != 0) {
|
||||
std::cerr << "Cannot start Audio/MIDI engine\n";
|
||||
::exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
Session* session = new Session (*engine, dir, state);
|
||||
|
@ -55,15 +64,52 @@ load_session (string dir, string state)
|
|||
return session;
|
||||
}
|
||||
|
||||
string session_name = "";
|
||||
string backend_client_name = "ardour";
|
||||
string backend_session_uuid;
|
||||
bool just_version = false;
|
||||
bool use_vst = true;
|
||||
bool try_hw_optimization = true;
|
||||
bool no_connect_ports = false;
|
||||
static void
|
||||
access_action (const std::string& action_group, const std::string& action_item)
|
||||
{
|
||||
if (action_group == "Common" && action_item == "Quit") {
|
||||
xthread.deliver ('x');
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
engine_halted (const char* reason)
|
||||
{
|
||||
cerr << "The audio backend has been shutdown";
|
||||
if (reason && strlen (reason) > 0) {
|
||||
cerr << ": " << reason;
|
||||
} else {
|
||||
cerr << ".";
|
||||
}
|
||||
cerr << endl;
|
||||
xthread.deliver ('x');
|
||||
}
|
||||
|
||||
#ifndef PLATFORM_WINDOWS
|
||||
static void
|
||||
wearedone (int)
|
||||
{
|
||||
cerr << "caught signal - terminating." << endl;
|
||||
xthread.deliver ('x');
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
print_version ()
|
||||
{
|
||||
cout
|
||||
<< PROGRAM_NAME
|
||||
<< VERSIONSTRING
|
||||
<< " (built using "
|
||||
<< ARDOUR::revision
|
||||
#ifdef __GNUC__
|
||||
<< " and GCC version " << __VERSION__
|
||||
#endif
|
||||
<< ')'
|
||||
<< endl;
|
||||
}
|
||||
|
||||
static void
|
||||
print_help ()
|
||||
{
|
||||
cout << "Usage: hardour [OPTIONS]... DIR SNAPSHOT_NAME\n\n"
|
||||
|
@ -79,129 +125,146 @@ print_help ()
|
|||
#ifdef WINDOWS_VST_SUPPORT
|
||||
<< " -V, --novst Do not use VST support\n"
|
||||
#endif
|
||||
;
|
||||
;
|
||||
}
|
||||
|
||||
int main (int argc, char* argv[])
|
||||
int
|
||||
main (int argc, char* argv[])
|
||||
{
|
||||
const char *optstring = "vhBdD:c:VOU:P";
|
||||
const char* optstring = "vhBdD:c:VOU:P";
|
||||
|
||||
/* clang-format off */
|
||||
const struct option longopts[] = {
|
||||
{ "version", 0, 0, 'v' },
|
||||
{ "help", 0, 0, 'h' },
|
||||
{ "bypass-plugins", 1, 0, 'B' },
|
||||
{ "disable-plugins", 1, 0, 'd' },
|
||||
{ "debug", 1, 0, 'D' },
|
||||
{ "name", 1, 0, 'c' },
|
||||
{ "novst", 0, 0, 'V' },
|
||||
{ "no-hw-optimizations", 0, 0, 'O' },
|
||||
{ "uuid", 1, 0, 'U' },
|
||||
{ "no-connect-ports", 0, 0, 'P' },
|
||||
{ "version", no_argument, 0, 'v' },
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ "bypass-plugins", no_argument, 0, 'B' },
|
||||
{ "disable-plugins", no_argument, 0, 'd' },
|
||||
{ "debug", required_argument, 0, 'D' },
|
||||
{ "name", required_argument, 0, 'c' },
|
||||
{ "novst", no_argument, 0, 'V' },
|
||||
{ "no-hw-optimizations", no_argument, 0, 'O' },
|
||||
{ "no-connect-ports", no_argument, 0, 'P' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
int option_index = 0;
|
||||
int c = 0;
|
||||
bool use_vst = true;
|
||||
bool try_hw_optimization = true;
|
||||
|
||||
while (1) {
|
||||
c = getopt_long (argc, argv, optstring, longopts, &option_index);
|
||||
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
backend_client_name = PBD::downcase (std::string (PROGRAM_NAME));
|
||||
|
||||
int c;
|
||||
while ((c = getopt_long (argc, argv, optstring, longopts, (int*)0)) != EOF) {
|
||||
switch (c) {
|
||||
case 0:
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
just_version = true;
|
||||
break;
|
||||
case 'v':
|
||||
print_version ();
|
||||
exit (EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
print_help ();
|
||||
exit (0);
|
||||
break;
|
||||
case 'h':
|
||||
print_help ();
|
||||
exit (EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
backend_client_name = optarg;
|
||||
break;
|
||||
case 'c':
|
||||
backend_client_name = optarg;
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
ARDOUR::Session::set_bypass_all_loaded_plugins (true);
|
||||
break;
|
||||
case 'B':
|
||||
ARDOUR::Session::set_bypass_all_loaded_plugins (true);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
ARDOUR::Session::set_disable_all_loaded_plugins (true);
|
||||
break;
|
||||
case 'd':
|
||||
ARDOUR::Session::set_disable_all_loaded_plugins (true);
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
if (PBD::parse_debug_options (optarg)) {
|
||||
::exit (1);
|
||||
}
|
||||
break;
|
||||
case 'D':
|
||||
if (PBD::parse_debug_options (optarg)) {
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
try_hw_optimization = false;
|
||||
break;
|
||||
case 'O':
|
||||
try_hw_optimization = false;
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
no_connect_ports = true;
|
||||
break;
|
||||
case 'P':
|
||||
ARDOUR::Port::set_connecting_blocked (true);
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
case 'V':
|
||||
#ifdef WINDOWS_VST_SUPPORT
|
||||
use_vst = false;
|
||||
use_vst = false;
|
||||
#endif /* WINDOWS_VST_SUPPORT */
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
backend_session_uuid = optarg;
|
||||
break;
|
||||
|
||||
default:
|
||||
print_help ();
|
||||
::exit (1);
|
||||
default:
|
||||
print_help ();
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (argc < 3) {
|
||||
print_help ();
|
||||
::exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!ARDOUR::init (false, true, localedir)) {
|
||||
cerr << "Ardour failed to initialize\n" << endl;
|
||||
::exit (1);
|
||||
if (!ARDOUR::init (use_vst, try_hw_optimization, localedir)) {
|
||||
cerr << "Ardour failed to initialize\n"
|
||||
<< endl;
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
Session* s = 0;
|
||||
|
||||
try {
|
||||
s = load_session (argv[optind], argv[optind+1]);
|
||||
s = load_session (argv[optind], argv[optind + 1]);
|
||||
} catch (failed_constructor& e) {
|
||||
cerr << "failed_constructor: " << e.what() << "\n";
|
||||
cerr << "failed_constructor: " << e.what () << "\n";
|
||||
exit (EXIT_FAILURE);
|
||||
} catch (AudioEngine::PortRegistrationFailure& e) {
|
||||
cerr << "PortRegistrationFailure: " << e.what() << "\n";
|
||||
cerr << "PortRegistrationFailure: " << e.what () << "\n";
|
||||
exit (EXIT_FAILURE);
|
||||
} catch (exception& e) {
|
||||
cerr << "exception: " << e.what() << "\n";
|
||||
cerr << "exception: " << e.what () << "\n";
|
||||
exit (EXIT_FAILURE);
|
||||
} catch (...) {
|
||||
cerr << "unknown exception.\n";
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* allow signal propagation, callback/thread-pool setup, etc
|
||||
* similar to to GUI "first idle"
|
||||
*/
|
||||
Glib::usleep (1000000); // 1 sec
|
||||
|
||||
if (!s) {
|
||||
cerr << "failed_to load session\n";
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
PBD::ScopedConnectionList con;
|
||||
BasicUI::AccessAction.connect_same_thread (con, boost::bind (&access_action, _1, _2));
|
||||
AudioEngine::instance ()->Halted.connect_same_thread (con, boost::bind (&engine_halted, _1));
|
||||
|
||||
#ifndef PLATFORM_WINDOWS
|
||||
signal (SIGINT, wearedone);
|
||||
signal (SIGTERM, wearedone);
|
||||
#endif
|
||||
|
||||
s->request_transport_speed (1.0);
|
||||
|
||||
sleep (-1);
|
||||
char msg;
|
||||
do {
|
||||
} while (0 == xthread.receive (msg, true));
|
||||
|
||||
AudioEngine::instance()->remove_session ();
|
||||
AudioEngine::instance ()->remove_session ();
|
||||
delete s;
|
||||
AudioEngine::instance()->stop ();
|
||||
AudioEngine::instance ()->stop ();
|
||||
|
||||
AudioEngine::destroy ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -74,26 +74,3 @@ def build(bld):
|
|||
|
||||
if bld.is_defined('NEED_INTL'):
|
||||
obj.linkflags = ' -lintl'
|
||||
|
||||
# Wrappers
|
||||
|
||||
wrapper_subst_dict = {
|
||||
'INSTALL_PREFIX' : bld.env['PREFIX'],
|
||||
'LIBDIR' : os.path.normpath(bld.env['LIBDIR']),
|
||||
'DATADIR' : os.path.normpath(bld.env['DATADIR']),
|
||||
'CONFDIR' : os.path.normpath(bld.env['CONFDIR']),
|
||||
'LIBS' : 'build/libs',
|
||||
'VERSION' : bld.env['VERSION'],
|
||||
'EXECUTABLE' : 'build/headless/hardour-' + str(bld.env['VERSION'])
|
||||
}
|
||||
|
||||
def set_subst_dict(obj, dict):
|
||||
for i in dict:
|
||||
setattr(obj, i, dict[i])
|
||||
|
||||
obj = bld(features = 'subst')
|
||||
obj.source = 'hardev_common.sh.in'
|
||||
obj.target = 'hardev_common_waf.sh'
|
||||
obj.chmod = Utils.O755
|
||||
obj.dict = wrapper_subst_dict
|
||||
set_subst_dict(obj, wrapper_subst_dict)
|
||||
|
|
|
@ -68,7 +68,6 @@ class LIBARDOUR_API AudioEngine : public PortManager, public SessionHandlePtr
|
|||
int discover_backends();
|
||||
std::vector<const AudioBackendInfo*> available_backends() const;
|
||||
std::string current_backend_name () const;
|
||||
boost::shared_ptr<AudioBackend> set_default_backend ();
|
||||
boost::shared_ptr<AudioBackend> set_backend (const std::string&, const std::string& arg1, const std::string& arg2);
|
||||
boost::shared_ptr<AudioBackend> current_backend() const { return _backend; }
|
||||
bool setup_required () const;
|
||||
|
|
|
@ -1,46 +1,43 @@
|
|||
/*
|
||||
Copyright (C) 2010 Paul Davis
|
||||
Copyright (C) 2017 Robin Gareus <robin@gareus.org>
|
||||
Author: Torben Hohn
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
* Copyright (C) 2010 Paul Davis
|
||||
* Copyright (C) 2017-2019 Robin Gareus <robin@gareus.org>
|
||||
* incl. some work from Torben Hohn
|
||||
*
|
||||
* 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_graph_h__
|
||||
#define __ardour_graph_h__
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "pbd/mpmc_queue.h"
|
||||
#include "pbd/semutils.h"
|
||||
|
||||
#include "ardour/libardour_visibility.h"
|
||||
#include "ardour/types.h"
|
||||
#include "ardour/audio_backend.h"
|
||||
#include "ardour/libardour_visibility.h"
|
||||
#include "ardour/session_handle.h"
|
||||
#include "ardour/types.h"
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class GraphNode;
|
||||
class Graph;
|
||||
|
||||
|
@ -50,27 +47,27 @@ class GraphEdges;
|
|||
|
||||
typedef boost::shared_ptr<GraphNode> node_ptr_t;
|
||||
|
||||
typedef std::list< node_ptr_t > node_list_t;
|
||||
typedef std::set< node_ptr_t > node_set_t;
|
||||
typedef std::list<node_ptr_t> node_list_t;
|
||||
typedef std::set<node_ptr_t> node_set_t;
|
||||
|
||||
class LIBARDOUR_API Graph : public SessionHandleRef
|
||||
{
|
||||
public:
|
||||
Graph (Session & session);
|
||||
Graph (Session& session);
|
||||
|
||||
void trigger (GraphNode * n);
|
||||
void rechain (boost::shared_ptr<RouteList>, GraphEdges const &);
|
||||
void trigger (GraphNode* n);
|
||||
void rechain (boost::shared_ptr<RouteList>, GraphEdges const&);
|
||||
|
||||
void dump (int chain);
|
||||
void dec_ref();
|
||||
void reached_terminal_node ();
|
||||
|
||||
void helper_thread();
|
||||
void helper_thread ();
|
||||
|
||||
int process_routes (pframes_t nframes, samplepos_t start_sample, samplepos_t end_sample, bool& need_butler);
|
||||
|
||||
int routes_no_roll (pframes_t nframes, samplepos_t start_sample, samplepos_t end_sample, bool non_rt_pending );
|
||||
int routes_no_roll (pframes_t nframes, samplepos_t start_sample, samplepos_t end_sample, bool non_rt_pending);
|
||||
|
||||
void process_one_route (Route * route);
|
||||
void process_one_route (Route* route);
|
||||
|
||||
void clear_other_chain ();
|
||||
|
||||
|
@ -80,36 +77,40 @@ protected:
|
|||
virtual void session_going_away ();
|
||||
|
||||
private:
|
||||
volatile bool _threads_active;
|
||||
|
||||
void reset_thread_list ();
|
||||
void drop_threads ();
|
||||
void restart_cycle();
|
||||
bool run_one();
|
||||
void main_thread();
|
||||
void prep();
|
||||
void run_one ();
|
||||
void main_thread ();
|
||||
void prep ();
|
||||
|
||||
node_list_t _nodes_rt[2];
|
||||
|
||||
node_list_t _init_trigger_list[2];
|
||||
|
||||
std::vector<GraphNode *> _trigger_queue;
|
||||
pthread_mutex_t _trigger_mutex;
|
||||
PBD::MPMCQueue<GraphNode*> _trigger_queue; ///< nodes that can be processed
|
||||
volatile guint _trigger_queue_size; ///< number of entries in trigger-queue
|
||||
|
||||
/** Start worker threads */
|
||||
PBD::Semaphore _execution_sem;
|
||||
|
||||
/** The number of processing threads that are asleep */
|
||||
volatile guint _idle_thread_cnt;
|
||||
|
||||
/** Signalled to start a run of the graph for a process callback */
|
||||
PBD::Semaphore _callback_start_sem;
|
||||
PBD::Semaphore _callback_done_sem;
|
||||
|
||||
/** The number of processing threads that are asleep */
|
||||
volatile gint _execution_tokens;
|
||||
/** The number of unprocessed nodes that do not feed any other node; updated during processing */
|
||||
volatile gint _finished_refcount;
|
||||
/** The initial number of nodes that do not feed any other node (for each chain) */
|
||||
volatile gint _init_finished_refcount[2];
|
||||
volatile guint _terminal_refcnt;
|
||||
|
||||
bool _graph_empty;
|
||||
/** The initial number of nodes that do not feed any other node (for each chain) */
|
||||
guint _n_terminal_nodes[2];
|
||||
bool _graph_empty;
|
||||
|
||||
/* number of background worker threads >= 0 */
|
||||
volatile guint _n_workers;
|
||||
|
||||
/* flag to terminate background threads */
|
||||
volatile gint _terminate;
|
||||
|
||||
/* chain swapping */
|
||||
Glib::Threads::Mutex _swap_mutex;
|
||||
|
@ -132,7 +133,7 @@ private:
|
|||
|
||||
/* engine / thread connection */
|
||||
PBD::ScopedConnectionList engine_connections;
|
||||
void engine_stopped ();
|
||||
void engine_stopped ();
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __ardour_graphnode_h__
|
||||
#define __ardour_graphnode_h__
|
||||
|
||||
|
@ -29,40 +28,48 @@
|
|||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class Graph;
|
||||
class GraphNode;
|
||||
|
||||
typedef boost::shared_ptr<GraphNode> node_ptr_t;
|
||||
typedef std::set< node_ptr_t > node_set_t;
|
||||
typedef std::list< node_ptr_t > node_list_t;
|
||||
typedef std::set<node_ptr_t> node_set_t;
|
||||
typedef std::list<node_ptr_t> node_list_t;
|
||||
|
||||
/** A node on our processing graph, ie a Route */
|
||||
class LIBARDOUR_API GraphNode
|
||||
class LIBARDOUR_API GraphActivision
|
||||
{
|
||||
public:
|
||||
GraphNode( boost::shared_ptr<Graph> Graph );
|
||||
virtual ~GraphNode();
|
||||
|
||||
void prep( int chain );
|
||||
void dec_ref();
|
||||
void finish( int chain );
|
||||
|
||||
virtual void process();
|
||||
|
||||
private:
|
||||
protected:
|
||||
friend class Graph;
|
||||
|
||||
/** Nodes that we directly feed */
|
||||
node_set_t _activation_set[2];
|
||||
|
||||
boost::shared_ptr<Graph> _graph;
|
||||
|
||||
gint _refcount;
|
||||
node_set_t _activation_set[2];
|
||||
/** The number of nodes that we directly feed us (one count for each chain) */
|
||||
gint _init_refcount[2];
|
||||
};
|
||||
|
||||
/** A node on our processing graph, ie a Route */
|
||||
class LIBARDOUR_API GraphNode : public GraphActivision
|
||||
{
|
||||
public:
|
||||
GraphNode (boost::shared_ptr<Graph> Graph);
|
||||
virtual ~GraphNode ();
|
||||
|
||||
void prep (int chain);
|
||||
void trigger ();
|
||||
|
||||
void
|
||||
run (int chain)
|
||||
{
|
||||
process ();
|
||||
finish (chain);
|
||||
}
|
||||
|
||||
private:
|
||||
void finish (int chain);
|
||||
void process ();
|
||||
|
||||
boost::shared_ptr<Graph> _graph;
|
||||
|
||||
gint _refcount;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -104,6 +104,8 @@ public:
|
|||
|
||||
boost::shared_ptr<Bundle> bundle () { return _bundle; }
|
||||
|
||||
bool can_add_port (DataType) const;
|
||||
|
||||
int add_port (std::string connection, void *src, DataType type = DataType::NIL);
|
||||
int remove_port (boost::shared_ptr<Port>, void *src);
|
||||
int connect (boost::shared_ptr<Port> our_port, std::string other_port, void *src);
|
||||
|
|
|
@ -123,6 +123,10 @@ public:
|
|||
static std::string get_factory_bytecode (const std::string&, const std::string& ffn = "factory", const std::string& fp = "f");
|
||||
static std::string user_script_dir ();
|
||||
|
||||
struct LIBARDOUR_API Sorter {
|
||||
bool operator() (LuaScriptInfoPtr const a, LuaScriptInfoPtr const b) const;
|
||||
};
|
||||
|
||||
private:
|
||||
static LuaScripting* _instance; // singleton
|
||||
LuaScripting ();
|
||||
|
|
|
@ -853,16 +853,6 @@ AudioEngine::drop_backend ()
|
|||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<AudioBackend>
|
||||
AudioEngine::set_default_backend ()
|
||||
{
|
||||
if (_backends.empty()) {
|
||||
return boost::shared_ptr<AudioBackend>();
|
||||
}
|
||||
|
||||
return set_backend (_backends.begin()->first, "", "");
|
||||
}
|
||||
|
||||
boost::shared_ptr<AudioBackend>
|
||||
AudioEngine::set_backend (const std::string& name, const std::string& arg1, const std::string& arg2)
|
||||
{
|
||||
|
|
|
@ -98,7 +98,7 @@ user_config_directory (int version)
|
|||
#endif
|
||||
if (home_dir.empty ()) {
|
||||
error << "Unable to determine home directory" << endmsg;
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
p = home_dir;
|
||||
|
||||
|
@ -119,7 +119,7 @@ user_config_directory (int version)
|
|||
if (g_mkdir_with_parents (p.c_str(), 0755)) {
|
||||
error << string_compose (_("Cannot create Configuration directory %1 - cannot run"),
|
||||
p) << endmsg;
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
} else if (!Glib::file_test (p, Glib::FILE_TEST_IS_DIR)) {
|
||||
fatal << string_compose (_("Configuration directory %1 already exists and is not a directory/folder - cannot run"),
|
||||
|
@ -156,7 +156,7 @@ user_cache_directory (std::string cachename)
|
|||
#endif
|
||||
if (home_dir.empty ()) {
|
||||
error << "Unable to determine home directory" << endmsg;
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
p = home_dir;
|
||||
|
||||
|
@ -187,7 +187,7 @@ user_cache_directory (std::string cachename)
|
|||
if (g_mkdir_with_parents (p.c_str(), 0755)) {
|
||||
error << string_compose (_("Cannot create cache directory %1 - cannot run"),
|
||||
p) << endmsg;
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
} else if (!Glib::file_test (p, Glib::FILE_TEST_IS_DIR)) {
|
||||
fatal << string_compose (_("Cache directory %1 already exists and is not a directory/folder - cannot run"),
|
||||
|
@ -209,7 +209,7 @@ ardour_dll_directory ()
|
|||
std::string s = Glib::getenv("ARDOUR_DLL_PATH");
|
||||
if (s.empty()) {
|
||||
std::cerr << _("ARDOUR_DLL_PATH not set in environment - exiting\n");
|
||||
::exit (1);
|
||||
::exit (EXIT_FAILURE);
|
||||
}
|
||||
return s;
|
||||
#endif
|
||||
|
|
|
@ -1,36 +1,37 @@
|
|||
/*
|
||||
Copyright (C) 2010 Paul Davis
|
||||
Author: Torben Hohn
|
||||
* Copyright (C) 2010 Paul Davis
|
||||
* Copyright (C) 2017-2019 Robin Gareus <robin@gareus.org>
|
||||
* incl. some work from Torben Hohn
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <cmath>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "pbd/compose.h"
|
||||
#include "pbd/debug_rt_alloc.h"
|
||||
#include "pbd/pthread_utils.h"
|
||||
|
||||
#include "ardour/audioengine.h"
|
||||
#include "ardour/debug.h"
|
||||
#include "ardour/graph.h"
|
||||
#include "ardour/types.h"
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/route.h"
|
||||
#include "ardour/process_thread.h"
|
||||
#include "ardour/audioengine.h"
|
||||
#include "ardour/route.h"
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/types.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
|
@ -43,44 +44,43 @@ static Graph* graph = 0;
|
|||
|
||||
extern "C" {
|
||||
|
||||
int alloc_allowed ()
|
||||
int
|
||||
alloc_allowed ()
|
||||
{
|
||||
return !graph->in_process_thread ();
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
Graph::Graph (Session & session)
|
||||
#define g_atomic_uint_get(x) static_cast<guint> (g_atomic_int_get (x))
|
||||
|
||||
Graph::Graph (Session& session)
|
||||
: SessionHandleRef (session)
|
||||
, _threads_active (false)
|
||||
, _execution_sem ("graph_execution", 0)
|
||||
, _callback_start_sem ("graph_start", 0)
|
||||
, _callback_done_sem ("graph_done", 0)
|
||||
, _graph_empty (true)
|
||||
, _current_chain (0)
|
||||
, _pending_chain (0)
|
||||
, _setup_chain (1)
|
||||
{
|
||||
pthread_mutex_init( &_trigger_mutex, NULL);
|
||||
g_atomic_int_set (&_terminal_refcnt, 0);
|
||||
g_atomic_int_set (&_terminate, 0);
|
||||
g_atomic_int_set (&_n_workers, 0);
|
||||
g_atomic_int_set (&_idle_thread_cnt, 0);
|
||||
g_atomic_int_set (&_trigger_queue_size, 0);
|
||||
|
||||
/* XXX: rather hacky `fix' to stop _trigger_queue.push_back() allocating
|
||||
* memory in the RT thread.
|
||||
*/
|
||||
_trigger_queue.reserve (8192);
|
||||
/* pre-allocate memory */
|
||||
_trigger_queue.reserve (1024);
|
||||
|
||||
_execution_tokens = 0;
|
||||
|
||||
_current_chain = 0;
|
||||
_pending_chain = 0;
|
||||
_setup_chain = 1;
|
||||
_graph_empty = true;
|
||||
|
||||
|
||||
ARDOUR::AudioEngine::instance()->Running.connect_same_thread (engine_connections, boost::bind (&Graph::reset_thread_list, this));
|
||||
ARDOUR::AudioEngine::instance()->Stopped.connect_same_thread (engine_connections, boost::bind (&Graph::engine_stopped, this));
|
||||
ARDOUR::AudioEngine::instance()->Halted.connect_same_thread (engine_connections, boost::bind (&Graph::engine_stopped, this));
|
||||
ARDOUR::AudioEngine::instance ()->Running.connect_same_thread (engine_connections, boost::bind (&Graph::reset_thread_list, this));
|
||||
ARDOUR::AudioEngine::instance ()->Stopped.connect_same_thread (engine_connections, boost::bind (&Graph::engine_stopped, this));
|
||||
ARDOUR::AudioEngine::instance ()->Halted.connect_same_thread (engine_connections, boost::bind (&Graph::engine_stopped, this));
|
||||
|
||||
reset_thread_list ();
|
||||
|
||||
#ifdef DEBUG_RT_ALLOC
|
||||
graph = this;
|
||||
graph = this;
|
||||
pbd_alloc_allowed = &::alloc_allowed;
|
||||
#endif
|
||||
}
|
||||
|
@ -89,9 +89,9 @@ void
|
|||
Graph::engine_stopped ()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
cerr << "Graph::engine_stopped. n_thread: " << AudioEngine::instance()->process_thread_count() << endl;
|
||||
cerr << "Graph::engine_stopped. n_thread: " << AudioEngine::instance ()->process_thread_count () << endl;
|
||||
#endif
|
||||
if (AudioEngine::instance()->process_thread_count() != 0) {
|
||||
if (AudioEngine::instance ()->process_thread_count () != 0) {
|
||||
drop_threads ();
|
||||
}
|
||||
}
|
||||
|
@ -101,73 +101,84 @@ void
|
|||
Graph::reset_thread_list ()
|
||||
{
|
||||
uint32_t num_threads = how_many_dsp_threads ();
|
||||
guint n_workers = g_atomic_uint_get (&_n_workers);
|
||||
|
||||
/* For now, we shouldn't be using the graph code if we only have 1 DSP thread */
|
||||
assert (num_threads > 1);
|
||||
assert (AudioEngine::instance ()->process_thread_count () == n_workers);
|
||||
|
||||
/* don't bother doing anything here if we already have the right
|
||||
* number of threads.
|
||||
*/
|
||||
|
||||
if (AudioEngine::instance()->process_thread_count() == num_threads) {
|
||||
if (AudioEngine::instance ()->process_thread_count () == num_threads) {
|
||||
return;
|
||||
}
|
||||
|
||||
Glib::Threads::Mutex::Lock lm (_session.engine().process_lock());
|
||||
Glib::Threads::Mutex::Lock lm (_session.engine ().process_lock ());
|
||||
|
||||
if (AudioEngine::instance()->process_thread_count() != 0) {
|
||||
if (n_workers > 0) {
|
||||
drop_threads ();
|
||||
}
|
||||
|
||||
_threads_active = true;
|
||||
/* Allow threads to run */
|
||||
g_atomic_int_set (&_terminate, 0);
|
||||
|
||||
if (AudioEngine::instance()->create_process_thread (boost::bind (&Graph::main_thread, this)) != 0) {
|
||||
if (AudioEngine::instance ()->create_process_thread (boost::bind (&Graph::main_thread, this)) != 0) {
|
||||
throw failed_constructor ();
|
||||
}
|
||||
|
||||
for (uint32_t i = 1; i < num_threads; ++i) {
|
||||
if (AudioEngine::instance()->create_process_thread (boost::bind (&Graph::helper_thread, this))) {
|
||||
if (AudioEngine::instance ()->create_process_thread (boost::bind (&Graph::helper_thread, this))) {
|
||||
throw failed_constructor ();
|
||||
}
|
||||
}
|
||||
|
||||
while (g_atomic_uint_get (&_n_workers) + 1 != num_threads) {
|
||||
sched_yield ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Graph::session_going_away()
|
||||
Graph::session_going_away ()
|
||||
{
|
||||
drop_threads ();
|
||||
|
||||
// now drop all references on the nodes.
|
||||
_nodes_rt[0].clear();
|
||||
_nodes_rt[1].clear();
|
||||
_init_trigger_list[0].clear();
|
||||
_init_trigger_list[1].clear();
|
||||
_trigger_queue.clear();
|
||||
_nodes_rt[0].clear ();
|
||||
_nodes_rt[1].clear ();
|
||||
_init_trigger_list[0].clear ();
|
||||
_init_trigger_list[1].clear ();
|
||||
g_atomic_int_set (&_trigger_queue_size, 0);
|
||||
_trigger_queue.clear ();
|
||||
}
|
||||
|
||||
void
|
||||
Graph::drop_threads ()
|
||||
{
|
||||
Glib::Threads::Mutex::Lock ls (_swap_mutex);
|
||||
_threads_active = false;
|
||||
|
||||
uint32_t thread_count = AudioEngine::instance()->process_thread_count ();
|
||||
/* Flag threads to terminate */
|
||||
g_atomic_int_set (&_terminate, 1);
|
||||
|
||||
for (unsigned int i=0; i < thread_count; i++) {
|
||||
pthread_mutex_lock (&_trigger_mutex);
|
||||
/* Wake-up sleeping threads */
|
||||
guint tc = g_atomic_uint_get (&_idle_thread_cnt);
|
||||
assert (tc == g_atomic_uint_get (&_n_workers));
|
||||
for (guint i = 0; i < tc; ++i) {
|
||||
_execution_sem.signal ();
|
||||
pthread_mutex_unlock (&_trigger_mutex);
|
||||
}
|
||||
|
||||
pthread_mutex_lock (&_trigger_mutex);
|
||||
/* and the main thread */
|
||||
_callback_start_sem.signal ();
|
||||
pthread_mutex_unlock (&_trigger_mutex);
|
||||
|
||||
AudioEngine::instance()->join_process_threads ();
|
||||
/* join process threads */
|
||||
AudioEngine::instance ()->join_process_threads ();
|
||||
|
||||
g_atomic_int_set (&_n_workers, 0);
|
||||
g_atomic_int_set (&_idle_thread_cnt, 0);
|
||||
|
||||
/* signal main process thread if it's waiting for an already terminated thread */
|
||||
_callback_done_sem.signal ();
|
||||
_execution_tokens = 0;
|
||||
|
||||
/* reset semaphores.
|
||||
* This is somewhat ugly, yet if a thread is killed (e.g jackd terminates
|
||||
|
@ -177,7 +188,7 @@ Graph::drop_threads ()
|
|||
int d1 = _execution_sem.reset ();
|
||||
int d2 = _callback_start_sem.reset ();
|
||||
int d3 = _callback_done_sem.reset ();
|
||||
cerr << "Graph::drop_threads() sema-counts: " << d1 << ", " << d2<< ", " << d3 << endl;
|
||||
cerr << "Graph::drop_threads() sema-counts: " << d1 << ", " << d2 << ", " << d3 << endl;
|
||||
#else
|
||||
_execution_sem.reset ();
|
||||
_callback_start_sem.reset ();
|
||||
|
@ -185,6 +196,7 @@ Graph::drop_threads ()
|
|||
#endif
|
||||
}
|
||||
|
||||
/* special case route removal -- called from Session::remove_routes */
|
||||
void
|
||||
Graph::clear_other_chain ()
|
||||
{
|
||||
|
@ -192,9 +204,8 @@ Graph::clear_other_chain ()
|
|||
|
||||
while (1) {
|
||||
if (_setup_chain != _pending_chain) {
|
||||
|
||||
for (node_list_t::iterator ni=_nodes_rt[_setup_chain].begin(); ni!=_nodes_rt[_setup_chain].end(); ni++) {
|
||||
(*ni)->_activation_set[_setup_chain].clear();
|
||||
for (node_list_t::iterator ni = _nodes_rt[_setup_chain].begin (); ni != _nodes_rt[_setup_chain].end (); ++ni) {
|
||||
(*ni)->_activation_set[_setup_chain].clear ();
|
||||
}
|
||||
|
||||
_nodes_rt[_setup_chain].clear ();
|
||||
|
@ -209,98 +220,107 @@ Graph::clear_other_chain ()
|
|||
}
|
||||
|
||||
void
|
||||
Graph::prep()
|
||||
Graph::prep ()
|
||||
{
|
||||
node_list_t::iterator i;
|
||||
int chain;
|
||||
|
||||
if (_swap_mutex.trylock()) {
|
||||
// we got the swap mutex.
|
||||
if (_current_chain != _pending_chain)
|
||||
{
|
||||
// printf ("chain swap ! %d -> %d\n", _current_chain, _pending_chain);
|
||||
_setup_chain = _current_chain;
|
||||
if (_swap_mutex.trylock ()) {
|
||||
/* swap mutex acquired */
|
||||
if (_current_chain != _pending_chain) {
|
||||
/* use new chain */
|
||||
_setup_chain = _current_chain;
|
||||
_current_chain = _pending_chain;
|
||||
/* ensure that all nodes can be queued */
|
||||
_trigger_queue.reserve (_nodes_rt[_current_chain].size ());
|
||||
assert (g_atomic_uint_get (&_trigger_queue_size) == 0);
|
||||
_cleanup_cond.signal ();
|
||||
}
|
||||
_swap_mutex.unlock ();
|
||||
}
|
||||
|
||||
chain = _current_chain;
|
||||
|
||||
_graph_empty = true;
|
||||
for (i=_nodes_rt[chain].begin(); i!=_nodes_rt[chain].end(); i++) {
|
||||
(*i)->prep( chain);
|
||||
|
||||
int chain = _current_chain;
|
||||
|
||||
node_list_t::iterator i;
|
||||
for (i = _nodes_rt[chain].begin (); i != _nodes_rt[chain].end (); ++i) {
|
||||
(*i)->prep (chain);
|
||||
_graph_empty = false;
|
||||
}
|
||||
_finished_refcount = _init_finished_refcount[chain];
|
||||
|
||||
assert (_graph_empty != (_n_terminal_nodes[chain] > 0));
|
||||
|
||||
g_atomic_int_set (&_terminal_refcnt, _n_terminal_nodes[chain]);
|
||||
|
||||
/* Trigger the initial nodes for processing, which are the ones at the `input' end */
|
||||
pthread_mutex_lock (&_trigger_mutex);
|
||||
for (i=_init_trigger_list[chain].begin(); i!=_init_trigger_list[chain].end(); i++) {
|
||||
/* don't use ::trigger here, as we have already locked the mutex */
|
||||
for (i = _init_trigger_list[chain].begin (); i != _init_trigger_list[chain].end (); i++) {
|
||||
g_atomic_int_inc (&_trigger_queue_size);
|
||||
_trigger_queue.push_back (i->get ());
|
||||
}
|
||||
pthread_mutex_unlock (&_trigger_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
Graph::trigger (GraphNode* n)
|
||||
{
|
||||
pthread_mutex_lock (&_trigger_mutex);
|
||||
g_atomic_int_inc (&_trigger_queue_size);
|
||||
_trigger_queue.push_back (n);
|
||||
pthread_mutex_unlock (&_trigger_mutex);
|
||||
}
|
||||
|
||||
/** Called when a node at the `output' end of the chain (ie one that has no-one to feed)
|
||||
* is finished.
|
||||
*/
|
||||
void
|
||||
Graph::dec_ref()
|
||||
Graph::reached_terminal_node ()
|
||||
{
|
||||
if (g_atomic_int_dec_and_test (const_cast<gint*> (&_finished_refcount))) {
|
||||
if (g_atomic_int_dec_and_test (&_terminal_refcnt)) {
|
||||
again:
|
||||
|
||||
/* We have run all the nodes that are at the `output' end of
|
||||
* the graph, so there is nothing more to do this time around.
|
||||
*/
|
||||
assert (g_atomic_uint_get (&_trigger_queue_size) == 0);
|
||||
|
||||
restart_cycle ();
|
||||
/* Notify caller */
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 cycle done.\n", pthread_name ()));
|
||||
|
||||
_callback_done_sem.signal ();
|
||||
|
||||
/* Ensure that all background threads are idle.
|
||||
* When freewheeling there may be an immediate restart:
|
||||
* If there are more threads than CPU cores, some worker-
|
||||
* threads may only be "on the way" to become idle.
|
||||
*/
|
||||
guint n_workers = g_atomic_uint_get (&_n_workers);
|
||||
while (g_atomic_uint_get (&_idle_thread_cnt) != n_workers) {
|
||||
sched_yield ();
|
||||
}
|
||||
|
||||
/* Block until the a process callback */
|
||||
_callback_start_sem.wait ();
|
||||
|
||||
if (g_atomic_int_get (&_terminate)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 prepare new cycle.\n", pthread_name ()));
|
||||
|
||||
/* Prepare next cycle:
|
||||
* - Reset terminal reference count
|
||||
* - queue initial nodes
|
||||
*/
|
||||
prep ();
|
||||
|
||||
if (_graph_empty && !g_atomic_int_get (&_terminate)) {
|
||||
goto again;
|
||||
}
|
||||
/* .. continue in worker-thread */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Graph::restart_cycle()
|
||||
{
|
||||
// we are through. wakeup our caller.
|
||||
DEBUG_TRACE(DEBUG::ProcessThreads, string_compose ("%1 cycle done.\n", pthread_name()));
|
||||
|
||||
again:
|
||||
_callback_done_sem.signal ();
|
||||
|
||||
/* Block until the a process callback triggers us */
|
||||
_callback_start_sem.wait();
|
||||
|
||||
if (!_threads_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_TRACE(DEBUG::ProcessThreads, string_compose ("%1 prepare new cycle.\n", pthread_name()));
|
||||
prep ();
|
||||
|
||||
if (_graph_empty && _threads_active) {
|
||||
goto again;
|
||||
}
|
||||
|
||||
// returning will restart the cycle.
|
||||
// starting with waking up the others.
|
||||
}
|
||||
|
||||
/** Rechain our stuff using a list of routes (which can be in any order) and
|
||||
* a directed graph of their interconnections, which is guaranteed to be
|
||||
* acyclic.
|
||||
*/
|
||||
void
|
||||
Graph::rechain (boost::shared_ptr<RouteList> routelist, GraphEdges const & edges)
|
||||
Graph::rechain (boost::shared_ptr<RouteList> routelist, GraphEdges const& edges)
|
||||
{
|
||||
Glib::Threads::Mutex::Lock ls (_swap_mutex);
|
||||
|
||||
|
@ -310,26 +330,25 @@ Graph::rechain (boost::shared_ptr<RouteList> routelist, GraphEdges const & edges
|
|||
/* This will become the number of nodes that do not feed any other node;
|
||||
* once we have processed this number of those nodes, we have finished.
|
||||
*/
|
||||
_init_finished_refcount[chain] = 0;
|
||||
_n_terminal_nodes[chain] = 0;
|
||||
|
||||
/* This will become a list of nodes that are not fed by another node, ie
|
||||
* those at the `input' end.
|
||||
*/
|
||||
_init_trigger_list[chain].clear();
|
||||
_init_trigger_list[chain].clear ();
|
||||
|
||||
_nodes_rt[chain].clear();
|
||||
_nodes_rt[chain].clear ();
|
||||
|
||||
/* Clear things out, and make _nodes_rt[chain] a copy of routelist */
|
||||
for (RouteList::iterator ri=routelist->begin(); ri!=routelist->end(); ri++) {
|
||||
for (RouteList::iterator ri = routelist->begin (); ri != routelist->end (); ri++) {
|
||||
(*ri)->_init_refcount[chain] = 0;
|
||||
(*ri)->_activation_set[chain].clear();
|
||||
(*ri)->_activation_set[chain].clear ();
|
||||
_nodes_rt[chain].push_back (*ri);
|
||||
}
|
||||
|
||||
// now add refs for the connections.
|
||||
|
||||
for (node_list_t::iterator ni = _nodes_rt[chain].begin(); ni != _nodes_rt[chain].end(); ni++) {
|
||||
|
||||
for (node_list_t::iterator ni = _nodes_rt[chain].begin (); ni != _nodes_rt[chain].end (); ni++) {
|
||||
boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (*ni);
|
||||
|
||||
/* The routes that are directly fed by r */
|
||||
|
@ -339,7 +358,7 @@ Graph::rechain (boost::shared_ptr<RouteList> routelist, GraphEdges const & edges
|
|||
bool const has_output = !fed_from_r.empty ();
|
||||
|
||||
/* Set up r's activation set */
|
||||
for (set<GraphVertex>::iterator i = fed_from_r.begin(); i != fed_from_r.end(); ++i) {
|
||||
for (set<GraphVertex>::iterator i = fed_from_r.begin (); i != fed_from_r.end (); ++i) {
|
||||
r->_activation_set[chain].insert (*i);
|
||||
}
|
||||
|
||||
|
@ -347,7 +366,7 @@ Graph::rechain (boost::shared_ptr<RouteList> routelist, GraphEdges const & edges
|
|||
bool const has_input = !edges.has_none_to (r);
|
||||
|
||||
/* Increment the refcount of any route that we directly feed */
|
||||
for (node_set_t::iterator ai = r->_activation_set[chain].begin(); ai != r->_activation_set[chain].end(); ai++) {
|
||||
for (node_set_t::iterator ai = r->_activation_set[chain].begin (); ai != r->_activation_set[chain].end (); ai++) {
|
||||
(*ai)->_init_refcount[chain] += 1;
|
||||
}
|
||||
|
||||
|
@ -360,128 +379,147 @@ Graph::rechain (boost::shared_ptr<RouteList> routelist, GraphEdges const & edges
|
|||
/* no output, so this is one of the nodes that we can count off to decide
|
||||
* if we've finished
|
||||
*/
|
||||
_init_finished_refcount[chain] += 1;
|
||||
_n_terminal_nodes[chain] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
_pending_chain = chain;
|
||||
dump(chain);
|
||||
dump (chain);
|
||||
}
|
||||
|
||||
/** Called by both the main thread and all helpers.
|
||||
* @return true to quit, false to carry on.
|
||||
*/
|
||||
bool
|
||||
Graph::run_one()
|
||||
/** Called by both the main thread and all helpers. */
|
||||
void
|
||||
Graph::run_one ()
|
||||
{
|
||||
GraphNode* to_run;
|
||||
GraphNode* to_run = NULL;
|
||||
|
||||
pthread_mutex_lock (&_trigger_mutex);
|
||||
if (_trigger_queue.size()) {
|
||||
to_run = _trigger_queue.back();
|
||||
_trigger_queue.pop_back();
|
||||
} else {
|
||||
to_run = 0;
|
||||
if (g_atomic_int_get (&_terminate)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* the number of threads that are asleep */
|
||||
int et = _execution_tokens;
|
||||
/* the number of nodes that need to be run */
|
||||
int ts = _trigger_queue.size();
|
||||
if (_trigger_queue.pop_front (to_run)) {
|
||||
/* Wake up idle threads, but at most as many as there's
|
||||
* work in the trigger queue that can be processed by
|
||||
* other threads.
|
||||
* This thread as not yet decreased _trigger_queue_size.
|
||||
*/
|
||||
guint idle_cnt = g_atomic_uint_get (&_idle_thread_cnt);
|
||||
guint work_avail = g_atomic_uint_get (&_trigger_queue_size);
|
||||
guint wakeup = std::min (idle_cnt + 1, work_avail);
|
||||
|
||||
/* hence how many threads to wake up */
|
||||
int wakeup = min (et, ts);
|
||||
/* update the number of threads that will still be sleeping */
|
||||
_execution_tokens -= wakeup;
|
||||
|
||||
DEBUG_TRACE(DEBUG::ProcessThreads, string_compose ("%1 signals %2\n", pthread_name(), wakeup));
|
||||
|
||||
for (int i = 0; i < wakeup; i++) {
|
||||
_execution_sem.signal ();
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 signals %2 threads\n", pthread_name (), wakeup));
|
||||
for (guint i = 1; i < wakeup; ++i) {
|
||||
_execution_sem.signal ();
|
||||
}
|
||||
}
|
||||
|
||||
while (to_run == 0) {
|
||||
_execution_tokens += 1;
|
||||
pthread_mutex_unlock (&_trigger_mutex);
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 goes to sleep\n", pthread_name()));
|
||||
while (!to_run) {
|
||||
/* Wait for work, fall asleep */
|
||||
g_atomic_int_inc (&_idle_thread_cnt);
|
||||
assert (g_atomic_uint_get (&_idle_thread_cnt) <= _n_workers);
|
||||
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 goes to sleep\n", pthread_name ()));
|
||||
_execution_sem.wait ();
|
||||
if (!_threads_active) {
|
||||
return true;
|
||||
}
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 is awake\n", pthread_name()));
|
||||
pthread_mutex_lock (&_trigger_mutex);
|
||||
if (_trigger_queue.size()) {
|
||||
to_run = _trigger_queue.back();
|
||||
_trigger_queue.pop_back();
|
||||
|
||||
if (g_atomic_int_get (&_terminate)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 is awake\n", pthread_name ()));
|
||||
|
||||
g_atomic_int_dec_and_test (&_idle_thread_cnt);
|
||||
|
||||
/* Try to find some work to do */
|
||||
_trigger_queue.pop_front (to_run);
|
||||
}
|
||||
pthread_mutex_unlock (&_trigger_mutex);
|
||||
|
||||
to_run->process();
|
||||
to_run->finish (_current_chain);
|
||||
/* Process the graph-node */
|
||||
g_atomic_int_dec_and_test (&_trigger_queue_size);
|
||||
to_run->run (_current_chain);
|
||||
|
||||
DEBUG_TRACE(DEBUG::ProcessThreads, string_compose ("%1 has finished run_one()\n", pthread_name()));
|
||||
|
||||
return !_threads_active;
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 has finished run_one()\n", pthread_name ()));
|
||||
}
|
||||
|
||||
void
|
||||
Graph::helper_thread()
|
||||
Graph::helper_thread ()
|
||||
{
|
||||
g_atomic_int_inc (&_n_workers);
|
||||
guint id = g_atomic_uint_get (&_n_workers);
|
||||
|
||||
/* This is needed for ARDOUR::Session requests called from rt-processors
|
||||
* in particular Lua scripts may do cross-thread calls */
|
||||
if (!SessionEvent::has_per_thread_pool ()) {
|
||||
char name[64];
|
||||
snprintf (name, 64, "RT-%u-%p", id, (void*)DEBUG_THREAD_SELF);
|
||||
pthread_set_name (name);
|
||||
SessionEvent::create_per_thread_pool (name, 64);
|
||||
PBD::notify_event_loops_about_thread_creation (pthread_self (), name, 64);
|
||||
}
|
||||
|
||||
suspend_rt_malloc_checks ();
|
||||
ProcessThread* pt = new ProcessThread ();
|
||||
resume_rt_malloc_checks ();
|
||||
|
||||
pt->get_buffers();
|
||||
pt->get_buffers ();
|
||||
|
||||
while(1) {
|
||||
if (run_one()) {
|
||||
break;
|
||||
}
|
||||
while (!g_atomic_int_get (&_terminate)) {
|
||||
run_one ();
|
||||
}
|
||||
|
||||
pt->drop_buffers();
|
||||
pt->drop_buffers ();
|
||||
delete pt;
|
||||
}
|
||||
|
||||
/** Here's the main graph thread */
|
||||
void
|
||||
Graph::main_thread()
|
||||
Graph::main_thread ()
|
||||
{
|
||||
/* first time setup */
|
||||
|
||||
suspend_rt_malloc_checks ();
|
||||
ProcessThread* pt = new ProcessThread ();
|
||||
|
||||
/* This is needed for ARDOUR::Session requests called from rt-processors
|
||||
* in particular Lua scripts may do cross-thread calls */
|
||||
if (!SessionEvent::has_per_thread_pool ()) {
|
||||
char name[64];
|
||||
snprintf (name, 64, "RT-main-%p", (void*)DEBUG_THREAD_SELF);
|
||||
pthread_set_name (name);
|
||||
SessionEvent::create_per_thread_pool (name, 64);
|
||||
PBD::notify_event_loops_about_thread_creation (pthread_self (), name, 64);
|
||||
}
|
||||
resume_rt_malloc_checks ();
|
||||
|
||||
pt->get_buffers();
|
||||
pt->get_buffers ();
|
||||
|
||||
/* Wait for initial process callback */
|
||||
again:
|
||||
_callback_start_sem.wait ();
|
||||
|
||||
DEBUG_TRACE(DEBUG::ProcessThreads, "main thread is awake\n");
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, "main thread is awake\n");
|
||||
|
||||
if (!_threads_active) {
|
||||
pt->drop_buffers();
|
||||
if (g_atomic_int_get (&_terminate)) {
|
||||
pt->drop_buffers ();
|
||||
delete (pt);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Bootstrap the trigger-list
|
||||
* (later this is done by Graph_reached_terminal_node) */
|
||||
prep ();
|
||||
|
||||
if (_graph_empty && _threads_active) {
|
||||
if (_graph_empty && !g_atomic_int_get (&_terminate)) {
|
||||
_callback_done_sem.signal ();
|
||||
DEBUG_TRACE(DEBUG::ProcessThreads, "main thread sees graph done, goes back to sleep\n");
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, "main thread sees graph done, goes back to sleep\n");
|
||||
goto again;
|
||||
}
|
||||
|
||||
/* This loop will run forever */
|
||||
while (1) {
|
||||
DEBUG_TRACE(DEBUG::ProcessThreads, string_compose ("main thread (%1) runs one graph node\n", pthread_name ()));
|
||||
if (run_one()) {
|
||||
break;
|
||||
}
|
||||
/* After setup, the main-thread just becomes a normal worker */
|
||||
while (!g_atomic_int_get (&_terminate)) {
|
||||
run_one ();
|
||||
}
|
||||
|
||||
pt->drop_buffers();
|
||||
pt->drop_buffers ();
|
||||
delete (pt);
|
||||
}
|
||||
|
||||
|
@ -490,25 +528,25 @@ Graph::dump (int chain)
|
|||
{
|
||||
#ifndef NDEBUG
|
||||
node_list_t::iterator ni;
|
||||
node_set_t::iterator ai;
|
||||
node_set_t::iterator ai;
|
||||
|
||||
chain = _pending_chain;
|
||||
|
||||
DEBUG_TRACE (DEBUG::Graph, "--------------------------------------------Graph dump:\n");
|
||||
for (ni=_nodes_rt[chain].begin(); ni!=_nodes_rt[chain].end(); ni++) {
|
||||
boost::shared_ptr<Route> rp = boost::dynamic_pointer_cast<Route>( *ni);
|
||||
DEBUG_TRACE (DEBUG::Graph, string_compose ("GraphNode: %1 refcount: %2\n", rp->name().c_str(), (*ni)->_init_refcount[chain]));
|
||||
for (ai=(*ni)->_activation_set[chain].begin(); ai!=(*ni)->_activation_set[chain].end(); ai++) {
|
||||
DEBUG_TRACE (DEBUG::Graph, string_compose (" triggers: %1\n", boost::dynamic_pointer_cast<Route>(*ai)->name().c_str()));
|
||||
for (ni = _nodes_rt[chain].begin (); ni != _nodes_rt[chain].end (); ni++) {
|
||||
boost::shared_ptr<Route> rp = boost::dynamic_pointer_cast<Route> (*ni);
|
||||
DEBUG_TRACE (DEBUG::Graph, string_compose ("GraphNode: %1 refcount: %2\n", rp->name ().c_str (), (*ni)->_init_refcount[chain]));
|
||||
for (ai = (*ni)->_activation_set[chain].begin (); ai != (*ni)->_activation_set[chain].end (); ai++) {
|
||||
DEBUG_TRACE (DEBUG::Graph, string_compose (" triggers: %1\n", boost::dynamic_pointer_cast<Route> (*ai)->name ().c_str ()));
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Graph, "------------- trigger list:\n");
|
||||
for (ni=_init_trigger_list[chain].begin(); ni!=_init_trigger_list[chain].end(); ni++) {
|
||||
DEBUG_TRACE (DEBUG::Graph, string_compose ("GraphNode: %1 refcount: %2\n", boost::dynamic_pointer_cast<Route>(*ni)->name().c_str(), (*ni)->_init_refcount[chain]));
|
||||
for (ni = _init_trigger_list[chain].begin (); ni != _init_trigger_list[chain].end (); ni++) {
|
||||
DEBUG_TRACE (DEBUG::Graph, string_compose ("GraphNode: %1 refcount: %2\n", boost::dynamic_pointer_cast<Route> (*ni)->name ().c_str (), (*ni)->_init_refcount[chain]));
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Graph, string_compose ("final activation refcount: %1\n", _init_finished_refcount[chain]));
|
||||
DEBUG_TRACE (DEBUG::Graph, string_compose ("final activation refcount: %1\n", _n_terminal_nodes[chain]));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -517,17 +555,19 @@ Graph::process_routes (pframes_t nframes, samplepos_t start_sample, samplepos_t
|
|||
{
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("graph execution from %1 to %2 = %3\n", start_sample, end_sample, nframes));
|
||||
|
||||
if (!_threads_active) return 0;
|
||||
if (g_atomic_int_get (&_terminate)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
_process_nframes = nframes;
|
||||
_process_nframes = nframes;
|
||||
_process_start_sample = start_sample;
|
||||
_process_end_sample = end_sample;
|
||||
_process_end_sample = end_sample;
|
||||
|
||||
_process_noroll = false;
|
||||
_process_retval = 0;
|
||||
_process_noroll = false;
|
||||
_process_retval = 0;
|
||||
_process_need_butler = false;
|
||||
|
||||
DEBUG_TRACE(DEBUG::ProcessThreads, "wake graph for non-silent process\n");
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, "wake graph for non-silent process\n");
|
||||
_callback_start_sem.signal ();
|
||||
_callback_done_sem.wait ();
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, "graph execution complete\n");
|
||||
|
@ -542,18 +582,20 @@ Graph::routes_no_roll (pframes_t nframes, samplepos_t start_sample, samplepos_t
|
|||
{
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("no-roll graph execution from %1 to %2 = %3\n", start_sample, end_sample, nframes));
|
||||
|
||||
if (!_threads_active) return 0;
|
||||
if (g_atomic_int_get (&_terminate)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
_process_nframes = nframes;
|
||||
_process_start_sample = start_sample;
|
||||
_process_end_sample = end_sample;
|
||||
_process_nframes = nframes;
|
||||
_process_start_sample = start_sample;
|
||||
_process_end_sample = end_sample;
|
||||
_process_non_rt_pending = non_rt_pending;
|
||||
|
||||
_process_noroll = true;
|
||||
_process_retval = 0;
|
||||
_process_noroll = true;
|
||||
_process_retval = 0;
|
||||
_process_need_butler = false;
|
||||
|
||||
DEBUG_TRACE(DEBUG::ProcessThreads, "wake graph for no-roll process\n");
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, "wake graph for no-roll process\n");
|
||||
_callback_start_sem.signal ();
|
||||
_callback_done_sem.wait ();
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, "graph execution complete\n");
|
||||
|
@ -564,11 +606,11 @@ void
|
|||
Graph::process_one_route (Route* route)
|
||||
{
|
||||
bool need_butler = false;
|
||||
int retval;
|
||||
int retval;
|
||||
|
||||
assert (route);
|
||||
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 runs route %2\n", pthread_name(), route->name()));
|
||||
DEBUG_TRACE (DEBUG::ProcessThreads, string_compose ("%1 runs route %2\n", pthread_name (), route->name ()));
|
||||
|
||||
if (_process_noroll) {
|
||||
retval = route->no_roll (_process_nframes, _process_start_sample, _process_end_sample, _process_non_rt_pending);
|
||||
|
@ -588,5 +630,5 @@ Graph::process_one_route (Route* route)
|
|||
bool
|
||||
Graph::in_process_thread () const
|
||||
{
|
||||
return AudioEngine::instance()->in_process_thread ();
|
||||
return AudioEngine::instance ()->in_process_thread ();
|
||||
}
|
||||
|
|
|
@ -25,11 +25,11 @@
|
|||
using namespace ARDOUR;
|
||||
|
||||
GraphNode::GraphNode (boost::shared_ptr<Graph> graph)
|
||||
: _graph(graph)
|
||||
: _graph (graph)
|
||||
{
|
||||
}
|
||||
|
||||
GraphNode::~GraphNode()
|
||||
GraphNode::~GraphNode ()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -37,19 +37,20 @@ void
|
|||
GraphNode::prep (int chain)
|
||||
{
|
||||
/* This is the number of nodes that directly feed us */
|
||||
_refcount = _init_refcount[chain];
|
||||
g_atomic_int_set (&_refcount, _init_refcount[chain]);
|
||||
}
|
||||
|
||||
/** Called by another node to tell us that one of the nodes that feed us
|
||||
* has been processed.
|
||||
*/
|
||||
/** Called by an upstream node, when it has completed processing */
|
||||
void
|
||||
GraphNode::dec_ref()
|
||||
GraphNode::trigger ()
|
||||
{
|
||||
/* check if we can run */
|
||||
if (g_atomic_int_dec_and_test (&_refcount)) {
|
||||
/* All the nodes that feed us are done, so we can queue this node
|
||||
* for processing.
|
||||
*/
|
||||
#if 0 // TODO optimize: remove prep()
|
||||
/* reset reference count for next cycle */
|
||||
g_atomic_int_set (&_refcount, _init_refcount[chain]);
|
||||
#endif
|
||||
/* All nodes that feed this node have completed, so this node be processed now. */
|
||||
_graph->trigger (this);
|
||||
}
|
||||
}
|
||||
|
@ -58,23 +59,23 @@ void
|
|||
GraphNode::finish (int chain)
|
||||
{
|
||||
node_set_t::iterator i;
|
||||
bool feeds_somebody = false;
|
||||
bool feeds = false;
|
||||
|
||||
/* Tell the nodes that we feed that we've finished */
|
||||
for (i=_activation_set[chain].begin(); i!=_activation_set[chain].end(); i++) {
|
||||
(*i)->dec_ref();
|
||||
feeds_somebody = true;
|
||||
/* Notify downstream nodes that depend on this node */
|
||||
for (i = _activation_set[chain].begin (); i != _activation_set[chain].end (); ++i) {
|
||||
(*i)->trigger ();
|
||||
feeds = true;
|
||||
}
|
||||
|
||||
if (!feeds_somebody) {
|
||||
/* This node does not feed anybody, so decrement the graph's finished count */
|
||||
_graph->dec_ref();
|
||||
if (!feeds) {
|
||||
/* This node is a terminal node that does not feed another note,
|
||||
* so notify the graph to decrement the the finished count */
|
||||
_graph->reached_terminal_node ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GraphNode::process()
|
||||
GraphNode::process ()
|
||||
{
|
||||
_graph->process_one_route (dynamic_cast<Route *>(this));
|
||||
_graph->process_one_route (dynamic_cast<Route*> (this));
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
#include "ardour/session.h"
|
||||
#include "pbd/memento_command.h"
|
||||
|
||||
#include "ptformat/ptfformat.h"
|
||||
#include "ptformat/ptformat.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
|
@ -201,7 +201,7 @@ Session::import_pt (PTFFormat& ptf, ImportStatus& status)
|
|||
|
||||
vector<ptflookup_t> ptfwavpair;
|
||||
vector<ptflookup_t> ptfregpair;
|
||||
vector<PTFFormat::wav_t>::iterator w;
|
||||
vector<PTFFormat::wav_t>::const_iterator w;
|
||||
|
||||
SourceList just_one_src;
|
||||
SourceList imported;
|
||||
|
@ -213,18 +213,18 @@ Session::import_pt (PTFFormat& ptf, ImportStatus& status)
|
|||
|
||||
Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock (), Glib::Threads::NOT_LOCK);
|
||||
|
||||
for (w = ptf.audiofiles.begin (); w != ptf.audiofiles.end () && !status.cancel; ++w) {
|
||||
for (w = ptf.audiofiles ().begin (); w != ptf.audiofiles ().end () && !status.cancel; ++w) {
|
||||
ptflookup_t p;
|
||||
ok = false;
|
||||
/* Try audio file */
|
||||
fullpath = Glib::build_filename (Glib::path_get_dirname (ptf.path), "Audio Files");
|
||||
fullpath = Glib::build_filename (Glib::path_get_dirname (ptf.path ()), "Audio Files");
|
||||
fullpath = Glib::build_filename (fullpath, w->filename);
|
||||
if (Glib::file_test (fullpath, Glib::FILE_TEST_EXISTS)) {
|
||||
just_one_src.clear();
|
||||
ok = import_sndfile_as_region (fullpath, SrcBest, pos, just_one_src, status);
|
||||
} else {
|
||||
/* Try fade file */
|
||||
fullpath = Glib::build_filename (Glib::path_get_dirname (ptf.path), "Fade Files");
|
||||
fullpath = Glib::build_filename (Glib::path_get_dirname (ptf.path ()), "Fade Files");
|
||||
fullpath = Glib::build_filename (fullpath, w->filename);
|
||||
if (Glib::file_test (fullpath, Glib::FILE_TEST_EXISTS)) {
|
||||
just_one_src.clear();
|
||||
|
@ -237,7 +237,7 @@ Session::import_pt (PTFFormat& ptf, ImportStatus& status)
|
|||
* it won't be resampled, so we can only do this
|
||||
* when sample rates are matching
|
||||
*/
|
||||
if (sample_rate () == ptf.sessionrate) {
|
||||
if (sample_rate () == ptf.sessionrate ()) {
|
||||
/* Insert reference to missing source */
|
||||
samplecnt_t sourcelen = w->length;
|
||||
XMLNode srcxml (X_("Source"));
|
||||
|
@ -276,8 +276,8 @@ Session::import_pt (PTFFormat& ptf, ImportStatus& status)
|
|||
lx.acquire();
|
||||
save_state("");
|
||||
|
||||
for (vector<PTFFormat::region_t>::iterator a = ptf.regions.begin ();
|
||||
a != ptf.regions.end (); ++a) {
|
||||
for (vector<PTFFormat::region_t>::const_iterator a = ptf.regions ().begin ();
|
||||
a != ptf.regions ().end (); ++a) {
|
||||
for (vector<ptflookup_t>::iterator p = ptfwavpair.begin ();
|
||||
p != ptfwavpair.end (); ++p) {
|
||||
if ((p->index1 == a->wave.index) && (strcmp (a->wave.filename.c_str (), "") != 0)) {
|
||||
|
@ -310,7 +310,7 @@ Session::import_pt (PTFFormat& ptf, ImportStatus& status)
|
|||
}
|
||||
}
|
||||
|
||||
for (vector<PTFFormat::track_t>::iterator a = ptf.tracks.begin (); a != ptf.tracks.end (); ++a) {
|
||||
for (vector<PTFFormat::track_t>::const_iterator a = ptf.tracks ().begin (); a != ptf.tracks ().end (); ++a) {
|
||||
for (vector<ptflookup_t>::iterator p = ptfregpair.begin ();
|
||||
p != ptfregpair.end (); ++p) {
|
||||
|
||||
|
@ -384,7 +384,7 @@ Session::import_pt (PTFFormat& ptf, ImportStatus& status)
|
|||
|
||||
trymidi:
|
||||
status.paths.clear();
|
||||
status.paths.push_back(ptf.path);
|
||||
status.paths.push_back(ptf.path ());
|
||||
status.current = 1;
|
||||
status.total = 1;
|
||||
status.freeze = false;
|
||||
|
@ -396,7 +396,7 @@ trymidi:
|
|||
|
||||
vector<midipair> uniquetr;
|
||||
|
||||
for (vector<PTFFormat::track_t>::iterator a = ptf.miditracks.begin (); a != ptf.miditracks.end (); ++a) {
|
||||
for (vector<PTFFormat::track_t>::const_iterator a = ptf.miditracks ().begin (); a != ptf.miditracks ().end (); ++a) {
|
||||
bool found = false;
|
||||
for (vector<midipair>::iterator b = uniquetr.begin (); b != uniquetr.end (); ++b) {
|
||||
if (b->trname == a->name) {
|
||||
|
@ -429,7 +429,7 @@ trymidi:
|
|||
}
|
||||
|
||||
/* MIDI - Add midi regions one-by-one to corresponding midi tracks */
|
||||
for (vector<PTFFormat::track_t>::iterator a = ptf.miditracks.begin (); a != ptf.miditracks.end (); ++a) {
|
||||
for (vector<PTFFormat::track_t>::const_iterator a = ptf.miditracks ().begin (); a != ptf.miditracks ().end (); ++a) {
|
||||
|
||||
boost::shared_ptr<MidiTrack> midi_track = midi_tracks[a->index];
|
||||
assert (midi_track);
|
||||
|
@ -453,7 +453,7 @@ trymidi:
|
|||
MidiModel::NoteDiffCommand *midicmd;
|
||||
midicmd = mm->new_note_diff_command ("Import ProTools MIDI");
|
||||
|
||||
for (vector<PTFFormat::midi_ev_t>::iterator j = a->reg.midi.begin (); j != a->reg.midi.end (); ++j) {
|
||||
for (vector<PTFFormat::midi_ev_t>::const_iterator j = a->reg.midi.begin (); j != a->reg.midi.end (); ++j) {
|
||||
//printf(" : MIDI : pos=%f len=%f\n", (float)j->pos / 960000., (float)j->length / 960000.);
|
||||
Temporal::Beats start = (Temporal::Beats)(j->pos / 960000.);
|
||||
Temporal::Beats len = (Temporal::Beats)(j->length / 960000.);
|
||||
|
|
|
@ -200,6 +200,19 @@ IO::connect (boost::shared_ptr<Port> our_port, string other_port, void* src)
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
IO::can_add_port (DataType type) const
|
||||
{
|
||||
switch (type) {
|
||||
case DataType::NIL:
|
||||
return false;
|
||||
case DataType::AUDIO:
|
||||
return true;
|
||||
case DataType::MIDI:
|
||||
return _ports.count ().n_midi() < 1;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
IO::remove_port (boost::shared_ptr<Port> port, void* src)
|
||||
{
|
||||
|
@ -269,6 +282,10 @@ IO::add_port (string destination, void* src, DataType type)
|
|||
type = _default_type;
|
||||
}
|
||||
|
||||
if (!can_add_port (type)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ChanCount before = _ports.count ();
|
||||
ChanCount after = before;
|
||||
after.set (type, after.get (type) + 1);
|
||||
|
|
|
@ -1066,6 +1066,7 @@ LuaBindings::common (lua_State* L)
|
|||
.addFunction ("set_name", &Route::set_name)
|
||||
.addFunction ("comment", &Route::comment)
|
||||
.addFunction ("active", &Route::active)
|
||||
.addFunction ("data_type", &Route::data_type)
|
||||
.addFunction ("set_active", &Route::set_active)
|
||||
.addFunction ("nth_plugin", &Route::nth_plugin)
|
||||
.addFunction ("nth_processor", &Route::nth_processor)
|
||||
|
@ -1170,6 +1171,10 @@ LuaBindings::common (lua_State* L)
|
|||
.addFunction ("bounce", &Track::bounce)
|
||||
.addFunction ("bounce_range", &Track::bounce_range)
|
||||
.addFunction ("playlist", &Track::playlist)
|
||||
.addFunction ("use_playlist", &Track::use_playlist)
|
||||
.addFunction ("use_copy_playlist", &Track::use_copy_playlist)
|
||||
.addFunction ("use_new_playlist", &Track::use_new_playlist)
|
||||
.addFunction ("find_and_use_playlist", &Track::find_and_use_playlist)
|
||||
.endClass ()
|
||||
|
||||
.deriveWSPtrClass <AudioTrack, Track> ("AudioTrack")
|
||||
|
@ -2251,6 +2256,8 @@ LuaBindings::common (lua_State* L)
|
|||
.addFunction ("request_stop", &Session::request_stop)
|
||||
.addFunction ("request_play_loop", &Session::request_play_loop)
|
||||
.addFunction ("get_play_loop", &Session::get_play_loop)
|
||||
.addFunction ("get_xrun_count", &Session::get_xrun_count)
|
||||
.addFunction ("reset_xrun_count", &Session::reset_xrun_count)
|
||||
.addFunction ("last_transport_start", &Session::last_transport_start)
|
||||
.addFunction ("goto_start", &Session::goto_start)
|
||||
.addFunction ("goto_end", &Session::goto_end)
|
||||
|
|
|
@ -624,13 +624,7 @@ LuaProc::connect_and_run (BufferSet& bufs,
|
|||
Plugin::connect_and_run (bufs, start, end, speed, in, out, nframes, offset);
|
||||
|
||||
// This is needed for ARDOUR::Session requests :(
|
||||
if (! SessionEvent::has_per_thread_pool ()) {
|
||||
char name[64];
|
||||
snprintf (name, 64, "Proc-%p", this);
|
||||
pthread_set_name (name);
|
||||
SessionEvent::create_per_thread_pool (name, 64);
|
||||
PBD::notify_event_loops_about_thread_creation (pthread_self(), name, 64);
|
||||
}
|
||||
assert (SessionEvent::has_per_thread_pool ());
|
||||
|
||||
uint32_t const n = parameter_count ();
|
||||
for (uint32_t i = 0; i < n; ++i) {
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "ardour/luascripting.h"
|
||||
#include "ardour/lua_script_params.h"
|
||||
#include "ardour/search_paths.h"
|
||||
#include "ardour/utils.h"
|
||||
|
||||
#include "lua/luastate.h"
|
||||
#include "LuaBridge/LuaBridge.h"
|
||||
|
@ -104,11 +105,10 @@ LuaScripting::refresh (bool run_scan)
|
|||
}
|
||||
}
|
||||
|
||||
struct ScriptSorter {
|
||||
bool operator () (LuaScriptInfoPtr a, LuaScriptInfoPtr b) {
|
||||
return a->name < b->name;
|
||||
}
|
||||
};
|
||||
bool
|
||||
LuaScripting::Sorter::operator() (LuaScriptInfoPtr const a, LuaScriptInfoPtr const b) const {
|
||||
return ARDOUR::cmp_nocase_utf8 (a->name, b->name) < 0;
|
||||
}
|
||||
|
||||
LuaScriptInfoPtr
|
||||
LuaScripting::script_info (const std::string &script) {
|
||||
|
@ -166,13 +166,13 @@ LuaScripting::scan ()
|
|||
}
|
||||
}
|
||||
|
||||
std::sort (_sl_dsp->begin(), _sl_dsp->end(), ScriptSorter());
|
||||
std::sort (_sl_session->begin(), _sl_session->end(), ScriptSorter());
|
||||
std::sort (_sl_hook->begin(), _sl_hook->end(), ScriptSorter());
|
||||
std::sort (_sl_action->begin(), _sl_action->end(), ScriptSorter());
|
||||
std::sort (_sl_snippet->begin(), _sl_snippet->end(), ScriptSorter());
|
||||
std::sort (_sl_setup->begin(), _sl_setup->end(), ScriptSorter());
|
||||
std::sort (_sl_tracks->begin(), _sl_tracks->end(), ScriptSorter());
|
||||
std::sort (_sl_dsp->begin(), _sl_dsp->end(), Sorter());
|
||||
std::sort (_sl_session->begin(), _sl_session->end(), Sorter());
|
||||
std::sort (_sl_hook->begin(), _sl_hook->end(), Sorter());
|
||||
std::sort (_sl_action->begin(), _sl_action->end(), Sorter());
|
||||
std::sort (_sl_snippet->begin(), _sl_snippet->end(), Sorter());
|
||||
std::sort (_sl_setup->begin(), _sl_setup->end(), Sorter());
|
||||
std::sort (_sl_tracks->begin(), _sl_tracks->end(), Sorter());
|
||||
|
||||
scripts_changed (); /* EMIT SIGNAL */
|
||||
}
|
||||
|
|
|
@ -218,7 +218,11 @@ Route::init ()
|
|||
|
||||
/* set default meter type */
|
||||
if (is_master()) {
|
||||
#ifdef MIXBUS
|
||||
set_meter_type (MeterK14);
|
||||
#else
|
||||
set_meter_type (Config->get_meter_type_master ());
|
||||
#endif
|
||||
} else if (dynamic_cast<Track*>(this)) {
|
||||
set_meter_type (Config->get_meter_type_track ());
|
||||
} else {
|
||||
|
|
|
@ -91,8 +91,6 @@ find_session_templates (vector<TemplateInfo>& template_names, bool read_xml)
|
|||
return;
|
||||
}
|
||||
|
||||
cerr << "Found " << templates.size() << " along " << template_search_path().to_string() << endl;
|
||||
|
||||
for (vector<string>::iterator i = templates.begin(); i != templates.end(); ++i) {
|
||||
string file = session_template_dir_to_file (*i);
|
||||
|
||||
|
|
|
@ -311,7 +311,7 @@ def configure(conf):
|
|||
autowaf.check_pkg(conf, 'sratom-0', uselib_store='SRATOM',
|
||||
atleast_version='0.2.0', mandatory=True)
|
||||
autowaf.check_pkg(conf, 'lilv-0', uselib_store='LILV',
|
||||
atleast_version='0.24.2', mandatory=False)
|
||||
atleast_version='0.24.2', mandatory=True)
|
||||
autowaf.check_pkg(conf, 'suil-0', uselib_store='SUIL',
|
||||
atleast_version='0.6.0', mandatory=False)
|
||||
conf.define ('LV2_SUPPORT', 1)
|
||||
|
|
|
@ -76,7 +76,7 @@ class DummyReceiver : public Receiver {
|
|||
std::cerr << prefix << str << std::endl;
|
||||
|
||||
if (chn == Transmitter::Fatal) {
|
||||
::exit (1);
|
||||
::exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -594,7 +594,7 @@ UI::process_error_message (Transmitter::Channel chn, const char *str)
|
|||
default:
|
||||
/* no choice but to use text/console output here */
|
||||
cerr << "programmer error in UI::check_error_messages (channel = " << chn << ")\n";
|
||||
::exit (1);
|
||||
::exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
errors->text().get_buffer()->begin_user_action();
|
||||
|
|
|
@ -77,7 +77,7 @@ PBD::open_uri (const char* uri)
|
|||
#else
|
||||
if (::vfork () == 0) {
|
||||
::execlp ("xdg-open", "xdg-open", s.c_str(), (char*)NULL);
|
||||
exit (0);
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* (C) 2017, 2019 Robin Gareus <robin@gareus.org>
|
||||
*
|
||||
* 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 _pbd_mpc_queue_h_
|
||||
#define _pbd_mpc_queue_h_
|
||||
|
||||
#include <cassert>
|
||||
#include <glib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace PBD {
|
||||
|
||||
/** Lock free multiple producer, multiple consumer queue
|
||||
*
|
||||
* inspired by http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
|
||||
* Kudos to Dmitry Vyukov
|
||||
*/
|
||||
template <typename T>
|
||||
class /*LIBPBD_API*/ MPMCQueue
|
||||
{
|
||||
public:
|
||||
MPMCQueue (size_t buffer_size = 8)
|
||||
: _buffer (0)
|
||||
, _buffer_mask (0)
|
||||
{
|
||||
reserve (buffer_size);
|
||||
}
|
||||
|
||||
~MPMCQueue ()
|
||||
{
|
||||
delete[] _buffer;
|
||||
}
|
||||
|
||||
static size_t
|
||||
power_of_two_size (size_t sz)
|
||||
{
|
||||
int32_t power_of_two;
|
||||
for (power_of_two = 1; 1U << power_of_two < sz; ++power_of_two) ;
|
||||
return 1U << power_of_two;
|
||||
}
|
||||
|
||||
void
|
||||
reserve (size_t buffer_size)
|
||||
{
|
||||
buffer_size = power_of_two_size (buffer_size);
|
||||
assert ((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0));
|
||||
if (_buffer_mask >= buffer_size - 1) {
|
||||
return;
|
||||
}
|
||||
delete[] _buffer;
|
||||
_buffer = new cell_t[buffer_size];
|
||||
_buffer_mask = buffer_size - 1;
|
||||
clear ();
|
||||
}
|
||||
|
||||
void
|
||||
clear ()
|
||||
{
|
||||
for (size_t i = 0; i <= _buffer_mask; ++i) {
|
||||
g_atomic_int_set (&_buffer[i]._sequence, i);
|
||||
}
|
||||
g_atomic_int_set (&_enqueue_pos, 0);
|
||||
g_atomic_int_set (&_dequeue_pos, 0);
|
||||
}
|
||||
|
||||
bool
|
||||
push_back (T const& data)
|
||||
{
|
||||
cell_t* cell;
|
||||
guint pos = g_atomic_int_get (&_enqueue_pos);
|
||||
for (;;) {
|
||||
cell = &_buffer[pos & _buffer_mask];
|
||||
guint seq = g_atomic_int_get (&cell->_sequence);
|
||||
intptr_t dif = (intptr_t)seq - (intptr_t)pos;
|
||||
if (dif == 0) {
|
||||
if (g_atomic_int_compare_and_exchange (&_enqueue_pos, pos, pos + 1)) {
|
||||
break;
|
||||
}
|
||||
} else if (dif < 0) {
|
||||
assert (0);
|
||||
return false;
|
||||
} else {
|
||||
pos = g_atomic_int_get (&_enqueue_pos);
|
||||
}
|
||||
}
|
||||
|
||||
cell->_data = data;
|
||||
g_atomic_int_set (&cell->_sequence, pos + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
pop_front (T& data)
|
||||
{
|
||||
cell_t* cell;
|
||||
guint pos = g_atomic_int_get (&_dequeue_pos);
|
||||
for (;;) {
|
||||
cell = &_buffer[pos & _buffer_mask];
|
||||
guint seq = g_atomic_int_get (&cell->_sequence);
|
||||
intptr_t dif = (intptr_t)seq - (intptr_t) (pos + 1);
|
||||
if (dif == 0) {
|
||||
if (g_atomic_int_compare_and_exchange (&_dequeue_pos, pos, pos + 1)) {
|
||||
break;
|
||||
}
|
||||
} else if (dif < 0) {
|
||||
return false;
|
||||
} else {
|
||||
pos = g_atomic_int_get (&_dequeue_pos);
|
||||
}
|
||||
}
|
||||
|
||||
data = cell->_data;
|
||||
g_atomic_int_set (&cell->_sequence, pos + _buffer_mask + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
struct cell_t {
|
||||
volatile guint _sequence;
|
||||
T _data;
|
||||
};
|
||||
|
||||
cell_t* _buffer;
|
||||
size_t _buffer_mask;
|
||||
|
||||
volatile guint _enqueue_pos;
|
||||
volatile guint _dequeue_pos;
|
||||
};
|
||||
|
||||
} /* end namespace */
|
||||
|
||||
#endif
|
|
@ -962,7 +962,7 @@ SystemExec::start (StdErrMode stderr_mode, const char *vfork_exec_wrapper)
|
|||
char buf = 0;
|
||||
(void) ::write (pok[1], &buf, 1);
|
||||
close_fd (pok[1]);
|
||||
exit (-1);
|
||||
exit (EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ Transmitter::deliver ()
|
|||
sigemptyset (&mask);
|
||||
sigsuspend (&mask);
|
||||
/*NOTREACHED*/
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
/* JE - From what I can tell, the above code suspends
|
||||
* program execution until (any) signal occurs. Not
|
||||
* sure at the moment what this achieves, unless it
|
||||
|
|
|
@ -262,7 +262,7 @@
|
|||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\ptfformat.cc"
|
||||
RelativePath="..\ptformat.cc"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
|
@ -272,7 +272,7 @@
|
|||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\ptformat\ptfformat.h"
|
||||
RelativePath="..\ptformat\ptformat.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,195 +0,0 @@
|
|||
/*
|
||||
* libptformat - a library to read ProTools sessions
|
||||
*
|
||||
* Copyright (C) 2015 Damien Zammit
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#ifndef PTFFORMAT_H
|
||||
#define PTFFORMAT_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
#include "ptformat/visibility.h"
|
||||
|
||||
class LIBPTFORMAT_API PTFFormat {
|
||||
public:
|
||||
PTFFormat();
|
||||
~PTFFormat();
|
||||
|
||||
/* Return values: 0 success
|
||||
-1 could not parse pt session
|
||||
*/
|
||||
int load(std::string path, int64_t targetsr);
|
||||
|
||||
/* Return values: 0 success
|
||||
-1 could not decrypt pt session
|
||||
*/
|
||||
int unxor(std::string path);
|
||||
|
||||
struct wav_t {
|
||||
std::string filename;
|
||||
uint16_t index;
|
||||
|
||||
int64_t posabsolute;
|
||||
int64_t length;
|
||||
|
||||
bool operator <(const struct wav_t& other) const {
|
||||
return (strcasecmp(this->filename.c_str(),
|
||||
other.filename.c_str()) < 0);
|
||||
}
|
||||
|
||||
bool operator ==(const struct wav_t& other) const {
|
||||
return (this->filename == other.filename ||
|
||||
this->index == other.index);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct midi_ev_t {
|
||||
uint64_t pos;
|
||||
uint64_t length;
|
||||
uint8_t note;
|
||||
uint8_t velocity;
|
||||
};
|
||||
|
||||
typedef struct region {
|
||||
std::string name;
|
||||
uint16_t index;
|
||||
int64_t startpos;
|
||||
int64_t sampleoffset;
|
||||
int64_t length;
|
||||
wav_t wave;
|
||||
std::vector<midi_ev_t> midi;
|
||||
|
||||
bool operator ==(const struct region& other) {
|
||||
return (this->index == other.index);
|
||||
}
|
||||
|
||||
bool operator <(const struct region& other) const {
|
||||
return (strcasecmp(this->name.c_str(),
|
||||
other.name.c_str()) < 0);
|
||||
}
|
||||
} region_t;
|
||||
|
||||
typedef struct compound {
|
||||
uint16_t curr_index;
|
||||
uint16_t unknown1;
|
||||
uint16_t level;
|
||||
uint16_t ontopof_index;
|
||||
uint16_t next_index;
|
||||
std::string name;
|
||||
} compound_t;
|
||||
|
||||
typedef struct track {
|
||||
std::string name;
|
||||
uint16_t index;
|
||||
uint8_t playlist;
|
||||
region_t reg;
|
||||
|
||||
bool operator ==(const struct track& other) {
|
||||
return (this->name == other.name);
|
||||
}
|
||||
} track_t;
|
||||
|
||||
std::vector<wav_t> audiofiles;
|
||||
std::vector<region_t> regions;
|
||||
std::vector<region_t> midiregions;
|
||||
std::vector<compound_t> compounds;
|
||||
std::vector<track_t> tracks;
|
||||
std::vector<track_t> miditracks;
|
||||
|
||||
static bool regionexistsin(std::vector<region_t> reg, uint16_t index) {
|
||||
std::vector<region_t>::iterator begin = reg.begin();
|
||||
std::vector<region_t>::iterator finish = reg.end();
|
||||
std::vector<region_t>::iterator found;
|
||||
|
||||
wav_t w = { std::string(""), 0, 0, 0 };
|
||||
std::vector<midi_ev_t> m;
|
||||
region_t r = { std::string(""), index, 0, 0, 0, w, m};
|
||||
|
||||
if ((found = std::find(begin, finish, r)) != finish) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool wavexistsin(std::vector<wav_t> wv, uint16_t index) {
|
||||
std::vector<wav_t>::iterator begin = wv.begin();
|
||||
std::vector<wav_t>::iterator finish = wv.end();
|
||||
std::vector<wav_t>::iterator found;
|
||||
|
||||
wav_t w = { std::string(""), index, 0, 0 };
|
||||
|
||||
if ((found = std::find(begin, finish, w)) != finish) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t sessionrate;
|
||||
int64_t targetrate;
|
||||
uint8_t version;
|
||||
uint8_t *product;
|
||||
std::string path;
|
||||
|
||||
unsigned char c0;
|
||||
unsigned char c1;
|
||||
unsigned char *ptfunxored;
|
||||
uint64_t len;
|
||||
bool is_bigendian;
|
||||
|
||||
private:
|
||||
bool jumpback(uint32_t *currpos, unsigned char *buf, const uint32_t maxoffset, const unsigned char *needle, const uint32_t needlelen);
|
||||
bool jumpto(uint32_t *currpos, unsigned char *buf, const uint32_t maxoffset, const unsigned char *needle, const uint32_t needlelen);
|
||||
bool foundin(std::string haystack, std::string needle);
|
||||
int64_t foundat(unsigned char *haystack, uint64_t n, const char *needle);
|
||||
uint16_t u_endian_read2(unsigned char *buf, bool);
|
||||
uint32_t u_endian_read3(unsigned char *buf, bool);
|
||||
uint32_t u_endian_read4(unsigned char *buf, bool);
|
||||
uint64_t u_endian_read5(unsigned char *buf, bool);
|
||||
|
||||
int parse(void);
|
||||
bool parse_version();
|
||||
uint8_t gen_xor_delta(uint8_t xor_value, uint8_t mul, bool negative);
|
||||
void setrates(void);
|
||||
void cleanup(void);
|
||||
void parse5header(void);
|
||||
void parse7header(void);
|
||||
void parse8header(void);
|
||||
void parse9header(void);
|
||||
void parse10header(void);
|
||||
void parserest5(void);
|
||||
void parserest89(void);
|
||||
void parserest12(void);
|
||||
void parseaudio5(void);
|
||||
void parseaudio(void);
|
||||
void parsemidi(void);
|
||||
void parsemidi12(void);
|
||||
void resort(std::vector<wav_t>& ws);
|
||||
void resort(std::vector<region_t>& rs);
|
||||
void filter(std::vector<region_t>& rs);
|
||||
std::vector<wav_t> actualwavs;
|
||||
float ratefactor;
|
||||
std::string extension;
|
||||
uint32_t upto;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* libptformat - a library to read ProTools sessions
|
||||
*
|
||||
* Copyright (C) 2015-2019 Damien Zammit
|
||||
* Copyright (C) 2015-2019 Robin Gareus
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#ifndef PTFFORMAT_H
|
||||
#define PTFFORMAT_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
#include "ptformat/visibility.h"
|
||||
|
||||
class LIBPTFORMAT_API PTFFormat {
|
||||
public:
|
||||
PTFFormat();
|
||||
~PTFFormat();
|
||||
|
||||
/* Return values: 0 success
|
||||
-1 error decrypting pt session
|
||||
-2 error detecting pt session
|
||||
-3 incompatible pt version
|
||||
-4 error parsing pt session
|
||||
*/
|
||||
int load(std::string const& path, int64_t targetsr);
|
||||
|
||||
/* Return values: 0 success
|
||||
-1 error decrypting pt session
|
||||
*/
|
||||
int unxor(std::string const& path);
|
||||
|
||||
struct wav_t {
|
||||
std::string filename;
|
||||
uint16_t index;
|
||||
|
||||
int64_t posabsolute;
|
||||
int64_t length;
|
||||
|
||||
bool operator <(const struct wav_t& other) const {
|
||||
return (strcasecmp(this->filename.c_str(),
|
||||
other.filename.c_str()) < 0);
|
||||
}
|
||||
|
||||
bool operator ==(const struct wav_t& other) const {
|
||||
return (this->filename == other.filename ||
|
||||
this->index == other.index);
|
||||
}
|
||||
|
||||
wav_t (uint16_t idx = 0) : index (idx), posabsolute (0), length (0) {}
|
||||
};
|
||||
|
||||
struct midi_ev_t {
|
||||
uint64_t pos;
|
||||
uint64_t length;
|
||||
uint8_t note;
|
||||
uint8_t velocity;
|
||||
midi_ev_t () : pos (0), length (0), note (0), velocity (0) {}
|
||||
};
|
||||
|
||||
struct region_t {
|
||||
std::string name;
|
||||
uint16_t index;
|
||||
int64_t startpos;
|
||||
int64_t sampleoffset;
|
||||
int64_t length;
|
||||
wav_t wave;
|
||||
std::vector<midi_ev_t> midi;
|
||||
|
||||
bool operator ==(const region_t& other) const {
|
||||
return (this->index == other.index);
|
||||
}
|
||||
|
||||
bool operator <(const region_t& other) const {
|
||||
return (strcasecmp(this->name.c_str(),
|
||||
other.name.c_str()) < 0);
|
||||
}
|
||||
region_t (uint16_t idx = 0) : index (idx), startpos (0), sampleoffset (0), length (0) {}
|
||||
};
|
||||
|
||||
struct track_t {
|
||||
std::string name;
|
||||
uint16_t index;
|
||||
uint8_t playlist;
|
||||
region_t reg;
|
||||
|
||||
bool operator <(const track_t& other) const {
|
||||
return (this->index < other.index);
|
||||
}
|
||||
|
||||
bool operator ==(const track_t& other) const {
|
||||
return (this->index == other.index);
|
||||
}
|
||||
track_t (uint16_t idx = 0) : index (idx), playlist (0) {}
|
||||
};
|
||||
|
||||
bool find_track(uint16_t index, track_t& tt) const {
|
||||
std::vector<track_t>::const_iterator begin = _tracks.begin();
|
||||
std::vector<track_t>::const_iterator finish = _tracks.end();
|
||||
std::vector<track_t>::const_iterator found;
|
||||
|
||||
track_t t (index);
|
||||
|
||||
if ((found = std::find(begin, finish, t)) != finish) {
|
||||
tt = *found;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool find_region(uint16_t index, region_t& rr) const {
|
||||
std::vector<region_t>::const_iterator begin = _regions.begin();
|
||||
std::vector<region_t>::const_iterator finish = _regions.end();
|
||||
std::vector<region_t>::const_iterator found;
|
||||
|
||||
region_t r;
|
||||
r.index = index;
|
||||
|
||||
if ((found = std::find(begin, finish, r)) != finish) {
|
||||
rr = *found;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool find_miditrack(uint16_t index, track_t& tt) const {
|
||||
std::vector<track_t>::const_iterator begin = _miditracks.begin();
|
||||
std::vector<track_t>::const_iterator finish = _miditracks.end();
|
||||
std::vector<track_t>::const_iterator found;
|
||||
|
||||
track_t t (index);
|
||||
|
||||
if ((found = std::find(begin, finish, t)) != finish) {
|
||||
tt = *found;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool find_midiregion(uint16_t index, region_t& rr) const {
|
||||
std::vector<region_t>::const_iterator begin = _midiregions.begin();
|
||||
std::vector<region_t>::const_iterator finish = _midiregions.end();
|
||||
std::vector<region_t>::const_iterator found;
|
||||
|
||||
region_t r (index);
|
||||
|
||||
if ((found = std::find(begin, finish, r)) != finish) {
|
||||
rr = *found;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool find_wav(uint16_t index, wav_t& ww) const {
|
||||
std::vector<wav_t>::const_iterator begin = _audiofiles.begin();
|
||||
std::vector<wav_t>::const_iterator finish = _audiofiles.end();
|
||||
std::vector<wav_t>::const_iterator found;
|
||||
|
||||
wav_t w (index);
|
||||
|
||||
if ((found = std::find(begin, finish, w)) != finish) {
|
||||
ww = *found;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool regionexistsin(std::vector<region_t> const& reg, uint16_t index) {
|
||||
std::vector<region_t>::const_iterator begin = reg.begin();
|
||||
std::vector<region_t>::const_iterator finish = reg.end();
|
||||
|
||||
region_t r (index);
|
||||
|
||||
if (std::find(begin, finish, r) != finish) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool wavexistsin (std::vector<wav_t> const& wv, uint16_t index) {
|
||||
std::vector<wav_t>::const_iterator begin = wv.begin();
|
||||
std::vector<wav_t>::const_iterator finish = wv.end();
|
||||
|
||||
wav_t w (index);
|
||||
|
||||
if (std::find(begin, finish, w) != finish) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t version () const { return _version; }
|
||||
int64_t sessionrate () const { return _sessionrate ; }
|
||||
const std::string& path () { return _path; }
|
||||
|
||||
const std::vector<wav_t>& audiofiles () const { return _audiofiles ; }
|
||||
const std::vector<region_t>& regions () const { return _regions ; }
|
||||
const std::vector<region_t>& midiregions () const { return _midiregions ; }
|
||||
const std::vector<track_t>& tracks () const { return _tracks ; }
|
||||
const std::vector<track_t>& miditracks () const { return _miditracks ; }
|
||||
|
||||
const unsigned char* unxored_data () const { return _ptfunxored; }
|
||||
uint64_t unxored_size () const { return _len; }
|
||||
|
||||
private:
|
||||
|
||||
std::vector<wav_t> _audiofiles;
|
||||
std::vector<region_t> _regions;
|
||||
std::vector<region_t> _midiregions;
|
||||
std::vector<track_t> _tracks;
|
||||
std::vector<track_t> _miditracks;
|
||||
|
||||
std::string _path;
|
||||
|
||||
unsigned char* _ptfunxored;
|
||||
uint64_t _len;
|
||||
int64_t _sessionrate;
|
||||
uint8_t _version;
|
||||
uint8_t* _product;
|
||||
int64_t _targetrate;
|
||||
float _ratefactor;
|
||||
bool is_bigendian;
|
||||
|
||||
struct block_t {
|
||||
uint8_t zmark; // 'Z'
|
||||
uint16_t block_type; // type of block
|
||||
uint32_t block_size; // size of block
|
||||
uint16_t content_type; // type of content
|
||||
uint32_t offset; // offset in file
|
||||
std::vector<block_t> child; // vector of child blocks
|
||||
};
|
||||
std::vector<block_t> blocks;
|
||||
|
||||
bool jumpback(uint32_t *currpos, unsigned char *buf, const uint32_t maxoffset, const unsigned char *needle, const uint32_t needlelen);
|
||||
bool jumpto(uint32_t *currpos, unsigned char *buf, const uint32_t maxoffset, const unsigned char *needle, const uint32_t needlelen);
|
||||
bool foundin(std::string const& haystack, std::string const& needle);
|
||||
int64_t foundat(unsigned char *haystack, uint64_t n, const char *needle);
|
||||
|
||||
std::string parsestring(uint32_t pos);
|
||||
const std::string get_content_description(uint16_t ctype);
|
||||
int parse(void);
|
||||
void parseblocks(void);
|
||||
bool parseheader(void);
|
||||
bool parserest(void);
|
||||
bool parseaudio(void);
|
||||
bool parsemidi(void);
|
||||
void dump(void);
|
||||
bool parse_block_at(uint32_t pos, struct block_t *b, struct block_t *parent, int level);
|
||||
void dump_block(struct block_t& b, int level);
|
||||
bool parse_version();
|
||||
void parse_region_info(uint32_t j, block_t& blk, region_t& r);
|
||||
void parse_three_point(uint32_t j, uint64_t& start, uint64_t& offset, uint64_t& length);
|
||||
uint8_t gen_xor_delta(uint8_t xor_value, uint8_t mul, bool negative);
|
||||
void setrates(void);
|
||||
void cleanup(void);
|
||||
void free_block(struct block_t& b);
|
||||
void free_all_blocks(void);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -30,10 +30,10 @@ def configure(conf):
|
|||
def build(bld):
|
||||
# Library
|
||||
if bld.is_defined ('INTERNAL_SHARED_LIBS'):
|
||||
obj = bld.shlib (features = 'cxx cxxshlib', source = [ 'ptfformat.cc' ])
|
||||
obj = bld.shlib (features = 'cxx cxxshlib', source = [ 'ptformat.cc' ])
|
||||
obj.defines = [ 'LIBPTFORMAT_DLL_EXPORTS=1' ]
|
||||
else:
|
||||
obj = bld.stdlib (source = [ 'ptfformat.cc' ])
|
||||
obj = bld.stdlib (source = [ 'ptformat.cc' ])
|
||||
obj.cxxflags = [ bld.env['compiler_flags_dict']['pic'] ]
|
||||
obj.cflags = [ bld.env['compiler_flags_dict']['pic'] ]
|
||||
|
||||
|
|
|
@ -21,7 +21,11 @@ function factory () return function ()
|
|||
for r in Session:get_tracks():iter() do
|
||||
print ("*", r:name())
|
||||
for p in Session:playlists():playlists_for_track (r:to_track()):iter() do
|
||||
print (" -", p:name(), p:n_regions())
|
||||
if (p == r:to_track():playlist()) then
|
||||
print (" >-", p:name(), p:n_regions())
|
||||
else
|
||||
print (" -", p:name(), p:n_regions())
|
||||
end
|
||||
end
|
||||
end
|
||||
end end
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
ardour {
|
||||
["type"] = "EditorAction",
|
||||
name = "Track Sort",
|
||||
author = "Ardour Lua Taskforce",
|
||||
description = [[Sort tracks alphabetically by name]]
|
||||
}
|
||||
|
||||
function factory () return function ()
|
||||
|
||||
-- sort compare function
|
||||
-- a,b here are http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route
|
||||
-- return true if route "a" should be ordered before route "b"
|
||||
function tsort (a, b)
|
||||
return a:name() < b:name()
|
||||
end
|
||||
|
||||
-- create a sortable list of tracks
|
||||
local tracklist = {}
|
||||
for t in Session:get_tracks():iter() do
|
||||
table.insert(tracklist, t)
|
||||
end
|
||||
|
||||
-- sort the list using the compare function
|
||||
table.sort(tracklist, tsort)
|
||||
|
||||
-- traverse the sorted list and assign "presentation-order" to each track
|
||||
local pos = 1;
|
||||
for _, t in ipairs(tracklist) do
|
||||
t:set_presentation_order(pos)
|
||||
pos = pos + 1
|
||||
end
|
||||
|
||||
-- drop all track references
|
||||
tracklist = nil
|
||||
collectgarbage ()
|
||||
end end
|
|
@ -290,7 +290,7 @@ int main (int argc, char* argv[])
|
|||
break;
|
||||
|
||||
case 'h':
|
||||
usage (0);
|
||||
usage (EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
|
@ -304,7 +304,7 @@ int main (int argc, char* argv[])
|
|||
case 'V':
|
||||
printf ("ardour-utils version %s\n\n", VERSIONSTRING);
|
||||
printf ("Copyright (C) GPL 2016 Robin Gareus <robin@gareus.org>\n");
|
||||
exit (0);
|
||||
exit (EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
|
@ -330,19 +330,19 @@ int main (int argc, char* argv[])
|
|||
|
||||
if (!ends_with (src, statefile_suffix)) {
|
||||
fprintf (stderr, "source is not a .ardour session file.\n");
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
if (!ends_with (dst, statefile_suffix)) {
|
||||
fprintf (stderr, "target is not a .ardour session file.\n");
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
if (!Glib::file_test (src, Glib::FILE_TEST_IS_REGULAR)) {
|
||||
fprintf (stderr, "source is not a regular file.\n");
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
if (!Glib::file_test (dst, Glib::FILE_TEST_IS_REGULAR)) {
|
||||
fprintf (stderr, "target is not a regular file.\n");
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::string src_path = Glib::path_get_dirname (src);
|
||||
|
|
|
@ -287,11 +287,11 @@ int main (int argc, char* argv[])
|
|||
case 'V':
|
||||
printf ("ardour-utils version %s\n\n", VERSIONSTRING);
|
||||
printf ("Copyright (C) GPL 2015,2017 Robin Gareus <robin@gareus.org>\n");
|
||||
exit (0);
|
||||
exit (EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
usage (0);
|
||||
usage (EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -416,18 +416,18 @@ int main (int argc, char* argv[])
|
|||
case 'o':
|
||||
outfile = optarg;
|
||||
if (outfile.empty()) {
|
||||
usage (0);
|
||||
usage (EXIT_SUCCESS);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
printf ("ardour-utils version %s\n\n", VERSIONSTRING);
|
||||
printf ("Copyright (C) GPL 2015 Robin Gareus <robin@gareus.org>\n");
|
||||
exit (0);
|
||||
exit (EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
usage (0);
|
||||
usage (EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -70,11 +70,11 @@ int main (int argc, char* argv[])
|
|||
case 'V':
|
||||
printf ("ardour-utils version %s\n\n", VERSIONSTRING);
|
||||
printf ("Copyright (C) GPL 2017 Robin Gareus <robin@gareus.org>\n");
|
||||
exit (0);
|
||||
exit (EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
usage (0);
|
||||
usage (EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
#include <cstdlib>
|
||||
#include <getopt.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <glibmm.h>
|
||||
|
||||
#include "ardour/audioengine.h"
|
||||
#include "ardour/filename_extensions.h"
|
||||
#include "ardour/template_utils.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ARDOUR;
|
||||
using namespace SessionUtils;
|
||||
|
||||
static void
|
||||
usage (int status)
|
||||
{
|
||||
// help2man compatible format (standard GNU help-text)
|
||||
printf (UTILNAME " - create a new session from the commandline.\n\n");
|
||||
printf ("Usage: " UTILNAME " [ OPTIONS ] <session-dir> [session-name]\n\n");
|
||||
printf ("Options:\n\
|
||||
-L, --list-templates List available templates and exit\n\
|
||||
-h, --help Display this help and exit\n\
|
||||
-m, --master-channels <chn> Master-bus channel count (default 2)\n\
|
||||
-s, --samplerate <rate> Samplerate to use (default 48000)\n\
|
||||
-t, --template <template> Use given template for new session\n\
|
||||
-V, --version Print version information and exit\n\
|
||||
\n");
|
||||
|
||||
printf ("\n\
|
||||
This tool creates a new Ardour session, optionally based on a\n\
|
||||
session-template.\n\
|
||||
\n\
|
||||
If the session-name is unspecified, the sesion-dir-name is used.\n\
|
||||
If specified, the tool expects a session-name without .ardour\n\
|
||||
file-name extension.\n\
|
||||
\n\
|
||||
If no template is specified, an empty session with a stereo master\n\
|
||||
bus is created. The -m option allows to specify the master-bus channel\n\
|
||||
count. If zero is used as channel count, no master-bus is created.\n\
|
||||
\n\
|
||||
Note: this tool can only use static session templates.\n\
|
||||
Interactive Lua init-scripts or dynamic templates are not supported.\n\
|
||||
\n");
|
||||
|
||||
printf ("\n\
|
||||
Examples:\n\
|
||||
" UTILNAME " -s 44100 -m 4 /tmp/NewSession\n\
|
||||
\n");
|
||||
|
||||
printf ("Report bugs to <http://tracker.ardour.org/>\n"
|
||||
"Website: <http://ardour.org/>\n");
|
||||
::exit (status);
|
||||
}
|
||||
|
||||
static void
|
||||
list_templates ()
|
||||
{
|
||||
vector<TemplateInfo> templates;
|
||||
find_session_templates (templates, false);
|
||||
|
||||
cout << "---- List of Session Templates ----\n";
|
||||
for (vector<TemplateInfo>::iterator x = templates.begin (); x != templates.end (); ++x) {
|
||||
cout << "[TPL] " << (*x).name << "\n";
|
||||
}
|
||||
cout << "----\n";
|
||||
}
|
||||
|
||||
static std::string
|
||||
template_path_from_name (std::string const& name)
|
||||
{
|
||||
vector<TemplateInfo> templates;
|
||||
find_session_templates (templates, false);
|
||||
|
||||
for (vector<TemplateInfo>::iterator x = templates.begin (); x != templates.end (); ++x) {
|
||||
if ((*x).name == name) {
|
||||
return (*x).path;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static Session*
|
||||
create_new_session (string const& dir, string const& state, float sample_rate, int master_bus_chn, string const& template_path)
|
||||
{
|
||||
AudioEngine* engine = AudioEngine::create ();
|
||||
|
||||
if (!engine->set_backend ("None (Dummy)", "Unit-Test", "")) {
|
||||
cerr << "Cannot create Audio/MIDI engine\n";
|
||||
::exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
engine->set_input_channels (256);
|
||||
engine->set_output_channels (256);
|
||||
|
||||
if (engine->set_sample_rate (sample_rate)) {
|
||||
cerr << "Cannot set session's samplerate.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (engine->start () != 0) {
|
||||
cerr << "Cannot start Audio/MIDI engine\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
string s = Glib::build_filename (dir, state + statefile_suffix);
|
||||
|
||||
if (Glib::file_test (dir, Glib::FILE_TEST_EXISTS)) {
|
||||
cerr << "Session folder already exists '" << dir << "'\n";
|
||||
}
|
||||
if (Glib::file_test (s, Glib::FILE_TEST_EXISTS)) {
|
||||
cerr << "Session file exists '" << s << "'\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
BusProfile bus_profile;
|
||||
BusProfile* bus_profile_ptr = NULL;
|
||||
|
||||
if (master_bus_chn > 0) {
|
||||
bus_profile_ptr = &bus_profile;
|
||||
bus_profile.master_out_channels = master_bus_chn;
|
||||
}
|
||||
|
||||
if (!template_path.empty ()) {
|
||||
bus_profile_ptr = NULL;
|
||||
}
|
||||
|
||||
Session* session = new Session (*engine, dir, state, bus_profile_ptr, template_path);
|
||||
engine->set_session (session);
|
||||
return session;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char* argv[])
|
||||
{
|
||||
int sample_rate = 48000;
|
||||
int master_bus_chn = 2;
|
||||
string template_path;
|
||||
|
||||
const char* optstring = "Lm:hs:t:V";
|
||||
|
||||
/* clang-format off */
|
||||
const struct option longopts[] = {
|
||||
{ "list-templates", no_argument, 0, 'L' },
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ "master-channels", no_argument, 0, 'm' },
|
||||
{ "samplerate", required_argument, 0, 's' },
|
||||
{ "template", required_argument, 0, 't' },
|
||||
{ "version", no_argument, 0, 'V' },
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
int c = 0;
|
||||
while (EOF != (c = getopt_long (argc, argv,
|
||||
optstring, longopts, (int*)0))) {
|
||||
switch (c) {
|
||||
case 'L':
|
||||
list_templates ();
|
||||
exit (EXIT_SUCCESS);
|
||||
break;
|
||||
case 'm': {
|
||||
const int mc = atoi (optarg);
|
||||
if (mc >= 0 && mc < 128) {
|
||||
master_bus_chn = mc;
|
||||
} else {
|
||||
cerr << "Invalid master bus channel count\n";
|
||||
}
|
||||
} break;
|
||||
case 's': {
|
||||
const int sr = atoi (optarg);
|
||||
if (sr >= 8000 && sr <= 192000) {
|
||||
sample_rate = sr;
|
||||
} else {
|
||||
cerr << "Invalid Samplerate\n";
|
||||
}
|
||||
} break;
|
||||
|
||||
case 't':
|
||||
template_path = template_path_from_name (optarg);
|
||||
if (template_path.empty ()) {
|
||||
cerr << "Invalid (non-existent) template:" << optarg << "\n";
|
||||
::exit (EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
printf ("ardour-utils version %s\n\n", VERSIONSTRING);
|
||||
printf ("Copyright (C) GPL 2019 Robin Gareus <robin@gareus.org>\n");
|
||||
exit (EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
usage (EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
default:
|
||||
usage (EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string snapshot_name;
|
||||
|
||||
if (optind + 2 == argc) {
|
||||
snapshot_name = argv[optind + 1];
|
||||
} else if (optind + 1 == argc) {
|
||||
snapshot_name = Glib::path_get_basename (argv[optind]);
|
||||
} else {
|
||||
usage (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (snapshot_name.empty ()) {
|
||||
cerr << "Error: Invalid empty session/snapshot name.\n";
|
||||
::exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* all systems go */
|
||||
|
||||
SessionUtils::init ();
|
||||
Session* s = 0;
|
||||
|
||||
try {
|
||||
s = create_new_session (argv[optind], snapshot_name, sample_rate, master_bus_chn, template_path);
|
||||
} catch (ARDOUR::SessionException& e) {
|
||||
cerr << "Error: " << e.what () << "\n";
|
||||
} catch (...) {
|
||||
cerr << "Error: unknown exception.\n";
|
||||
}
|
||||
|
||||
/* save is implicit when creating a new session */
|
||||
|
||||
if (s) {
|
||||
cout << "Created session in '" << s->path () << "'" << endl;
|
||||
}
|
||||
|
||||
SessionUtils::unload_session (s);
|
||||
SessionUtils::cleanup ();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -52,7 +52,7 @@ foreach (json_decode ($json, true) as $b) {
|
|||
|
||||
if (count ($doc) == 0) {
|
||||
fwrite (STDERR, "Failed to read luadoc.json\n");
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
################################################################################
|
||||
|
|
Loading…
Reference in New Issue