#include <cerrno>
#include <fstream>
+#include <stdarg.h>
+
#ifndef PLATFORM_WINDOWS
#include <sys/resource.h>
#endif
#include "pbd/file_utils.h"
#include "pbd/localtime_r.h"
#include "pbd/pthread_utils.h"
+#include "pbd/replace_all.h"
+#include "pbd/xml++.h"
#include "gtkmm2ext/application.h"
#include "gtkmm2ext/bindings.h"
#include "ardour/session_route.h"
#include "ardour/session_state_utils.h"
#include "ardour/session_utils.h"
+#include "ardour/source_factory.h"
#include "ardour/slave.h"
#include "ardour/system_exec.h"
typedef uint64_t microseconds_t;
#include "about.h"
+#include "editing.h"
#include "actions.h"
#include "add_route_dialog.h"
#include "ambiguous_file_dialog.h"
#include "ardour_ui.h"
#include "audio_clock.h"
+#include "audio_region_view.h"
#include "big_clock_window.h"
#include "bundle_manager.h"
#include "engine_dialog.h"
#include "rc_option_editor.h"
#include "route_time_axis.h"
#include "route_params_ui.h"
+#include "save_as_dialog.h"
#include "session_dialog.h"
#include "session_metadata_dialog.h"
#include "session_option_editor.h"
using namespace Gtkmm2ext;
using namespace Gtk;
using namespace std;
+using namespace Editing;
ARDOUR_UI *ARDOUR_UI::theArdourUI = 0;
sigc::signal<void, framepos_t, bool, framepos_t> ARDOUR_UI::Clock;
sigc::signal<void> ARDOUR_UI::CloseAllDialogs;
+float ARDOUR_UI::ui_scale = 1.0;
static bool
ask_about_configuration_copy (string const & old_dir, string const & new_dir, int version)
{
MessageDialog msg (string_compose (_("%1 %2.x has discovered configuration files from %1 %3.x.\n\n"
- "Would you like to copy the relevant files before starting to use the program?\n\n"
+ "Would you like these files to be copied and used for %1 %2.x?\n\n"
"(This will require you to restart %1.)"),
- PROGRAM_NAME, PROGRAM_VERSION, version), true);
-
- msg.add_button (Gtk::Stock::NO, Gtk::RESPONSE_NO);
+ PROGRAM_NAME, PROGRAM_VERSION, version),
+ false, /* no markup */
+ Gtk::MESSAGE_INFO,
+ Gtk::BUTTONS_YES_NO,
+ true /* modal, though it hardly matters since it is the only window */
+ );
+
+ msg.set_default_response (Gtk::RESPONSE_YES);
msg.show_all ();
- return (msg.run() == Gtk::RESPONSE_OK);
+ return (msg.run() == Gtk::RESPONSE_YES);
}
-ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
+static void
+libxml_generic_error_func (void* /* parsing_context*/,
+ const char* msg,
+ ...)
+{
+ va_list ap;
+ char buf[2048];
- : Gtkmm2ext::UI (PROGRAM_NAME, argcp, argvp)
- , ui_config (new UIConfiguration)
- , gui_object_state (new GUIObjectState)
+ va_start (ap, msg);
+ vsnprintf (buf, sizeof (buf), msg, ap);
+ error << buf << endmsg;
+ va_end (ap);
+}
+
+static void
+libxml_structured_error_func (void* /* parsing_context*/,
+ xmlErrorPtr err)
+{
+ string msg;
- , primary_clock (new MainClock (X_("primary"), false, X_("transport"), true, true, true, false, true))
- , secondary_clock (new MainClock (X_("secondary"), false, X_("secondary"), true, true, false, false, true))
-
- /* big clock */
+ if (err->message)
+ msg = err->message;
+ replace_all (msg, "\n", "");
+
+ if (err->file && err->line) {
+ error << X_("XML error: ") << msg << " in " << err->file << " at line " << err->line;
+
+ if (err->int2) {
+ error << ':' << err->int2;
+ }
+ }
+ error << endmsg;
+}
+
+
+ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir, UIConfiguration* uic)
+
+ : Gtkmm2ext::UI (PROGRAM_NAME, argcp, argvp)
+ , ui_config (uic->post_gui_init ())
+ , session_loaded (false)
+ , gui_object_state (new GUIObjectState)
+ , primary_clock (new MainClock (X_("primary"), X_("transport"), true ))
+ , secondary_clock (new MainClock (X_("secondary"), X_("secondary"), false))
, big_clock (new AudioClock (X_("bigclock"), false, "big", true, true, false, false))
, video_timeline(0)
-
- /* start of private members */
+ , ignore_dual_punch (false)
, editor (0)
, mixer (0)
, nsm (0)
, _was_dirty (false)
, _mixer_on_top (false)
+ , _initial_verbose_plugin_scan (false)
, first_time_engine_run (true)
-
- /* transport */
-
, roll_controllable (new TransportControllable ("transport roll", *this, TransportControllable::Roll))
, stop_controllable (new TransportControllable ("transport stop", *this, TransportControllable::Stop))
, goto_start_controllable (new TransportControllable ("transport goto start", *this, TransportControllable::GotoStart))
, auto_loop_controllable (new TransportControllable ("transport auto loop", *this, TransportControllable::AutoLoop))
, play_selection_controllable (new TransportControllable ("transport play selection", *this, TransportControllable::PlaySelection))
, rec_controllable (new TransportControllable ("transport rec-enable", *this, TransportControllable::RecordEnable))
-
, auto_return_button (ArdourButton::led_default_elements)
, follow_edits_button (ArdourButton::led_default_elements)
, auto_input_button (ArdourButton::led_default_elements)
-
, auditioning_alert_button (_("Audition"))
, solo_alert_button (_("Solo"))
, feedback_alert_button (_("Feedback"))
, error_alert_button ( ArdourButton::just_led_default_elements )
-
, editor_meter(0)
, editor_meter_peak_display()
-
+ , session_selector_window (0)
+ , _numpad_locate_happening (false)
+ , _session_is_new (false)
+ , last_key_press_time (0)
+ , save_as_dialog (0)
+ , meterbridge (0)
, speaker_config_window (X_("speaker-config"), _("Speaker Configuration"))
, key_editor (X_("key-editor"), _("Key Bindings"))
, rc_option_editor (X_("rc-options-editor"), _("Preferences"))
, big_clock_window (X_("big-clock"), _("Big Clock"), boost::bind (&ARDOUR_UI::create_big_clock_window, this))
, audio_port_matrix (X_("audio-connection-manager"), _("Audio Connections"), boost::bind (&ARDOUR_UI::create_global_port_matrix, this, ARDOUR::DataType::AUDIO))
, midi_port_matrix (X_("midi-connection-manager"), _("MIDI Connections"), boost::bind (&ARDOUR_UI::create_global_port_matrix, this, ARDOUR::DataType::MIDI))
-
+ , video_server_process (0)
+ , splash (0)
+ , have_configure_timeout (false)
+ , last_configure_time (0)
+ , last_peak_grab (0)
+ , have_disk_speed_dialog_displayed (false)
, _status_bar_visibility (X_("status-bar"))
, _feedback_exists (false)
, _log_not_acknowledged (LogLevelNone)
{
- Gtkmm2ext::init(localedir);
+ Gtkmm2ext::init (localedir);
if (ARDOUR::handle_old_configuration_files (boost::bind (ask_about_configuration_copy, _1, _2, _3))) {
MessageDialog msg (string_compose (_("Your configuration files were copied. You can now restart %1."), PROGRAM_NAME), true);
_exit (0);
}
-
- splash = 0;
-
- _numpad_locate_happening = false;
-
if (theArdourUI == 0) {
theArdourUI = this;
}
+ /* stop libxml from spewing to stdout/stderr */
+
+ xmlSetGenericErrorFunc (this, libxml_generic_error_func);
+ xmlSetStructuredErrorFunc (this, libxml_structured_error_func);
+
ui_config->ParameterChanged.connect (sigc::mem_fun (*this, &ARDOUR_UI::parameter_changed));
boost::function<void (string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
ui_config->map_parameters (pc);
- editor = 0;
- mixer = 0;
- meterbridge = 0;
- editor = 0;
- _session_is_new = false;
- session_selector_window = 0;
- last_key_press_time = 0;
- video_server_process = 0;
- open_session_selector = 0;
- have_configure_timeout = false;
- have_disk_speed_dialog_displayed = false;
- session_loaded = false;
- ignore_dual_punch = false;
-
roll_button.set_controllable (roll_controllable);
stop_button.set_controllable (stop_controllable);
goto_start_button.set_controllable (goto_start_controllable);
rec_button.set_name ("transport recenable button");
midi_panic_button.set_name ("transport button");
- last_configure_time= 0;
- last_peak_grab = 0;
-
ARDOUR::Diskstream::DiskOverrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_overrun_handler, this), gui_context());
ARDOUR::Diskstream::DiskUnderrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_underrun_handler, this), gui_context());
setup_gtk_ardour_enums ();
setup_profile ();
- SessionEvent::create_per_thread_pool ("GUI", 512);
+ SessionEvent::create_per_thread_pool ("GUI", 4096);
/* we like keyboards */
/* Trigger setting up the color scheme and loading the GTK RC file */
ARDOUR_UI::config()->load_rc_file (false);
-
+
_process_thread = new ProcessThread ();
_process_thread->init ();
if (first_time_engine_run) {
post_engine();
first_time_engine_run = false;
- }
-
+ }
+
+ if (_session) {
+ _session->reset_xrun_count ();
+ }
update_disk_space ();
update_cpu_load ();
+ update_xrun_count ();
update_sample_rate (AudioEngine::instance()->sample_rate());
update_timecode_format ();
+ update_peak_thread_work ();
}
void
MessageDialog msg (*editor, msgstr);
pop_back_splash (msg);
msg.run ();
-
+
if (free_reason) {
free (const_cast<char*> (reason));
}
#endif
ARDOUR::init_post_engine ();
-
+
/* connect to important signals */
AudioEngine::instance()->Stopped.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::engine_stopped, this), gui_context());
AudioEngine::instance()->SampleRateChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_sample_rate, this, _1), gui_context());
AudioEngine::instance()->BufferSizeChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_sample_rate, this, _1), gui_context());
AudioEngine::instance()->Halted.connect_same_thread (halt_connection, boost::bind (&ARDOUR_UI::engine_halted, this, _1, false));
+ AudioEngine::instance()->BecameSilent.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::audioengine_became_silent, this), gui_context());
_tooltips.enable();
if (n) {
_status_bar_visibility.set_state (*n);
}
-
+
check_memory_locking();
/* this is the first point at which all the keybindings are available */
delete primary_clock;
delete secondary_clock;
delete _process_thread;
+ delete meterbridge;
+ delete editor;
+ delete mixer;
+ delete nsm;
delete gui_object_state;
FastMeter::flush_pattern_cache ();
+ PixFader::flush_pattern_cache ();
}
+
+#ifndef NDEBUG
+ /* Small trick to flush main-thread event pool.
+ * Other thread-pools are destroyed at pthread_exit(),
+ * but tmain thread termination is too late to trigger Pool::~Pool()
+ */
+ SessionEvent* ev = new SessionEvent (SessionEvent::SetTransportSpeed, SessionEvent::Clear, SessionEvent::Immediate, 0, 0); // get the pool reference, values don't matter since the event is never queued.
+ delete ev->event_pool();
+#endif
}
void
#ifdef __APPLE__
_annc_filename = PROGRAM_NAME "_announcements_osx_";
+#elif defined PLATFORM_WINDOWS
+ _annc_filename = PROGRAM_NAME "_announcements_windows_";
#else
_annc_filename = PROGRAM_NAME "_announcements_linux_";
#endif
_annc_filename.append (VERSIONSTRING);
+ _announce_string = "";
+
std::string path = Glib::build_filename (user_config_directory(), _annc_filename);
- std::ifstream announce_file (path.c_str());
- if ( announce_file.fail() )
- _announce_string = "";
- else {
- std::stringstream oss;
- oss << announce_file.rdbuf();
- _announce_string = oss.str();
+ FILE* fin = g_fopen (path.c_str(), "rb");
+ if (fin) {
+ while (!feof (fin)) {
+ char tmp[1024];
+ size_t len;
+ if ((len = fread (tmp, sizeof(char), 1024, fin)) == 0 || ferror (fin)) {
+ break;
+ }
+ _announce_string.append (tmp, len);
+ }
+ fclose (fin);
}
pingback (VERSIONSTRING, path);
/* we need to create this early because it may need to set the
* audio backend end up.
*/
-
+
try {
audio_midi_setup.get (true);
} catch (...) {
if ((nsm_url = g_getenv ("NSM_URL")) != 0) {
nsm = new NSM_Client;
if (!nsm->init (nsm_url)) {
- nsm->announce (PROGRAM_NAME, ":dirty:", "ardour3");
+ /* the ardour executable may have different names:
+ *
+ * waf's obj.target for distro versions: eg ardour4, ardourvst4
+ * Ardour4, Mixbus3 for bundled versions + full path on OSX & windows
+ * argv[0] does not apply since we need the wrapper-script (not the binary itself)
+ *
+ * The wrapper startup script should set the environment variable 'ARDOUR_SELF'
+ */
+ const char *process_name = g_getenv ("ARDOUR_SELF");
+ nsm->announce (PROGRAM_NAME, ":dirty:", process_name ? process_name : "ardour4");
unsigned int i = 0;
// wait for announce reply from nsm server
}
} else {
-
+
if (brand_new_user) {
+ _initial_verbose_plugin_scan = true;
ArdourStartup s;
s.present ();
main().run();
s.hide ();
+ _initial_verbose_plugin_scan = false;
switch (s.response ()) {
case Gtk::RESPONSE_OK:
break;
ARDOUR::read_recent_sessions (rs);
string path = Glib::build_filename (user_config_directory(), ".iknowaboutfreeversion");
-
+
if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS) && !rs.empty()) {
-
+
/* already used Ardour, have sessions ... warn about plugin state */
-
+
ArdourDialog d (_("Free/Demo Version Warning"), true);
Label l;
Button b (string_compose (_("Subscribe and support development of %1"), PROGRAM_NAME));
CheckButton c (_("Don't warn me about this again"));
-
+
l.set_markup (string_compose (_("<span weight=\"bold\" size=\"large\">%1</span>\n\n<b>%2</b>\n\n<i>%3</i>\n\n%4"),
string_compose (_("This is a free/demo version of %1"), PROGRAM_NAME),
_("It will not restore OR save any plugin settings"),
_("To get full access to updates without this limitation\n"
"consider becoming a subscriber for a low cost every month.")));
l.set_justify (JUSTIFY_CENTER);
-
+
b.signal_clicked().connect (mem_fun(*this, &ARDOUR_UI::launch_subscribe));
-
+
d.get_vbox()->pack_start (l, true, true);
d.get_vbox()->pack_start (b, false, false, 12);
d.get_vbox()->pack_start (c, false, false, 12);
-
+
d.add_button (_("Quit now"), RESPONSE_CANCEL);
d.add_button (string_compose (_("Continue using %1"), PROGRAM_NAME), RESPONSE_OK);
-
+
d.show_all ();
c.signal_toggled().connect (sigc::hide_return (sigc::bind (sigc::ptr_fun (toggle_file_existence), path)));
-
+
if (d.run () != RESPONSE_OK) {
_exit (0);
}
}
#endif
-
+
/* go get a session */
const bool new_session_required = (ARDOUR_COMMAND_LINE::new_session || brand_new_user);
"runs out of memory. \n\n"
"You can view the memory limit with 'ulimit -l', "
"and it is normally controlled by %2"),
- PROGRAM_NAME,
+ PROGRAM_NAME,
#ifdef __FreeBSD__
X_("/etc/login.conf")
#else
ARDOUR_UI::every_second ()
{
update_cpu_load ();
+ update_xrun_count ();
update_buffer_load ();
update_disk_space ();
update_timecode_format ();
+ update_peak_thread_work ();
if (nsm && nsm->is_active ()) {
nsm->check ();
case RF64:
s << _("RF64");
break;
+ case RF64_WAV:
+ s << _("RF64/WAV");
+ break;
+ case MBWF:
+ s << _("MBWF");
+ break;
}
s << " ";
-
+
switch (_session->config.get_native_file_data_format ()) {
case FormatFloat:
s << _("32-float");
format_label.set_markup (s.str ());
}
+void
+ARDOUR_UI::update_xrun_count ()
+{
+ char buf[64];
+
+ /* If this text is changed, the set_size_request_to_display_given_text call in ARDOUR_UI::resize_text_widgets
+ should also be changed.
+ */
+
+ if (_session) {
+ const unsigned int x = _session->get_xrun_count ();
+ if (x > 9999) {
+ snprintf (buf, sizeof (buf), _("X: <span foreground=\"%s\">>10K</span>"), X_("red"));
+ } else {
+ snprintf (buf, sizeof (buf), _("X: <span foreground=\"%s\">%u</span>"), x > 0 ? X_("red") : X_("green"), x);
+ }
+ } else {
+ snprintf (buf, sizeof (buf), _("X: <span foreground=\"%s\">?</span>"), X_("yellow"));
+ }
+ xrun_label.set_markup (buf);
+ set_tip (xrun_label, _("Audio dropouts. Shift+click to reset"));
+}
+
void
ARDOUR_UI::update_cpu_load ()
{
cpu_load_label.set_markup (buf);
}
+void
+ARDOUR_UI::update_peak_thread_work ()
+{
+ char buf[64];
+ const int c = SourceFactory::peak_work_queue_length ();
+ if (c > 0) {
+ snprintf (buf, sizeof (buf), _("PkBld: <span foreground=\"%s\">%d</span>"), c >= 2 ? X_("red") : X_("green"), c);
+ peak_thread_work_label.set_markup (buf);
+ } else {
+ peak_thread_work_label.set_markup (X_(""));
+ }
+}
+
void
ARDOUR_UI::update_buffer_load ()
{
/* If this text is changed, the set_size_request_to_display_given_text call in ARDOUR_UI::resize_text_widgets
should also be changed.
*/
-
+
if (_session) {
snprintf (
buf, sizeof (buf),
mins = frames / (fr * 60);
frames -= mins * fr * 60;
secs = frames / fr;
-
+
bool const low = (hrs == 0 && mins <= 30);
-
+
snprintf (
buf, sizeof(buf),
_("Disk: <span foreground=\"%s\">%02dh:%02dm:%02ds</span>"),
} else {
matching = true;
}
-
+
snprintf (buf, sizeof (buf), S_("Timecode|TC: <span foreground=\"%s\">%s</span>"),
matching ? X_("green") : X_("red"),
Timecode::timecode_format_name (_session->config.get_timecode_format()).c_str());
}
timecode_format_label.set_markup (buf);
-}
+}
gint
ARDOUR_UI::update_wall_clock ()
}
- /* popup selector window */
+ /* ardour sessions are folders */
+ Gtk::FileChooserDialog open_session_selector(_("Open Session"), FILE_CHOOSER_ACTION_OPEN);
+ open_session_selector.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ open_session_selector.add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
+ open_session_selector.set_default_response(Gtk::RESPONSE_ACCEPT);
- if (open_session_selector == 0) {
- /* ardour sessions are folders */
- open_session_selector = new Gtk::FileChooserDialog (_("Open Session"), FILE_CHOOSER_ACTION_OPEN);
- open_session_selector->add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
- open_session_selector->add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
- open_session_selector->set_default_response(Gtk::RESPONSE_ACCEPT);
-
- if (_session) {
- string session_parent_dir = Glib::path_get_dirname(_session->path());
- string::size_type last_dir_sep = session_parent_dir.rfind(G_DIR_SEPARATOR);
- session_parent_dir = session_parent_dir.substr(0, last_dir_sep);
- open_session_selector->set_current_folder(session_parent_dir);
- } else {
- open_session_selector->set_current_folder(Config->get_default_session_parent_dir());
- }
+ if (_session) {
+ string session_parent_dir = Glib::path_get_dirname(_session->path());
+ open_session_selector.set_current_folder(session_parent_dir);
+ } else {
+ open_session_selector.set_current_folder(Config->get_default_session_parent_dir());
+ }
+ try {
+ /* add_shortcut_folder throws an exception if the folder being added already has a shortcut */
+#ifdef GTKOSX
+ open_session_selector.add_shortcut_folder_uri("file:///Volumes");
+#endif
string default_session_folder = Config->get_default_session_parent_dir();
- try {
- /* add_shortcut_folder throws an exception if the folder being added already has a shortcut */
- open_session_selector->add_shortcut_folder (default_session_folder);
- }
- catch (Glib::Error & e) {
- std::cerr << "open_session_selector->add_shortcut_folder (" << default_session_folder << ") threw Glib::Error " << e.what() << std::endl;
- }
+ open_session_selector.add_shortcut_folder (default_session_folder);
+ }
+ catch (Glib::Error & e) {
+ std::cerr << "open_session_selector.add_shortcut_folder() threw Glib::Error " << e.what() << std::endl;
+ }
- FileFilter session_filter;
- session_filter.add_pattern (string_compose(X_("*%1"), ARDOUR::statefile_suffix));
- session_filter.set_name (string_compose (_("%1 sessions"), PROGRAM_NAME));
- open_session_selector->add_filter (session_filter);
- open_session_selector->set_filter (session_filter);
- }
+ FileFilter session_filter;
+ session_filter.add_pattern (string_compose(X_("*%1"), ARDOUR::statefile_suffix));
+ session_filter.set_name (string_compose (_("%1 sessions"), PROGRAM_NAME));
+ open_session_selector.add_filter (session_filter);
+ open_session_selector.set_filter (session_filter);
- int response = open_session_selector->run();
- open_session_selector->hide ();
+ int response = open_session_selector.run();
+ open_session_selector.hide ();
- switch (response) {
- case RESPONSE_ACCEPT:
- break;
- default:
- open_session_selector->hide();
+ if (response == Gtk::RESPONSE_CANCEL) {
return;
}
- open_session_selector->hide();
- string session_path = open_session_selector->get_filename();
+ string session_path = open_session_selector.get_filename();
string path, name;
bool isnew;
void
-ARDOUR_UI::session_add_mixed_track (const ChanCount& input, const ChanCount& output, RouteGroup* route_group,
+ARDOUR_UI::session_add_mixed_track (const ChanCount& input, const ChanCount& output, RouteGroup* route_group,
uint32_t how_many, const string& name_template, PluginInfoPtr instrument)
{
list<boost::shared_ptr<MidiTrack> > tracks;
try {
tracks = _session->new_midi_track (input, output, instrument, ARDOUR::Normal, route_group, how_many, name_template);
-
+
if (tracks.size() != how_many) {
error << string_compose(P_("could not create %1 new mixed track", "could not create %1 new mixed tracks", how_many), how_many) << endmsg;
}
msg.run ();
}
}
-
+
void
ARDOUR_UI::session_add_midi_route (bool disk, RouteGroup* route_group, uint32_t how_many, const string& name_template, PluginInfoPtr instrument)
tracks = _session->new_audio_track (input_channels, output_channels, mode, route_group, how_many, name_template);
if (tracks.size() != how_many) {
- error << string_compose (P_("could not create %1 new audio track", "could not create %1 new audio tracks", how_many), how_many)
+ error << string_compose (P_("could not create %1 new audio track", "could not create %1 new audio tracks", how_many), how_many)
<< endmsg;
}
time (&now);
localtime_r (&now, &tmnow);
-
- int frame_rate = _session->frame_rate();
-
+
+ framecnt_t frame_rate = _session->frame_rate();
+
if (frame_rate == 0) {
/* no frame rate available */
return;
/** Check if any tracks are record enabled. If none are, record enable all of them.
* @return true if track record-enabled status was changed, false otherwise.
- */
+ */
bool
ARDOUR_UI::trx_record_enable_all_tracks ()
{
if (none_record_enabled) {
_session->set_record_enabled (rl, true, Session::rt_cleanup);
- }
+ }
return none_record_enabled;
}
/* stop loop playback but keep rolling */
_session->request_play_loop (false, false);
}
- }
+ }
} else if (_session->get_play_range () ) {
/* stop playing a range if we currently are */
void
ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode)
{
-
if (!_session) {
return;
}
_session->request_play_loop (true, true);
}
}
-
+
//show the loop markers
looploc->set_hidden (false, this);
}
if (!_session) {
return;
}
-
+
float current_transport_speed = _session->transport_speed();
-
+
if (current_transport_speed <= 0.0f) {
switch (option) {
case 0:
if (!_session) return;
if (editor && !editor->dragging_playhead()) {
- Clock (_session->audible_frame(), false, editor->get_preferred_edit_position()); /* EMIT_SIGNAL */
+ Clock (_session->audible_frame(), false, editor->get_preferred_edit_position (EDIT_IGNORE_PHEAD)); /* EMIT_SIGNAL */
}
}
clock_signal_connection.disconnect ();
}
+bool
+ARDOUR_UI::save_as_progress_update (float fraction, int64_t cnt, int64_t total, Gtk::Label* label, Gtk::ProgressBar* bar)
+{
+ char buf[256];
+
+ snprintf (buf, sizeof (buf), _("Copied %" PRId64 " of %" PRId64), cnt, total);
+
+ label->set_text (buf);
+ bar->set_fraction (fraction);
+
+ /* process events, redraws, etc. */
+
+ while (gtk_events_pending()) {
+ gtk_main_iteration ();
+ }
+
+ return true; /* continue with save-as */
+}
+
+void
+ARDOUR_UI::save_session_as ()
+{
+ if (!_session) {
+ return;
+ }
+
+ if (!save_as_dialog) {
+ save_as_dialog = new SaveAsDialog;
+ }
+
+ save_as_dialog->set_name (_session->name());
+
+ int response = save_as_dialog->run ();
+
+ save_as_dialog->hide ();
+
+ switch (response) {
+ case Gtk::RESPONSE_OK:
+ break;
+ default:
+ return;
+ }
+
+
+ Session::SaveAs sa;
+
+ sa.new_parent_folder = save_as_dialog->new_parent_folder ();
+ sa.new_name = save_as_dialog->new_name ();
+ sa.switch_to = save_as_dialog->switch_to();
+ sa.copy_media = save_as_dialog->copy_media();
+ sa.copy_external = save_as_dialog->copy_external();
+ sa.include_media = save_as_dialog->include_media ();
+
+ /* Only bother with a progress dialog if we're going to copy
+ media into the save-as target. Without that choice, this
+ will be very fast because we're only talking about a few kB's to
+ perhaps a couple of MB's of data.
+ */
+
+ ArdourDialog progress_dialog (_("Save As"), true);
+
+ if (sa.include_media && sa.copy_media) {
+
+ Gtk::Label label;
+ Gtk::ProgressBar progress_bar;
+
+ progress_dialog.get_vbox()->pack_start (label);
+ progress_dialog.get_vbox()->pack_start (progress_bar);
+ label.show ();
+ progress_bar.show ();
+
+ /* this signal will be emitted from within this, the calling thread,
+ * after every file is copied. It provides information on percentage
+ * complete (in terms of total data to copy), the number of files
+ * copied so far, and the total number to copy.
+ */
+
+ ScopedConnection c;
+
+ sa.Progress.connect_same_thread (c, boost::bind (&ARDOUR_UI::save_as_progress_update, this, _1, _2, _3, &label, &progress_bar));
+
+ progress_dialog.show_all ();
+ progress_dialog.present ();
+ }
+
+ if (_session->save_as (sa)) {
+ /* ERROR MESSAGE */
+ MessageDialog msg (string_compose (_("Save As failed: %1"), sa.failure_message));
+ msg.run ();
+ }
+
+ if (!sa.include_media) {
+ unload_session (false);
+ load_session (sa.final_session_folder_name, sa.new_name);
+ }
+}
+
/** Ask the user for the name of a new snapshot and then take it.
*/
}
}
}
-
+
break;
}
bool cancel_not_quit;
/* deal with any existing DIRTY session now, rather than later. don't
- * treat a non-dirty session this way, so that it stays visible
+ * treat a non-dirty session this way, so that it stays visible
* as we bring up the new session dialog.
*/
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)) {
if (Glib::file_test (session_path.c_str(), Glib::FILE_TEST_IS_REGULAR)) {
session_name = "";
session_dialog.clear_given ();
}
-
+
if (should_be_new || session_name.empty()) {
/* need the dialog to get info from user */
}
/* if we run the startup dialog again, offer more than just "new session" */
-
+
should_be_new = false;
-
+
session_name = session_dialog.session_name (likely_new);
session_path = session_dialog.session_folder ();
}
string::size_type suffix = session_name.find (statefile_suffix);
-
+
if (suffix != string::npos) {
session_name = session_name.substr (0, suffix);
}
-
+
/* this shouldn't happen, but we catch it just in case it does */
-
+
if (session_name.empty()) {
continue;
}
-
+
if (session_dialog.use_session_template()) {
template_name = session_dialog.session_template_name();
_session_is_new = true;
}
-
+
if (session_name[0] == G_DIR_SEPARATOR ||
#ifdef PLATFORM_WINDOWS
(session_name.length() > 3 && session_name[1] == ':' && session_name[2] == G_DIR_SEPARATOR)
#endif
)
{
-
+
/* absolute path or cwd-relative path specified for session name: infer session folder
from what was given.
*/
-
+
session_path = Glib::path_get_dirname (session_name);
session_name = Glib::path_get_basename (session_name);
-
+
} else {
session_path = session_dialog.session_folder();
-
+
char illegal = Session::session_name_is_legal (session_name);
-
+
if (illegal) {
MessageDialog msg (session_dialog,
string_compose (_("To ensure compatibility with various systems\n"
continue;
}
}
-
+
if (Glib::file_test (session_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
if (_session) {
unload_status = unload_session ();
-
+
if (unload_status < 0) {
goto out;
} else if (unload_status > 0) {
}
goto out;
}
+ catch (SessionException e) {
+ MessageDialog msg (string_compose(
+ _("Session \"%1 (snapshot %2)\" did not load successfully: %3"),
+ path, snap_name, e.what()),
+ true,
+ Gtk::MESSAGE_INFO,
+ BUTTONS_OK);
+
+ msg.set_title (_("Loading Error"));
+ msg.set_position (Gtk::WIN_POS_CENTER);
+ pop_back_splash (msg);
+ msg.present ();
+
+ dump_errors (cerr);
+
+ (void) msg.run ();
+ msg.hide ();
+ goto out;
+ }
catch (...) {
MessageDialog msg (string_compose(
msg.set_position (Gtk::WIN_POS_CENTER);
pop_back_splash (msg);
msg.present ();
+
+ dump_errors (cerr);
+
(void) msg.run ();
msg.hide ();
(void) msg.run ();
msg.hide ();
}
-
+
/* Now the session been created, add the transport controls */
new_session->add_controllable(roll_controllable);
new_session = new Session (*AudioEngine::instance(), path, snap_name, &bus_profile);
}
+ catch (SessionException e) {
+ dump_errors (cerr);
+ MessageDialog msg (string_compose(_("Could not create session in \"%1\": %2"), path, e.what()));
+ msg.set_title (_("Loading Error"));
+ msg.set_position (Gtk::WIN_POS_CENTER);
+ pop_back_splash (msg);
+ msg.run ();
+ return -1;
+ }
catch (...) {
-
+ dump_errors (cerr);
MessageDialog msg (string_compose(_("Could not create session in \"%1\""), path));
+ msg.set_title (_("Loading Error"));
+ msg.set_position (Gtk::WIN_POS_CENTER);
pop_back_splash (msg);
msg.run ();
return -1;
display_cleanup_results (rep, _("deleted file"), true);
}
+void
+ARDOUR_UI::cleanup_peakfiles ()
+{
+ if (_session == 0) {
+ /* shouldn't happen: menu item is insensitive */
+ return;
+ }
+
+ if (! _session->can_cleanup_peakfiles ()) {
+ return;
+ }
+
+ // get all region-views in this session
+ RegionSelection rs;
+ TrackViewList empty;
+ empty.clear();
+ editor->get_regions_after(rs, (framepos_t) 0, empty);
+ std::list<RegionView*> views = rs.by_layer();
+
+ // remove displayed audio-region-views waveforms
+ for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
+ AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
+ if (!arv) { continue ; }
+ arv->delete_waves();
+ }
+
+ // cleanup peak files:
+ // - stop pending peakfile threads
+ // - close peakfiles if any
+ // - remove peak dir in session
+ // - setup peakfiles (background thread)
+ _session->cleanup_peakfiles ();
+
+ // re-add waves to ARV
+ for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
+ AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
+ if (!arv) { continue ; }
+ arv->create_waves();
+ }
+}
+
void
ARDOUR_UI::setup_order_hint (AddRouteDialog::InsertAt place)
{
- uint32_t order_hint = 0;
+ uint32_t order_hint = UINT32_MAX;
+
+ if (editor->get_selection().tracks.empty()) {
+ return;
+ }
/*
- we want the new routes to have their order keys set starting from
+ we want the new routes to have their order keys set starting from
the highest order key in the selection + 1 (if available).
*/
- if (place == AddRouteDialog::MixerSelection) {
- for (RouteUISelection::iterator s = mixer->selection().routes.begin(); s != mixer->selection().routes.end(); ++s) {
- if ((*s)->route()->order_key() > order_hint) {
- order_hint = (*s)->route()->order_key();
- }
- }
- if (!mixer->selection().routes.empty()) {
+ if (place == AddRouteDialog::AfterSelection) {
+ RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView*> (editor->get_selection().tracks.back());
+ if (rtav) {
+ order_hint = rtav->route()->order_key();
order_hint++;
- } else {
- return;
- }
-
- } else if (place == AddRouteDialog::EditorSelection){
- for (TrackSelection::iterator s = editor->get_selection().tracks.begin(); s != editor->get_selection().tracks.end(); ++s) {
- RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (*s);
- if (tav && tav->route() && tav->route()->order_key() > order_hint) {
- order_hint = tav->route()->order_key();
- }
}
-
- if (!editor->get_selection().tracks.empty()) {
- order_hint++;
- } else {
- return;
+ } else if (place == AddRouteDialog::BeforeSelection) {
+ RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView*> (editor->get_selection().tracks.front());
+ if (rtav) {
+ order_hint = rtav->route()->order_key();
}
-
} else if (place == AddRouteDialog::First) {
order_hint = 0;
} else {
- /** AddRouteDialog::Last
+ /* leave order_hint at UINT32_MAX */
+ }
+
+ if (order_hint == UINT32_MAX) {
+ /** AddRouteDialog::Last or selection with first/last not a RouteTimeAxisView
* not setting an order hint will place new routes last.
*/
return;
Config->set_video_advanced_setup(false);
} else {
std::ostringstream osstream;
- osstream << "http://localhost:" << video_server_dialog->get_listenport() << "/";
+ osstream << "http://127.0.0.1:" << video_server_dialog->get_listenport() << "/";
Config->set_video_server_url(osstream.str());
Config->set_video_server_docroot(icsd_docroot);
Config->set_video_advanced_setup(true);
void
ARDOUR_UI::create_xrun_marker (framepos_t where)
{
- editor->mouse_add_new_marker (where, false, true);
+ if (_session) {
+ Location *location = new Location (*_session, where, where, _("xrun"), Location::IsMark);
+ _session->locations()->add (location);
+ }
}
void
static MessageDialog *scan_dlg = NULL;
static ProgressBar *scan_pbar = NULL;
static HBox *scan_tbox = NULL;
+static Gtk::Button *scan_timeout_button;
void
ARDOUR_UI::cancel_plugin_scan ()
ARDOUR_UI::cancel_plugin_timeout ()
{
PluginManager::instance().cancel_plugin_timeout();
- scan_tbox->hide();
+ scan_timeout_button->set_sensitive (false);
}
void
return;
}
if (timeout > 0) {
+ scan_pbar->set_sensitive (false);
+ scan_timeout_button->set_sensitive (true);
scan_pbar->set_fraction ((float) timeout / (float) Config->get_vst_scan_timeout());
scan_tbox->show();
} else {
- scan_tbox->hide();
+ scan_pbar->set_sensitive (false);
+ scan_timeout_button->set_sensitive (false);
}
gui_idle_handler();
}
}
const bool cancelled = PluginManager::instance().cancelled();
- if (type != X_("closeme") && !ui_config->get_show_plugin_scan_window()) {
+ if (type != X_("closeme") && (!ui_config->get_show_plugin_scan_window()) && !_initial_verbose_plugin_scan) {
if (cancelled && scan_dlg->is_mapped()) {
scan_dlg->hide();
gui_idle_handler();
}
static Gtk::Button *cancel_button;
- static Gtk::Button *timeout_button;
if (!scan_dlg) {
scan_dlg = new MessageDialog("", false, MESSAGE_INFO, BUTTONS_NONE); // TODO manage
VBox* vbox = scan_dlg->get_vbox();
scan_tbox = manage( new HBox() );
- timeout_button = manage(new Gtk::Button(_("Stop Timeout")));
- timeout_button->set_name ("EditorGTKButton");
- timeout_button->signal_clicked().connect ( mem_fun (*this, &ARDOUR_UI::cancel_plugin_timeout) );
- timeout_button->show();
+ scan_timeout_button = manage(new Gtk::Button(_("Stop Timeout")));
+ scan_timeout_button->set_name ("EditorGTKButton");
+ scan_timeout_button->signal_clicked().connect ( mem_fun (*this, &ARDOUR_UI::cancel_plugin_timeout) );
+ scan_timeout_button->show();
scan_pbar = manage(new ProgressBar());
scan_pbar->set_orientation(Gtk::PROGRESS_RIGHT_TO_LEFT);
scan_pbar->show();
scan_tbox->pack_start (*scan_pbar, PACK_EXPAND_WIDGET, 4);
- scan_tbox->pack_start (*timeout_button, PACK_SHRINK, 4);
+ scan_tbox->pack_start (*scan_timeout_button, PACK_SHRINK, 4);
scan_dlg->get_vbox()->pack_start (*scan_tbox, PACK_SHRINK, 4);
}
assert(scan_dlg && scan_tbox && cancel_button);
if (type == X_("closeme")) {
+ scan_tbox->hide();
scan_dlg->hide();
} else {
scan_dlg->set_message(type + ": " + Glib::path_get_basename(plugin));
scan_dlg->show();
}
if (!can_cancel || !cancelled) {
- scan_tbox->hide();
+ scan_timeout_button->set_sensitive(false);
}
cancel_button->set_sensitive(can_cancel && !cancelled);
*/
halt_connection.disconnect ();
-
+
if (AudioEngine::instance()->stop ()) {
MessageDialog msg (*editor, _("Could not disconnect from Audio/MIDI engine"));
msg.run ();
} else {
AudioEngine::instance()->Halted.connect_same_thread (halt_connection, boost::bind (&ARDOUR_UI::engine_halted, this, _1, false));
}
-
+
update_sample_rate (0);
return 0;
}
}
return -1;
}
-
+
update_sample_rate (0);
return 0;
}
ARDOUR_UI::update_transport_clocks (framepos_t pos)
{
if (ui_config->get_primary_clock_delta_edit_cursor()) {
- primary_clock->set (pos, false, editor->get_preferred_edit_position());
+ primary_clock->set (pos, false, editor->get_preferred_edit_position (EDIT_IGNORE_PHEAD));
} else {
primary_clock->set (pos);
}
if (ui_config->get_secondary_clock_delta_edit_cursor()) {
- secondary_clock->set (pos, false, editor->get_preferred_edit_position());
+ secondary_clock->set (pos, false, editor->get_preferred_edit_position (EDIT_IGNORE_PHEAD));
} else {
secondary_clock->set (pos);
}
for (vector<AudioClock*>::iterator x = AudioClock::clocks.begin(); x != AudioClock::clocks.end(); ++x) {
XMLNode* child = new XMLNode (X_("Clock"));
-
+
child->add_property (X_("name"), (*x)->name());
child->add_property (X_("mode"), enum_2_string ((*x)->mode()));
child->add_property (X_("on"), ((*x)->off() ? X_("no") : X_("yes")));
if ( _numpad_locate_happening ) {
_pending_locate_num = _pending_locate_num*10 + num;
} else {
- switch (num) {
+ switch (num) {
case 0: toggle_roll(false, false); break;
case 1: transport_rewind(1); break;
case 2: transport_forward(1); break;
{
CairoWidget::set_flat_buttons( config()->get_flat_buttons() );
}
+
+void
+ARDOUR_UI::audioengine_became_silent ()
+{
+ MessageDialog msg (string_compose (_("This is a free/demo copy of %1. It has just switched to silent mode."), PROGRAM_NAME),
+ true,
+ Gtk::MESSAGE_WARNING,
+ Gtk::BUTTONS_NONE,
+ true);
+
+ msg.set_title (string_compose (_("%1 is now silent"), PROGRAM_NAME));
+
+ Gtk::Label pay_label (string_compose (_("Please consider paying for a copy of %1 - you can pay whatever you want."), PROGRAM_NAME));
+ Gtk::Label subscribe_label (_("Better yet become a subscriber - subscriptions start at US$1 per month."));
+ Gtk::Button pay_button (_("Pay for a copy (via the web)"));
+ Gtk::Button subscribe_button (_("Become a subscriber (via the web)"));
+ Gtk::HBox pay_button_box;
+ Gtk::HBox subscribe_button_box;
+
+ pay_button_box.pack_start (pay_button, true, false);
+ subscribe_button_box.pack_start (subscribe_button, true, false);
+
+ bool (*openuri)(const char*) = PBD::open_uri; /* this forces selection of the const char* variant of PBD::open_uri(), which we need to avoid ambiguity below */
+
+ pay_button.signal_clicked().connect (sigc::hide_return (sigc::bind (sigc::ptr_fun (openuri), (const char*) "https://ardour.org/download")));
+ subscribe_button.signal_clicked().connect (sigc::hide_return (sigc::bind (sigc::ptr_fun (openuri), (const char*) "https://community.ardour.org/s/subscribe")));
+
+ msg.get_vbox()->pack_start (pay_label);
+ msg.get_vbox()->pack_start (pay_button_box);
+ msg.get_vbox()->pack_start (subscribe_label);
+ msg.get_vbox()->pack_start (subscribe_button_box);
+
+ msg.get_vbox()->show_all ();
+
+ msg.add_button (_("Remain silent"), Gtk::RESPONSE_CANCEL);
+ msg.add_button (_("Save and quit"), Gtk::RESPONSE_NO);
+ msg.add_button (_("Give me more time"), Gtk::RESPONSE_YES);
+
+ int r = msg.run ();
+
+ switch (r) {
+ case Gtk::RESPONSE_YES:
+ AudioEngine::instance()->reset_silence_countdown ();
+ break;
+
+ case Gtk::RESPONSE_NO:
+ /* save and quit */
+ save_state_canfail ("");
+ exit (0);
+ break;
+
+ case Gtk::RESPONSE_CANCEL:
+ default:
+ /* don't reset, save session and exit */
+ break;
+ }
+}
+
+void
+ARDOUR_UI::hide_application ()
+{
+ Application::instance ()-> hide ();
+}