summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2013-09-12 11:28:50 -0400
committerPaul Davis <paul@linuxaudiosystems.com>2013-09-12 11:28:50 -0400
commitbb826f4beebfcedc50570b818c305560d2233e47 (patch)
tree1484460f914e64fdbdce58230ffe7934bc0761f4
parent8c5cff60912c7e0a7256f635641399500d8d00d9 (diff)
parentf85b362351a5f9167f93b6988f2c8a4c7e03a33c (diff)
incomplete merge of master into windows (requires upcoming changes to master to be complete)
-rw-r--r--gtk2_ardour/actions.cc4
-rw-r--r--gtk2_ardour/actions.h4
-rw-r--r--gtk2_ardour/ardev_common.sh.in1
-rw-r--r--gtk2_ardour/ardour.menus.in26
-rw-r--r--gtk2_ardour/ardour3_fonts.rc.in5
-rw-r--r--gtk2_ardour/ardour3_styles.rc.in5
-rw-r--r--gtk2_ardour/ardour_ui.cc481
-rw-r--r--gtk2_ardour/ardour_ui.h19
-rw-r--r--gtk2_ardour/ardour_ui_dialogs.cc9
-rw-r--r--gtk2_ardour/ardour_ui_ed.cc94
-rwxr-xr-xgtk2_ardour/arval1
-rw-r--r--gtk2_ardour/editor.cc3
-rw-r--r--gtk2_ardour/editor_drag.cc9
-rw-r--r--gtk2_ardour/engine_dialog.cc1842
-rw-r--r--gtk2_ardour/engine_dialog.h225
-rw-r--r--gtk2_ardour/export_video_dialog.cc10
-rw-r--r--gtk2_ardour/gain_meter.cc6
-rw-r--r--gtk2_ardour/generic_pluginui.cc2
-rw-r--r--gtk2_ardour/global_port_matrix.cc31
-rw-r--r--gtk2_ardour/level_meter.cc1
-rw-r--r--gtk2_ardour/main.cc36
-rw-r--r--gtk2_ardour/meterbridge.cc4
-rw-r--r--gtk2_ardour/midi_tracer.cc60
-rw-r--r--gtk2_ardour/nsm.cc2
-rw-r--r--gtk2_ardour/opts.cc10
-rw-r--r--gtk2_ardour/opts.h4
-rw-r--r--gtk2_ardour/panner_ui.cc1
-rw-r--r--gtk2_ardour/plugin_ui.cc2
-rw-r--r--gtk2_ardour/port_group.cc56
-rw-r--r--gtk2_ardour/port_insert_ui.cc4
-rw-r--r--gtk2_ardour/processor_box.cc4
-rw-r--r--gtk2_ardour/rc_option_editor.cc6
-rw-r--r--gtk2_ardour/route_params_ui.cc2
-rw-r--r--gtk2_ardour/route_ui.cc2
-rw-r--r--gtk2_ardour/startup.cc42
-rw-r--r--gtk2_ardour/startup.h8
-rw-r--r--gtk2_ardour/utils.cc2
-rw-r--r--gtk2_ardour/window_manager.cc2
-rw-r--r--gtk2_ardour/wscript2
-rw-r--r--libs/ardour/ardour/ardour.h4
-rw-r--r--libs/ardour/ardour/async_midi_port.h (renamed from libs/midi++2/midi++/jack_midi_port.h)58
-rw-r--r--libs/ardour/ardour/audio_backend.h432
-rw-r--r--libs/ardour/ardour/audio_diskstream.h4
-rw-r--r--libs/ardour/ardour/audio_port.h6
-rw-r--r--libs/ardour/ardour/audioengine.h481
-rw-r--r--libs/ardour/ardour/backend_search_path.h39
-rw-r--r--libs/ardour/ardour/buffer_set.h2
-rw-r--r--libs/ardour/ardour/data_type.h27
-rw-r--r--libs/ardour/ardour/debug.h1
-rw-r--r--libs/ardour/ardour/directory_names.h1
-rw-r--r--libs/ardour/ardour/diskstream.h4
-rw-r--r--libs/ardour/ardour/graph.h2
-rw-r--r--libs/ardour/ardour/midi_buffer.h1
-rw-r--r--libs/ardour/ardour/midi_diskstream.h2
-rw-r--r--libs/ardour/ardour/midi_port.h27
-rw-r--r--libs/ardour/ardour/midi_ui.h6
-rw-r--r--libs/ardour/ardour/midiport_manager.h99
-rw-r--r--libs/ardour/ardour/port.h47
-rw-r--r--libs/ardour/ardour/port_engine.h345
-rw-r--r--libs/ardour/ardour/port_manager.h169
-rw-r--r--libs/ardour/ardour/public_diskstream.h4
-rw-r--r--libs/ardour/ardour/rc_configuration.h6
-rw-r--r--libs/ardour/ardour/session.h63
-rw-r--r--libs/ardour/ardour/slave.h26
-rw-r--r--libs/ardour/ardour/ticker.h53
-rw-r--r--libs/ardour/ardour/track.h4
-rw-r--r--libs/ardour/ardour/types.h40
-rw-r--r--libs/ardour/async_midi_port.cc303
-rw-r--r--libs/ardour/audio_diskstream.cc12
-rw-r--r--libs/ardour/audio_port.cc10
-rw-r--r--libs/ardour/audio_track.cc2
-rw-r--r--libs/ardour/audioengine.cc1584
-rw-r--r--libs/ardour/backend_search_path.cc45
-rw-r--r--libs/ardour/buffer_set.cc6
-rw-r--r--libs/ardour/bundle.cc19
-rw-r--r--libs/ardour/capturing_processor.cc2
-rw-r--r--libs/ardour/debug.cc1
-rw-r--r--libs/ardour/delivery.cc2
-rw-r--r--libs/ardour/directory_names.cc1
-rw-r--r--libs/ardour/export_channel.cc2
-rw-r--r--libs/ardour/export_graph_builder.cc4
-rw-r--r--libs/ardour/globals.cc49
-rw-r--r--libs/ardour/internal_send.cc2
-rw-r--r--libs/ardour/io.cc8
-rw-r--r--libs/ardour/jack_slave.cc33
-rw-r--r--libs/ardour/ladspa_plugin.cc4
-rw-r--r--libs/ardour/ltc_slave.cc24
-rw-r--r--libs/ardour/lv2_plugin.cc4
-rw-r--r--libs/ardour/midi_buffer.cc55
-rw-r--r--libs/ardour/midi_clock_slave.cc21
-rw-r--r--libs/ardour/midi_diskstream.cc22
-rw-r--r--libs/ardour/midi_port.cc74
-rw-r--r--libs/ardour/midi_ui.cc42
-rw-r--r--libs/ardour/midiport_manager.cc170
-rw-r--r--libs/ardour/mtc_slave.cc68
-rw-r--r--libs/ardour/panner_manager.cc1
-rw-r--r--libs/ardour/port.cc259
-rw-r--r--libs/ardour/port_insert.cc10
-rw-r--r--libs/ardour/port_manager.cc659
-rw-r--r--libs/ardour/rc_configuration.cc27
-rw-r--r--libs/ardour/route.cc10
-rw-r--r--libs/ardour/session.cc428
-rw-r--r--libs/ardour/session_export.cc7
-rw-r--r--libs/ardour/session_jack.cc185
-rw-r--r--libs/ardour/session_ltc.cc4
-rw-r--r--libs/ardour/session_midi.cc60
-rw-r--r--libs/ardour/session_process.cc5
-rw-r--r--libs/ardour/session_state.cc380
-rw-r--r--libs/ardour/session_time.cc97
-rw-r--r--libs/ardour/session_transport.cc44
-rw-r--r--libs/ardour/slave.cc4
-rw-r--r--libs/ardour/ticker.cc180
-rw-r--r--libs/ardour/track.cc8
-rw-r--r--libs/ardour/wscript66
-rw-r--r--libs/backends/jack/jack_api.cc102
-rw-r--r--libs/backends/jack/jack_audiobackend.cc1002
-rw-r--r--libs/backends/jack/jack_audiobackend.h190
-rw-r--r--libs/backends/jack/jack_connection.cc164
-rw-r--r--libs/backends/jack/jack_connection.h40
-rw-r--r--libs/backends/jack/jack_portengine.cc569
-rw-r--r--libs/backends/jack/jack_portengine.h132
-rw-r--r--libs/backends/jack/jack_utils.cc897
-rw-r--r--libs/backends/jack/jack_utils.h235
-rw-r--r--libs/backends/jack/wscript51
-rw-r--r--libs/backends/wscript27
-rw-r--r--libs/midi++2/jack_midi_port.cc486
-rw-r--r--libs/midi++2/manager.cc183
-rw-r--r--libs/midi++2/midi++/manager.h103
-rw-r--r--libs/midi++2/midi++/mmc.h11
-rw-r--r--libs/midi++2/midi++/parser.h4
-rw-r--r--libs/midi++2/midi++/port.h7
-rw-r--r--libs/midi++2/mmc.cc17
-rw-r--r--libs/midi++2/mtc.cc2
-rw-r--r--libs/midi++2/parser.cc3
-rw-r--r--libs/midi++2/port.cc2
-rw-r--r--libs/midi++2/wscript5
-rw-r--r--libs/surfaces/generic_midi/generic_midi_control_protocol.cc19
-rw-r--r--libs/surfaces/generic_midi/generic_midi_control_protocol.h18
-rw-r--r--libs/surfaces/generic_midi/midiaction.cc2
-rw-r--r--libs/surfaces/generic_midi/midiaction.h4
-rw-r--r--libs/surfaces/generic_midi/midicontrollable.cc37
-rw-r--r--libs/surfaces/generic_midi/midicontrollable.h15
-rw-r--r--libs/surfaces/generic_midi/midifunction.cc2
-rw-r--r--libs/surfaces/generic_midi/midifunction.h3
-rw-r--r--libs/surfaces/generic_midi/midiinvokable.cc22
-rw-r--r--libs/surfaces/generic_midi/midiinvokable.h7
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol.cc34
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol.h10
-rw-r--r--libs/surfaces/mackie/surface.cc7
-rw-r--r--libs/surfaces/mackie/surface_port.cc58
-rw-r--r--libs/surfaces/mackie/surface_port.h23
-rw-r--r--midi_maps/Novation_Impulse61.map56
-rw-r--r--wscript1
153 files changed, 9337 insertions, 5196 deletions
diff --git a/gtk2_ardour/actions.cc b/gtk2_ardour/actions.cc
index 3b0133b535..cefaeeb4cf 100644
--- a/gtk2_ardour/actions.cc
+++ b/gtk2_ardour/actions.cc
@@ -58,8 +58,8 @@ vector<RefPtr<Gtk::Action> > ActionManager::playlist_selection_sensitive_actions
vector<RefPtr<Gtk::Action> > ActionManager::mouse_edit_point_requires_canvas_actions;
vector<RefPtr<Gtk::Action> > ActionManager::range_sensitive_actions;
-vector<RefPtr<Gtk::Action> > ActionManager::jack_sensitive_actions;
-vector<RefPtr<Gtk::Action> > ActionManager::jack_opposite_sensitive_actions;
+vector<RefPtr<Gtk::Action> > ActionManager::engine_sensitive_actions;
+vector<RefPtr<Gtk::Action> > ActionManager::engine_opposite_sensitive_actions;
vector<RefPtr<Gtk::Action> > ActionManager::transport_sensitive_actions;
vector<RefPtr<Gtk::Action> > ActionManager::edit_point_in_region_sensitive_actions;
diff --git a/gtk2_ardour/actions.h b/gtk2_ardour/actions.h
index 20c0532636..f3fba51de5 100644
--- a/gtk2_ardour/actions.h
+++ b/gtk2_ardour/actions.h
@@ -46,8 +46,8 @@ namespace ActionManager {
extern std::vector<Glib::RefPtr<Gtk::Action> > range_sensitive_actions;
extern std::vector<Glib::RefPtr<Gtk::Action> > transport_sensitive_actions;
- extern std::vector<Glib::RefPtr<Gtk::Action> > jack_sensitive_actions;
- extern std::vector<Glib::RefPtr<Gtk::Action> > jack_opposite_sensitive_actions;
+ extern std::vector<Glib::RefPtr<Gtk::Action> > engine_sensitive_actions;
+ extern std::vector<Glib::RefPtr<Gtk::Action> > engine_opposite_sensitive_actions;
extern std::vector<Glib::RefPtr<Gtk::Action> > edit_point_in_region_sensitive_actions;
extern void map_some_state (const char* group, const char* action, bool (ARDOUR::RCConfiguration::*get)() const);
diff --git a/gtk2_ardour/ardev_common.sh.in b/gtk2_ardour/ardev_common.sh.in
index 897bfcf7c2..850a83c954 100644
--- a/gtk2_ardour/ardev_common.sh.in
+++ b/gtk2_ardour/ardev_common.sh.in
@@ -17,6 +17,7 @@ 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
diff --git a/gtk2_ardour/ardour.menus.in b/gtk2_ardour/ardour.menus.in
index 15f1bde1b6..197b484698 100644
--- a/gtk2_ardour/ardour.menus.in
+++ b/gtk2_ardour/ardour.menus.in
@@ -495,27 +495,27 @@
<menuitem action='show-marker-lines'/>
</menu>
<menu name='JACK' action='JACK'>
- <menuitem action='JACKDisconnect'/>
- <menuitem action='JACKReconnect'/>
+ <menuitem action='EngineDisconnect'/>
+ <menuitem action='EngineReconnect'/>
<menu name='Latency' action='Latency'>
- <menuitem action='JACKLatency32'/>
- <menuitem action='JACKLatency64'/>
- <menuitem action='JACKLatency128'/>
- <menuitem action='JACKLatency256'/>
- <menuitem action='JACKLatency512'/>
- <menuitem action='JACKLatency1024'/>
- <menuitem action='JACKLatency2048'/>
- <menuitem action='JACKLatency4096'/>
- <menuitem action='JACKLatency8192'/>
+ <menuitem action='EngineLatency32'/>
+ <menuitem action='EngineLatency64'/>
+ <menuitem action='EngineLatency128'/>
+ <menuitem action='EngineLatency256'/>
+ <menuitem action='EngineLatency512'/>
+ <menuitem action='EngineLatency1024'/>
+ <menuitem action='EngineLatency2048'/>
+ <menuitem action='EngineLatency4096'/>
+ <menuitem action='EngineLatency8192'/>
</menu>
</menu>
<menu action = 'WindowMenu'>
+ <menuitem action='toggle-audio-midi-setup'/>
+ <separator/>
<menuitem action='toggle-mixer'/>
<menuitem action='toggle-meterbridge'/>
<menuitem action='toggle-editor-mixer'/>
-
<separator/>
-
<menuitem action='toggle-inspector'/>
<menuitem action='toggle-locations'/>
<menuitem action='toggle-key-editor'/>
diff --git a/gtk2_ardour/ardour3_fonts.rc.in b/gtk2_ardour/ardour3_fonts.rc.in
index 7d017df4b3..8d09ff0f1d 100644
--- a/gtk2_ardour/ardour3_fonts.rc.in
+++ b/gtk2_ardour/ardour3_fonts.rc.in
@@ -50,6 +50,11 @@ style "large_bold_text"
font_name = "bold @FONT_LARGE@"
}
+style "big_text"
+{
+ font_name = "@FONT_BIG@"
+}
+
style "bigger_mono_text"
{
font_name = "@MONOSPACE@ @FONT_BIGGER@"
diff --git a/gtk2_ardour/ardour3_styles.rc.in b/gtk2_ardour/ardour3_styles.rc.in
index e3978d063a..8b5b5e4c99 100644
--- a/gtk2_ardour/ardour3_styles.rc.in
+++ b/gtk2_ardour/ardour3_styles.rc.in
@@ -1011,3 +1011,8 @@ style "meter_strip_sep" = "default"
{
bg[NORMAL] = { 0.0, 0.0, 0.0 }
}
+
+style "settings_notebook" = "big_text"
+{
+
+}
diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc
index 33f5a5099e..307eb925b3 100644
--- a/gtk2_ardour/ardour_ui.cc
+++ b/gtk2_ardour/ardour_ui.cc
@@ -62,9 +62,8 @@
#include "gtkmm2ext/popup.h"
#include "gtkmm2ext/window_title.h"
-#include "midi++/manager.h"
-
#include "ardour/ardour.h"
+#include "ardour/audio_backend.h"
#include "ardour/audioengine.h"
#include "ardour/audiofilesource.h"
#include "ardour/automation_watch.h"
@@ -162,10 +161,10 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
/* start of private members */
, _startup (0)
- , engine (0)
, nsm (0)
, _was_dirty (false)
, _mixer_on_top (false)
+ , first_time_engine_run (true)
/* transport */
@@ -207,7 +206,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
, _status_bar_visibility (X_("status-bar"))
, _feedback_exists (false)
-
+ , _audio_midi_setup (0)
{
Gtkmm2ext::init(localedir);
@@ -218,12 +217,12 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
}
ui_config = new UIConfiguration();
+ _audio_midi_setup = new EngineControl;
editor = 0;
mixer = 0;
meterbridge = 0;
editor = 0;
- engine = 0;
_session_is_new = false;
session_selector_window = 0;
last_key_press_time = 0;
@@ -271,6 +270,10 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
ARDOUR::Session::AskAboutPendingState.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::pending_state_dialog, this));
+ /* handle Audio/MIDI setup when session requires it */
+
+ ARDOUR::Session::AudioEngineSetupRequired.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::do_audio_midi_setup, this, _1));
+
/* handle sr mismatch with a dialog (PROBLEM: needs to return a value and thus cannot be x-thread) */
ARDOUR::Session::AskAboutSampleRateMismatch.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::sr_mismatch_dialog, this, _1, _2));
@@ -369,6 +372,8 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
_process_thread->init ();
DPIReset.connect (sigc::mem_fun (*this, &ARDOUR_UI::resize_text_widgets));
+
+ attach_to_engine ();
}
GlobalPortMatrixWindow*
@@ -380,42 +385,146 @@ ARDOUR_UI::create_global_port_matrix (ARDOUR::DataType type)
return new GlobalPortMatrixWindow (_session, type);
}
-int
-ARDOUR_UI::create_engine ()
+void
+ARDOUR_UI::attach_to_engine ()
{
- // this gets called every time by new_session()
+ AudioEngine::instance()->Stopped.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::engine_stopped, this), gui_context());
+ AudioEngine::instance()->Running.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::engine_running, this), gui_context());
+ AudioEngine::instance()->SampleRateChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_sample_rate, this, _1), gui_context());
- if (engine) {
- return 0;
+ AudioEngine::instance()->Halted.connect_same_thread (halt_connection, boost::bind (&ARDOUR_UI::engine_halted, this, _1, false));
+
+ ARDOUR::Port::set_connecting_blocked (ARDOUR_COMMAND_LINE::no_connect_ports);
+
+ /* if there is only one audio/midi backend, and it does not require setup, get our use of it underway
+ * right here (we need to know the client name and potential session ID
+ * to do this, which is why this is here, rather than in, say,
+ * ARDOUR::init().
+ */
+
+ if (!AudioEngine::instance()->setup_required()) {
+ const AudioBackendInfo* backend = AudioEngine::instance()->available_backends().front();
+ AudioEngine::instance()->set_backend (backend->name, ARDOUR_COMMAND_LINE::backend_client_name, ARDOUR_COMMAND_LINE::backend_session_uuid);
+ AudioEngine::instance()->start ();
}
+}
- loading_message (_("Starting audio engine"));
+void
+ARDOUR_UI::engine_stopped ()
+{
+ ENSURE_GUI_THREAD (*this, &ARDOUR_UI::engine_stopped)
+ ActionManager::set_sensitive (ActionManager::engine_sensitive_actions, false);
+ ActionManager::set_sensitive (ActionManager::engine_opposite_sensitive_actions, true);
+}
- try {
- engine = new ARDOUR::AudioEngine (ARDOUR_COMMAND_LINE::jack_client_name, ARDOUR_COMMAND_LINE::jack_session_uuid);
+void
+ARDOUR_UI::engine_running ()
+{
+ if (first_time_engine_run) {
+ post_engine();
+ first_time_engine_run = false;
+ }
+
+ ActionManager::set_sensitive (ActionManager::engine_sensitive_actions, true);
+ ActionManager::set_sensitive (ActionManager::engine_opposite_sensitive_actions, false);
- } catch (...) {
+ Glib::RefPtr<Action> action;
+ const char* action_name = 0;
- return -1;
+ switch (AudioEngine::instance()->samples_per_cycle()) {
+ case 32:
+ action_name = X_("JACKLatency32");
+ break;
+ case 64:
+ action_name = X_("JACKLatency64");
+ break;
+ case 128:
+ action_name = X_("JACKLatency128");
+ break;
+ case 512:
+ action_name = X_("JACKLatency512");
+ break;
+ case 1024:
+ action_name = X_("JACKLatency1024");
+ break;
+ case 2048:
+ action_name = X_("JACKLatency2048");
+ break;
+ case 4096:
+ action_name = X_("JACKLatency4096");
+ break;
+ case 8192:
+ action_name = X_("JACKLatency8192");
+ break;
+ default:
+ /* XXX can we do anything useful ? */
+ break;
}
- engine->Stopped.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::engine_stopped, this), gui_context());
- engine->Running.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::engine_running, this), gui_context());
- engine->SampleRateChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_sample_rate, this, _1), gui_context());
+ if (action_name) {
- engine->Halted.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::engine_halted, this, _1, false));
+ action = ActionManager::get_action (X_("JACK"), action_name);
- ARDOUR::Port::set_connecting_blocked (ARDOUR_COMMAND_LINE::no_connect_ports);
+ if (action) {
+ Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (action);
+ ract->set_active ();
+ }
- post_engine ();
+ update_disk_space ();
+ update_cpu_load ();
+ update_sample_rate (AudioEngine::instance()->sample_rate());
+ update_timecode_format ();
+ }
+}
- return 0;
+void
+ARDOUR_UI::engine_halted (const char* reason, bool free_reason)
+{
+ if (!Gtkmm2ext::UI::instance()->caller_is_ui_thread()) {
+ /* we can't rely on the original string continuing to exist when we are called
+ again in the GUI thread, so make a copy and note that we need to
+ free it later.
+ */
+ char *copy = strdup (reason);
+ Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&ARDOUR_UI::engine_halted, this, copy, true));
+ return;
+ }
+
+ ActionManager::set_sensitive (ActionManager::engine_sensitive_actions, false);
+ ActionManager::set_sensitive (ActionManager::engine_opposite_sensitive_actions, true);
+
+ update_sample_rate (0);
+
+ string msgstr;
+
+ /* if the reason is a non-empty string, it means that the backend was shutdown
+ rather than just Ardour.
+ */
+
+ if (strlen (reason)) {
+ msgstr = string_compose (_("The audio backend (JACK) was shutdown because:\n\n%1"), reason);
+ } else {
+ msgstr = string_compose (_("\
+JACK has either been shutdown or it\n\
+disconnected %1 because %1\n\
+was not fast enough. Try to restart\n\
+JACK, reconnect and save the session."), PROGRAM_NAME);
+ }
+
+ MessageDialog msg (*editor, msgstr);
+ pop_back_splash (msg);
+ msg.set_keep_above (true);
+ msg.run ();
+
+ if (free_reason) {
+ free (const_cast<char*> (reason));
+ }
}
void
ARDOUR_UI::post_engine ()
{
- /* Things to be done once we create the AudioEngine
+ /* Things to be done once (and once ONLY) after we have a backend running in the AudioEngine
*/
ARDOUR::init_post_engine ();
@@ -481,25 +590,9 @@ ARDOUR_UI::post_engine ()
Glib::signal_timeout().connect_seconds (sigc::mem_fun(*this, &ARDOUR_UI::update_wall_clock), 1);
#endif
- update_disk_space ();
- update_cpu_load ();
- update_sample_rate (engine->frame_rate());
- update_timecode_format ();
-
Config->ParameterChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::parameter_changed, this, _1), gui_context());
boost::function<void (string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
Config->map_parameters (pc);
-
- /* now start and maybe save state */
-
- if (do_engine_start () == 0) {
- if (_session && _session_is_new) {
- /* we need to retain initial visual
- settings for a new session
- */
- _session->save_state ("");
- }
- }
}
ARDOUR_UI::~ARDOUR_UI ()
@@ -685,6 +778,7 @@ ARDOUR_UI::startup ()
{
Application* app = Application::instance ();
char *nsm_url;
+
app->ShouldQuit.connect (sigc::mem_fun (*this, &ARDOUR_UI::queue_finish));
app->ShouldLoad.connect (sigc::mem_fun (*this, &ARDOUR_UI::idle_load));
@@ -779,7 +873,7 @@ ARDOUR_UI::check_memory_locking ()
XMLNode* memory_warning_node = Config->instant_xml (X_("no-memory-warning"));
- if (engine->is_realtime() && memory_warning_node == 0) {
+ if (AudioEngine::instance()->is_realtime() && memory_warning_node == 0) {
struct rlimit limits;
int64_t ram;
@@ -919,7 +1013,8 @@ If you still wish to quit, please use the\n\n\
_session = 0;
}
- engine->stop (true);
+ halt_connection.disconnect ();
+ AudioEngine::instance()->stop ();
quit ();
}
@@ -1048,22 +1143,28 @@ ARDOUR_UI::update_sample_rate (framecnt_t)
ENSURE_GUI_THREAD (*this, &ARDOUR_UI::update_sample_rate, ignored)
- if (!engine->connected()) {
+ if (!AudioEngine::instance()->connected()) {
- snprintf (buf, sizeof (buf), "%s", _("disconnected"));
+ snprintf (buf, sizeof (buf), _("Audio: <span foreground=\"red\">none</span>"));
} else {
- framecnt_t rate = engine->frame_rate();
+ framecnt_t rate = AudioEngine::instance()->sample_rate();
- if (fmod (rate, 1000.0) != 0.0) {
- snprintf (buf, sizeof (buf), _("JACK: <span foreground=\"green\">%.1f kHz / %4.1f ms</span>"),
- (float) rate/1000.0f,
- (engine->frames_per_cycle() / (float) rate) * 1000.0f);
+ if (rate == 0) {
+ /* no sample rate available */
+ snprintf (buf, sizeof (buf), _("Audio: <span foreground=\"red\">none</span>"));
} else {
- snprintf (buf, sizeof (buf), _("JACK: <span foreground=\"green\">%" PRId64 " kHz / %4.1f ms</span>"),
- rate/1000,
- (engine->frames_per_cycle() / (float) rate) * 1000.0f);
+
+ if (fmod (rate, 1000.0) != 0.0) {
+ snprintf (buf, sizeof (buf), _("Audio: <span foreground=\"green\">%.1f kHz / %4.1f ms</span>"),
+ (float) rate / 1000.0f,
+ (AudioEngine::instance()->usecs_per_cycle() / 1000.0f));
+ } else {
+ snprintf (buf, sizeof (buf), _("Audio: <span foreground=\"green\">%" PRId64 " kHz / %4.1f ms</span>"),
+ rate/1000,
+ (AudioEngine::instance()->usecs_per_cycle() / 1000.0f));
+ }
}
}
@@ -1133,7 +1234,7 @@ ARDOUR_UI::update_cpu_load ()
should also be changed.
*/
- float const c = engine->get_cpu_load ();
+ float const c = AudioEngine::instance()->get_cpu_load ();
snprintf (buf, sizeof (buf), _("DSP: <span foreground=\"%s\">%5.1f%%</span>"), c >= 90 ? X_("red") : X_("green"), c);
cpu_load_label.set_markup (buf);
}
@@ -1187,6 +1288,11 @@ ARDOUR_UI::update_disk_space()
char buf[64];
framecnt_t fr = _session->frame_rate();
+ if (fr == 0) {
+ /* skip update - no SR available */
+ return;
+ }
+
if (!opt_frames) {
/* Available space is unknown */
snprintf (buf, sizeof (buf), "%s", _("Disk: <span foreground=\"green\">Unknown</span>"));
@@ -1449,20 +1555,16 @@ ARDOUR_UI::open_recent_session ()
bool
ARDOUR_UI::check_audioengine ()
{
- if (engine) {
- if (!engine->connected()) {
- MessageDialog msg (string_compose (
- _("%1 is not connected to JACK\n"
- "You cannot open or close sessions in this condition"),
- PROGRAM_NAME));
- pop_back_splash (msg);
- msg.run ();
- return false;
- }
- return true;
- } else {
+ if (!AudioEngine::instance()->connected()) {
+ MessageDialog msg (string_compose (
+ _("%1 is not connected to any audio backend.\n"
+ "You cannot open or close sessions in this condition"),
+ PROGRAM_NAME));
+ pop_back_splash (msg);
+ msg.run ();
return false;
}
+ return true;
}
void
@@ -1668,10 +1770,17 @@ ARDOUR_UI::transport_goto_wallclock ()
time (&now);
localtime_r (&now, &tmnow);
+
+ int frame_rate = _session->frame_rate();
+
+ if (frame_rate == 0) {
+ /* no frame rate available */
+ return;
+ }
- frames = tmnow.tm_hour * (60 * 60 * _session->frame_rate());
- frames += tmnow.tm_min * (60 * _session->frame_rate());
- frames += tmnow.tm_sec * _session->frame_rate();
+ frames = tmnow.tm_hour * (60 * 60 * frame_rate);
+ frames += tmnow.tm_min * (60 * frame_rate);
+ frames += tmnow.tm_sec * frame_rate;
_session->request_locate (frames, _session->transport_rolling ());
@@ -2032,127 +2141,6 @@ ARDOUR_UI::map_transport_state ()
}
void
-ARDOUR_UI::engine_stopped ()
-{
- ENSURE_GUI_THREAD (*this, &ARDOUR_UI::engine_stopped)
- ActionManager::set_sensitive (ActionManager::jack_sensitive_actions, false);
- ActionManager::set_sensitive (ActionManager::jack_opposite_sensitive_actions, true);
-}
-
-void
-ARDOUR_UI::engine_running ()
-{
- ENSURE_GUI_THREAD (*this, &ARDOUR_UI::engine_running)
- ActionManager::set_sensitive (ActionManager::jack_sensitive_actions, true);
- ActionManager::set_sensitive (ActionManager::jack_opposite_sensitive_actions, false);
-
- Glib::RefPtr<Action> action;
- const char* action_name = 0;
-
- switch (engine->frames_per_cycle()) {
- case 32:
- action_name = X_("JACKLatency32");
- break;
- case 64:
- action_name = X_("JACKLatency64");
- break;
- case 128:
- action_name = X_("JACKLatency128");
- break;
- case 512:
- action_name = X_("JACKLatency512");
- break;
- case 1024:
- action_name = X_("JACKLatency1024");
- break;
- case 2048:
- action_name = X_("JACKLatency2048");
- break;
- case 4096:
- action_name = X_("JACKLatency4096");
- break;
- case 8192:
- action_name = X_("JACKLatency8192");
- break;
- default:
- /* XXX can we do anything useful ? */
- break;
- }
-
- if (action_name) {
-
- action = ActionManager::get_action (X_("JACK"), action_name);
-
- if (action) {
- Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (action);
- ract->set_active ();
- }
- }
-}
-
-void
-ARDOUR_UI::engine_halted (const char* reason, bool free_reason)
-{
- if (!Gtkmm2ext::UI::instance()->caller_is_ui_thread()) {
- /* we can't rely on the original string continuing to exist when we are called
- again in the GUI thread, so make a copy and note that we need to
- free it later.
- */
- char *copy = strdup (reason);
- Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&ARDOUR_UI::engine_halted, this, copy, true));
- return;
- }
-
- ActionManager::set_sensitive (ActionManager::jack_sensitive_actions, false);
- ActionManager::set_sensitive (ActionManager::jack_opposite_sensitive_actions, true);
-
- update_sample_rate (0);
-
- string msgstr;
-
- /* if the reason is a non-empty string, it means that the backend was shutdown
- rather than just Ardour.
- */
-
- if (strlen (reason)) {
- msgstr = string_compose (_("The audio backend (JACK) was shutdown because:\n\n%1"), reason);
- } else {
- msgstr = string_compose (_("\
-JACK has either been shutdown or it\n\
-disconnected %1 because %1\n\
-was not fast enough. Try to restart\n\
-JACK, reconnect and save the session."), PROGRAM_NAME);
- }
-
- MessageDialog msg (*editor, msgstr);
- pop_back_splash (msg);
- msg.set_keep_above (true);
- msg.run ();
-
- if (free_reason) {
- free (const_cast<char*> (reason));
- }
-}
-
-int32_t
-ARDOUR_UI::do_engine_start ()
-{
- try {
- engine->start();
- }
-
- catch (...) {
- engine->stop ();
- error << _("Unable to start the session running")
- << endmsg;
- unload_session ();
- return -2;
- }
-
- return 0;
-}
-
-void
ARDOUR_UI::update_clocks ()
{
if (!editor || !editor->dragging_playhead()) {
@@ -2702,10 +2690,6 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
}
}
- if (create_engine ()) {
- break;
- }
-
if (Glib::file_test (session_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
if (likely_new && !nsm) {
@@ -2801,25 +2785,23 @@ ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name,
int unload_status;
int retval = -1;
- session_loaded = false;
-
- if (!check_audioengine()) {
- return -2;
+ if (_session) {
+ unload_status = unload_session ();
+
+ if (unload_status < 0) {
+ goto out;
+ } else if (unload_status > 0) {
+ retval = 0;
+ goto out;
+ }
}
- unload_status = unload_session ();
-
- if (unload_status < 0) {
- goto out;
- } else if (unload_status > 0) {
- retval = 0;
- goto out;
- }
+ session_loaded = false;
loading_message (string_compose (_("Please wait while %1 loads your session"), PROGRAM_NAME));
try {
- new_session = new Session (*engine, path, snap_name, 0, mix_template);
+ new_session = new Session (*AudioEngine::instance(), path, snap_name, 0, mix_template);
}
/* this one is special */
@@ -2920,12 +2902,7 @@ ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name,
Session *new_session;
int x;
- if (!check_audioengine()) {
- return -1;
- }
-
session_loaded = false;
-
x = unload_session ();
if (x < 0) {
@@ -2937,7 +2914,7 @@ ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name,
_session_is_new = true;
try {
- new_session = new Session (*engine, path, snap_name, &bus_profile);
+ new_session = new Session (*AudioEngine::instance(), path, snap_name, &bus_profile);
}
catch (...) {
@@ -3832,31 +3809,33 @@ audio may be played at the wrong sample rate.\n"), desired, PROGRAM_NAME, actual
return 1;
}
-
void
-ARDOUR_UI::disconnect_from_jack ()
+ARDOUR_UI::disconnect_from_engine ()
{
- if (engine) {
- if (engine->disconnect_from_jack ()) {
- MessageDialog msg (*editor, _("Could not disconnect from JACK"));
- msg.run ();
- }
-
- update_sample_rate (0);
+ /* drop connection to AudioEngine::Halted so that we don't act
+ * as if the engine unexpectedly shut down
+ */
+ halt_connection.disconnect ();
+
+ if (AudioEngine::instance()->stop ()) {
+ MessageDialog msg (*editor, _("Could not disconnect from JACK"));
+ msg.run ();
+ } else {
+ AudioEngine::instance()->Halted.connect_same_thread (halt_connection, boost::bind (&ARDOUR_UI::engine_halted, this, _1, false));
}
+
+ update_sample_rate (0);
}
void
-ARDOUR_UI::reconnect_to_jack ()
+ARDOUR_UI::reconnect_to_engine ()
{
- if (engine) {
- if (engine->reconnect_to_jack ()) {
- MessageDialog msg (*editor, _("Could not reconnect to JACK"));
- msg.run ();
- }
-
- update_sample_rate (0);
+ if (AudioEngine::instance()->start ()) {
+ MessageDialog msg (*editor, _("Could not reconnect to JACK"));
+ msg.run ();
}
+
+ update_sample_rate (0);
}
void
@@ -4163,3 +4142,49 @@ ARDOUR_UI::reset_route_peak_display (Route* route)
reset_peak_display ();
}
}
+
+void
+ARDOUR_UI::toggle_audio_midi_setup ()
+{
+ Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("toggle-audio-midi-setup"));
+ if (!act) {
+ return;
+ }
+
+ Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
+
+ if (tact->get_active()) {
+ launch_audio_midi_setup ();
+ } else {
+ _audio_midi_setup->hide ();
+ }
+}
+
+void
+ARDOUR_UI::launch_audio_midi_setup ()
+{
+ if (!_audio_midi_setup) {
+ _audio_midi_setup = new EngineControl ();
+ }
+
+ _audio_midi_setup->present ();
+}
+
+int
+ARDOUR_UI::do_audio_midi_setup (uint32_t desired_sample_rate)
+{
+ launch_audio_midi_setup ();
+
+ _audio_midi_setup->set_desired_sample_rate (desired_sample_rate);
+
+ int r = _audio_midi_setup->run ();
+
+ switch (r) {
+ case Gtk::RESPONSE_OK:
+ return 0;
+ case Gtk::RESPONSE_APPLY:
+ return 0;
+ default:
+ return -1;
+ }
+}
diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h
index 15413b5ac9..64309c7e38 100644
--- a/gtk2_ardour/ardour_ui.h
+++ b/gtk2_ardour/ardour_ui.h
@@ -90,6 +90,7 @@ class BigClockWindow;
class BundleManager;
class ButtonJoiner;
class ConnectionEditor;
+class EngineControl;
class KeyEditor;
class LocationUIWindow;
class MainClock;
@@ -264,7 +265,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
session_add_midi_route (false);
}*/
- int create_engine ();
+ void attach_to_engine ();
void post_engine ();
gint exit_on_main_window_close (GdkEventAny *);
@@ -302,19 +303,18 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
void reenable_hide_loop_punch_ruler_if_appropriate ();
void toggle_auto_return ();
void toggle_click ();
-
+ void toggle_audio_midi_setup ();
void toggle_session_auto_loop ();
-
void toggle_rc_options_window ();
void toggle_session_options_window ();
private:
ArdourStartup* _startup;
- ARDOUR::AudioEngine *engine;
Gtk::Tooltips _tooltips;
NSM_Client *nsm;
bool _was_dirty;
bool _mixer_on_top;
+ bool first_time_engine_run;
void goto_editor_window ();
void goto_mixer_window ();
@@ -667,9 +667,9 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
int pending_state_dialog ();
int sr_mismatch_dialog (ARDOUR::framecnt_t, ARDOUR::framecnt_t);
- void disconnect_from_jack ();
- void reconnect_to_jack ();
- void set_jack_buffer_size (ARDOUR::pframes_t);
+ void disconnect_from_engine ();
+ void reconnect_to_engine ();
+ void set_engine_buffer_size (ARDOUR::pframes_t);
Gtk::MenuItem* jack_disconnect_item;
Gtk::MenuItem* jack_reconnect_item;
@@ -710,6 +710,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
void loading_message (const std::string& msg);
PBD::ScopedConnectionList forever_connections;
+ PBD::ScopedConnection halt_connection;
void step_edit_status_change (bool);
@@ -746,6 +747,10 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
std::string _announce_string;
void check_announcements ();
+
+ EngineControl* _audio_midi_setup;
+ void launch_audio_midi_setup ();
+ int do_audio_midi_setup (uint32_t);
};
#endif /* __ardour_gui_h__ */
diff --git a/gtk2_ardour/ardour_ui_dialogs.cc b/gtk2_ardour/ardour_ui_dialogs.cc
index bca762569f..a0d61afdff 100644
--- a/gtk2_ardour/ardour_ui_dialogs.cc
+++ b/gtk2_ardour/ardour_ui_dialogs.cc
@@ -165,10 +165,6 @@ ARDOUR_UI::set_session (Session *s)
_session->locations()->removed.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::handle_locations_change, this, _1), gui_context());
_session->config.ParameterChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::session_parameter_changed, this, _1), gui_context ());
-#ifdef HAVE_JACK_SESSION
- engine->JackSessionEvent.connect (*_session, MISSING_INVALIDATOR, boost::bind (&Session::jack_session_event, _session, _1), gui_context());
-#endif
-
/* Clocks are on by default after we are connected to a session, so show that here.
*/
@@ -283,7 +279,10 @@ ARDOUR_UI::unload_session (bool hide_stuff)
rec_button.set_sensitive (false);
WM::Manager::instance().set_session ((ARDOUR::Session*) 0);
- ARDOUR_UI::instance()->video_timeline->close_session();
+
+ if (ARDOUR_UI::instance()->video_timeline) {
+ ARDOUR_UI::instance()->video_timeline->close_session();
+ }
stop_blinking ();
stop_clocking ();
diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc
index 6c050578ec..53ea7c02de 100644
--- a/gtk2_ardour/ardour_ui_ed.cc
+++ b/gtk2_ardour/ardour_ui_ed.cc
@@ -183,38 +183,38 @@ ARDOUR_UI::install_actions ()
ActionManager::write_sensitive_actions.push_back (act);
ActionManager::session_sensitive_actions.push_back (act);
- /* JACK actions for controlling ... JACK */
-
- Glib::RefPtr<ActionGroup> jack_actions = ActionGroup::create (X_("JACK"));
- ActionManager::register_action (jack_actions, X_("JACK"), _("JACK"));
- ActionManager::register_action (jack_actions, X_("Latency"), _("Latency"));
-
- act = ActionManager::register_action (jack_actions, X_("JACKReconnect"), _("Reconnect"), sigc::mem_fun (*(ARDOUR_UI::instance()), &ARDOUR_UI::reconnect_to_jack));
- ActionManager::jack_opposite_sensitive_actions.push_back (act);
-
- act = ActionManager::register_action (jack_actions, X_("JACKDisconnect"), _("Disconnect"), sigc::mem_fun (*(ARDOUR_UI::instance()), &ARDOUR_UI::disconnect_from_jack));
- ActionManager::jack_sensitive_actions.push_back (act);
-
- RadioAction::Group jack_latency_group;
-
- act = ActionManager::register_radio_action (jack_actions, jack_latency_group, X_("JACKLatency32"), X_("32"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 32));
- ActionManager::jack_sensitive_actions.push_back (act);
- act = ActionManager::register_radio_action (jack_actions, jack_latency_group, X_("JACKLatency64"), X_("64"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 64));
- ActionManager::jack_sensitive_actions.push_back (act);
- act = ActionManager::register_radio_action (jack_actions, jack_latency_group, X_("JACKLatency128"), X_("128"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 128));
- ActionManager::jack_sensitive_actions.push_back (act);
- act = ActionManager::register_radio_action (jack_actions, jack_latency_group, X_("JACKLatency256"), X_("256"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 256));
- ActionManager::jack_sensitive_actions.push_back (act);
- act = ActionManager::register_radio_action (jack_actions, jack_latency_group, X_("JACKLatency512"), X_("512"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 512));
- ActionManager::jack_sensitive_actions.push_back (act);
- act = ActionManager::register_radio_action (jack_actions, jack_latency_group, X_("JACKLatency1024"), X_("1024"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 1024));
- ActionManager::jack_sensitive_actions.push_back (act);
- act = ActionManager::register_radio_action (jack_actions, jack_latency_group, X_("JACKLatency2048"), X_("2048"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 2048));
- ActionManager::jack_sensitive_actions.push_back (act);
- act = ActionManager::register_radio_action (jack_actions, jack_latency_group, X_("JACKLatency4096"), X_("4096"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 4096));
- ActionManager::jack_sensitive_actions.push_back (act);
- act = ActionManager::register_radio_action (jack_actions, jack_latency_group, X_("JACKLatency8192"), X_("8192"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 8192));
- ActionManager::jack_sensitive_actions.push_back (act);
+ /* AudioEngine actions */
+
+ Glib::RefPtr<ActionGroup> engine_actions = ActionGroup::create (X_("Audio"));
+ ActionManager::register_action (engine_actions, X_("JACK"), _("JACK"));
+ ActionManager::register_action (engine_actions, X_("Latency"), _("Latency"));
+
+ act = ActionManager::register_action (engine_actions, X_("EngineReconnect"), _("Reconnect"), sigc::mem_fun (*(ARDOUR_UI::instance()), &ARDOUR_UI::reconnect_to_engine));
+ ActionManager::engine_opposite_sensitive_actions.push_back (act);
+
+ act = ActionManager::register_action (engine_actions, X_("EngineDisconnect"), _("Disconnect"), sigc::mem_fun (*(ARDOUR_UI::instance()), &ARDOUR_UI::disconnect_from_engine));
+ ActionManager::engine_sensitive_actions.push_back (act);
+
+ RadioAction::Group latency_group;
+
+ act = ActionManager::register_radio_action (engine_actions, latency_group, X_("EngineLatency32"), X_("32"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_engine_buffer_size), (pframes_t) 32));
+ ActionManager::engine_sensitive_actions.push_back (act);
+ act = ActionManager::register_radio_action (engine_actions, latency_group, X_("EngineLatency64"), X_("64"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_engine_buffer_size), (pframes_t) 64));
+ ActionManager::engine_sensitive_actions.push_back (act);
+ act = ActionManager::register_radio_action (engine_actions, latency_group, X_("EngineLatency128"), X_("128"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_engine_buffer_size), (pframes_t) 128));
+ ActionManager::engine_sensitive_actions.push_back (act);
+ act = ActionManager::register_radio_action (engine_actions, latency_group, X_("EngineLatency256"), X_("256"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_engine_buffer_size), (pframes_t) 256));
+ ActionManager::engine_sensitive_actions.push_back (act);
+ act = ActionManager::register_radio_action (engine_actions, latency_group, X_("EngineLatency512"), X_("512"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_engine_buffer_size), (pframes_t) 512));
+ ActionManager::engine_sensitive_actions.push_back (act);
+ act = ActionManager::register_radio_action (engine_actions, latency_group, X_("EngineLatency1024"), X_("1024"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_engine_buffer_size), (pframes_t) 1024));
+ ActionManager::engine_sensitive_actions.push_back (act);
+ act = ActionManager::register_radio_action (engine_actions, latency_group, X_("EngineLatency2048"), X_("2048"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_engine_buffer_size), (pframes_t) 2048));
+ ActionManager::engine_sensitive_actions.push_back (act);
+ act = ActionManager::register_radio_action (engine_actions, latency_group, X_("EngineLatency4096"), X_("4096"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_engine_buffer_size), (pframes_t) 4096));
+ ActionManager::engine_sensitive_actions.push_back (act);
+ act = ActionManager::register_radio_action (engine_actions, latency_group, X_("EngineLatency8192"), X_("8192"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_engine_buffer_size), (pframes_t) 8192));
+ ActionManager::engine_sensitive_actions.push_back (act);
/* these actions are intended to be shared across all windows */
@@ -230,6 +230,7 @@ ARDOUR_UI::install_actions ()
ActionManager::register_toggle_action (common_actions, X_("toggle-mixer"), S_("Window|Mixer"), sigc::mem_fun(*this, &ARDOUR_UI::toggle_mixer_window));
ActionManager::register_action (common_actions, X_("toggle-editor-mixer"), _("Toggle Editor+Mixer"), sigc::mem_fun(*this, &ARDOUR_UI::toggle_editor_mixer));
ActionManager::register_toggle_action (common_actions, X_("toggle-meterbridge"), S_("Window|Meterbridge"), sigc::mem_fun(*this, &ARDOUR_UI::toggle_meterbridge));
+ ActionManager::register_toggle_action (common_actions, X_("toggle-audio-midi-setup"), S_("Window|Audio/MIDI Setup"), sigc::mem_fun(*this, &ARDOUR_UI::toggle_audio_midi_setup));
act = ActionManager::register_action (common_actions, X_("NewMIDITracer"), _("MIDI Tracer"), sigc::mem_fun(*this, &ARDOUR_UI::new_midi_tracer_window));
ActionManager::session_sensitive_actions.push_back (act);
@@ -421,7 +422,7 @@ ARDOUR_UI::install_actions ()
ActionManager::add_action_group (shuttle_actions);
ActionManager::add_action_group (option_actions);
- ActionManager::add_action_group (jack_actions);
+ ActionManager::add_action_group (engine_actions);
ActionManager::add_action_group (transport_actions);
ActionManager::add_action_group (main_actions);
ActionManager::add_action_group (main_menu_actions);
@@ -430,38 +431,38 @@ ARDOUR_UI::install_actions ()
}
void
-ARDOUR_UI::set_jack_buffer_size (pframes_t nframes)
+ARDOUR_UI::set_engine_buffer_size (pframes_t nframes)
{
Glib::RefPtr<Action> action;
const char* action_name = 0;
switch (nframes) {
case 32:
- action_name = X_("JACKLatency32");
+ action_name = X_("EngineLatency32");
break;
case 64:
- action_name = X_("JACKLatency64");
+ action_name = X_("EngineLatency64");
break;
case 128:
- action_name = X_("JACKLatency128");
+ action_name = X_("EngineLatency128");
break;
case 256:
- action_name = X_("JACKLatency256");
+ action_name = X_("EngineLatency256");
break;
case 512:
- action_name = X_("JACKLatency512");
+ action_name = X_("EngineLatency512");
break;
case 1024:
- action_name = X_("JACKLatency1024");
+ action_name = X_("EngineLatency1024");
break;
case 2048:
- action_name = X_("JACKLatency2048");
+ action_name = X_("EngineLatency2048");
break;
case 4096:
- action_name = X_("JACKLatency4096");
+ action_name = X_("EngineLatency4096");
break;
case 8192:
- action_name = X_("JACKLatency8192");
+ action_name = X_("EngineLatency8192");
break;
default:
/* XXX can we do anything useful ? */
@@ -476,7 +477,7 @@ ARDOUR_UI::set_jack_buffer_size (pframes_t nframes)
Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (action);
if (ract && ract->get_active()) {
- engine->request_buffer_size (nframes);
+ AudioEngine::instance()->request_buffer_size (nframes);
update_sample_rate (0);
}
}
@@ -647,11 +648,10 @@ ARDOUR_UI::save_ardour_state ()
window_node->add_child_nocopy (*tearoff_node);
Config->add_extra_xml (*window_node);
+ Config->add_extra_xml (_audio_midi_setup->get_state());
- if (_startup && _startup->engine_control() && _startup->engine_control()->was_used()) {
- Config->add_extra_xml (_startup->engine_control()->get_state());
- }
Config->save_state();
+
if (ui_config->dirty()) {
ui_config->save_state ();
}
diff --git a/gtk2_ardour/arval b/gtk2_ardour/arval
index 6503bc1ede..2209dcb110 100755
--- a/gtk2_ardour/arval
+++ b/gtk2_ardour/arval
@@ -10,5 +10,6 @@ LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
export ARDOUR_RUNNING_UNDER_VALGRIND=TRUE
exec valgrind --tool=memcheck \
$VALGRIND_OPTIONS \
+ --track-origins=yes \
--suppressions=`dirname "$0"`/../tools/valgrind.supp \
$TOP/$EXECUTABLE --novst "$@"
diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc
index 300e317a38..a0418892e7 100644
--- a/gtk2_ardour/editor.cc
+++ b/gtk2_ardour/editor.cc
@@ -1258,7 +1258,8 @@ Editor::set_session (Session *t)
/* These signals can all be emitted by a non-GUI thread. Therefore the
handlers for them must not attempt to directly interact with the GUI,
- but use Gtkmm2ext::UI::instance()->call_slot();
+ but use PBD::Signal<T>::connect() which accepts an event loop
+ ("context") where the handler will be asked to run.
*/
_session->StepEditStatusChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::step_edit_status_change, this, _1), gui_context());
diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc
index b083e851c5..9c65892ebd 100644
--- a/gtk2_ardour/editor_drag.cc
+++ b/gtk2_ardour/editor_drag.cc
@@ -2371,9 +2371,14 @@ CursorDrag::fake_locate (framepos_t t)
Session* s = _editor->session ();
if (s->timecode_transmission_suspended ()) {
framepos_t const f = _editor->playhead_cursor->current_frame;
+ /* This is asynchronous so it will be sent "now"
+ */
s->send_mmc_locate (f);
- s->send_full_time_code (f);
- s->send_song_position_pointer (f);
+ /* These are synchronous and will be sent during the next
+ process cycle
+ */
+ s->queue_full_time_code ();
+ s->queue_song_position_pointer ();
}
show_verbose_cursor_time (t);
diff --git a/gtk2_ardour/engine_dialog.cc b/gtk2_ardour/engine_dialog.cc
index 05cd9a661e..873498f6ec 100644
--- a/gtk2_ardour/engine_dialog.cc
+++ b/gtk2_ardour/engine_dialog.cc
@@ -17,6 +17,7 @@
*/
+#include <exception>
#include <vector>
#include <cmath>
#include <fstream>
@@ -24,37 +25,26 @@
#include <boost/scoped_ptr.hpp>
-#include <glibmm.h>
#include <gtkmm/messagedialog.h>
-#include "pbd/epa.h"
+#include "pbd/error.h"
#include "pbd/xml++.h"
-#ifdef __APPLE__
-#include <CoreAudio/CoreAudio.h>
-#include <CoreFoundation/CFString.h>
-#include <sys/param.h>
-#include <mach-o/dyld.h>
-#elif !defined(__FreeBSD__)
-#include <alsa/asoundlib.h>
-#endif
-
-#include <jack/jack.h>
-
#include <gtkmm/stock.h>
+#include <gtkmm/notebook.h>
#include <gtkmm2ext/utils.h>
+#include "ardour/audio_backend.h"
+#include "ardour/audioengine.h"
+#include "ardour/mtdm.h"
#include "ardour/rc_configuration.h"
+#include "ardour/types.h"
#include "pbd/convert.h"
#include "pbd/error.h"
-#include "pbd/pathscanner.h"
-
-#ifdef __APPLE
-#include <CFBundle.h>
-#endif
#include "engine_dialog.h"
+#include "gui_thread.h"
#include "i18n.h"
using namespace std;
@@ -64,322 +54,236 @@ using namespace PBD;
using namespace Glib;
EngineControl::EngineControl ()
- : periods_adjustment (2, 2, 16, 1, 2),
- periods_spinner (periods_adjustment),
- ports_adjustment (128, 8, 1024, 1, 16),
- ports_spinner (ports_adjustment),
- input_latency_adjustment (0, 0, 99999, 1),
- input_latency (input_latency_adjustment),
- output_latency_adjustment (0, 0, 99999, 1),
- output_latency (output_latency_adjustment),
- realtime_button (_("Realtime")),
- no_memory_lock_button (_("Do not lock memory")),
- unlock_memory_button (_("Unlock memory")),
- soft_mode_button (_("No zombies")),
- monitor_button (_("Provide monitor ports")),
- force16bit_button (_("Force 16 bit")),
- hw_monitor_button (_("H/W monitoring")),
- hw_meter_button (_("H/W metering")),
- verbose_output_button (_("Verbose output")),
- start_button (_("Start")),
- stop_button (_("Stop")),
-#ifdef __APPLE___
- basic_packer (5, 2),
- options_packer (4, 2),
- device_packer (4, 2)
-#else
- basic_packer (8, 2),
- options_packer (14, 2),
- device_packer (6, 2)
-#endif
+ : ArdourDialog (_("Audio/MIDI Setup"))
+ , input_latency_adjustment (0, 0, 99999, 1)
+ , input_latency (input_latency_adjustment)
+ , output_latency_adjustment (0, 0, 99999, 1)
+ , output_latency (output_latency_adjustment)
+ , input_channels_adjustment (0, 0, 256, 1)
+ , input_channels (input_channels_adjustment)
+ , output_channels_adjustment (0, 0, 256, 1)
+ , output_channels (output_channels_adjustment)
+ , ports_adjustment (128, 8, 1024, 1, 16)
+ , ports_spinner (ports_adjustment)
+ , control_app_button (_("Launch Control App"))
+ , lm_measure_button (_("Measure latency"))
+ , lm_use_button (_("Use results"))
+ , lm_table (5, 2)
+ , basic_packer (9, 3)
+ , ignore_changes (0)
+ , _desired_sample_rate (0)
+{
+ build_notebook ();
+
+ get_vbox()->set_border_width (12);
+ get_vbox()->pack_start (notebook);
+
+ control_app_button.signal_clicked().connect (mem_fun (*this, &EngineControl::control_app_button_clicked));
+ manage_control_app_sensitivity ();
+
+ add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
+ add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY);
+
+ /* Pick up any existing audio setup configuration, if appropriate */
+
+ XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioMIDISetup");
+
+ /* push a change as if we altered the backend */
+ backend_changed ();
+
+ if (audio_setup) {
+ set_state (*audio_setup);
+ }
+
+ ARDOUR::AudioEngine::instance()->Stopped.connect (*this, MISSING_INVALIDATOR, boost::bind (&EngineControl::disable_latency_tab, this), gui_context());
+
+ if (!ARDOUR::AudioEngine::instance()->connected()) {
+ ARDOUR::AudioEngine::instance()->Running.connect (*this, MISSING_INVALIDATOR, boost::bind (&EngineControl::enable_latency_tab, this), gui_context());
+ disable_latency_tab ();
+ } else {
+ enable_latency_tab ();
+ }
+}
+
+void
+EngineControl::on_response (int response_id)
+{
+ ArdourDialog::on_response (response_id);
+
+ switch (response_id) {
+ case RESPONSE_APPLY:
+ push_state_to_backend (true);
+ break;
+ case RESPONSE_OK:
+ push_state_to_backend (true);
+ hide ();
+ break;
+ default:
+ hide ();
+ }
+}
+
+void
+EngineControl::build_notebook ()
{
using namespace Notebook_Helpers;
Label* label;
vector<string> strings;
int row = 0;
- _used = false;
-
- strings.push_back (_("8000Hz"));
- strings.push_back (_("22050Hz"));
- strings.push_back (_("44100Hz"));
- strings.push_back (_("48000Hz"));
- strings.push_back (_("88200Hz"));
- strings.push_back (_("96000Hz"));
- strings.push_back (_("192000Hz"));
- set_popdown_strings (sample_rate_combo, strings);
- sample_rate_combo.set_active_text ("48000Hz");
-
- strings.clear ();
- strings.push_back ("32");
- strings.push_back ("64");
- strings.push_back ("128");
- strings.push_back ("256");
- strings.push_back ("512");
- strings.push_back ("1024");
- strings.push_back ("2048");
- strings.push_back ("4096");
- strings.push_back ("8192");
- set_popdown_strings (period_size_combo, strings);
- period_size_combo.set_active_text ("1024");
-
- strings.clear ();
- strings.push_back (_("None"));
- strings.push_back (_("Triangular"));
- strings.push_back (_("Rectangular"));
- strings.push_back (_("Shaped"));
- set_popdown_strings (dither_mode_combo, strings);
- dither_mode_combo.set_active_text (_("None"));
-
- /* basic parameters */
+ vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
+ for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
+ strings.push_back ((*b)->name);
+ }
+
+ set_popdown_strings (backend_combo, strings);
+ backend_combo.set_active_text (strings.front());
basic_packer.set_spacings (6);
+ basic_packer.set_border_width (12);
+ basic_packer.set_homogeneous (true);
- strings.clear ();
-#ifdef __APPLE__
- strings.push_back (X_("CoreAudio"));
-#else
-#ifndef __FreeBSD__
- strings.push_back (X_("ALSA"));
-#endif
- strings.push_back (X_("OSS"));
- strings.push_back (X_("FreeBoB"));
- strings.push_back (X_("FFADO"));
-#endif
- strings.push_back (X_("NetJACK"));
- strings.push_back (X_("Dummy"));
- set_popdown_strings (driver_combo, strings);
- driver_combo.set_active_text (strings.front());
+ row = 0;
- driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
- driver_changed ();
-
- strings.clear ();
- strings.push_back (_("Playback/recording on 1 device"));
- strings.push_back (_("Playback/recording on 2 devices"));
- strings.push_back (_("Playback only"));
- strings.push_back (_("Recording only"));
- set_popdown_strings (audio_mode_combo, strings);
- audio_mode_combo.set_active_text (strings.front());
-
- audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed));
- audio_mode_changed ();
-
- strings.clear ();
- strings.push_back (_("None"));
-#ifdef __APPLE__
- strings.push_back (_("coremidi"));
-#else
- strings.push_back (_("seq"));
- strings.push_back (_("raw"));
-#endif
- set_popdown_strings (midi_driver_combo, strings);
- midi_driver_combo.set_active_text (strings.front ());
+ AttachOptions xopt = AttachOptions (FILL|EXPAND);
- row = 0;
+ label = manage (left_aligned_label (_("Audio System:")));
+ basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+ basic_packer.attach (backend_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
+ row++;
label = manage (left_aligned_label (_("Driver:")));
- basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
+ basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+ basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
row++;
- label = manage (left_aligned_label (_("Audio Interface:")));
- basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
+ label = manage (left_aligned_label (_("Device:")));
+ basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+ basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
row++;
label = manage (left_aligned_label (_("Sample rate:")));
- basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
+ basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+ basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
row++;
- label = manage (left_aligned_label (_("Buffer size:")));
- basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- row++;
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
- label = manage (left_aligned_label (_("Number of buffers:")));
- basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- periods_spinner.set_value (2);
+ label = manage (left_aligned_label (_("Buffer size:")));
+ basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+ basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
+ buffer_size_duration_label.set_alignment (0.0); /* left-align */
+ basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
row++;
-#endif
- label = manage (left_aligned_label (_("Approximate latency:")));
- basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- row++;
- sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
- periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
- period_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
- redisplay_latency();
- row++;
- /* no audio mode with CoreAudio, its duplex or nuthin' */
+ label = manage (left_aligned_label (_("Input Channels:")));
+ basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
+ basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
+ ++row;
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
- label = manage (left_aligned_label (_("Audio mode:")));
- basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- row++;
-#endif
- interface_combo.set_size_request (250, -1);
- input_device_combo.set_size_request (250, -1);
- output_device_combo.set_size_request (250, -1);
+ label = manage (left_aligned_label (_("Output Channels:")));
+ basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
+ basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
+ ++row;
- /*
- if (engine_running()) {
- start_button.set_sensitive (false);
- } else {
- stop_button.set_sensitive (false);
- }
+ label = manage (left_aligned_label (_("Hardware input latency:")));
+ basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
+ basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
+ label = manage (left_aligned_label (_("samples")));
+ basic_packer.attach (*label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
+ ++row;
- start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
- stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
- */
+ label = manage (left_aligned_label (_("Hardware output latency:")));
+ basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
+ basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
+ label = manage (left_aligned_label (_("samples")));
+ basic_packer.attach (*label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
+ ++row;
- button_box.pack_start (start_button, false, false);
- button_box.pack_start (stop_button, false, false);
+ basic_hbox.pack_start (basic_packer, false, false);
+ basic_vbox.pack_start (basic_hbox, false, false);
- // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
+ Gtk::HBox* hpacker = manage (new HBox);
+ hpacker->set_border_width (12);
+ hpacker->pack_start (control_app_button, false, false);
+ hpacker->show ();
+ control_app_button.show();
+ basic_vbox.pack_start (*hpacker);
- /* options */
+ midi_packer.set_border_width (12);
- options_packer.set_spacings (6);
+ /* latency measurement tab */
+
+ lm_title.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Latency Measurement Tool")));
+
row = 0;
+ lm_table.set_row_spacings (12);
- options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- ++row;
+ lm_table.attach (lm_title, 0, 2, row, row+1, xopt, (AttachOptions) 0);
+ row++;
- realtime_button.set_active (true);
+ lm_preamble.set_width_chars (60);
+ lm_preamble.set_line_wrap (true);
+ lm_preamble.set_markup (_("1. <span weight=\"bold\">Turn down the volume on your hardware to a very low level.</span>\n\n\
+2. Connect the two channels that you select below using either a cable or (less ideally) a speaker \
+and microphone.\n\n\
+3. Once the channels are connected, click the \"Measure latency\" button.\n\n\
+4. When satisfied with the results, click the \"Use results\" button."));
-#if PROVIDE_TOO_MANY_OPTIONS
+ lm_table.attach (lm_preamble, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
+ row++;
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
- options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- ++row;
- options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- ++row;
- options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- ++row;
- options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- ++row;
- options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- ++row;
- options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- ++row;
- options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- ++row;
- options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- ++row;
-#else
- options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- ++row;
-#endif
-
- strings.clear ();
- strings.push_back (_("Ignore"));
- strings.push_back ("500 msec");
- strings.push_back ("1 sec");
- strings.push_back ("2 sec");
- strings.push_back ("10 sec");
- set_popdown_strings (timeout_combo, strings);
- timeout_combo.set_active_text (strings.front ());
-
- label = manage (new Label (_("Client timeout")));
- label->set_alignment (1.0, 0.5);
- options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
- options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
+ label = manage (new Label (_("Output channel")));
+ lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
+ lm_table.attach (lm_output_channel_combo, 1, 2, row, row+1, xopt, (AttachOptions) 0);
++row;
-#endif /* PROVIDE_TOO_MANY_OPTIONS */
- label = manage (left_aligned_label (_("Number of ports:")));
- options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
- options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
+ label = manage (new Label (_("Input channel")));
+ lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
+ lm_table.attach (lm_input_channel_combo, 1, 2, row, row+1, xopt, (AttachOptions) 0);
++row;
- label = manage (left_aligned_label (_("MIDI driver:")));
- options_packer.attach (midi_driver_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
- options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- ++row;
+ xopt = AttachOptions(0);
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
- label = manage (left_aligned_label (_("Dither:")));
- options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
- options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
+ lm_measure_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::latency_button_toggled));
+ lm_use_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::use_latency_button_clicked));
+ lm_use_button.set_sensitive (false);
+
+ lm_table.attach (lm_measure_button, 0, 2, row, row+1, xopt, (AttachOptions) 0);
++row;
-#endif
-
- find_jack_servers (server_strings);
-
- if (server_strings.empty()) {
- fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
- /*NOTREACHED*/
- }
-
- set_popdown_strings (serverpath_combo, server_strings);
- serverpath_combo.set_active_text (server_strings.front());
-
- if (server_strings.size() > 1) {
- label = manage (left_aligned_label (_("Server:")));
- options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
- ++row;
- }
-
- /* device settings */
-
- device_packer.set_spacings (6);
- row = 0;
-
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
- label = manage (left_aligned_label (_("Input device:")));
- device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
- device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
+ lm_table.attach (lm_results, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
++row;
- label = manage (left_aligned_label (_("Output device:")));
- device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
- device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
- ++row;
-#endif
- label = manage (left_aligned_label (_("Hardware input latency:")));
- device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
- device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
- label = manage (left_aligned_label (_("samples")));
- device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
- ++row;
- label = manage (left_aligned_label (_("Hardware output latency:")));
- device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
- device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
- label = manage (left_aligned_label (_("samples")));
- device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
+ lm_table.attach (lm_use_button, 0, 2, row, row+1, xopt, (AttachOptions) 0);
++row;
- basic_hbox.pack_start (basic_packer, false, false);
- options_hbox.pack_start (options_packer, false, false);
+ lm_results.set_markup ("<i>No measurement results yet</i>");
- device_packer.set_border_width (12);
- options_packer.set_border_width (12);
- basic_packer.set_border_width (12);
+ lm_vbox.pack_start (lm_table, false, false);
- notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
- notebook.pages().push_back (TabElem (options_hbox, _("Options")));
- notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
+ /* pack it all up */
+
+ notebook.pages().push_back (TabElem (basic_vbox, _("Audio")));
+ notebook.pages().push_back (TabElem (midi_hbox, _("MIDI")));
+ notebook.pages().push_back (TabElem (lm_vbox, _("Latency")));
notebook.set_border_width (12);
- set_border_width (12);
- pack_start (notebook);
+ notebook.set_tab_pos (POS_RIGHT);
+ notebook.show_all ();
- /* Pick up any existing audio setup configuration, if appropriate */
+ notebook.set_name ("SettingsNotebook");
- XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioSetup");
-
- if (audio_setup) {
- set_state (*audio_setup);
- }
+ /* Connect to signals */
+
+ backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
+ driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
+ sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::sample_rate_changed));
+ buffer_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::buffer_size_changed));
+ input_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &input_channels));
+ output_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &output_channels));
+ device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::device_changed));
}
EngineControl::~EngineControl ()
@@ -388,555 +292,543 @@ EngineControl::~EngineControl ()
}
void
-EngineControl::build_command_line (vector<string>& cmd)
+EngineControl::disable_latency_tab ()
{
- string str;
- string driver;
- bool using_alsa = false;
- bool using_coreaudio = false;
- bool using_dummy = false;
- bool using_ffado = false;
-
- /* first, path to jackd */
-
- cmd.push_back (serverpath_combo.get_active_text ());
-
- /* now jackd arguments */
-
- str = timeout_combo.get_active_text ();
-
- if (str != _("Ignore")) {
-
- double secs = 0;
- uint32_t msecs;
- secs = atof (str);
- msecs = (uint32_t) floor (secs * 1000.0);
+ vector<string> empty;
+ set_popdown_strings (lm_output_channel_combo, empty);
+ set_popdown_strings (lm_input_channel_combo, empty);
+}
- if (msecs > 0) {
- cmd.push_back ("-t");
- cmd.push_back (to_string (msecs, std::dec));
- }
- }
+void
+EngineControl::enable_latency_tab ()
+{
+ vector<string> outputs;
+ ARDOUR::AudioEngine::instance()->get_physical_outputs (ARDOUR::DataType::AUDIO, outputs);
+ set_popdown_strings (lm_output_channel_combo, outputs);
+ lm_output_channel_combo.set_active_text (outputs.front());
+
+ vector<string> inputs;
+ ARDOUR::AudioEngine::instance()->get_physical_inputs (ARDOUR::DataType::AUDIO, inputs);
+ set_popdown_strings (lm_input_channel_combo, inputs);
+ lm_input_channel_combo.set_active_text (inputs.front());
+}
- if (no_memory_lock_button.get_active()) {
- cmd.push_back ("-m"); /* no munlock */
+void
+EngineControl::backend_changed ()
+{
+ if (ignore_changes) {
+ return;
}
- cmd.push_back ("-p"); /* port max */
- cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
+ string backend_name = backend_combo.get_active_text();
+ boost::shared_ptr<ARDOUR::AudioBackend> backend;
- if (realtime_button.get_active()) {
- cmd.push_back ("-R");
- } else {
- cmd.push_back ("-r"); /* override jackd's default --realtime */
+ if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
+ /* eh? setting the backend failed... how ? */
+ return;
}
- if (unlock_memory_button.get_active()) {
- cmd.push_back ("-u");
+ if (backend->requires_driver_selection()) {
+ vector<string> drivers = backend->enumerate_drivers();
+ driver_combo.set_sensitive (true);
+ set_popdown_strings (driver_combo, drivers);
+ driver_combo.set_active_text (drivers.front());
+ driver_changed ();
+ } else {
+ driver_combo.set_sensitive (false);
+ list_devices ();
}
+
+ maybe_display_saved_state ();
+}
- if (verbose_output_button.get_active()) {
- cmd.push_back ("-v");
+bool
+EngineControl::print_channel_count (Gtk::SpinButton* sb)
+{
+ uint32_t cnt = (uint32_t) sb->get_value();
+ if (cnt == 0) {
+ sb->set_text (_("all available channels"));
+ } else {
+ char buf[32];
+ snprintf (buf, sizeof (buf), "%d", cnt);
+ sb->set_text (buf);
}
+ return true;
+}
- /* now add fixed arguments (not user-selectable) */
+void
+EngineControl::list_devices ()
+{
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+ assert (backend);
- cmd.push_back ("-T"); // temporary */
+ /* now fill out devices, mark sample rates, buffer sizes insensitive */
+
+ vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
+
+ /* NOTE: Ardour currently does not display the "available" field of the
+ * returned devices.
+ *
+ * Doing so would require a different GUI widget than the combo
+ * box/popdown that we currently use, since it has no way to list
+ * items that are not selectable. Something more like a popup menu,
+ * which could have unselectable items, would be appropriate.
+ */
- /* setup coremidi before the driver, otherwise jack won't start */
+ vector<string> available_devices;
- if (midi_driver_combo.get_active_text() == _("coremidi")) {
- cmd.push_back ("-X coremidi");
+ for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
+ available_devices.push_back (i->name);
}
- /* next the driver */
-
- cmd.push_back ("-d");
-
- driver = driver_combo.get_active_text ();
-
- if (driver == X_("ALSA")) {
- using_alsa = true;
- cmd.push_back ("alsa");
- } else if (driver == X_("OSS")) {
- cmd.push_back ("oss");
- } else if (driver == X_("CoreAudio")) {
- using_coreaudio = true;
- cmd.push_back ("coreaudio");
- } else if (driver == X_("NetJACK")) {
- cmd.push_back ("netjack");
- } else if (driver == X_("FreeBoB")) {
- cmd.push_back ("freebob");
- } else if (driver == X_("FFADO")) {
- using_ffado = true;
- cmd.push_back ("firewire");
- } else if ( driver == X_("Dummy")) {
- using_dummy = true;
- cmd.push_back ("dummy");
+ set_popdown_strings (device_combo, available_devices);
+
+ if (!available_devices.empty()) {
+ device_combo.set_active_text (available_devices.front());
}
- /* driver arguments */
-
- if (!using_coreaudio) {
- str = audio_mode_combo.get_active_text();
-
- if (str == _("Playback/recording on 1 device")) {
-
- /* relax */
-
- } else if (str == _("Playback/recording on 2 devices")) {
-
- string input_device = get_device_name (driver, input_device_combo.get_active_text());
- string output_device = get_device_name (driver, output_device_combo.get_active_text());
-
- if (input_device.empty() || output_device.empty()) {
- cmd.clear ();
- return;
- }
-
- cmd.push_back ("-C");
- cmd.push_back (input_device);
-
- cmd.push_back ("-P");
- cmd.push_back (output_device);
-
- } else if (str == _("Playback only")) {
- cmd.push_back ("-P");
- } else if (str == _("Recording only")) {
- cmd.push_back ("-C");
- }
-
- if (!using_dummy) {
- cmd.push_back ("-n");
- cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
- }
+ device_changed ();
+}
+
+void
+EngineControl::driver_changed ()
+{
+ if (ignore_changes) {
+ return;
}
- cmd.push_back ("-r");
- cmd.push_back (to_string (get_rate(), std::dec));
-
- cmd.push_back ("-p");
- cmd.push_back (period_size_combo.get_active_text());
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+ assert (backend);
- if (using_alsa || using_ffado || using_coreaudio) {
+ backend->set_driver (driver_combo.get_active_text());
+ list_devices ();
- double val = input_latency_adjustment.get_value();
-
- if (val) {
- cmd.push_back ("-I");
- cmd.push_back (to_string ((uint32_t) val, std::dec));
- }
-
- val = output_latency_adjustment.get_value();
+ maybe_display_saved_state ();
+}
- if (val) {
- cmd.push_back ("-O");
- cmd.push_back (to_string ((uint32_t) val, std::dec));
- }
+void
+EngineControl::device_changed ()
+{
+ if (ignore_changes) {
+ return;
}
- if (using_alsa) {
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+ assert (backend);
+ string device_name = device_combo.get_active_text ();
+ vector<string> s;
- if (audio_mode_combo.get_active_text() != _("Playback/recording on 2 devices")) {
-
- string device = get_device_name (driver, interface_combo.get_active_text());
- if (device.empty()) {
- cmd.clear ();
- return;
- }
+ /* don't allow programmatic change to sample_rate_combo to cause a
+ recursive call to this method.
+ */
+
+ ignore_changes++;
- cmd.push_back ("-d");
- cmd.push_back (device);
- }
+ /* sample rates */
+
+ string desired;
- if (hw_meter_button.get_active()) {
- cmd.push_back ("-M");
+ vector<float> sr = backend->available_sample_rates (device_name);
+ for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
+ char buf[32];
+ if (fmod (*x, 1000.0f)) {
+ snprintf (buf, sizeof (buf), "%.1f kHz", (*x)/1000.0);
+ } else {
+ snprintf (buf, sizeof (buf), "%.0f kHz", (*x)/1000.0);
}
-
- if (hw_monitor_button.get_active()) {
- cmd.push_back ("-H");
+ s.push_back (buf);
+ if (*x == _desired_sample_rate) {
+ desired = buf;
}
+ }
- str = dither_mode_combo.get_active_text();
+ set_popdown_strings (sample_rate_combo, s);
+ if (desired.empty()) {
+ sample_rate_combo.set_active_text (s.front());
+ } else {
+ sample_rate_combo.set_active_text (desired);
+ }
- if (str == _("None")) {
- } else if (str == _("Triangular")) {
- cmd.push_back ("-z triangular");
- } else if (str == _("Rectangular")) {
- cmd.push_back ("-z rectangular");
- } else if (str == _("Shaped")) {
- cmd.push_back ("-z shaped");
- }
+ vector<uint32_t> bs = backend->available_buffer_sizes(device_name);
+ s.clear ();
+ for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
+ char buf[32];
+ /* Translators: "samples" is always plural here, so no
+ need for plural+singular forms.
+ */
+ snprintf (buf, sizeof (buf), _("%u samples"), *x);
+ s.push_back (buf);
+ }
- if (force16bit_button.get_active()) {
- cmd.push_back ("-S");
- }
+ set_popdown_strings (buffer_size_combo, s);
+ buffer_size_combo.set_active_text (s.front());
+ show_buffer_duration ();
- if (soft_mode_button.get_active()) {
- cmd.push_back ("-s");
- }
+ manage_control_app_sensitivity ();
- str = midi_driver_combo.get_active_text ();
+ ignore_changes--;
- if (str == _("seq")) {
- cmd.push_back ("-X seq");
- } else if (str == _("raw")) {
- cmd.push_back ("-X raw");
- }
- } else if (using_coreaudio) {
+ maybe_display_saved_state ();
+}
-#ifdef __APPLE__
- // note: older versions of the CoreAudio JACK backend use -n instead of -d here
+void
+EngineControl::sample_rate_changed ()
+{
+ if (ignore_changes) {
+ return;
+ }
- string device = get_device_name (driver, interface_combo.get_active_text());
- if (device.empty()) {
- cmd.clear ();
- return;
- }
+ /* reset the strings for buffer size to show the correct msec value
+ (reflecting the new sample rate).
+ */
- cmd.push_back ("-d");
- cmd.push_back (device);
-#endif
+ show_buffer_duration ();
+ save_state ();
- }
}
-bool
-EngineControl::need_setup ()
+void
+EngineControl::buffer_size_changed ()
{
- return !engine_running();
+ if (ignore_changes) {
+ return;
+ }
+
+ show_buffer_duration ();
+ save_state ();
}
-bool
-EngineControl::engine_running ()
+void
+EngineControl::show_buffer_duration ()
{
- EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa ();
- boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa;
-
- /* revert all environment settings back to whatever they were when
- * ardour started, because ardour's startup script may have reset
- * something in ways that interfere with finding/starting JACK.
- */
- if (global_epa) {
- current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */
- global_epa->restore ();
- }
+ /* buffer sizes - convert from just samples to samples + msecs for
+ * the displayed string
+ */
- jack_status_t status;
- jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
+ string bs_text = buffer_size_combo.get_active_text ();
+ uint32_t samples = atoi (bs_text); /* will ignore trailing text */
+ uint32_t rate = get_rate();
- if (status == 0) {
- jack_client_close (c);
- return true;
- }
- return false;
+ /* Translators: "msecs" is ALWAYS plural here, so we do not
+ need singular form as well.
+ */
+ /* Developers: note the hard-coding of a double buffered model
+ in the (2 * samples) computation of latency. we always start
+ the audiobackend in this configuration.
+ */
+ char buf[32];
+ snprintf (buf, sizeof (buf), _("(%.1f msecs)"), (2 * samples) / (rate/1000.0));
+ buffer_size_duration_label.set_text (buf);
}
-int
-EngineControl::setup_engine ()
+EngineControl::State*
+EngineControl::get_matching_state (const string& backend,
+ const string& driver,
+ const string& device)
{
- vector<string> args;
- std::string cwd = "/tmp";
-
- build_command_line (args);
-
- if (args.empty()) {
- return 1; // try again
+ for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
+ if ((*i).backend == backend &&
+ (*i).driver == driver &&
+ (*i).device == device) {
+ return &(*i);
+ }
}
+ return 0;
+}
- std::string jackdrc_path = Glib::get_home_dir();
- jackdrc_path += "/.jackdrc";
-
- ofstream jackdrc (jackdrc_path.c_str());
- if (!jackdrc) {
- error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
- return -1;
- }
+EngineControl::State*
+EngineControl::get_current_state ()
+{
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
- for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
- jackdrc << (*i) << ' ';
+ if (backend) {
+ return get_matching_state (backend_combo.get_active_text(),
+ (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
+ device_combo.get_active_text());
}
- jackdrc << endl;
- jackdrc.close ();
-
- _used = true;
- return 0;
+ return get_matching_state (backend_combo.get_active_text(),
+ string(),
+ device_combo.get_active_text());
}
void
-EngineControl::enumerate_devices (const string& driver)
+EngineControl::save_state ()
{
- /* note: case matters for the map keys */
-
- if (driver == "CoreAudio") {
-#ifdef __APPLE__
- devices[driver] = enumerate_coreaudio_devices ();
-#endif
-
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
- } else if (driver == "ALSA") {
- devices[driver] = enumerate_alsa_devices ();
- } else if (driver == "FreeBOB") {
- devices[driver] = enumerate_freebob_devices ();
- } else if (driver == "FFADO") {
- devices[driver] = enumerate_ffado_devices ();
- } else if (driver == "OSS") {
- devices[driver] = enumerate_oss_devices ();
- } else if (driver == "Dummy") {
- devices[driver] = enumerate_dummy_devices ();
- } else if (driver == "NetJACK") {
- devices[driver] = enumerate_netjack_devices ();
+ bool existing = true;
+ State* state = get_current_state ();
+
+ if (!state) {
+ existing = false;
+ state = new State;
+ }
+
+ state->backend = backend_combo.get_active_text ();
+ state->driver = driver_combo.get_active_text ();
+ state->device = device_combo.get_active_text ();
+ state->buffer_size = buffer_size_combo.get_active_text ();
+ state->sample_rate = sample_rate_combo.get_active_text ();
+ state->input_latency = (uint32_t) input_latency.get_value();
+ state->output_latency = (uint32_t) output_latency.get_value();
+ state->input_channels = (uint32_t) input_channels.get_value();
+ state->output_channels = (uint32_t) output_channels.get_value();
+
+ if (!existing) {
+ states.push_back (*state);
}
-#else
- }
-#endif
}
-#ifdef __APPLE__
-static OSStatus
-getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
+void
+EngineControl::maybe_display_saved_state ()
{
- UInt32 size = sizeof(CFStringRef);
- CFStringRef UI;
- OSStatus res = AudioDeviceGetProperty(id, 0, false,
- kAudioDevicePropertyDeviceUID, &size, &UI);
- if (res == noErr)
- CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
- CFRelease(UI);
- return res;
-}
+ State* state = get_current_state ();
-vector<string>
-EngineControl::enumerate_coreaudio_devices ()
-{
- vector<string> devs;
-
- // Find out how many Core Audio devices are there, if any...
- // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
- OSStatus err;
- Boolean isWritable;
- UInt32 outSize = sizeof(isWritable);
-
- backend_devs.clear ();
-
- err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
- &outSize, &isWritable);
- if (err == noErr) {
- // Calculate the number of device available...
- int numCoreDevices = outSize / sizeof(AudioDeviceID);
- // Make space for the devices we are about to get...
- AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
- err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
- &outSize, (void *) coreDeviceIDs);
- if (err == noErr) {
- // Look for the CoreAudio device name...
- char coreDeviceName[256];
- UInt32 nameSize;
-
- for (int i = 0; i < numCoreDevices; i++) {
-
- nameSize = sizeof (coreDeviceName);
-
- /* enforce duplex devices only */
-
- err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
- 0, true, kAudioDevicePropertyStreams,
- &outSize, &isWritable);
-
- if (err != noErr || outSize == 0) {
- continue;
- }
-
- err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
- 0, false, kAudioDevicePropertyStreams,
- &outSize, &isWritable);
-
- if (err != noErr || outSize == 0) {
- continue;
- }
-
- err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
- 0, true, kAudioDevicePropertyDeviceName,
- &outSize, &isWritable);
- if (err == noErr) {
- err = AudioDeviceGetProperty(coreDeviceIDs[i],
- 0, true, kAudioDevicePropertyDeviceName,
- &nameSize, (void *) coreDeviceName);
- if (err == noErr) {
- char drivername[128];
-
- // this returns the unique id for the device
- // that must be used on the commandline for jack
-
- if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
- devs.push_back (coreDeviceName);
- backend_devs.push_back (drivername);
- }
- }
- }
- }
+ if (state) {
+ ignore_changes++;
+ if (!_desired_sample_rate) {
+ sample_rate_combo.set_active_text (state->sample_rate);
}
- delete [] coreDeviceIDs;
+ buffer_size_combo.set_active_text (state->buffer_size);
+ input_latency.set_value (state->input_latency);
+ output_latency.set_value (state->output_latency);
+ ignore_changes--;
}
+}
+
+XMLNode&
+EngineControl::get_state ()
+{
+ XMLNode* root = new XMLNode ("AudioMIDISetup");
+ std::string path;
-
- if (devs.size() == 0) {
- MessageDialog msg (string_compose (_("\
-You do not have any audio devices capable of\n\
-simultaneous playback and recording.\n\n\
-Please use Applications -> Utilities -> Audio MIDI Setup\n\
-to create an \"aggregrate\" device, or install a suitable\n\
-audio interface.\n\n\
-Please send email to Apple and ask them why new Macs\n\
-have no duplex audio device.\n\n\
-Alternatively, if you really want just playback\n\
-or recording but not both, start JACK before running\n\
-%1 and choose the relevant device then."
- ), PROGRAM_NAME),
- true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
- msg.set_title (_("No suitable audio devices"));
- msg.set_position (Gtk::WIN_POS_MOUSE);
- msg.run ();
- exit (1);
+ if (!states.empty()) {
+ XMLNode* state_nodes = new XMLNode ("EngineStates");
+
+ for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
+
+ XMLNode* node = new XMLNode ("State");
+
+ node->add_property ("backend", (*i).backend);
+ node->add_property ("driver", (*i).driver);
+ node->add_property ("device", (*i).device);
+ node->add_property ("sample-rate", (*i).sample_rate);
+ node->add_property ("buffer-size", (*i).buffer_size);
+ node->add_property ("input-latency", (*i).input_latency);
+ node->add_property ("output-latency", (*i).output_latency);
+ node->add_property ("input-channels", (*i).input_channels);
+ node->add_property ("output-channels", (*i).output_channels);
+ node->add_property ("active", (*i).active ? "yes" : "no");
+
+ state_nodes->add_child_nocopy (*node);
+ }
+
+ root->add_child_nocopy (*state_nodes);
}
-
- return devs;
+ return *root;
}
-#else
-#if !defined(__FreeBSD__)
-vector<string>
-EngineControl::enumerate_alsa_devices ()
+void
+EngineControl::set_state (const XMLNode& root)
{
- vector<string> devs;
+ XMLNodeList clist, cclist;
+ XMLNodeConstIterator citer, cciter;
+ XMLNode* child;
+ XMLNode* grandchild;
+ XMLProperty* prop = NULL;
- snd_ctl_t *handle;
- snd_ctl_card_info_t *info;
- snd_pcm_info_t *pcminfo;
- snd_ctl_card_info_alloca(&info);
- snd_pcm_info_alloca(&pcminfo);
- string devname;
- int cardnum = -1;
- int device = -1;
+ if (root.name() != "AudioMIDISetup") {
+ return;
+ }
- backend_devs.clear ();
+ clist = root.children();
- while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
+ states.clear ();
- devname = "hw:";
- devname += to_string (cardnum, std::dec);
+ for (citer = clist.begin(); citer != clist.end(); ++citer) {
- if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
+ child = *citer;
+
+ if (child->name() != "EngineStates") {
+ continue;
+ }
- while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
+ cclist = child->children();
- snd_pcm_info_set_device (pcminfo, device);
- snd_pcm_info_set_subdevice (pcminfo, 0);
- snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
+ for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
+ State state;
+
+ grandchild = *cciter;
- if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
- devs.push_back (snd_pcm_info_get_name (pcminfo));
- devname += ',';
- devname += to_string (device, std::dec);
- backend_devs.push_back (devname);
- }
+ if (grandchild->name() != "State") {
+ continue;
+ }
+
+ if ((prop = grandchild->property ("backend")) == 0) {
+ continue;
}
+ state.backend = prop->value ();
+
+ if ((prop = grandchild->property ("driver")) == 0) {
+ continue;
+ }
+ state.driver = prop->value ();
+
+ if ((prop = grandchild->property ("device")) == 0) {
+ continue;
+ }
+ state.device = prop->value ();
+
+ if ((prop = grandchild->property ("sample-rate")) == 0) {
+ continue;
+ }
+ state.sample_rate = prop->value ();
+
+ if ((prop = grandchild->property ("buffer-size")) == 0) {
+ continue;
+ }
+ state.buffer_size = prop->value ();
+
+ if ((prop = grandchild->property ("input-latency")) == 0) {
+ continue;
+ }
+ state.input_latency = atoi (prop->value ());
+
+ if ((prop = grandchild->property ("output-latency")) == 0) {
+ continue;
+ }
+ state.output_latency = atoi (prop->value ());
+
+ if ((prop = grandchild->property ("input-channels")) == 0) {
+ continue;
+ }
+ state.input_channels = atoi (prop->value ());
+
+ if ((prop = grandchild->property ("output-channels")) == 0) {
+ continue;
+ }
+ state.output_channels = atoi (prop->value ());
- snd_ctl_close(handle);
+ if ((prop = grandchild->property ("active")) == 0) {
+ continue;
+ }
+ state.active = string_is_affirmative (prop->value ());
+
+ states.push_back (state);
}
}
- return devs;
+ /* now see if there was an active state and switch the setup to it */
+
+ for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
+ if ((*i).active) {
+ ignore_changes++;
+ backend_combo.set_active_text ((*i).backend);
+ driver_combo.set_active_text ((*i).driver);
+ device_combo.set_active_text ((*i).device);
+ sample_rate_combo.set_active_text ((*i).sample_rate);
+ buffer_size_combo.set_active_text ((*i).buffer_size);
+ input_latency.set_value ((*i).input_latency);
+ output_latency.set_value ((*i).output_latency);
+ ignore_changes--;
+
+ push_state_to_backend (false);
+ break;
+ }
+ }
}
-#endif
-vector<string>
-EngineControl::enumerate_ffado_devices ()
-{
- vector<string> devs;
- backend_devs.clear ();
- return devs;
-}
-vector<string>
-EngineControl::enumerate_freebob_devices ()
+int
+EngineControl::push_state_to_backend (bool start)
{
- vector<string> devs;
- return devs;
-}
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-vector<string>
-EngineControl::enumerate_oss_devices ()
-{
- vector<string> devs;
- return devs;
-}
-vector<string>
-EngineControl::enumerate_dummy_devices ()
-{
- vector<string> devs;
- return devs;
-}
-vector<string>
-EngineControl::enumerate_netjack_devices ()
-{
- vector<string> devs;
- return devs;
-}
-#endif
+ if (!backend) {
+ return 0;
+ }
-void
-EngineControl::driver_changed ()
-{
- string driver = driver_combo.get_active_text();
- string::size_type maxlen = 0;
- int n = 0;
+ /* grab the parameters from the GUI and apply them */
- enumerate_devices (driver);
+ try {
+ if (backend->requires_driver_selection()) {
+ if (backend->set_driver (get_driver())) {
+ return -1;
+ }
+ }
- vector<string>& strings = devices[driver];
+ if (backend->set_device_name (get_device_name())) {
+ return -1;
+ }
- if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") {
- return;
- }
+ if (backend->set_sample_rate (get_rate())) {
+ error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
+ return -1;
+ }
+ if (backend->set_buffer_size (get_buffer_size())) {
+ error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
+ return -1;
+ }
+ if (backend->set_input_channels (get_input_channels())) {
+ error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
+ return -1;
+ }
+ if (backend->set_output_channels (get_output_channels())) {
+ error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
+ return -1;
+ }
+ if (backend->set_systemic_input_latency (get_input_latency())) {
+ error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
+ return -1;
+ }
+ if (backend->set_systemic_output_latency (get_output_latency())) {
+ error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
+ return -1;
+ }
- for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
- if ((*i).length() > maxlen) {
- maxlen = (*i).length();
+ /* get a pointer to the current state object, creating one if
+ * necessary
+ */
+
+ State* state = get_current_state ();
+
+ if (!state) {
+ save_state ();
+ state = get_current_state ();
+ assert (state);
}
- }
- set_popdown_strings (interface_combo, strings);
- set_popdown_strings (input_device_combo, strings);
- set_popdown_strings (output_device_combo, strings);
+ /* all off */
- if (!strings.empty()) {
- interface_combo.set_active_text (strings.front());
- input_device_combo.set_active_text (strings.front());
- output_device_combo.set_active_text (strings.front());
- }
+ for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
+ (*i).active = false;
+ }
- if (driver == "ALSA") {
- soft_mode_button.set_sensitive (true);
- force16bit_button.set_sensitive (true);
- hw_monitor_button.set_sensitive (true);
- hw_meter_button.set_sensitive (true);
- monitor_button.set_sensitive (true);
- } else {
- soft_mode_button.set_sensitive (false);
- force16bit_button.set_sensitive (false);
- hw_monitor_button.set_sensitive (false);
- hw_meter_button.set_sensitive (false);
- monitor_button.set_sensitive (false);
+ /* mark this one active (to be used next time the dialog is
+ * shown)
+ */
+
+ state->active = true;
+
+ if (start) {
+ if (ARDOUR::AudioEngine::instance()->start()) {
+ return -1;
+ }
+ }
+
+ manage_control_app_sensitivity ();
+ return 0;
+
+ } catch (...) {
+ cerr << "exception thrown...\n";
+ return -1;
}
}
uint32_t
-EngineControl::get_rate ()
+EngineControl::get_rate () const
{
double r = atof (sample_rate_combo.get_active_text ());
/* the string may have been translated with an abbreviation for
@@ -948,412 +840,200 @@ EngineControl::get_rate ()
return lrint (r);
}
-void
-EngineControl::redisplay_latency ()
+uint32_t
+EngineControl::get_buffer_size () const
{
- uint32_t rate = get_rate();
-#if defined(__APPLE__) || defined(__FreeBSD__)
- float periods = 2;
-#else
- float periods = periods_adjustment.get_value();
-#endif
- float period_size = atof (period_size_combo.get_active_text());
+ string txt = buffer_size_combo.get_active_text ();
+ uint32_t samples;
- char buf[32];
- snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
+ if (sscanf (txt.c_str(), "%d", &samples) != 1) {
+ throw exception ();
+ }
- latency_label.set_text (buf);
- latency_label.set_alignment (0, 0.5);
+ return samples;
}
-void
-EngineControl::audio_mode_changed ()
+uint32_t
+EngineControl::get_input_channels() const
{
- std::string str = audio_mode_combo.get_active_text();
-
- if (str == _("Playback/recording on 1 device")) {
- input_device_combo.set_sensitive (false);
- output_device_combo.set_sensitive (false);
- } else if (str == _("Playback/recording on 2 devices")) {
- input_device_combo.set_sensitive (true);
- output_device_combo.set_sensitive (true);
- } else if (str == _("Playback only")) {
- output_device_combo.set_sensitive (true);
- input_device_combo.set_sensitive (false);
- } else if (str == _("Recording only")) {
- input_device_combo.set_sensitive (true);
- output_device_combo.set_sensitive (false);
- }
+ return (uint32_t) input_channels_adjustment.get_value();
}
-static bool jack_server_filter(const string& str, void */*arg*/)
+uint32_t
+EngineControl::get_output_channels() const
{
- return str == "jackd" || str == "jackdmp";
+ return (uint32_t) output_channels_adjustment.get_value();
}
-void
-EngineControl::find_jack_servers (vector<string>& strings)
+uint32_t
+EngineControl::get_input_latency() const
{
-#ifdef __APPLE__
- /* this magic lets us finds the path to the OSX bundle, and then
- we infer JACK's location from there
- */
-
- char execpath[MAXPATHLEN+1];
- uint32_t pathsz = sizeof (execpath);
+ return (uint32_t) input_latency_adjustment.get_value();
+}
- _NSGetExecutablePath (execpath, &pathsz);
+uint32_t
+EngineControl::get_output_latency() const
+{
+ return (uint32_t) output_latency_adjustment.get_value();
+}
- string path (Glib::path_get_dirname (execpath));
- path += "/jackd";
+string
+EngineControl::get_driver () const
+{
+ return driver_combo.get_active_text ();
+}
- if (Glib::file_test (path, FILE_TEST_EXISTS)) {
- strings.push_back (path);
- }
+string
+EngineControl::get_device_name () const
+{
+ return device_combo.get_active_text ();
+}
- if (getenv ("ARDOUR_WITH_JACK")) {
- /* no other options - only use the JACK we supply */
- if (strings.empty()) {
- fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
- /*NOTREACHED*/
- }
+void
+EngineControl::control_app_button_clicked ()
+{
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+
+ if (!backend) {
return;
}
-#else
- string path;
-#endif
-
- PathScanner scanner;
- vector<string *> *jack_servers;
- std::map<string,int> un;
- char *p;
- bool need_minimal_path = false;
-
- p = getenv ("PATH");
-
- if (p && *p) {
- path = p;
- } else {
- need_minimal_path = true;
- }
-
-#ifdef __APPLE__
- // many mac users don't have PATH set up to include
- // likely installed locations of JACK
- need_minimal_path = true;
-#endif
-
- if (need_minimal_path) {
- if (path.empty()) {
- path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
- } else {
- path += ":/usr/local/bin:/opt/local/bin";
- }
- }
-
-#ifdef __APPLE__
- // push it back into the environment so that auto-started JACK can find it.
- // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
- setenv ("PATH", path.c_str(), 1);
-#endif
+
+ backend->launch_control_app ();
+}
- jack_servers = scanner (path, jack_server_filter, 0, false, true);
- if (!jack_servers) {
+void
+EngineControl::manage_control_app_sensitivity ()
+{
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+
+ if (!backend) {
return;
}
+
+ string appname = backend->control_app_name();
- vector<string *>::iterator iter;
-
- for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
- string p = **iter;
-
- if (un[p]++ == 0) {
- strings.push_back(p);
- }
+ if (appname.empty()) {
+ control_app_button.set_sensitive (false);
+ } else {
+ control_app_button.set_sensitive (true);
}
}
-
-string
-EngineControl::get_device_name (const string& driver, const string& human_readable)
+void
+EngineControl::set_desired_sample_rate (uint32_t sr)
{
- vector<string>::iterator n;
- vector<string>::iterator i;
-
- if (human_readable.empty()) {
- /* this can happen if the user's .ardourrc file has a device name from
- another computer system in it
- */
- MessageDialog msg (_("You need to choose an audio device first."));
- msg.set_position (WIN_POS_MOUSE);
- msg.run ();
- return string();
- }
-
- if (backend_devs.empty()) {
- return human_readable;
- }
+ _desired_sample_rate = sr;
+ device_changed ();
+}
- for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
- if (human_readable == (*i)) {
- return (*n);
- }
- }
+/* latency measurement */
- if (i == devices[driver].end()) {
- warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
- }
-
- return string();
+void
+EngineControl::update_latency_display ()
+{
+ ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
+ if (sample_rate == 0) {
+ lm_results.set_text (_("Disconnected from audio engine"));
+ } else {
+ char buf[64];
+ //snprintf (buf, sizeof (buf), "%10.3lf frames %10.3lf ms",
+ //(float)_pi->latency(), (float)_pi->latency() * 1000.0f/sample_rate);
+ strcpy (buf, "got something");
+ lm_results.set_text(buf);
+ }
}
-XMLNode&
-EngineControl::get_state ()
+bool
+EngineControl::check_latency_measurement ()
{
- XMLNode* root = new XMLNode ("AudioSetup");
- XMLNode* child;
- std::string path;
-
- child = new XMLNode ("periods");
- child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
- root->add_child_nocopy (*child);
-
- child = new XMLNode ("ports");
- child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
- root->add_child_nocopy (*child);
-
- child = new XMLNode ("inlatency");
- child->add_property ("val", to_string (input_latency.get_value(), std::dec));
- root->add_child_nocopy (*child);
-
- child = new XMLNode ("outlatency");
- child->add_property ("val", to_string (output_latency.get_value(), std::dec));
- root->add_child_nocopy (*child);
-
- child = new XMLNode ("realtime");
- child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
- root->add_child_nocopy (*child);
-
- child = new XMLNode ("nomemorylock");
- child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
- root->add_child_nocopy (*child);
-
- child = new XMLNode ("unlockmemory");
- child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
- root->add_child_nocopy (*child);
-
- child = new XMLNode ("softmode");
- child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
- root->add_child_nocopy (*child);
-
- child = new XMLNode ("force16bit");
- child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
- root->add_child_nocopy (*child);
-
- child = new XMLNode ("hwmonitor");
- child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
- root->add_child_nocopy (*child);
-
- child = new XMLNode ("hwmeter");
- child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
- root->add_child_nocopy (*child);
-
- child = new XMLNode ("verbose");
- child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
- root->add_child_nocopy (*child);
-
- child = new XMLNode ("samplerate");
- child->add_property ("val", sample_rate_combo.get_active_text());
- root->add_child_nocopy (*child);
-
- child = new XMLNode ("periodsize");
- child->add_property ("val", period_size_combo.get_active_text());
- root->add_child_nocopy (*child);
+ MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
+ static uint32_t cnt = 0;
+
+ if (mtdm->resolve () < 0) {
+ string txt = _("No signal detected ");
+ uint32_t dots = cnt++%10;
+ for (uint32_t i = 0; i < dots; ++i) {
+ txt += '.';
+ }
+ lm_results.set_text (txt);
+ return true;
+ }
- child = new XMLNode ("serverpath");
- child->add_property ("val", serverpath_combo.get_active_text());
- root->add_child_nocopy (*child);
+ if (mtdm->err () > 0.3) {
+ mtdm->invert ();
+ mtdm->resolve ();
+ }
- child = new XMLNode ("driver");
- child->add_property ("val", driver_combo.get_active_text());
- root->add_child_nocopy (*child);
+ char buf[128];
+ ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
- child = new XMLNode ("interface");
- child->add_property ("val", interface_combo.get_active_text());
- root->add_child_nocopy (*child);
+ if (sample_rate == 0) {
+ lm_results.set_text (_("Disconnected from audio engine"));
+ ARDOUR::AudioEngine::instance()->stop_latency_detection ();
+ return false;
+ }
- child = new XMLNode ("timeout");
- child->add_property ("val", timeout_combo.get_active_text());
- root->add_child_nocopy (*child);
+ uint32_t frames_total = mtdm->del();
+ uint32_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
- child = new XMLNode ("dither");
- child->add_property ("val", dither_mode_combo.get_active_text());
- root->add_child_nocopy (*child);
+ snprintf (buf, sizeof (buf), "%u samples %10.3lf ms", extra, extra * 1000.0f/sample_rate);
- child = new XMLNode ("audiomode");
- child->add_property ("val", audio_mode_combo.get_active_text());
- root->add_child_nocopy (*child);
+ bool solid = true;
- child = new XMLNode ("inputdevice");
- child->add_property ("val", input_device_combo.get_active_text());
- root->add_child_nocopy (*child);
+ if (mtdm->err () > 0.2) {
+ strcat (buf, " ??");
+ solid = false;
+ }
- child = new XMLNode ("outputdevice");
- child->add_property ("val", output_device_combo.get_active_text());
- root->add_child_nocopy (*child);
+ if (mtdm->inv ()) {
+ strcat (buf, " (Inv)");
+ solid = false;
+ }
- child = new XMLNode ("mididriver");
- child->add_property ("val", midi_driver_combo.get_active_text());
- root->add_child_nocopy (*child);
+ if (solid) {
+ // _pi->set_measured_latency (rint (mtdm->del()));
+ lm_measure_button.set_active (false);
+ lm_use_button.set_sensitive (true);
+ strcat (buf, " (set)");
+ }
+
+ lm_results.set_text (buf);
- return *root;
+ return true;
}
void
-EngineControl::set_state (const XMLNode& root)
+EngineControl::latency_button_toggled ()
{
- XMLNodeList clist;
- XMLNodeConstIterator citer;
- XMLNode* child;
- XMLProperty* prop = NULL;
- bool using_dummy = false;
- bool using_ffado = false;
-
- int val;
- string strval;
-
- if ( (child = root.child ("driver"))){
- prop = child->property("val");
+ if (lm_measure_button.get_active ()) {
+
+ ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
+ ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
+ cerr << "latency detection on " << lm_input_channel_combo.get_active_text() << " => " << lm_output_channel_combo.get_active_text() << endl;
+ ARDOUR::AudioEngine::instance()->start_latency_detection ();
+ lm_results.set_text (_("Detecting ..."));
+ latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_latency_measurement), 250);
+
+ } else {
+ ARDOUR::AudioEngine::instance()->stop_latency_detection ();
+ latency_timeout.disconnect ();
+ update_latency_display ();
+ }
+}
- if (prop && (prop->value() == "Dummy") ) {
- using_dummy = true;
- }
- if (prop && (prop->value() == "FFADO") ) {
- using_ffado = true;
- }
+void
+EngineControl::use_latency_button_clicked ()
+{
+ MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
+ if (!mtdm) {
+ return;
}
- clist = root.children();
-
- for (citer = clist.begin(); citer != clist.end(); ++citer) {
-
- child = *citer;
-
- prop = child->property ("val");
-
- if (!prop || prop->value().empty()) {
+ uint32_t frames_total = mtdm->del();
+ uint32_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
+ uint32_t one_way = extra/2;
- if (((using_dummy || using_ffado)
- && ( child->name() == "interface"
- || child->name() == "inputdevice"
- || child->name() == "outputdevice"))
- || child->name() == "timeout")
- {
- continue;
- }
-
- error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
- continue;
- }
-
- strval = prop->value();
-
- /* adjustments/spinners */
-
- if (child->name() == "periods") {
- val = atoi (strval);
- periods_adjustment.set_value(val);
- } else if (child->name() == "ports") {
- val = atoi (strval);
- ports_adjustment.set_value(val);
- } else if (child->name() == "inlatency") {
- val = atoi (strval);
- input_latency.set_value(val);
- } else if (child->name() == "outlatency") {
- val = atoi (strval);
- output_latency.set_value(val);
- }
-
- /* buttons */
-
- else if (child->name() == "realtime") {
- val = atoi (strval);
- realtime_button.set_active(val);
- } else if (child->name() == "nomemorylock") {
- val = atoi (strval);
- no_memory_lock_button.set_active(val);
- } else if (child->name() == "unlockmemory") {
- val = atoi (strval);
- unlock_memory_button.set_active(val);
- } else if (child->name() == "softmode") {
- val = atoi (strval);
- soft_mode_button.set_active(val);
- } else if (child->name() == "force16bit") {
- val = atoi (strval);
- force16bit_button.set_active(val);
- } else if (child->name() == "hwmonitor") {
- val = atoi (strval);
- hw_monitor_button.set_active(val);
- } else if (child->name() == "hwmeter") {
- val = atoi (strval);
- hw_meter_button.set_active(val);
- } else if (child->name() == "verbose") {
- val = atoi (strval);
- verbose_output_button.set_active(val);
- }
-
- /* combos */
-
- else if (child->name() == "samplerate") {
- sample_rate_combo.set_active_text(strval);
- } else if (child->name() == "periodsize") {
- period_size_combo.set_active_text(strval);
- } else if (child->name() == "serverpath") {
-
- /* only attempt to set this if we have bothered to look
- up server names already. otherwise this is all
- redundant (actually, all of this dialog/widget
- is redundant in that case ...)
- */
-
- if (!server_strings.empty()) {
- /* do not allow us to use a server path that doesn't
- exist on this system. this handles cases where
- the user has an RC file listing a serverpath
- from some other machine.
- */
- vector<string>::iterator x;
- for (x = server_strings.begin(); x != server_strings.end(); ++x) {
- if (*x == strval) {
- break;
- }
- }
- if (x != server_strings.end()) {
- serverpath_combo.set_active_text (strval);
- } else {
- warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
- strval)
- << endmsg;
- }
- }
-
- } else if (child->name() == "driver") {
- driver_combo.set_active_text(strval);
- } else if (child->name() == "interface") {
- interface_combo.set_active_text(strval);
- } else if (child->name() == "timeout") {
- timeout_combo.set_active_text(strval);
- } else if (child->name() == "dither") {
- dither_mode_combo.set_active_text(strval);
- } else if (child->name() == "audiomode") {
- audio_mode_combo.set_active_text(strval);
- } else if (child->name() == "inputdevice") {
- input_device_combo.set_active_text(strval);
- } else if (child->name() == "outputdevice") {
- output_device_combo.set_active_text(strval);
- } else if (child->name() == "mididriver") {
- midi_driver_combo.set_active_text(strval);
- }
- }
+ input_latency_adjustment.set_value (one_way);
+ output_latency_adjustment.set_value (one_way);
}
diff --git a/gtk2_ardour/engine_dialog.h b/gtk2_ardour/engine_dialog.h
index 0d7ce29b46..3872917add 100644
--- a/gtk2_ardour/engine_dialog.h
+++ b/gtk2_ardour/engine_dialog.h
@@ -26,7 +26,6 @@
#include <gtkmm/checkbutton.h>
#include <gtkmm/spinbutton.h>
-#include <gtkmm/notebook.h>
#include <gtkmm/comboboxtext.h>
#include <gtkmm/table.h>
#include <gtkmm/expander.h>
@@ -34,93 +33,149 @@
#include <gtkmm/buttonbox.h>
#include <gtkmm/button.h>
-class EngineControl : public Gtk::VBox {
- public:
- EngineControl ();
- ~EngineControl ();
-
- static bool need_setup ();
- int setup_engine ();
+#include "pbd/signals.h"
- bool was_used() const { return _used; }
- XMLNode& get_state ();
- void set_state (const XMLNode&);
+#include "ardour_dialog.h"
+class EngineControl : public ArdourDialog, public PBD::ScopedConnectionList {
+ public:
+ EngineControl ();
+ ~EngineControl ();
+
+ static bool need_setup ();
+
+ XMLNode& get_state ();
+ void set_state (const XMLNode&);
+
+ void set_desired_sample_rate (uint32_t);
+
private:
- Gtk::Adjustment periods_adjustment;
- Gtk::SpinButton periods_spinner;
- Gtk::Adjustment ports_adjustment;
- Gtk::SpinButton ports_spinner;
- Gtk::Adjustment input_latency_adjustment;
- Gtk::SpinButton input_latency;
- Gtk::Adjustment output_latency_adjustment;
- Gtk::SpinButton output_latency;
- Gtk::Label latency_label;
-
- Gtk::CheckButton realtime_button;
- Gtk::CheckButton no_memory_lock_button;
- Gtk::CheckButton unlock_memory_button;
- Gtk::CheckButton soft_mode_button;
- Gtk::CheckButton monitor_button;
- Gtk::CheckButton force16bit_button;
- Gtk::CheckButton hw_monitor_button;
- Gtk::CheckButton hw_meter_button;
- Gtk::CheckButton verbose_output_button;
-
- Gtk::Button start_button;
- Gtk::Button stop_button;
- Gtk::HButtonBox button_box;
-
- Gtk::ComboBoxText sample_rate_combo;
- Gtk::ComboBoxText period_size_combo;
-
- Gtk::ComboBoxText preset_combo;
- Gtk::ComboBoxText serverpath_combo;
- Gtk::ComboBoxText driver_combo;
- Gtk::ComboBoxText interface_combo;
- Gtk::ComboBoxText timeout_combo;
- Gtk::ComboBoxText dither_mode_combo;
- Gtk::ComboBoxText audio_mode_combo;
- Gtk::ComboBoxText input_device_combo;
- Gtk::ComboBoxText output_device_combo;
- Gtk::ComboBoxText midi_driver_combo;
-
- Gtk::Table basic_packer;
- Gtk::Table options_packer;
- Gtk::Table device_packer;
- Gtk::HBox basic_hbox;
- Gtk::HBox options_hbox;
- Gtk::HBox device_hbox;
- Gtk::Notebook notebook;
-
- bool _used;
-
- static bool engine_running ();
-
- void driver_changed ();
- void build_command_line (std::vector<std::string>&);
-
- std::map<std::string,std::vector<std::string> > devices;
- std::vector<std::string> backend_devs;
- void enumerate_devices (const std::string& driver);
-
-#ifdef __APPLE__
- std::vector<std::string> enumerate_coreaudio_devices ();
-#else
- std::vector<std::string> enumerate_alsa_devices ();
- std::vector<std::string> enumerate_oss_devices ();
- std::vector<std::string> enumerate_netjack_devices ();
- std::vector<std::string> enumerate_freebob_devices ();
- std::vector<std::string> enumerate_ffado_devices ();
- std::vector<std::string> enumerate_dummy_devices ();
-#endif
-
- void redisplay_latency ();
- uint32_t get_rate();
- void audio_mode_changed ();
- std::vector<std::string> server_strings;
- void find_jack_servers (std::vector<std::string>&);
- std::string get_device_name (const std::string& driver, const std::string& human_readable_name);
+ Gtk::Notebook notebook;
+
+ /* core fields used by all backends */
+
+ Gtk::ComboBoxText backend_combo;
+ Gtk::ComboBoxText sample_rate_combo;
+ Gtk::ComboBoxText buffer_size_combo;
+ Gtk::Label buffer_size_duration_label;
+ Gtk::Adjustment input_latency_adjustment;
+ Gtk::SpinButton input_latency;
+ Gtk::Adjustment output_latency_adjustment;
+ Gtk::SpinButton output_latency;
+ Gtk::Adjustment input_channels_adjustment;
+ Gtk::SpinButton input_channels;
+ Gtk::Adjustment output_channels_adjustment;
+ Gtk::SpinButton output_channels;
+ Gtk::Adjustment ports_adjustment;
+ Gtk::SpinButton ports_spinner;
+
+ Gtk::Button control_app_button;
+
+ /* latency measurement */
+
+ Gtk::ComboBoxText lm_output_channel_combo;
+ Gtk::ComboBoxText lm_input_channel_combo;
+ Gtk::ToggleButton lm_measure_button;
+ Gtk::Button lm_use_button;
+ Gtk::Label lm_title;
+ Gtk::Label lm_preamble;
+ Gtk::Label lm_results;
+ Gtk::Table lm_table;
+ Gtk::VBox lm_vbox;
+
+ /* JACK specific */
+
+ Gtk::CheckButton realtime_button;
+ Gtk::CheckButton no_memory_lock_button;
+ Gtk::CheckButton unlock_memory_button;
+ Gtk::CheckButton soft_mode_button;
+ Gtk::CheckButton monitor_button;
+ Gtk::CheckButton force16bit_button;
+ Gtk::CheckButton hw_monitor_button;
+ Gtk::CheckButton hw_meter_button;
+ Gtk::CheckButton verbose_output_button;
+
+ Gtk::ComboBoxText preset_combo;
+ Gtk::ComboBoxText serverpath_combo;
+ Gtk::ComboBoxText driver_combo;
+ Gtk::ComboBoxText device_combo;
+ Gtk::ComboBoxText timeout_combo;
+ Gtk::ComboBoxText dither_mode_combo;
+ Gtk::ComboBoxText audio_mode_combo;
+ Gtk::ComboBoxText midi_driver_combo;
+
+ Gtk::Table basic_packer;
+ Gtk::Table midi_packer;
+ Gtk::HBox basic_hbox;
+ Gtk::VBox basic_vbox;
+ Gtk::HBox midi_hbox;
+
+ uint32_t ignore_changes;
+
+ static bool engine_running ();
+
+ void driver_changed ();
+ void backend_changed ();
+ void sample_rate_changed ();
+ void buffer_size_changed ();
+
+ uint32_t get_rate() const;
+ uint32_t get_buffer_size() const;
+ uint32_t get_input_channels() const;
+ uint32_t get_output_channels() const;
+ uint32_t get_input_latency() const;
+ uint32_t get_output_latency() const;
+ std::string get_device_name() const;
+ std::string get_driver() const;
+
+ void device_changed ();
+ void list_devices ();
+ void show_buffer_duration ();
+
+ struct State {
+ std::string backend;
+ std::string driver;
+ std::string device;
+ std::string sample_rate;
+ std::string buffer_size;
+ uint32_t input_latency;
+ uint32_t output_latency;
+ uint32_t input_channels;
+ uint32_t output_channels;
+ bool active;
+
+ State() : active (false) {};
+ };
+
+ typedef std::list<State> StateList;
+
+ StateList states;
+
+ State* get_matching_state (const std::string& backend,
+ const std::string& driver,
+ const std::string& device);
+ State* get_current_state ();
+ void maybe_display_saved_state ();
+ void save_state ();
+
+ static bool print_channel_count (Gtk::SpinButton*);
+
+ void build_notebook ();
+
+ void on_response (int);
+ void control_app_button_clicked ();
+ void use_latency_button_clicked ();
+ void manage_control_app_sensitivity ();
+ int push_state_to_backend (bool start);
+ uint32_t _desired_sample_rate;
+
+ /* latency measurement */
+ void latency_button_toggled ();
+ bool check_latency_measurement ();
+ void update_latency_display ();
+ sigc::connection latency_timeout;
+ void enable_latency_tab ();
+ void disable_latency_tab ();
};
#endif /* __gtk2_ardour_engine_dialog_h__ */
diff --git a/gtk2_ardour/export_video_dialog.cc b/gtk2_ardour/export_video_dialog.cc
index 09283d4c8d..f54232a681 100644
--- a/gtk2_ardour/export_video_dialog.cc
+++ b/gtk2_ardour/export_video_dialog.cc
@@ -259,8 +259,7 @@ ExportVideoDialog::ExportVideoDialog (PublicEditor& ed, Session* s)
video_codec_combo.append_text("mjpeg");
video_codec_combo.append_text("mpeg2video");
video_codec_combo.append_text("mpeg4");
- video_codec_combo.append_text("x264 (baseline)");
- video_codec_combo.append_text("x264 (hq)");
+ video_codec_combo.append_text("h264");
video_codec_combo.append_text("vpx (webm)");
video_codec_combo.append_text("copy");
video_codec_combo.set_active(4);
@@ -614,13 +613,8 @@ ExportVideoDialog::encode_pass (int pass)
ffs["-strict"] = "-2";
}
- if (video_codec_combo.get_active_text() == "x264 (hq)" ) {
+ if (video_codec_combo.get_active_text() == "h264" ) {
ffs["-vcodec"] = "libx264";
- ffs["-vprofile"] = "high";
- }
- else if (video_codec_combo.get_active_text() == "x264 (baseline)" ) {
- ffs["-vcodec"] = "libx264";
- ffs["-vpre"] = "baseline";
}
else if (video_codec_combo.get_active_text() == "vpx (webm)" ) {
ffs["-vcodec"] = "libvpx";
diff --git a/gtk2_ardour/gain_meter.cc b/gtk2_ardour/gain_meter.cc
index c2e4a43858..d4610da601 100644
--- a/gtk2_ardour/gain_meter.cc
+++ b/gtk2_ardour/gain_meter.cc
@@ -32,7 +32,6 @@
#include <gtkmm2ext/fastmeter.h>
#include <gtkmm2ext/barcontroller.h>
#include <gtkmm2ext/gtk_ui.h>
-#include "midi++/manager.h"
#include "pbd/fastlog.h"
#include "pbd/stacktrace.h"
@@ -280,14 +279,15 @@ GainMeterBase::setup_meters (int len)
case Wide:
meter_ticks1_area.show();
meter_ticks2_area.show();
+ meter_metric_area.show();
if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
meter_width = 10;
}
break;
case Narrow:
- meter_width = 2;
meter_ticks1_area.hide();
meter_ticks2_area.hide();
+ meter_metric_area.hide();
break;
}
level_meter->setup_meters(len, meter_width);
@@ -864,7 +864,7 @@ GainMeterBase::update_meters()
}
}
-void GainMeterBase::color_handler(bool /* dpi */)
+void GainMeterBase::color_handler(bool /*dpi*/)
{
setup_meters();
}
diff --git a/gtk2_ardour/generic_pluginui.cc b/gtk2_ardour/generic_pluginui.cc
index 6cea8f3c0b..83f091a1e0 100644
--- a/gtk2_ardour/generic_pluginui.cc
+++ b/gtk2_ardour/generic_pluginui.cc
@@ -37,8 +37,6 @@
#include <gtkmm2ext/doi.h>
#include <gtkmm2ext/slider_controller.h>
-#include "midi++/manager.h"
-
#include "ardour/plugin.h"
#include "ardour/plugin_insert.h"
#include "ardour/session.h"
diff --git a/gtk2_ardour/global_port_matrix.cc b/gtk2_ardour/global_port_matrix.cc
index e1bdfb199e..218cfe7a1c 100644
--- a/gtk2_ardour/global_port_matrix.cc
+++ b/gtk2_ardour/global_port_matrix.cc
@@ -19,6 +19,7 @@
#include <gtkmm/image.h>
#include <gtkmm/stock.h>
+
#include "global_port_matrix.h"
#include "utils.h"
@@ -82,9 +83,9 @@ GlobalPortMatrix::set_state (BundleChannel c[2], bool s)
} else {
/* two non-Ardour ports */
if (s) {
- jack_connect (_session->engine().jack (), j->c_str(), i->c_str());
+ AudioEngine::instance()->connect (*j, *i);
} else {
- jack_disconnect (_session->engine().jack (), j->c_str(), i->c_str());
+ AudioEngine::instance()->disconnect (*j, *i);
}
}
}
@@ -113,33 +114,25 @@ GlobalPortMatrix::get_state (BundleChannel c[2]) const
for (Bundle::PortList::const_iterator i = in_ports.begin(); i != in_ports.end(); ++i) {
for (Bundle::PortList::const_iterator j = out_ports.begin(); j != out_ports.end(); ++j) {
- boost::shared_ptr<Port> p = _session->engine().get_port_by_name (*i);
- boost::shared_ptr<Port> q = _session->engine().get_port_by_name (*j);
+ boost::shared_ptr<Port> p = AudioEngine::instance()->get_port_by_name (*i);
+ boost::shared_ptr<Port> q = AudioEngine::instance()->get_port_by_name (*j);
if (!p && !q) {
/* two non-Ardour ports; things are slightly more involved */
- /* XXX: is this the easiest way to do this? */
- /* XXX: isn't this very inefficient? */
- jack_client_t* jack = _session->engine().jack ();
- jack_port_t* jp = jack_port_by_name (jack, i->c_str());
- if (jp == 0) {
+ /* get a port handle for one of them .. */
+
+ PortEngine::PortHandle ph = AudioEngine::instance()->port_engine().get_port_by_name (*i);
+ if (!ph) {
return PortMatrixNode::NOT_ASSOCIATED;
}
- char const ** c = jack_port_get_all_connections (jack, jp);
-
- char const ** p = c;
+ /* see if it is connected to the other one ... */
- while (p && *p != 0) {
- if (strcmp (*p, j->c_str()) == 0) {
- free (c);
- return PortMatrixNode::ASSOCIATED;
- }
- ++p;
+ if (AudioEngine::instance()->port_engine().connected_to (ph, *j, false)) {
+ return PortMatrixNode::ASSOCIATED;
}
- free (c);
return PortMatrixNode::NOT_ASSOCIATED;
}
diff --git a/gtk2_ardour/level_meter.cc b/gtk2_ardour/level_meter.cc
index 6f48864992..bf9823518b 100644
--- a/gtk2_ardour/level_meter.cc
+++ b/gtk2_ardour/level_meter.cc
@@ -23,7 +23,6 @@
#include <gtkmm2ext/utils.h>
#include <gtkmm2ext/barcontroller.h>
-#include "midi++/manager.h"
#include "pbd/fastlog.h"
#include "ardour_ui.h"
diff --git a/gtk2_ardour/main.cc b/gtk2_ardour/main.cc
index dfa0bda945..f55405a84d 100644
--- a/gtk2_ardour/main.cc
+++ b/gtk2_ardour/main.cc
@@ -373,34 +373,30 @@ static void load_custom_fonts() {
#endif
static gboolean
-tell_about_jack_death (void* /* ignored */)
+tell_about_backend_death (void* /* ignored */)
{
if (AudioEngine::instance()->processed_frames() == 0) {
/* died during startup */
- MessageDialog msg (_("JACK exited"), false);
+ MessageDialog msg (string_compose (_("The audio backend (%1) has failed, or terminated"), AudioEngine::instance()->current_backend_name()), false);
msg.set_position (Gtk::WIN_POS_CENTER);
msg.set_secondary_text (string_compose (_(
-"JACK exited unexpectedly, and without notifying %1.\n\
+"%2 exited unexpectedly, and without notifying %1.\n\
\n\
-This could be due to misconfiguration or to an error inside JACK.\n\
+This could be due to misconfiguration or to an error inside %2.\n\
\n\
-Click OK to exit %1."), PROGRAM_NAME));
+Click OK to exit %1."), PROGRAM_NAME, AudioEngine::instance()->current_backend_name()));
msg.run ();
_exit (0);
} else {
- /* engine has already run, so this is a mid-session JACK death */
-
- MessageDialog* msg = manage (new MessageDialog (_("JACK exited"), false));
- msg->set_secondary_text (string_compose (_(
-"JACK exited unexpectedly, and without notifying %1.\n\
-\n\
-This is probably due to an error inside JACK. You should restart JACK\n\
-and reconnect %1 to it, or exit %1 now. You cannot save your\n\
-session at this time, because we would lose your connection information.\n"), PROGRAM_NAME));
- msg->present ();
+ /* engine has already run, so this is a mid-session backend death */
+
+ MessageDialog msg (string_compose (_("The audio backend (%1) has failed, or terminated"), AudioEngine::instance()->current_backend_name()), false);
+ msg.set_secondary_text (string_compose (_("%2 exited unexpectedly, and without notifying %1."),
+ PROGRAM_NAME, AudioEngine::instance()->current_backend_name()));
+ msg.present ();
}
return false; /* do not call again */
}
@@ -408,15 +404,15 @@ session at this time, because we would lose your connection information.\n"), PR
static void
sigpipe_handler (int /*signal*/)
{
- /* XXX fix this so that we do this again after a reconnect to JACK
+ /* XXX fix this so that we do this again after a reconnect to the backend
*/
- static bool done_the_jack_thing = false;
+ static bool done_the_backend_thing = false;
- if (!done_the_jack_thing) {
+ if (!done_the_backend_thing) {
AudioEngine::instance()->died ();
- g_idle_add (tell_about_jack_death, 0);
- done_the_jack_thing = true;
+ g_idle_add (tell_about_backend_death, 0);
+ done_the_backend_thing = true;
}
}
diff --git a/gtk2_ardour/meterbridge.cc b/gtk2_ardour/meterbridge.cc
index 4a4f1dd8dd..8756089e00 100644
--- a/gtk2_ardour/meterbridge.cc
+++ b/gtk2_ardour/meterbridge.cc
@@ -702,11 +702,11 @@ Meterbridge::sync_order_keys (RouteSortOrderKey)
MeterType nmt = (*i).s->meter_type();
if (nmt == MeterKrms) nmt = MeterPeak; // identical metrics
- if (pos == 0) {
+ if (vis == 1) {
(*i).s->set_tick_bar(1);
}
- if ((*i).visible && nmt != lmt && pos == 0) {
+ if ((*i).visible && nmt != lmt && vis == 1) {
lmt = nmt;
metrics_left.set_metric_mode(1, lmt);
} else if ((*i).visible && nmt != lmt) {
diff --git a/gtk2_ardour/midi_tracer.cc b/gtk2_ardour/midi_tracer.cc
index c4fe76252b..934dffd11c 100644
--- a/gtk2_ardour/midi_tracer.cc
+++ b/gtk2_ardour/midi_tracer.cc
@@ -27,7 +27,10 @@
#include "pbd/timersub.h"
#include "midi++/parser.h"
-#include "midi++/manager.h"
+
+#include "ardour/async_midi_port.h"
+#include "ardour/midi_port.h"
+#include "ardour/audioengine.h"
#include "midi_tracer.h"
#include "gui_thread.h"
@@ -56,7 +59,8 @@ MidiTracer::MidiTracer ()
, collect_button (_("Enabled"))
, delta_time_button (_("Delta times"))
{
- Manager::instance()->PortsChanged.connect (_manager_connection, invalidator (*this), boost::bind (&MidiTracer::ports_changed, this), gui_context());
+ ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect
+ (_manager_connection, invalidator (*this), boost::bind (&MidiTracer::ports_changed, this), gui_context());
_last_receipt.tv_sec = 0;
_last_receipt.tv_usec = 0;
@@ -129,24 +133,58 @@ MidiTracer::ports_changed ()
{
string const c = _port_combo.get_active_text ();
_port_combo.clear ();
+
+ ARDOUR::PortManager::PortList pl;
+ ARDOUR::AudioEngine::instance()->get_ports (ARDOUR::DataType::MIDI, pl);
+
+ if (pl.empty()) {
+ _port_combo.set_active_text ("");
+ return;
+ }
- boost::shared_ptr<const Manager::PortList> p = Manager::instance()->get_midi_ports ();
- for (Manager::PortList::const_iterator i = p->begin(); i != p->end(); ++i) {
+ for (ARDOUR::PortManager::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
_port_combo.append_text ((*i)->name());
}
- _port_combo.set_active_text (c);
+ if (c.empty()) {
+ _port_combo.set_active_text (pl.front()->name());
+ } else {
+ _port_combo.set_active_text (c);
+ }
}
void
MidiTracer::port_changed ()
{
+ using namespace ARDOUR;
+
disconnect ();
- Port* p = Manager::instance()->port (_port_combo.get_active_text());
+ boost::shared_ptr<ARDOUR::Port> p = AudioEngine::instance()->get_port_by_name (_port_combo.get_active_text());
+
+ if (!p) {
+ std::cerr << "port not found\n";
+ return;
+ }
+
+ /* The inheritance heirarchy makes this messy. AsyncMIDIPort has two
+ * available MIDI::Parsers what we could connect to, ::self_parser()
+ * (from ARDOUR::MidiPort) and ::parser() from MIDI::Port. One day,
+ * this mess will all go away ...
+ */
+
+ boost::shared_ptr<AsyncMIDIPort> async = boost::dynamic_pointer_cast<AsyncMIDIPort> (p);
+
+ if (!async) {
- if (p) {
- p->parser()->any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3));
+ boost::shared_ptr<ARDOUR::MidiPort> mp = boost::dynamic_pointer_cast<ARDOUR::MidiPort> (p);
+
+ if (mp) {
+ mp->self_parser().any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3));
+ }
+
+ } else {
+ async->parser()->any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3));
}
}
@@ -369,9 +407,9 @@ MidiTracer::tracer (Parser&, byte* msg, size_t len)
fifo.write (&buf, 1);
- if (g_atomic_int_get (&_update_queued) == 0) {
+ if (g_atomic_int_get (const_cast<gint*> (&_update_queued)) == 0) {
gui_context()->call_slot (invalidator (*this), boost::bind (&MidiTracer::update, this));
- g_atomic_int_inc (&_update_queued);
+ g_atomic_int_inc (const_cast<gint*> (&_update_queued));
}
}
@@ -379,7 +417,7 @@ void
MidiTracer::update ()
{
bool updated = false;
- g_atomic_int_dec_and_test (&_update_queued);
+ g_atomic_int_dec_and_test (const_cast<gint*> (&_update_queued));
RefPtr<TextBuffer> buf (text.get_buffer());
diff --git a/gtk2_ardour/nsm.cc b/gtk2_ardour/nsm.cc
index 18d88fec0a..e3ceba355f 100644
--- a/gtk2_ardour/nsm.cc
+++ b/gtk2_ardour/nsm.cc
@@ -50,7 +50,7 @@ NSM_Client::command_open(const char* name,
int r = ERR_OK;
ARDOUR_COMMAND_LINE::session_name = name;
- ARDOUR_COMMAND_LINE::jack_client_name = client_id;
+ ARDOUR_COMMAND_LINE::backend_client_name = client_id;
if (ARDOUR_UI::instance()->get_session_parameters(true, false, "")) {
return ERR_GENERAL;
diff --git a/gtk2_ardour/opts.cc b/gtk2_ardour/opts.cc
index b29f106bfe..90d753af4c 100644
--- a/gtk2_ardour/opts.cc
+++ b/gtk2_ardour/opts.cc
@@ -32,7 +32,8 @@
using namespace std;
string ARDOUR_COMMAND_LINE::session_name = "";
-string ARDOUR_COMMAND_LINE::jack_client_name = "ardour";
+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::no_splash = false;
bool ARDOUR_COMMAND_LINE::just_version = false;
@@ -45,7 +46,6 @@ string ARDOUR_COMMAND_LINE::keybindings_path = ""; /* empty means use builtin de
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::jack_session_uuid;
string ARDOUR_COMMAND_LINE::load_template;
bool ARDOUR_COMMAND_LINE::check_announcements = true;
@@ -60,7 +60,7 @@ print_help (const char *execname)
<< _(" -h, --help Print this message\n")
<< _(" -a, --no-announcements Do not contact website for announcements\n")
<< _(" -b, --bindings Print all possible keyboard binding names\n")
- << _(" -c, --name <name> Use a specific jack client name, default is ardour\n")
+ << _(" -c, --name <name> Use a specific backend client name, default is ardour\n")
<< _(" -d, --disable-plugins Disable all plugins in an existing session\n")
<< _(" -D, --debug <options> Set debug flags. Use \"-D list\" to see available options\n")
<< _(" -n, --no-splash Do not show splash screen\n")
@@ -199,7 +199,7 @@ ARDOUR_COMMAND_LINE::parse_opts (int argc, char *argv[])
break;
case 'c':
- jack_client_name = optarg;
+ backend_client_name = optarg;
break;
case 'C':
@@ -215,7 +215,7 @@ ARDOUR_COMMAND_LINE::parse_opts (int argc, char *argv[])
break;
case 'U':
- jack_session_uuid = optarg;
+ backend_session_uuid = optarg;
break;
default:
diff --git a/gtk2_ardour/opts.h b/gtk2_ardour/opts.h
index b9faa36d72..fdf29157d2 100644
--- a/gtk2_ardour/opts.h
+++ b/gtk2_ardour/opts.h
@@ -28,7 +28,8 @@ extern std::string session_name;
extern bool show_key_actions;
extern bool no_splash;
extern bool just_version;
-extern std::string jack_client_name;
+extern std::string backend_client_name;
+extern std::string backend_session_uuid;
extern bool use_vst;
extern bool new_session;
extern char* curvetest_file;
@@ -39,7 +40,6 @@ extern std::string keybindings_path;
extern std::string menus_file;
extern bool finder_invoked_ardour;
extern std::string immediate_save;
-extern std::string jack_session_uuid;
extern std::string load_template;
extern bool check_announcements;
diff --git a/gtk2_ardour/panner_ui.cc b/gtk2_ardour/panner_ui.cc
index a5ed0c7aee..95d7491921 100644
--- a/gtk2_ardour/panner_ui.cc
+++ b/gtk2_ardour/panner_ui.cc
@@ -21,7 +21,6 @@
#include <gtkmm2ext/utils.h>
#include <gtkmm2ext/barcontroller.h>
-#include "midi++/manager.h"
#include "pbd/fastlog.h"
#include "ardour/pannable.h"
diff --git a/gtk2_ardour/plugin_ui.cc b/gtk2_ardour/plugin_ui.cc
index c19738f02f..28ef9b1297 100644
--- a/gtk2_ardour/plugin_ui.cc
+++ b/gtk2_ardour/plugin_ui.cc
@@ -40,8 +40,6 @@
#include <gtkmm2ext/slider_controller.h>
#include <gtkmm2ext/application.h>
-#include "midi++/manager.h"
-
#include "ardour/session.h"
#include "ardour/plugin.h"
#include "ardour/plugin_insert.h"
diff --git a/gtk2_ardour/port_group.cc b/gtk2_ardour/port_group.cc
index 531a5cf2c4..2a93cf6bee 100644
--- a/gtk2_ardour/port_group.cc
+++ b/gtk2_ardour/port_group.cc
@@ -21,7 +21,6 @@
#include <boost/shared_ptr.hpp>
#include <boost/algorithm/string.hpp>
-#include "midi++/manager.h"
#include "midi++/mmc.h"
#include "ardour/audioengine.h"
@@ -29,9 +28,12 @@
#include "ardour/bundle.h"
#include "ardour/control_protocol_manager.h"
#include "ardour/io_processor.h"
+#include "ardour/midi_port.h"
+#include "ardour/midiport_manager.h"
#include "ardour/session.h"
#include "ardour/user_bundle.h"
#include "ardour/port.h"
+
#include "control_protocol/control_protocol.h"
#include "gui_thread.h"
@@ -452,37 +454,35 @@ PortGroupList::gather (ARDOUR::Session* session, ARDOUR::DataType type, bool inp
/* Ardour's sync ports */
- MIDI::Manager* midi_manager = MIDI::Manager::instance ();
- if (midi_manager && (type == DataType::MIDI || type == DataType::NIL)) {
+ if ((type == DataType::MIDI || type == DataType::NIL)) {
boost::shared_ptr<Bundle> sync (new Bundle (_("Sync"), inputs));
- MIDI::MachineControl* mmc = midi_manager->mmc ();
- AudioEngine& ae = session->engine ();
+ AudioEngine* ae = AudioEngine::instance();
if (inputs) {
sync->add_channel (
- _("MTC in"), DataType::MIDI, ae.make_port_name_non_relative (midi_manager->mtc_input_port()->name())
+ _("MTC in"), DataType::MIDI, ae->make_port_name_non_relative (session->mtc_input_port()->name())
);
sync->add_channel (
- _("MIDI control in"), DataType::MIDI, ae.make_port_name_non_relative (midi_manager->midi_input_port()->name())
+ _("MIDI control in"), DataType::MIDI, ae->make_port_name_non_relative (session->midi_input_port()->name())
);
sync->add_channel (
- _("MIDI clock in"), DataType::MIDI, ae.make_port_name_non_relative (midi_manager->midi_clock_input_port()->name())
+ _("MIDI clock in"), DataType::MIDI, ae->make_port_name_non_relative (session->midi_clock_input_port()->name())
);
sync->add_channel (
- _("MMC in"), DataType::MIDI, ae.make_port_name_non_relative (mmc->input_port()->name())
+ _("MMC in"), DataType::MIDI, ae->make_port_name_non_relative (session->mmc_input_port()->name())
);
} else {
sync->add_channel (
- _("MTC out"), DataType::MIDI, ae.make_port_name_non_relative (midi_manager->mtc_output_port()->name())
+ _("MTC out"), DataType::MIDI, ae->make_port_name_non_relative (session->mtc_output_port()->name())
);
sync->add_channel (
- _("MIDI control out"), DataType::MIDI, ae.make_port_name_non_relative (midi_manager->midi_output_port()->name())
+ _("MIDI control out"), DataType::MIDI, ae->make_port_name_non_relative (session->midi_output_port()->name())
);
sync->add_channel (
- _("MIDI clock out"), DataType::MIDI, ae.make_port_name_non_relative (midi_manager->midi_clock_output_port()->name())
+ _("MIDI clock out"), DataType::MIDI, ae->make_port_name_non_relative (session->midi_clock_output_port()->name())
);
sync->add_channel (
- _("MMC out"), DataType::MIDI, ae.make_port_name_non_relative (mmc->output_port()->name())
+ _("MMC out"), DataType::MIDI, ae->make_port_name_non_relative (session->mmc_output_port()->name())
);
}
@@ -499,20 +499,12 @@ PortGroupList::gather (ARDOUR::Session* session, ARDOUR::DataType type, bool inp
string lpnc = lpn;
lpnc += ':';
- const char ** ports = 0;
- if (type == DataType::NIL) {
- ports = session->engine().get_ports ("", "", inputs ? JackPortIsInput : JackPortIsOutput);
- } else {
- ports = session->engine().get_ports ("", type.to_jack_type(), inputs ? JackPortIsInput : JackPortIsOutput);
- }
-
- if (ports) {
-
- int n = 0;
+ vector<string> ports;
+ if (AudioEngine::instance()->get_ports ("", type, inputs ? IsInput : IsOutput, ports) > 0) {
- while (ports[n]) {
+ for (vector<string>::const_iterator s = ports.begin(); s != ports.end(); ) {
- std::string const p = ports[n];
+ std::string const p = *s;
if (!system->has_port(p) &&
!bus->has_port(p) &&
@@ -526,7 +518,7 @@ PortGroupList::gather (ARDOUR::Session* session, ARDOUR::DataType type, bool inp
*/
if (p.find ("Midi-Through") != string::npos) {
- ++n;
+ ++s;
continue;
}
@@ -539,15 +531,15 @@ PortGroupList::gather (ARDOUR::Session* session, ARDOUR::DataType type, bool inp
if ((lp.find (N_(":monitor")) != string::npos) &&
(lp.find (lpn) != string::npos)) {
- ++n;
+ ++s;
continue;
}
/* can't use the audio engine for this as we are looking at non-Ardour ports */
- jack_port_t* jp = jack_port_by_name (session->engine().jack(), p.c_str());
- if (jp) {
- DataType t (jack_port_type (jp));
+ PortEngine::PortHandle ph = AudioEngine::instance()->port_engine().get_port_by_name (p);
+ if (ph) {
+ DataType t (AudioEngine::instance()->port_engine().port_data_type (ph));
if (t != DataType::NIL) {
if (port_has_prefix (p, N_("system:")) ||
port_has_prefix (p, N_("alsa_pcm")) ||
@@ -560,10 +552,8 @@ PortGroupList::gather (ARDOUR::Session* session, ARDOUR::DataType type, bool inp
}
}
- ++n;
+ ++s;
}
-
- free (ports);
}
for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
diff --git a/gtk2_ardour/port_insert_ui.cc b/gtk2_ardour/port_insert_ui.cc
index 0511b357db..3c495f0e79 100644
--- a/gtk2_ardour/port_insert_ui.cc
+++ b/gtk2_ardour/port_insert_ui.cc
@@ -66,7 +66,7 @@ PortInsertUI::PortInsertUI (Gtk::Window* parent, ARDOUR::Session* sess, boost::s
void
PortInsertUI::update_latency_display ()
{
- framecnt_t const sample_rate = input_selector.session()->engine().frame_rate();
+ framecnt_t const sample_rate = AudioEngine::instance()->sample_rate();
if (sample_rate == 0) {
latency_display.set_text (_("Disconnected from audio engine"));
} else {
@@ -93,7 +93,7 @@ PortInsertUI::check_latency_measurement ()
}
char buf[128];
- framecnt_t const sample_rate = AudioEngine::instance()->frame_rate();
+ framecnt_t const sample_rate = AudioEngine::instance()->sample_rate();
if (sample_rate == 0) {
latency_display.set_text (_("Disconnected from audio engine"));
diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc
index 77f0e2da01..07781fe7ee 100644
--- a/gtk2_ardour/processor_box.cc
+++ b/gtk2_ardour/processor_box.cc
@@ -2202,10 +2202,10 @@ ProcessorBox::register_actions ()
act = ActionManager::register_action (popup_act_grp, X_("newinsert"), _("New Insert"),
sigc::ptr_fun (ProcessorBox::rb_choose_insert));
- ActionManager::jack_sensitive_actions.push_back (act);
+ ActionManager::engine_sensitive_actions.push_back (act);
act = ActionManager::register_action (popup_act_grp, X_("newsend"), _("New External Send ..."),
sigc::ptr_fun (ProcessorBox::rb_choose_send));
- ActionManager::jack_sensitive_actions.push_back (act);
+ ActionManager::engine_sensitive_actions.push_back (act);
ActionManager::register_action (popup_act_grp, X_("newaux"), _("New Aux Send ..."));
diff --git a/gtk2_ardour/rc_option_editor.cc b/gtk2_ardour/rc_option_editor.cc
index 98a4ffd014..ed9ffda4ef 100644
--- a/gtk2_ardour/rc_option_editor.cc
+++ b/gtk2_ardour/rc_option_editor.cc
@@ -31,8 +31,6 @@
#include "pbd/fpu.h"
#include "pbd/cpus.h"
-#include "midi++/manager.h"
-
#include "ardour/audioengine.h"
#include "ardour/dB.h"
#include "ardour/rc_configuration.h"
@@ -1468,8 +1466,8 @@ RCOptionEditor::RCOptionEditor ()
#ifndef __APPLE__
/* no JACK monitoring on CoreAudio */
- if (AudioEngine::instance()->can_request_hardware_monitoring()) {
- mm->add (HardwareMonitoring, _("JACK"));
+ if (AudioEngine::instance()->port_engine().can_monitor_input()) {
+ mm->add (HardwareMonitoring, _("via Audio Driver"));
}
#endif
mm->add (SoftwareMonitoring, _("ardour"));
diff --git a/gtk2_ardour/route_params_ui.cc b/gtk2_ardour/route_params_ui.cc
index bb10bea16f..76f3d4e264 100644
--- a/gtk2_ardour/route_params_ui.cc
+++ b/gtk2_ardour/route_params_ui.cc
@@ -277,7 +277,7 @@ RouteParams_UI::cleanup_latency_frame ()
void
RouteParams_UI::setup_latency_frame ()
{
- latency_widget = new LatencyGUI (*(_route->output()), _session->frame_rate(), _session->engine().frames_per_cycle());
+ latency_widget = new LatencyGUI (*(_route->output()), _session->frame_rate(), AudioEngine::instance()->samples_per_cycle());
char buf[128];
snprintf (buf, sizeof (buf), _("Playback delay: %" PRId64 " samples"), _route->initial_delay());
diff --git a/gtk2_ardour/route_ui.cc b/gtk2_ardour/route_ui.cc
index 9494d42353..3b3f32ac04 100644
--- a/gtk2_ardour/route_ui.cc
+++ b/gtk2_ardour/route_ui.cc
@@ -1692,7 +1692,7 @@ RouteUI::map_frozen ()
void
RouteUI::adjust_latency ()
{
- LatencyDialog dialog (_route->name() + _(" latency"), *(_route->output()), _session->frame_rate(), _session->engine().frames_per_cycle());
+ LatencyDialog dialog (_route->name() + _(" latency"), *(_route->output()), _session->frame_rate(), AudioEngine::instance()->samples_per_cycle());
}
void
diff --git a/gtk2_ardour/startup.cc b/gtk2_ardour/startup.cc
index e24e7d6989..266a7e2a27 100644
--- a/gtk2_ardour/startup.cc
+++ b/gtk2_ardour/startup.cc
@@ -34,6 +34,7 @@
#include "pbd/stacktrace.h"
#include "pbd/openuri.h"
+#include "ardour/audioengine.h"
#include "ardour/filesystem_paths.h"
#include "ardour/recent_sessions.h"
#include "ardour/session.h"
@@ -74,7 +75,6 @@ ArdourStartup::ArdourStartup (bool require_new, const std::string& session_name,
, monitor_via_hardware_button (string_compose (_("Use an external mixer or the hardware mixer of your audio interface.\n"
"%1 will play NO role in monitoring"), PROGRAM_NAME))
, monitor_via_ardour_button (string_compose (_("Ask %1 to play back material as it is being recorded"), PROGRAM_NAME))
- , engine_dialog (0)
, new_folder_chooser (FILE_CHOOSER_ACTION_SELECT_FOLDER)
, more_new_session_options_button (_("I'd like more options for this session"))
, _output_limit_count_adj (1, 0, 100, 1, 10, 0)
@@ -91,13 +91,12 @@ ArdourStartup::ArdourStartup (bool require_new, const std::string& session_name,
, _existing_session_chooser_used (false)
{
new_user = !Glib::file_test (been_here_before_path(), Glib::FILE_TEST_EXISTS);
- need_audio_setup = EngineControl::need_setup ();
need_session_info = (session_name.empty() || require_new);
_provided_session_name = session_name;
_provided_session_path = session_path;
- if (need_audio_setup || need_session_info || new_user) {
+ if (need_session_info || new_user) {
use_template_button.set_group (session_template_group);
use_session_as_template_button.set_group (session_template_group);
@@ -139,18 +138,10 @@ ArdourStartup::ArdourStartup (bool require_new, const std::string& session_name,
setup_monitoring_choice_page ();
setup_monitor_section_choice_page ();
- if (need_audio_setup) {
- setup_audio_page ();
- }
-
ic_new_session_button.set_active (true); // always create new session on first run
} else {
- if (need_audio_setup) {
- setup_audio_page ();
- }
-
setup_initial_choice_page ();
}
@@ -183,7 +174,7 @@ ArdourStartup::~ArdourStartup ()
bool
ArdourStartup::ready_without_display () const
{
- return !new_user && !need_audio_setup && !need_session_info;
+ return !new_user && !need_session_info;
}
void
@@ -311,24 +302,6 @@ ArdourStartup::session_folder ()
}
void
-ArdourStartup::setup_audio_page ()
-{
- engine_dialog = manage (new EngineControl);
-
- engine_dialog->set_border_width (12);
-
- engine_dialog->show_all ();
-
- audio_page_index = append_page (*engine_dialog);
- set_page_type (*engine_dialog, ASSISTANT_PAGE_CONTENT);
- set_page_title (*engine_dialog, _("Audio / MIDI Setup"));
-
- /* the default parameters should work, so the page is potentially complete */
-
- set_page_complete (*engine_dialog, true);
-}
-
-void
ArdourStartup::setup_new_user_page ()
{
Label* foomatic = manage (new Label);
@@ -403,8 +376,6 @@ Where would you like new %1 sessions to be stored by default?\n\n\
vbox->pack_start (*txt, false, false);
vbox->pack_start (*hbox, false, true);
- cerr << "Setting defaultDIR session dir to [" << Config->get_default_session_parent_dir() << "]\n";
-
default_dir_chooser->set_current_folder (poor_mans_glob (Config->get_default_session_parent_dir()));
default_dir_chooser->signal_current_folder_changed().connect (sigc::mem_fun (*this, &ArdourStartup::default_dir_changed));
default_dir_chooser->show ();
@@ -661,13 +632,6 @@ ArdourStartup::on_delete_event (GdkEventAny*)
void
ArdourStartup::on_apply ()
{
- if (engine_dialog) {
- if (engine_dialog->setup_engine ()) {
- set_current_page (audio_page_index);
- return;
- }
- }
-
if (config_modified) {
if (default_dir_chooser) {
diff --git a/gtk2_ardour/startup.h b/gtk2_ardour/startup.h
index 4af6ffbc2a..4dcc3a9fb2 100644
--- a/gtk2_ardour/startup.h
+++ b/gtk2_ardour/startup.h
@@ -56,8 +56,6 @@ class ArdourStartup : public Gtk::Assistant {
bool use_session_template();
std::string session_template_name();
- EngineControl* engine_control() { return engine_dialog; }
-
// advanced session options
bool create_master_bus() const;
@@ -82,7 +80,6 @@ class ArdourStartup : public Gtk::Assistant {
gint _response;
bool config_modified;
bool new_user;
- bool need_audio_setup;
bool need_session_info;
bool new_only;
std::string _provided_session_name;
@@ -175,11 +172,6 @@ class ArdourStartup : public Gtk::Assistant {
void existing_session_selected ();
- /* audio setup page */
-
- void setup_audio_page ();
- EngineControl* engine_dialog;
-
/* new sessions */
void setup_new_session_page ();
diff --git a/gtk2_ardour/utils.cc b/gtk2_ardour/utils.cc
index 93e4ee98c7..28e0907f44 100644
--- a/gtk2_ardour/utils.cc
+++ b/gtk2_ardour/utils.cc
@@ -395,7 +395,7 @@ emulate_key_event (Gtk::Widget* w, unsigned int keyval)
ev.state = 0;
ev.keyval = keyval;
ev.length = 0;
- ev.string = "";
+ ev.string = (const gchar*) "";
ev.hardware_keycode = keymapkey[0].keycode;
ev.group = keymapkey[0].group;
g_free(keymapkey);
diff --git a/gtk2_ardour/window_manager.cc b/gtk2_ardour/window_manager.cc
index 5fa1b6c7b0..3ab9e1adff 100644
--- a/gtk2_ardour/window_manager.cc
+++ b/gtk2_ardour/window_manager.cc
@@ -410,7 +410,7 @@ ProxyBase::hide ()
}
bool
-ProxyBase::handle_win_event (GdkEventAny *ev)
+ProxyBase::handle_win_event (GdkEventAny* /*ev*/)
{
hide();
return true;
diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript
index 12681cfe2d..a02e27f83d 100644
--- a/gtk2_ardour/wscript
+++ b/gtk2_ardour/wscript
@@ -520,6 +520,7 @@ def build(bld):
'SMALLER' : '9',
'SMALL' : '10',
'NORMAL' : '11',
+ 'BIG' : '13',
'BIGGER' : '17',
'LARGE' : '18',
'LARGER' : '28',
@@ -545,6 +546,7 @@ def build(bld):
'SMALLER' : '8',
'SMALL' : '9',
'NORMAL' : '10',
+ 'BIG' : '14',
'BIGGER' : '17',
'LARGE' : '18',
'LARGER' : '24',
diff --git a/libs/ardour/ardour/ardour.h b/libs/ardour/ardour/ardour.h
index eaf6b572fd..311611997f 100644
--- a/libs/ardour/ardour/ardour.h
+++ b/libs/ardour/ardour/ardour.h
@@ -75,9 +75,7 @@ namespace ARDOUR {
bool translations_are_enabled ();
bool set_translations_enabled (bool);
- static inline microseconds_t get_microseconds () {
- return (microseconds_t) jack_get_time();
- }
+ microseconds_t get_microseconds ();
void setup_fpu ();
std::vector<SyncSource> get_available_sync_options();
diff --git a/libs/midi++2/midi++/jack_midi_port.h b/libs/ardour/ardour/async_midi_port.h
index 284df0ef2d..896301b398 100644
--- a/libs/midi++2/midi++/jack_midi_port.h
+++ b/libs/ardour/ardour/async_midi_port.h
@@ -16,14 +16,12 @@
*/
-#ifndef __libmidi_port_h__
-#define __libmidi_port_h__
+#ifndef __libardour_async_midiport_h__
+#define __libardour_async_midiport_h__
#include <string>
#include <iostream>
-#include <jack/types.h>
-
#include "pbd/xml++.h"
#include "pbd/crossthread.h"
#include "pbd/signals.h"
@@ -36,26 +34,26 @@
#include "midi++/parser.h"
#include "midi++/port.h"
-namespace MIDI {
+#include "ardour/midi_port.h"
+
+namespace ARDOUR {
-class Channel;
-class PortRequest;
+ class AsyncMIDIPort : public ARDOUR::MidiPort, public MIDI::Port {
-class JackMIDIPort : public Port {
public:
- JackMIDIPort (std::string const &, Port::Flags, jack_client_t *);
- JackMIDIPort (const XMLNode&, jack_client_t *);
- ~JackMIDIPort ();
+ AsyncMIDIPort (std::string const &, PortFlags);
+ ~AsyncMIDIPort ();
- XMLNode& get_state () const;
- void set_state (const XMLNode&);
+ /* called from an RT context */
void cycle_start (pframes_t nframes);
- void cycle_end ();
-
+ void cycle_end (pframes_t nframes);
+
+ /* called from non-RT context */
+
void parse (framecnt_t timestamp);
- int write (const byte *msg, size_t msglen, timestamp_t timestamp);
- int read (byte *buf, size_t bufsize);
+ int write (const MIDI::byte *msg, size_t msglen, MIDI::timestamp_t timestamp);
+ int read (MIDI::byte *buf, size_t bufsize);
void drain (int check_interval_usecs);
int selectable () const {
#ifdef PLATFORM_WINDOWS
@@ -65,26 +63,15 @@ class JackMIDIPort : public Port {
#endif
}
- pframes_t nframes_this_cycle() const { return _nframes_this_cycle; }
-
- void reestablish (jack_client_t *);
- void reconnect ();
-
static void set_process_thread (pthread_t);
static pthread_t get_process_thread () { return _process_thread; }
static bool is_process_thread();
- static PBD::Signal0<void> MakeConnections;
- static PBD::Signal0<void> JackHalted;
-
-private:
- bool _currently_in_cycle;
- pframes_t _nframes_this_cycle;
- jack_client_t* _jack_client;
- jack_port_t* _jack_port;
- timestamp_t _last_write_timestamp;
+ private:
+ bool _currently_in_cycle;
+ MIDI::timestamp_t _last_write_timestamp;
RingBuffer< Evoral::Event<double> > output_fifo;
- Evoral::EventRingBuffer<timestamp_t> input_fifo;
+ Evoral::EventRingBuffer<MIDI::timestamp_t> input_fifo;
Glib::Threads::Mutex output_fifo_lock;
#ifndef PLATFORM_WINDOWS
CrossThreadChannel xthread;
@@ -102,10 +89,11 @@ private:
void make_connections ();
void init (std::string const &, Flags);
- static pthread_t _process_thread;
+ void flush_output_fifo (pframes_t);
+ static pthread_t _process_thread;
};
-} // namespace MIDI
+} // namespace ARDOUR
-#endif // __libmidi_port_h__
+#endif /* __libardour_async_midiport_h__ */
diff --git a/libs/ardour/ardour/audio_backend.h b/libs/ardour/ardour/audio_backend.h
new file mode 100644
index 0000000000..ab37bea526
--- /dev/null
+++ b/libs/ardour/ardour/audio_backend.h
@@ -0,0 +1,432 @@
+/*
+ Copyright (C) 2013 Paul Davis
+
+ 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.
+
+*/
+
+#ifndef __libardour_audiobackend_h__
+#define __libardour_audiobackend_h__
+
+#include <string>
+#include <vector>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <boost/function.hpp>
+
+#include "ardour/types.h"
+
+namespace ARDOUR {
+
+class AudioEngine;
+class PortEngine;
+class PortManager;
+
+class AudioBackend {
+ public:
+
+ AudioBackend (AudioEngine& e) : engine (e){}
+ virtual ~AudioBackend () {}
+
+ /** Return the name of this backend.
+ *
+ * Should use a well-known, unique term. Expected examples
+ * might include "JACK", "CoreAudio", "ASIO" etc.
+ */
+ virtual std::string name() const = 0;
+
+ /** Return a private, type-free pointer to any data
+ * that might be useful to a concrete implementation
+ */
+ virtual void* private_handle() const = 0;
+
+ /** Return true if the underlying mechanism/API is still available
+ * for us to utilize. return false if some or all of the AudioBackend
+ * API can no longer be effectively used.
+ */
+ virtual bool connected() const = 0;
+
+ /** Return true if the callback from the underlying mechanism/API
+ * (CoreAudio, JACK, ASIO etc.) occurs in a thread subject to realtime
+ * constraints. Return false otherwise.
+ */
+ virtual bool is_realtime () const = 0;
+
+ /* Discovering devices and parameters */
+
+ /** Return true if this backend requires the selection of a "driver"
+ * before any device can be selected. Return false otherwise.
+ *
+ * Intended mainly to differentiate between meta-APIs like JACK
+ * which can still expose different backends (such as ALSA or CoreAudio
+ * or FFADO or netjack) and those like ASIO or CoreAudio which
+ * do not.
+ */
+ virtual bool requires_driver_selection() const { return false; }
+
+ /** If the return value of requires_driver_selection() is true,
+ * then this function can return the list of known driver names.
+ *
+ * If the return value of requires_driver_selection() is false,
+ * then this function should not be called. If it is called
+ * its return value is an empty vector of strings.
+ */
+ virtual std::vector<std::string> enumerate_drivers() const { return std::vector<std::string>(); }
+
+ /** Returns zero if the backend can successfully use @param name as the
+ * driver, non-zero otherwise.
+ *
+ * Should not be used unless the backend returns true from
+ * requires_driver_selection()
+ */
+ virtual int set_driver (const std::string& /*drivername*/) { return 0; }
+
+ /** used to list device names along with whether or not they are currently
+ * available.
+ */
+ struct DeviceStatus {
+ std::string name;
+ bool available;
+
+ DeviceStatus (const std::string& s, bool avail) : name (s), available (avail) {}
+ };
+
+ /** Returns a collection of DeviceStatuses identifying devices discovered
+ * by this backend since the start of the process.
+ *
+ * Any of the names in each DeviceStatus may be used to identify a
+ * device in other calls to the backend, though any of them may become
+ * invalid at any time.
+ */
+ virtual std::vector<DeviceStatus> enumerate_devices () const = 0;
+
+ /** Returns a collection of float identifying sample rates that are
+ * potentially usable with the hardware identified by @param device.
+ * Any of these values may be supplied in other calls to this backend
+ * as the desired sample rate to use with the name device, but the
+ * requested sample rate may turn out to be unavailable, or become invalid
+ * at any time.
+ */
+ virtual std::vector<float> available_sample_rates (const std::string& device) const = 0;
+ /** Returns a collection of uint32 identifying buffer sizes that are
+ * potentially usable with the hardware identified by @param device.
+ * Any of these values may be supplied in other calls to this backend
+ * as the desired buffer size to use with the name device, but the
+ * requested buffer size may turn out to be unavailable, or become invalid
+ * at any time.
+ */
+ virtual std::vector<uint32_t> available_buffer_sizes (const std::string& device) const = 0;
+
+ /** Returns the maximum number of input channels that are potentially
+ * usable with the hardware identified by @param device. Any number from 1
+ * to the value returned may be supplied in other calls to this backend as
+ * the input channel count to use with the name device, but the requested
+ * count may turn out to be unavailable, or become invalid at any time.
+ */
+ virtual uint32_t available_input_channel_count (const std::string& device) const = 0;
+
+ /** Returns the maximum number of output channels that are potentially
+ * usable with the hardware identified by @param device. Any number from 1
+ * to the value returned may be supplied in other calls to this backend as
+ * the output channel count to use with the name device, but the requested
+ * count may turn out to be unavailable, or become invalid at any time.
+ */
+ virtual uint32_t available_output_channel_count (const std::string& device) const = 0;
+
+ /* Return true if the derived class can change the sample rate of the
+ * device in use while the device is already being used. Return false
+ * otherwise. (example: JACK cannot do this as of September 2013)
+ */
+ virtual bool can_change_sample_rate_when_running () const = 0;
+ /* Return true if the derived class can change the buffer size of the
+ * device in use while the device is already being used. Return false
+ * otherwise.
+ */
+ virtual bool can_change_buffer_size_when_running () const = 0;
+
+ /* Set the hardware parameters.
+ *
+ * If called when the current state is stopped or paused,
+ * the changes will not take effect until the state changes to running.
+ *
+ * If called while running, the state will change as fast as the
+ * implementation allows.
+ *
+ * All set_*() methods return zero on success, non-zero otherwise.
+ */
+
+ /** Set the name of the device to be used
+ */
+ virtual int set_device_name (const std::string&) = 0;
+ /** Set the sample rate to be used
+ */
+ virtual int set_sample_rate (float) = 0;
+ /** Set the buffer size to be used.
+ *
+ * The device is assumed to use a double buffering scheme, so that one
+ * buffer's worth of data can be processed by hardware while software works
+ * on the other buffer. All known suitable audio APIs support this model
+ * (though ALSA allows for alternate numbers of buffers, and CoreAudio
+ * doesn't directly expose the concept).
+ */
+ virtual int set_buffer_size (uint32_t) = 0;
+ /** Set the preferred underlying hardware sample format
+ *
+ * This does not change the sample format (32 bit float) read and
+ * written to the device via the Port API.
+ */
+ virtual int set_sample_format (SampleFormat) = 0;
+ /** Set the preferred underlying hardware data layout.
+ * If @param yn is true, then the hardware will interleave
+ * samples for successive channels; otherwise, the hardware will store
+ * samples for a single channel contiguously.
+ *
+ * Setting this does not change the fact that all data streams
+ * to and from Ports are mono (essentially, non-interleaved)
+ */
+ virtual int set_interleaved (bool yn) = 0;
+ /** Set the number of input channels that should be used
+ */
+ virtual int set_input_channels (uint32_t) = 0;
+ /** Set the number of output channels that should be used
+ */
+ virtual int set_output_channels (uint32_t) = 0;
+ /** Set the (additional) input latency that cannot be determined via
+ * the implementation's underlying code (e.g. latency from
+ * external D-A/D-A converters. Units are samples.
+ */
+ virtual int set_systemic_input_latency (uint32_t) = 0;
+ /** Set the (additional) output latency that cannot be determined via
+ * the implementation's underlying code (e.g. latency from
+ * external D-A/D-A converters. Units are samples.
+ */
+ virtual int set_systemic_output_latency (uint32_t) = 0;
+
+ /* Retrieving parameters */
+
+ virtual std::string device_name () const = 0;
+ virtual float sample_rate () const = 0;
+ virtual uint32_t buffer_size () const = 0;
+ virtual SampleFormat sample_format () const = 0;
+ virtual bool interleaved () const = 0;
+ virtual uint32_t input_channels () const = 0;
+ virtual uint32_t output_channels () const = 0;
+ virtual uint32_t systemic_input_latency () const = 0;
+ virtual uint32_t systemic_output_latency () const = 0;
+
+ /** Return the name of a control application for the
+ * selected/in-use device. If no such application exists,
+ * or if no device has been selected or is in-use,
+ * return an empty string.
+ */
+ virtual std::string control_app_name() const = 0;
+ /** Launch the control app for the currently in-use or
+ * selected device. May do nothing if the control
+ * app is undefined or cannot be launched.
+ */
+ virtual void launch_control_app () = 0;
+ /* Basic state control */
+
+ /** Start using the device named in the most recent call
+ * to set_device(), with the parameters set by various
+ * the most recent calls to set_sample_rate() etc. etc.
+ *
+ * At some undetermined time after this function is successfully called,
+ * the backend will start calling the ::process_callback() method of
+ * the AudioEngine referenced by @param engine. These calls will
+ * occur in a thread created by and/or under the control of the backend.
+ *
+ * Return zero if successful, negative values otherwise.
+ */
+ virtual int start () = 0;
+
+ /** Stop using the device currently in use.
+ *
+ * If the function is successfully called, no subsequent calls to the
+ * process_callback() of @param engine will be made after the function
+ * returns, until parameters are reset and start() are called again.
+ *
+ * The backend is considered to be un-configured after a successful
+ * return, and requires calls to set hardware parameters before it can be
+ * start()-ed again. See pause() for a way to avoid this. stop() should
+ * only be used when reconfiguration is required OR when there are no
+ * plans to use the backend in the future with a reconfiguration.
+ *
+ * Return zero if successful, 1 if the device is not in use, negative values on error
+ */
+ virtual int stop () = 0;
+
+ /** Temporarily cease using the device named in the most recent call to set_parameters().
+ *
+ * If the function is successfully called, no subsequent calls to the
+ * process_callback() of @param engine will be made after the function
+ * returns, until start() is called again.
+ *
+ * The backend will retain its existing parameter configuration after a successful
+ * return, and does NOT require any calls to set hardware parameters before it can be
+ * start()-ed again.
+ *
+ * Return zero if successful, 1 if the device is not in use, negative values on error
+ */
+ virtual int pause () = 0;
+
+ /** While remaining connected to the device, and without changing its
+ * configuration, start (or stop) calling the process_callback() of @param engine
+ * without waiting for the device. Once process_callback() has returned, it
+ * will be called again immediately, thus allowing for faster-than-realtime
+ * processing.
+ *
+ * All registered ports remain in existence and all connections remain
+ * unaltered. However, any physical ports should NOT be used by the
+ * process_callback() during freewheeling - the data behaviour is undefined.
+ *
+ * If @param start_stop is true, begin this behaviour; otherwise cease this
+ * behaviour if it currently occuring, and return to calling
+ * process_callback() of @param engine by waiting for the device.
+ *
+ * Return zero on success, non-zero otherwise.
+ */
+ virtual int freewheel (bool start_stop) = 0;
+
+ /** return the fraction of the time represented by the current buffer
+ * size that is being used for each buffer process cycle, as a value
+ * from 0.0 to 1.0
+ *
+ * E.g. if the buffer size represents 5msec and current processing
+ * takes 1msec, the returned value should be 0.2.
+ *
+ * Implementations can feel free to smooth the values returned over
+ * time (e.g. high pass filtering, or its equivalent).
+ */
+ virtual float cpu_load() const = 0;
+
+ /* Transport Control (JACK is the only audio API that currently offers
+ the concept of shared transport control)
+ */
+
+ /** Attempt to change the transport state to TransportRolling.
+ */
+ virtual void transport_start () {}
+ /** Attempt to change the transport state to TransportStopped.
+ */
+ virtual void transport_stop () {}
+ /** return the current transport state
+ */
+ virtual TransportState transport_state () const { return TransportStopped; }
+ /** Attempt to locate the transport to @param pos
+ */
+ virtual void transport_locate (framepos_t /*pos*/) {}
+ /** Return the current transport location, in samples measured
+ * from the origin (defined by the transport time master)
+ */
+ virtual framepos_t transport_frame() const { return 0; }
+
+ /** If @param yn is true, become the time master for any inter-application transport
+ * timebase, otherwise cease to be the time master for the same.
+ *
+ * Return zero on success, non-zero otherwise
+ *
+ * JACK is the only currently known audio API with the concept of a shared
+ * transport timebase.
+ */
+ virtual int set_time_master (bool /*yn*/) { return 0; }
+
+ virtual int usecs_per_cycle () const { return 1000000 * (buffer_size() / sample_rate()); }
+ virtual size_t raw_buffer_size (DataType t) = 0;
+
+ /* Process time */
+
+ /** return the time according to the sample clock in use, measured in
+ * samples since an arbitrary zero time in the past. The value should
+ * increase monotonically and linearly, without interruption from any
+ * source (including CPU frequency scaling).
+ *
+ * It is extremely likely that any implementation will use a DLL, since
+ * this function can be called from any thread, at any time, and must be
+ * able to accurately determine the correct sample time.
+ *
+ * Can be called from any thread.
+ */
+ virtual pframes_t sample_time () = 0;
+
+ /** Return the time according to the sample clock in use when the most
+ * recent buffer process cycle began. Can be called from any thread.
+ */
+ virtual pframes_t sample_time_at_cycle_start () = 0;
+
+ /** Return the time since the current buffer process cycle started,
+ * in samples, according to the sample clock in use.
+ *
+ * Can ONLY be called from within a process() callback tree (which
+ * implies that it can only be called by a process thread)
+ */
+ virtual pframes_t samples_since_cycle_start () = 0;
+
+ /** Return true if it possible to determine the offset in samples of the
+ * first video frame that starts within the current buffer process cycle,
+ * measured from the first sample of the cycle. If returning true,
+ * set @param offset to that offset.
+ *
+ * Eg. if it can be determined that the first video frame within the cycle
+ * starts 28 samples after the first sample of the cycle, then this method
+ * should return true and set @param offset to 28.
+ *
+ * May be impossible to support outside of JACK, which has specific support
+ * (in some cases, hardware support) for this feature.
+ *
+ * Can ONLY be called from within a process() callback tree (which implies
+ * that it can only be called by a process thread)
+ */
+ virtual bool get_sync_offset (pframes_t& /*offset*/) const { return false; }
+
+ /** Create a new thread suitable for running part of the buffer process
+ * cycle (i.e. Realtime scheduling, memory allocation, etc. etc are all
+ * correctly setup), with a stack size given in bytes by specified @param
+ * stacksize. The thread will begin executing @param func, and will exit
+ * when that function returns.
+ */
+ virtual int create_process_thread (boost::function<void()> func, pthread_t*, size_t stacksize) = 0;
+
+ virtual void update_latencies () = 0;
+
+ protected:
+ AudioEngine& engine;
+};
+
+struct AudioBackendInfo {
+ const char* name;
+
+ int (*instantiate) (const std::string& arg1, const std::string& arg2);
+ int (*deinstantiate) (void);
+
+ boost::shared_ptr<AudioBackend> (*backend_factory) (AudioEngine&);
+ boost::shared_ptr<PortEngine> (*portengine_factory) (PortManager&);
+
+ /** Return true if the underlying mechanism/API has been
+ * configured and does not need (re)configuration in order
+ * to be usable. Return false otherwise.
+ *
+ * Note that this may return true if (re)configuration, even though
+ * not currently required, is still possible.
+ */
+ bool (*already_configured)();
+};
+
+} // namespace
+
+#endif /* __libardour_audiobackend_h__ */
+
diff --git a/libs/ardour/ardour/audio_diskstream.h b/libs/ardour/ardour/audio_diskstream.h
index b636cb4734..5a856e9b36 100644
--- a/libs/ardour/ardour/audio_diskstream.h
+++ b/libs/ardour/ardour/audio_diskstream.h
@@ -114,7 +114,7 @@ class AudioDiskstream : public Diskstream
XMLNode& get_state(void);
int set_state(const XMLNode& node, int version);
- void request_jack_monitors_input (bool);
+ void request_input_monitoring (bool);
static void swap_by_ptr (Sample *first, Sample *last) {
while (first < last) {
@@ -160,7 +160,7 @@ class AudioDiskstream : public Diskstream
std::string name;
bool is_physical () const;
- void request_jack_monitors_input (bool) const;
+ void request_input_monitoring (bool) const;
};
/** Information about one of our channels */
diff --git a/libs/ardour/ardour/audio_port.h b/libs/ardour/ardour/audio_port.h
index 7f084a5c85..f87b134e9e 100644
--- a/libs/ardour/ardour/audio_port.h
+++ b/libs/ardour/ardour/audio_port.h
@@ -46,10 +46,10 @@ class AudioPort : public Port
AudioBuffer& get_audio_buffer (pframes_t nframes);
protected:
- friend class AudioEngine;
+ friend class PortManager;
+ AudioPort (std::string const &, PortFlags);
- AudioPort (std::string const &, Flags);
- /* special access for engine only */
+ /* special access for PortManager only (hah, C++) */
Sample* engine_get_whole_audio_buffer ();
private:
diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h
index ec31dd1ac2..cf4f91d4d0 100644
--- a/libs/ardour/ardour/audioengine.h
+++ b/libs/ardour/ardour/audioengine.h
@@ -33,29 +33,22 @@
#include <glibmm/threads.h>
-#include "pbd/rcu.h"
#include "pbd/signals.h"
#include "pbd/stacktrace.h"
-#ifndef PLATFORM_WINDOWS
-#include <jack/weakjack.h>
-#endif
-
-#include <jack/jack.h>
-#include <jack/transport.h>
-#include <jack/thread.h>
-
#include "ardour/ardour.h"
-
#include "ardour/data_type.h"
#include "ardour/session_handle.h"
#include "ardour/types.h"
#include "ardour/chan_count.h"
+#include "ardour/port_manager.h"
#ifdef HAVE_JACK_SESSION
#include <jack/session.h>
#endif
+class MTDM;
+
namespace ARDOUR {
class InternalPort;
@@ -63,300 +56,190 @@ class MidiPort;
class Port;
class Session;
class ProcessThread;
+class AudioBackend;
+class AudioBackendInfo;
-class AudioEngine : public SessionHandlePtr
+class AudioEngine : public SessionHandlePtr, public PortManager
{
public:
- typedef std::map<std::string,boost::shared_ptr<Port> > Ports;
-
- AudioEngine (std::string client_name, std::string session_uuid);
- virtual ~AudioEngine ();
-
- jack_client_t* jack() const;
- bool connected() const { return _jack != 0; }
-
- bool is_realtime () const;
-
- ProcessThread* main_thread() const { return _main_thread; }
-
- std::string client_name() const { return jack_client_name; }
-
- int reconnect_to_jack ();
- int disconnect_from_jack();
-
- int stop (bool forever = false);
- int start ();
- bool running() const { return _running; }
-
- Glib::Threads::Mutex& process_lock() { return _process_lock; }
-
- framecnt_t frame_rate () const;
- pframes_t frames_per_cycle () const;
-
- size_t raw_buffer_size(DataType t);
-
- int usecs_per_cycle () const { return _usecs_per_cycle; }
-
- bool get_sync_offset (pframes_t & offset) const;
-
- pframes_t frames_since_cycle_start () {
- jack_client_t* _priv_jack = _jack;
- if (!_running || !_priv_jack) {
- return 0;
- }
- return jack_frames_since_cycle_start (_priv_jack);
- }
-
- pframes_t frame_time () {
- jack_client_t* _priv_jack = _jack;
- if (!_running || !_priv_jack) {
- return 0;
- }
- return jack_frame_time (_priv_jack);
- }
-
- pframes_t frame_time_at_cycle_start () {
- jack_client_t* _priv_jack = _jack;
- if (!_running || !_priv_jack) {
- return 0;
- }
- return jack_last_frame_time (_priv_jack);
- }
-
- pframes_t transport_frame () const {
- const jack_client_t* _priv_jack = _jack;
- if (!_running || !_priv_jack) {
- return 0;
- }
- return jack_get_current_transport_frame (_priv_jack);
- }
-
- int request_buffer_size (pframes_t);
-
- framecnt_t processed_frames() const { return _processed_frames; }
-
- float get_cpu_load() {
- jack_client_t* _priv_jack = _jack;
- if (!_running || !_priv_jack) {
- return 0;
- }
- return jack_cpu_load (_priv_jack);
- }
-
- void set_session (Session *);
- void remove_session (); // not a replacement for SessionHandle::session_going_away()
-
- class PortRegistrationFailure : public std::exception {
- public:
- PortRegistrationFailure (std::string const & why = "")
- : reason (why) {}
-
- ~PortRegistrationFailure () throw () {}
-
- virtual const char *what() const throw () { return reason.c_str(); }
-
- private:
- std::string reason;
- };
-
- class NoBackendAvailable : public std::exception {
- public:
- virtual const char *what() const throw() { return "could not connect to engine backend"; }
- };
-
- boost::shared_ptr<Port> register_input_port (DataType, const std::string& portname);
- boost::shared_ptr<Port> register_output_port (DataType, const std::string& portname);
- int unregister_port (boost::shared_ptr<Port>);
-
- bool port_is_physical (const std::string&) const;
- void request_jack_monitors_input (const std::string&, bool) const;
-
- void split_cycle (pframes_t offset);
-
- int connect (const std::string& source, const std::string& destination);
- int disconnect (const std::string& source, const std::string& destination);
- int disconnect (boost::shared_ptr<Port>);
-
- const char ** get_ports (const std::string& port_name_pattern, const std::string& type_name_pattern, uint32_t flags);
-
- bool can_request_hardware_monitoring ();
-
- ChanCount n_physical_outputs () const;
- ChanCount n_physical_inputs () const;
-
- void get_physical_outputs (DataType type, std::vector<std::string>&);
- void get_physical_inputs (DataType type, std::vector<std::string>&);
- boost::shared_ptr<Port> get_port_by_name (const std::string &);
- void port_renamed (const std::string&, const std::string&);
-
- enum TransportState {
- TransportStopped = JackTransportStopped,
- TransportRolling = JackTransportRolling,
- TransportLooping = JackTransportLooping,
- TransportStarting = JackTransportStarting
- };
-
- void transport_start ();
- void transport_stop ();
- void transport_locate (framepos_t);
- TransportState transport_state ();
-
- int reset_timebase ();
-
- void update_latencies ();
-
- /* start/stop freewheeling */
-
- int freewheel (bool onoff);
- bool freewheeling() const { return _freewheeling; }
-
- /* this signal is sent for every process() cycle while freewheeling.
-_ the regular process() call to session->process() is not made.
- */
-
- PBD::Signal1<int, pframes_t> Freewheel;
-
- PBD::Signal0<void> Xrun;
-
- /* this signal is if JACK notifies us of a graph order event */
-
- PBD::Signal0<void> GraphReordered;
-
-#ifdef HAVE_JACK_SESSION
- PBD::Signal1<void,jack_session_event_t *> JackSessionEvent;
-#endif
-
-
- /* this signal is emitted if the sample rate changes */
-
- PBD::Signal1<void, framecnt_t> SampleRateChanged;
-
- /* this signal is sent if JACK ever disconnects us */
-
- PBD::Signal1<void,const char*> Halted;
-
- /* these two are emitted when the engine itself is
- started and stopped
- */
-
- PBD::Signal0<void> Running;
- PBD::Signal0<void> Stopped;
-
- /** Emitted if a JACK port is registered or unregistered */
- PBD::Signal0<void> PortRegisteredOrUnregistered;
-
- /** Emitted if a JACK port is connected or disconnected.
- * The Port parameters are the ports being connected / disconnected, or 0 if they are not known to Ardour.
- * The std::string parameters are the (long) port names.
- * The bool parameter is true if ports were connected, or false for disconnected.
- */
- PBD::Signal5<void, boost::weak_ptr<Port>, std::string, boost::weak_ptr<Port>, std::string, bool> PortConnectedOrDisconnected;
-
- std::string make_port_name_relative (std::string) const;
- std::string make_port_name_non_relative (std::string) const;
- bool port_is_mine (const std::string&) const;
-
- static AudioEngine* instance() { return _instance; }
- static void destroy();
- void died ();
-
- int create_process_thread (boost::function<void()>, jack_native_thread_t*, size_t stacksize);
- bool stop_process_thread (jack_native_thread_t);
-
-private:
- static AudioEngine* _instance;
-
- jack_client_t* volatile _jack; /* could be reset to null by SIGPIPE or another thread */
- std::string jack_client_name;
- Glib::Threads::Mutex _process_lock;
- Glib::Threads::Cond session_removed;
- bool session_remove_pending;
- frameoffset_t session_removal_countdown;
- gain_t session_removal_gain;
- gain_t session_removal_gain_step;
- bool _running;
- bool _has_run;
- mutable framecnt_t _buffer_size;
- std::map<DataType,size_t> _raw_buffer_sizes;
- mutable framecnt_t _frame_rate;
- /// number of frames between each check for changes in monitor input
- framecnt_t monitor_check_interval;
- /// time of the last monitor check in frames
- framecnt_t last_monitor_check;
- /// the number of frames processed since start() was called
- framecnt_t _processed_frames;
- bool _freewheeling;
- bool _pre_freewheel_mmc_enabled;
- int _usecs_per_cycle;
- bool port_remove_in_progress;
- Glib::Threads::Thread* m_meter_thread;
- ProcessThread* _main_thread;
-
- SerializedRCUManager<Ports> ports;
-
- boost::shared_ptr<Port> register_port (DataType type, const std::string& portname, bool input);
-
- int process_callback (pframes_t nframes);
- void* process_thread ();
- void remove_all_ports ();
-
- ChanCount n_physical (unsigned long) const;
- void get_physical (DataType, unsigned long, std::vector<std::string> &);
-
- void port_registration_failure (const std::string& portname);
-
- static int _xrun_callback (void *arg);
-#ifdef HAVE_JACK_SESSION
- static void _session_callback (jack_session_event_t *event, void *arg);
-#endif
- static int _graph_order_callback (void *arg);
- static void* _process_thread (void *arg);
- static int _sample_rate_callback (pframes_t nframes, void *arg);
- static int _bufsize_callback (pframes_t nframes, void *arg);
- static void _jack_timebase_callback (jack_transport_state_t, pframes_t, jack_position_t*, int, void*);
- static int _jack_sync_callback (jack_transport_state_t, jack_position_t*, void *arg);
- static void _freewheel_callback (int , void *arg);
- static void _registration_callback (jack_port_id_t, int, void *);
- static void _connect_callback (jack_port_id_t, jack_port_id_t, int, void *);
-
- void jack_timebase_callback (jack_transport_state_t, pframes_t, jack_position_t*, int);
- int jack_sync_callback (jack_transport_state_t, jack_position_t*);
- int jack_bufsize_callback (pframes_t);
- int jack_sample_rate_callback (pframes_t);
- void freewheel_callback (int);
- void connect_callback (jack_port_id_t, jack_port_id_t, int);
-
- void set_jack_callbacks ();
-
- static void _latency_callback (jack_latency_callback_mode_t, void*);
- void jack_latency_callback (jack_latency_callback_mode_t);
-
- int connect_to_jack (std::string client_name, std::string session_uuid);
-
- static void halted (void *);
- static void halted_info (jack_status_t,const char*,void *);
-
- void meter_thread ();
- void start_metering_thread ();
- void stop_metering_thread ();
-
- static gint m_meter_exit;
-
- struct ThreadData {
- AudioEngine* engine;
- boost::function<void()> f;
- size_t stacksize;
-
- ThreadData (AudioEngine* ae, boost::function<void()> fp, size_t stacksz)
- : engine (ae) , f (fp) , stacksize (stacksz) {}
- };
-
- static void* _start_process_thread (void*);
- void parameter_changed (const std::string&);
- PBD::ScopedConnection config_connection;
+ static AudioEngine* create ();
+
+ virtual ~AudioEngine ();
+
+ int discover_backends();
+ std::vector<const AudioBackendInfo*> available_backends() const;
+ std::string current_backend_name () const;
+ 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;
+
+ ProcessThread* main_thread() const { return _main_thread; }
+
+ /* START BACKEND PROXY API
+ *
+ * See audio_backend.h for full documentation and semantics. These wrappers
+ * just forward to a backend implementation.
+ */
+
+ int start ();
+ int stop ();
+ int pause ();
+ int freewheel (bool start_stop);
+ float get_cpu_load() const ;
+ void transport_start ();
+ void transport_stop ();
+ TransportState transport_state ();
+ void transport_locate (framepos_t pos);
+ framepos_t transport_frame();
+ framecnt_t sample_rate () const;
+ pframes_t samples_per_cycle () const;
+ int usecs_per_cycle () const;
+ size_t raw_buffer_size (DataType t);
+ pframes_t sample_time ();
+ pframes_t sample_time_at_cycle_start ();
+ pframes_t samples_since_cycle_start ();
+ bool get_sync_offset (pframes_t& offset) const;
+ int create_process_thread (boost::function<void()> func, pthread_t*, size_t stacksize);
+ bool is_realtime() const;
+ bool connected() const;
+
+ int set_device_name (const std::string&);
+ int set_sample_rate (float);
+ int set_buffer_size (uint32_t);
+ int set_sample_format (SampleFormat);
+ int set_interleaved (bool yn);
+ int set_input_channels (uint32_t);
+ int set_output_channels (uint32_t);
+ int set_systemic_input_latency (uint32_t);
+ int set_systemic_output_latency (uint32_t);
+
+ /* END BACKEND PROXY API */
+
+ bool freewheeling() const { return _freewheeling; }
+ bool running() const { return _running; }
+
+ Glib::Threads::Mutex& process_lock() { return _process_lock; }
+
+ int request_buffer_size (pframes_t samples) {
+ return set_buffer_size (samples);
+ }
+
+ framecnt_t processed_frames() const { return _processed_frames; }
+
+ void set_session (Session *);
+ void remove_session (); // not a replacement for SessionHandle::session_going_away()
+ Session* session() const { return _session; }
+
+ class NoBackendAvailable : public std::exception {
+ public:
+ virtual const char *what() const throw() { return "could not connect to engine backend"; }
+ };
+
+ void split_cycle (pframes_t offset);
+
+ int reset_timebase ();
+
+ void update_latencies ();
+
+ /* this signal is sent for every process() cycle while freewheeling.
+ (the regular process() call to session->process() is not made)
+ */
+
+ PBD::Signal1<int, pframes_t> Freewheel;
+
+ PBD::Signal0<void> Xrun;
+
+ /* this signal is emitted if the sample rate changes */
+
+ PBD::Signal1<void, framecnt_t> SampleRateChanged;
+
+ /* this signal is sent if the backend ever disconnects us */
+
+ PBD::Signal1<void,const char*> Halted;
+
+ /* these two are emitted when the engine itself is
+ started and stopped
+ */
+
+ PBD::Signal0<void> Running;
+ PBD::Signal0<void> Stopped;
+
+ static AudioEngine* instance() { return _instance; }
+ static void destroy();
+ void died ();
+
+ /* The backend will cause these at the appropriate time(s)
+ */
+ int process_callback (pframes_t nframes);
+ int buffer_size_change (pframes_t nframes);
+ int sample_rate_change (pframes_t nframes);
+ void freewheel_callback (bool);
+ void timebase_callback (TransportState state, pframes_t nframes, framepos_t pos, int new_position);
+ int sync_callback (TransportState state, framepos_t position);
+ int port_registration_callback ();
+ void latency_callback (bool for_playback);
+ void halted_callback (const char* reason);
+
+ /* sets up the process callback thread */
+ static void thread_init_callback (void *);
+
+ /* latency measurement */
+
+ MTDM* mtdm();
+ void start_latency_detection ();
+ void stop_latency_detection ();
+ void set_latency_input_port (const std::string&);
+ void set_latency_output_port (const std::string&);
+ uint32_t latency_signal_delay () const { return _latency_signal_latency; }
+
+ private:
+ AudioEngine ();
+
+ static AudioEngine* _instance;
+
+ boost::shared_ptr<AudioBackend> _backend;
+
+ Glib::Threads::Mutex _process_lock;
+ Glib::Threads::Cond session_removed;
+ bool session_remove_pending;
+ frameoffset_t session_removal_countdown;
+ gain_t session_removal_gain;
+ gain_t session_removal_gain_step;
+ bool _running;
+ bool _freewheeling;
+ /// number of frames between each check for changes in monitor input
+ framecnt_t monitor_check_interval;
+ /// time of the last monitor check in frames
+ framecnt_t last_monitor_check;
+ /// the number of frames processed since start() was called
+ framecnt_t _processed_frames;
+ Glib::Threads::Thread* m_meter_thread;
+ ProcessThread* _main_thread;
+ MTDM* _mtdm;
+ bool _measuring_latency;
+ PortEngine::PortHandle _latency_input_port;
+ PortEngine::PortHandle _latency_output_port;
+ framecnt_t _latency_flush_frames;
+ std::string _latency_input_name;
+ std::string _latency_output_name;
+ framecnt_t _latency_signal_latency;
+
+ void meter_thread ();
+ void start_metering_thread ();
+ void stop_metering_thread ();
+
+ static gint m_meter_exit;
+
+ void parameter_changed (const std::string&);
+ PBD::ScopedConnection config_connection;
+
+ typedef std::map<std::string,AudioBackendInfo*> BackendMap;
+ BackendMap _backends;
+ AudioBackendInfo* backend_discover (const std::string&);
+ void drop_backend ();
};
-
+
} // namespace ARDOUR
#endif /* __ardour_audioengine_h__ */
diff --git a/libs/ardour/ardour/backend_search_path.h b/libs/ardour/ardour/backend_search_path.h
new file mode 100644
index 0000000000..e8a5082c71
--- /dev/null
+++ b/libs/ardour/ardour/backend_search_path.h
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 2011 Paul Davis
+
+ 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.
+*/
+
+#ifndef __ardour_backend_search_path_h__
+#define __ardour_backend_search_path_h__
+
+#include "pbd/search_path.h"
+
+namespace ARDOUR {
+
+ /**
+ * return a SearchPath containing directories in which to look for
+ * backend plugins.
+ *
+ * If ARDOUR_BACKEND_PATH is defined then the SearchPath returned
+ * will contain only those directories specified in it, otherwise it will
+ * contain the user and system directories which may contain audio/MIDI
+ * backends.
+ */
+ PBD::Searchpath backend_search_path ();
+
+} // namespace ARDOUR
+
+#endif /* __ardour_backend_search_path_h__ */
diff --git a/libs/ardour/ardour/buffer_set.h b/libs/ardour/ardour/buffer_set.h
index 12e9dbc63b..26d47682af 100644
--- a/libs/ardour/ardour/buffer_set.h
+++ b/libs/ardour/ardour/buffer_set.h
@@ -70,7 +70,7 @@ public:
void clear();
void attach_buffers (PortSet& ports);
- void get_jack_port_addresses (PortSet &, framecnt_t);
+ void get_backend_port_addresses (PortSet &, framecnt_t);
/* the capacity here is a size_t and has a different interpretation depending
on the DataType of the buffers. for audio, its a frame count. for MIDI
diff --git a/libs/ardour/ardour/data_type.h b/libs/ardour/ardour/data_type.h
index 6a016ae75a..b4129e2629 100644
--- a/libs/ardour/ardour/data_type.h
+++ b/libs/ardour/ardour/data_type.h
@@ -21,11 +21,11 @@
#define __ardour_data_type_h__
#include <string>
-#include <jack/jack.h>
+#include <stdint.h>
+#include <glib.h>
namespace ARDOUR {
-
/** A type of Data Ardour is capable of processing.
*
* The majority of this class is dedicated to conversion to and from various
@@ -61,33 +61,25 @@ public:
/** Construct from a string (Used for loading from XML and Ports)
* The string can be as in an XML file (eg "audio" or "midi"), or a
- * Jack type string (from jack_port_type) */
+ */
DataType(const std::string& str)
: _symbol(NIL) {
- if (str == "audio" || str == JACK_DEFAULT_AUDIO_TYPE)
+ if (!g_ascii_strncasecmp(str.c_str(), "audio", str.length())) {
_symbol = AUDIO;
- else if (str == "midi" || str == JACK_DEFAULT_MIDI_TYPE)
+ } else if (!g_ascii_strncasecmp(str.c_str(), "midi", str.length())) {
_symbol = MIDI;
- }
-
- /** Get the Jack type this DataType corresponds to */
- const char* to_jack_type() const {
- switch (_symbol) {
- case AUDIO: return JACK_DEFAULT_AUDIO_TYPE;
- case MIDI: return JACK_DEFAULT_MIDI_TYPE;
- default: return "";
}
}
/** Inverse of the from-string constructor */
const char* to_string() const {
switch (_symbol) {
- case AUDIO: return "audio";
- case MIDI: return "midi";
- default: return "unknown"; // reeeally shouldn't ever happen
+ case AUDIO: return "audio";
+ case MIDI: return "midi";
+ default: return "unknown"; // reeeally shouldn't ever happen
}
}
-
+
const char* to_i18n_string () const;
inline operator uint32_t() const { return (uint32_t)_symbol; }
@@ -125,7 +117,6 @@ private:
};
-
} // namespace ARDOUR
#endif // __ardour_data_type_h__
diff --git a/libs/ardour/ardour/debug.h b/libs/ardour/ardour/debug.h
index ae18e59c04..47eee69df5 100644
--- a/libs/ardour/ardour/debug.h
+++ b/libs/ardour/ardour/debug.h
@@ -64,6 +64,7 @@ namespace PBD {
extern uint64_t OrderKeys;
extern uint64_t Automation;
extern uint64_t WiimoteControl;
+ extern uint64_t Ports;
}
}
diff --git a/libs/ardour/ardour/directory_names.h b/libs/ardour/ardour/directory_names.h
index cb4701d336..72a456efe6 100644
--- a/libs/ardour/ardour/directory_names.h
+++ b/libs/ardour/ardour/directory_names.h
@@ -39,6 +39,7 @@ extern const char* const surfaces_dir_name;
extern const char* const ladspa_dir_name;
extern const char* const user_config_dir_name;
extern const char* const panner_dir_name;
+extern const char* const backend_dir_name;
};
diff --git a/libs/ardour/ardour/diskstream.h b/libs/ardour/ardour/diskstream.h
index 427b52b054..7a4bccbb46 100644
--- a/libs/ardour/ardour/diskstream.h
+++ b/libs/ardour/ardour/diskstream.h
@@ -133,8 +133,8 @@ class Diskstream : public SessionObject, public PublicDiskstream
virtual XMLNode& get_state(void);
virtual int set_state(const XMLNode&, int version);
- virtual void request_jack_monitors_input (bool) {}
- virtual void ensure_jack_monitors_input (bool) {}
+ virtual void request_input_monitoring (bool) {}
+ virtual void ensure_input_monitoring (bool) {}
framecnt_t capture_offset() const { return _capture_offset; }
virtual void set_capture_offset ();
diff --git a/libs/ardour/ardour/graph.h b/libs/ardour/ardour/graph.h
index 0a288d68d3..cac09d34af 100644
--- a/libs/ardour/ardour/graph.h
+++ b/libs/ardour/ardour/graph.h
@@ -92,7 +92,7 @@ protected:
virtual void session_going_away ();
private:
- std::list<jack_native_thread_t> _thread_list;
+ std::list<pthread_t> _thread_list;
volatile bool _quit_threads;
void reset_thread_list ();
diff --git a/libs/ardour/ardour/midi_buffer.h b/libs/ardour/ardour/midi_buffer.h
index 5ef5e4c845..781396a598 100644
--- a/libs/ardour/ardour/midi_buffer.h
+++ b/libs/ardour/ardour/midi_buffer.h
@@ -44,7 +44,6 @@ public:
void copy(const MidiBuffer& copy);
bool push_back(const Evoral::MIDIEvent<TimeType>& event);
- bool push_back(const jack_midi_event_t& event);
bool push_back(TimeType time, size_t size, const uint8_t* data);
uint8_t* reserve(TimeType time, size_t size);
diff --git a/libs/ardour/ardour/midi_diskstream.h b/libs/ardour/ardour/midi_diskstream.h
index 34fa0ae79a..b1c126b339 100644
--- a/libs/ardour/ardour/midi_diskstream.h
+++ b/libs/ardour/ardour/midi_diskstream.h
@@ -81,7 +81,7 @@ class MidiDiskstream : public Diskstream
XMLNode& get_state(void);
int set_state(const XMLNode&, int version);
- void ensure_jack_monitors_input (bool);
+ void ensure_input_monitoring (bool);
boost::shared_ptr<SMFSource> write_source () { return _write_source; }
diff --git a/libs/ardour/ardour/midi_port.h b/libs/ardour/ardour/midi_port.h
index 5dc55398cb..00617d90ec 100644
--- a/libs/ardour/ardour/midi_port.h
+++ b/libs/ardour/ardour/midi_port.h
@@ -21,6 +21,8 @@
#ifndef __ardour_midi_port_h__
#define __ardour_midi_port_h__
+#include "midi++/parser.h"
+
#include "ardour/port.h"
#include "ardour/midi_buffer.h"
#include "ardour/midi_state_tracker.h"
@@ -56,18 +58,35 @@ class MidiPort : public Port {
MidiBuffer& get_midi_buffer (pframes_t nframes);
+ void set_always_parse (bool yn);
+ MIDI::Parser& self_parser() { return _self_parser; }
+
protected:
- friend class AudioEngine;
+ friend class PortManager;
- MidiPort (const std::string& name, Flags);
+ MidiPort (const std::string& name, PortFlags);
private:
MidiBuffer* _buffer;
bool _has_been_mixed_down;
bool _resolve_required;
bool _input_active;
-
- void resolve_notes (void* jack_buffer, MidiBuffer::TimeType when);
+ bool _always_parse;
+
+ /* Naming this is tricky. AsyncMIDIPort inherits (for now, aug 2013) from
+ * both MIDI::Port, which has _parser, and this (ARDOUR::MidiPort). We
+ * need parsing support in this object, independently of what the
+ * MIDI::Port/AsyncMIDIPort stuff does. Rather than risk errors coming
+ * from not explicitly naming which _parser we want, we will call this
+ * _self_parser for now.
+ *
+ * Ultimately, MIDI::Port should probably go away or be fully integrated
+ * into this object, somehow.
+ */
+
+ MIDI::Parser _self_parser;
+
+ void resolve_notes (void* buffer, MidiBuffer::TimeType when);
};
} // namespace ARDOUR
diff --git a/libs/ardour/ardour/midi_ui.h b/libs/ardour/ardour/midi_ui.h
index 34e97494be..c15a530057 100644
--- a/libs/ardour/ardour/midi_ui.h
+++ b/libs/ardour/ardour/midi_ui.h
@@ -26,13 +26,11 @@
#include "pbd/signals.h"
#include "pbd/stacktrace.h"
-namespace MIDI {
- class Port;
-}
namespace ARDOUR {
class Session;
+class AsyncMIDIPort;
/* this is mostly a placeholder because I suspect that at some
point we will want to add more members to accomodate
@@ -67,7 +65,7 @@ class MidiControlUI : public AbstractUI<MidiUIRequest>
ARDOUR::Session& _session;
PBD::ScopedConnection rebind_connection;
- bool midi_input_handler (Glib::IOCondition, MIDI::Port*);
+ bool midi_input_handler (Glib::IOCondition, AsyncMIDIPort*);
void reset_ports ();
void clear_ports ();
diff --git a/libs/ardour/ardour/midiport_manager.h b/libs/ardour/ardour/midiport_manager.h
new file mode 100644
index 0000000000..9f93c43d5a
--- /dev/null
+++ b/libs/ardour/ardour/midiport_manager.h
@@ -0,0 +1,99 @@
+/*
+ Copyright (C) 1998 Paul Barton-Davis
+
+ 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.
+
+*/
+
+#ifndef __midiport_manager_h__
+#define __midiport_manager_h__
+
+#include <list>
+
+#include <string>
+
+#include "pbd/rcu.h"
+
+#include "midi++/types.h"
+#include "midi++/port.h"
+
+#include "ardour/types.h"
+
+namespace ARDOUR {
+
+class MidiPort;
+class Port;
+
+class MidiPortManager {
+ public:
+ MidiPortManager();
+ virtual ~MidiPortManager ();
+
+ /* Ports used for control. These are read/written to outside of the
+ * process callback (asynchronously with respect to when data
+ * actually arrives).
+ *
+ * More detail: we do actually read/write data for these ports
+ * inside the process callback, but incoming data is only parsed
+ * and outgoing data is only generated *outside* the process
+ * callback.
+ */
+
+ MIDI::Port* midi_input_port () const { return _midi_input_port; }
+ MIDI::Port* midi_output_port () const { return _midi_output_port; }
+ MIDI::Port* mmc_input_port () const { return _mmc_input_port; }
+ MIDI::Port* mmc_output_port () const { return _mmc_output_port; }
+
+ /* Ports used for synchronization. These have their I/O handled inside the
+ * process callback.
+ */
+
+ boost::shared_ptr<MidiPort> mtc_input_port() const { return _mtc_input_port; }
+ boost::shared_ptr<MidiPort> mtc_output_port() const { return _mtc_output_port; }
+ boost::shared_ptr<MidiPort> midi_clock_input_port() const { return _midi_clock_input_port; }
+ boost::shared_ptr<MidiPort> midi_clock_output_port() const { return _midi_clock_output_port; }
+
+ void set_midi_port_states (const XMLNodeList&);
+ std::list<XMLNode*> get_midi_port_states () const;
+
+ PBD::Signal0<void> PortsChanged;
+
+ protected:
+ /* asynchronously handled ports: MIDI::Port */
+ MIDI::Port* _midi_input_port;
+ MIDI::Port* _midi_output_port;
+ MIDI::Port* _mmc_input_port;
+ MIDI::Port* _mmc_output_port;
+ /* these point to the same objects as the 4 members above,
+ but cast to their ARDOUR::Port base class
+ */
+ boost::shared_ptr<Port> _midi_in;
+ boost::shared_ptr<Port> _midi_out;
+ boost::shared_ptr<Port> _mmc_in;
+ boost::shared_ptr<Port> _mmc_out;
+
+ /* synchronously handled ports: ARDOUR::MidiPort */
+ boost::shared_ptr<MidiPort> _mtc_input_port;
+ boost::shared_ptr<MidiPort> _mtc_output_port;
+ boost::shared_ptr<MidiPort> _midi_clock_input_port;
+ boost::shared_ptr<MidiPort> _midi_clock_output_port;
+
+ void create_ports ();
+
+};
+
+} // namespace MIDI
+
+#endif // __midi_port_manager_h__
diff --git a/libs/ardour/ardour/port.h b/libs/ardour/ardour/port.h
index e225117d94..77bf2b6f71 100644
--- a/libs/ardour/ardour/port.h
+++ b/libs/ardour/ardour/port.h
@@ -30,6 +30,7 @@
#include "pbd/signals.h"
#include "ardour/data_type.h"
+#include "ardour/port_engine.h"
#include "ardour/types.h"
namespace ARDOUR {
@@ -40,11 +41,6 @@ class Buffer;
class Port : public boost::noncopyable
{
public:
- enum Flags {
- IsInput = JackPortIsInput,
- IsOutput = JackPortIsOutput,
- };
-
virtual ~Port ();
static void set_connecting_blocked( bool yn ) {
@@ -62,7 +58,7 @@ public:
int set_name (std::string const &);
/** @return flags */
- Flags flags () const {
+ PortFlags flags () const {
return _flags;
}
@@ -90,24 +86,24 @@ public:
virtual int connect (Port *);
int disconnect (Port *);
- void ensure_jack_monitors_input (bool);
- bool jack_monitoring_input () const;
+ void request_input_monitoring (bool);
+ void ensure_input_monitoring (bool);
+ bool monitoring_input () const;
int reestablish ();
int reconnect ();
- void request_jack_monitors_input (bool);
bool last_monitor() const { return _last_monitor; }
void set_last_monitor (bool yn) { _last_monitor = yn; }
- jack_port_t* jack_port() const { return _jack_port; }
+ PortEngine::PortHandle port_handle() { return _port_handle; }
- void get_connected_latency_range (jack_latency_range_t& range, bool playback) const;
+ void get_connected_latency_range (LatencyRange& range, bool playback) const;
- void set_private_latency_range (jack_latency_range_t& range, bool playback);
- const jack_latency_range_t& private_latency_range (bool playback) const;
+ void set_private_latency_range (LatencyRange& range, bool playback);
+ const LatencyRange& private_latency_range (bool playback) const;
- void set_public_latency_range (jack_latency_range_t& range, bool playback) const;
- jack_latency_range_t public_latency_range (bool playback) const;
+ void set_public_latency_range (LatencyRange& range, bool playback) const;
+ LatencyRange public_latency_range (bool playback) const;
virtual void reset ();
@@ -122,8 +118,6 @@ public:
bool physically_connected () const;
- static void set_engine (AudioEngine *);
-
PBD::Signal1<void,bool> MonitorInputChanged;
static PBD::Signal2<void,boost::shared_ptr<Port>,boost::shared_ptr<Port> > PostDisconnect;
static PBD::Signal0<void> PortDrop;
@@ -141,11 +135,16 @@ public:
virtual void increment_port_buffer_offset (pframes_t n);
+ virtual XMLNode& get_state (void) const;
+ virtual int set_state (const XMLNode&, int version);
+
+ static std::string state_node_name;
+
protected:
- Port (std::string const &, DataType, Flags);
+ Port (std::string const &, DataType, PortFlags);
- jack_port_t* _jack_port; ///< JACK port
+ PortEngine::PortHandle _port_handle;
static bool _connecting_blocked;
static pframes_t _global_port_buffer_offset; /* access only from process() tree */
@@ -153,18 +152,16 @@ protected:
framecnt_t _port_buffer_offset; /* access only from process() tree */
- jack_latency_range_t _private_playback_latency;
- jack_latency_range_t _private_capture_latency;
-
- static AudioEngine* _engine; ///< the AudioEngine
+ LatencyRange _private_playback_latency;
+ LatencyRange _private_capture_latency;
private:
std::string _name; ///< port short name
- Flags _flags; ///< flags
+ PortFlags _flags; ///< flags
bool _last_monitor;
/** ports that we are connected to, kept so that we can
- reconnect to JACK when required
+ reconnect to the backend when required
*/
std::set<std::string> _connections;
diff --git a/libs/ardour/ardour/port_engine.h b/libs/ardour/ardour/port_engine.h
new file mode 100644
index 0000000000..71f93ea05e
--- /dev/null
+++ b/libs/ardour/ardour/port_engine.h
@@ -0,0 +1,345 @@
+/*
+ Copyright (C) 2013 Paul Davis
+
+ 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.
+
+*/
+
+#ifndef __libardour_port_engine_h__
+#define __libardour_port_engine_h__
+
+#include <vector>
+#include <string>
+
+#include <stdint.h>
+
+#include "ardour/data_type.h"
+#include "ardour/types.h"
+
+namespace ARDOUR {
+
+class PortManager;
+
+/** PortEngine is an abstract base class that defines the functionality
+ * required by Ardour.
+ *
+ * A Port is basically an endpoint for a datastream (which can either be
+ * continuous, like audio, or event-based, like MIDI). Ports have buffers
+ * associated with them into which data can be written (if they are output
+ * ports) and from which data can be read (if they input ports). Ports can be
+ * connected together so that data written to an output port can be read from
+ * an input port. These connections can be 1:1, 1:N OR N:1.
+ *
+ * Ports may be associated with software only, or with hardware. Hardware
+ * related ports are often referred to as physical, and correspond to some
+ * relevant physical entity on a hardware device, such as an audio jack or a
+ * MIDI connector. Physical ports may be potentially asked to monitor their
+ * inputs, though some implementations may not support this.
+ *
+ * Most physical ports will also be considered "terminal", which means that
+ * data delivered there or read from there will go to or comes from a system
+ * outside of the PortEngine implementation's control (e.g. the analog domain
+ * for audio, or external MIDI devices for MIDI). Non-physical ports can also
+ * be considered "terminal". For example, the output port of a software
+ * synthesizer is a terminal port, because the data contained in its buffer
+ * does not and cannot be considered to come from any other port - it is
+ * synthesized by its owner.
+ *
+ * Ports also have latency associated with them. Each port has a playback
+ * latency and a capture latency:
+ *
+ * <b>capture latency</b>: how long since the data read from the buffer of a
+ * port arrived at at a terminal port. The data will have
+ * come from the "outside world" if the terminal port is also
+ * physical, or will have been synthesized by the entity that
+ * owns the terminal port.
+ *
+ * <b>playback latency</b>: how long until the data written to the buffer of
+ * port will reach a terminal port.
+ *
+ *
+ * For more detailed questions about the PortEngine API, consult the JACK API
+ * documentation, on which this entire object is based.
+ */
+
+class PortEngine {
+ public:
+ PortEngine (PortManager& pm) : manager (pm) {}
+ virtual ~PortEngine() {}
+
+ /* We use void* here so that the API can be defined for any implementation.
+ *
+ * We could theoretically use a template (PortEngine<T>) and define
+ * PortHandle as T, but this complicates the desired inheritance
+ * pattern in which FooPortEngine handles things for the Foo API,
+ * rather than being a derivative of PortEngine<Foo>.
+ */
+
+ typedef void* PortHandle;
+
+ /** Return a typeless pointer to an object that may be of interest
+ * that understands the internals of a particular PortEngine
+ * implementation.
+ *
+ * XXX the existence of this method is a band-aid over some design
+ * issues and will it will be removed in the future
+ */
+ virtual void* private_handle() const = 0;
+
+ virtual bool connected() const = 0;
+
+ /** Return the name of this process as used by the port manager
+ * when naming ports.
+ */
+ virtual const std::string& my_name() const = 0;
+
+ /** Return the maximum size of a port name
+ */
+ virtual uint32_t port_name_size() const = 0;
+
+ /** Returns zero if the port referred to by @param port was set to @param
+ * name. Return non-zero otherwise.
+ */
+ virtual int set_port_name (PortHandle port, const std::string& name) = 0;
+ /** Return the name of the port referred to by @param port. If the port
+ * does not exist, return an empty string.
+ */
+ virtual std::string get_port_name (PortHandle) const = 0;
+ /** Return a reference to a port with the fullname @param name. Return
+ * a null pointer if no such port exists.
+ */
+ virtual PortHandle* get_port_by_name (const std::string&) const = 0;
+
+ /** Find the set of ports whose names, types and flags match
+ * specified values, place the names of each port into @param ports,
+ * and return the count of the number found.
+ *
+ * To avoid selecting by name, pass an empty string for @param
+ * port_name_pattern.
+ *
+ * To avoid selecting by type, pass DataType::NIL as @param type.
+ *
+ * To avoid selecting by flags, pass PortFlags (0) as @param flags.
+ */
+ virtual int get_ports (const std::string& port_name_pattern, DataType type, PortFlags flags, std::vector<std::string>& ports) const = 0;
+
+ /** Return the Ardour data type handled by the port referred to by @param
+ * port. Returns DataType::NIL if the port does not exist.
+ */
+ virtual DataType port_data_type (PortHandle port) const = 0;
+
+ /** Create a new port whose fullname will be the conjuction of my_name(),
+ * ":" and @param shortname. The port will handle data specified by @param
+ * type and will have the flags given by @param flags. If successfull,
+ * return a reference to the port, otherwise return a null pointer.
+ */
+ virtual PortHandle register_port (const std::string& shortname, ARDOUR::DataType type, ARDOUR::PortFlags flags) = 0;
+
+ /* Destroy the port referred to by @param port, including all resources
+ * associated with it. This will also disconnect @param port from any ports it
+ * is connected to.
+ */
+ virtual void unregister_port (PortHandle) = 0;
+
+ /* Connection management */
+
+ /** Ensure that data written to the port named by @param src will be
+ * readable from the port named by @param dst. Return zero on success,
+ * non-zero otherwise.
+ */
+ virtual int connect (const std::string& src, const std::string& dst) = 0;
+
+ /** Remove any existing connection between the ports named by @param src and
+ * @param dst. Return zero on success, non-zero otherwise.
+ */
+ virtual int disconnect (const std::string& src, const std::string& dst) = 0;
+
+
+ /** Ensure that data written to the port referenced by @param portwill be
+ * readable from the port named by @param dst. Return zero on success,
+ * non-zero otherwise.
+ */
+ virtual int connect (PortHandle src, const std::string& dst) = 0;
+ /** Remove any existing connection between the port referenced by @param src and
+ * the port named @param dst. Return zero on success, non-zero otherwise.
+ */
+ virtual int disconnect (PortHandle src, const std::string& dst) = 0;
+
+ /** Remove all connections between the port referred to by @param port and
+ * any other ports. Return zero on success, non-zero otherwise.
+ */
+ virtual int disconnect_all (PortHandle port) = 0;
+
+ /** Return true if the port referred to by @param port has any connections
+ * to other ports. Return false otherwise.
+ */
+ virtual bool connected (PortHandle port, bool process_callback_safe = true) = 0;
+ /** Return true if the port referred to by @param port is connected to
+ * the port named by @param name. Return false otherwise.
+ */
+ virtual bool connected_to (PortHandle, const std::string& name, bool process_callback_safe = true) = 0;
+
+ /** Return true if the port referred to by @param port has any connections
+ * to ports marked with the PortFlag IsPhysical. Return false otherwise.
+ */
+ virtual bool physically_connected (PortHandle port, bool process_callback_safe = true) = 0;
+
+ /** Place the names of all ports connected to the port named by @param
+ * ports into @param names, and return the number of connections.
+ */
+ virtual int get_connections (PortHandle port, std::vector<std::string>& names, bool process_callback_safe = true) = 0;
+
+ /* MIDI */
+
+ /** Retrieve a MIDI event from the data at @param port_buffer. The event
+ number to be retrieved is given by @param event_index (a value of zero
+ indicates that the first event in the port_buffer should be retrieved).
+ *
+ * The data associated with the event will be copied into the buffer at
+ * @param buf and the number of bytes written will be stored in @param
+ * size. The timestamp of the event (which is always relative to the start
+ * of the current process cycle, in samples) will be stored in @param
+ * timestamp
+ */
+ virtual int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** buf, void* port_buffer, uint32_t event_index) = 0;
+
+ /** Place a MIDI event consisting of @param size bytes copied from the data
+ * at @param buf into the port buffer referred to by @param
+ * port_buffer. The MIDI event will be marked with a time given by @param
+ * timestamp. Return zero on success, non-zero otherwise.
+ *
+ * Events must be added monotonically to a port buffer. An attempt to
+ * add a non-monotonic event (e.g. out-of-order) will cause this method
+ * to return a failure status.
+ */
+ virtual int midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size) = 0;
+
+ /** Return the number of MIDI events in the data at @param port_buffer
+ */
+ virtual uint32_t get_midi_event_count (void* port_buffer) = 0;
+
+ /** Clear the buffer at @param port_buffer of all MIDI events.
+ *
+ * After a call to this method, an immediate, subsequent call to
+ * get_midi_event_count() with the same @param port_buffer argument must
+ * return zero.
+ */
+ virtual void midi_clear (void* port_buffer) = 0;
+
+ /* Monitoring */
+
+ /** Return true if the implementation can offer input monitoring.
+ *
+ * Input monitoring involves the (selective) routing of incoming data
+ * to an outgoing data stream, without the data being passed to the CPU.
+ *
+ * Only certain audio hardware can provide this, and only certain audio
+ * APIs can offer it.
+ */
+ virtual bool can_monitor_input() const = 0;
+ /** Increment or decrement the number of requests to monitor the input
+ * of the hardware channel represented by the port referred to by @param
+ * port.
+ *
+ * If the number of requests rises above zero, input monitoring will
+ * be enabled (if can_monitor_input() returns true for the implementation).
+ *
+ * If the number of requests falls to zero, input monitoring will be
+ * disabled (if can_monitor_input() returns true for the implementation)
+ */
+ virtual int request_input_monitoring (PortHandle port, bool yn) = 0;
+ /* Force input monitoring of the hardware channel represented by the port
+ * referred to by @param port to be on or off, depending on the true/false
+ * status of @param yn. The request count is ignored when using this
+ * method, so if this is called with yn set to false, input monitoring will
+ * be disabled regardless of the number of requests to enable it.
+ */
+ virtual int ensure_input_monitoring (PortHandle port, bool yn) = 0;
+ /** Return true if input monitoring is enabled for the hardware channel
+ * represented by the port referred to by @param port. Return false
+ * otherwise.
+ */
+ virtual bool monitoring_input (PortHandle port) = 0;
+
+ /* Latency management
+ */
+
+ /** Set the latency range for the port referred to by @param port to @param
+ * r. The playback range will be set if @param for_playback is true,
+ * otherwise the capture range will be set.
+ */
+ virtual void set_latency_range (PortHandle port, bool for_playback, LatencyRange r) = 0;
+ /** Return the latency range for the port referred to by @param port.
+ * The playback range will be returned if @param for_playback is true,
+ * otherwise the capture range will be returned.
+ */
+ virtual LatencyRange get_latency_range (PortHandle port, bool for_playback) = 0;
+
+ /* Discovering physical ports */
+
+ /** Return true if the port referred to by @param port has the IsPhysical
+ * flag set. Return false otherwise.
+ */
+ virtual bool port_is_physical (PortHandle port) const = 0;
+
+ /** Store into @param names the names of all ports with the IsOutput and
+ * IsPhysical flag set, that handle data of type @param type.
+ *
+ * This can be used to discover outputs associated with hardware devices.
+ */
+ virtual void get_physical_outputs (DataType type, std::vector<std::string>& names) = 0;
+ /** Store into @param names the names of all ports with the IsInput and
+ * IsPhysical flags set, that handle data of type @param type.
+ *
+ * This can be used to discover inputs associated with hardware devices.
+ */
+ virtual void get_physical_inputs (DataType type, std::vector<std::string>& names) = 0;
+ /** Return the total count (possibly mixed between different data types)
+ of the number of ports with the IsPhysical and IsOutput flags set.
+ */
+ virtual ChanCount n_physical_outputs () const = 0;
+ /** Return the total count (possibly mixed between different data types)
+ of the number of ports with the IsPhysical and IsInput flags set.
+ */
+ virtual ChanCount n_physical_inputs () const = 0;
+
+ /** Return the address of the memory area where data for the port can be
+ * written (if the port has the PortFlag IsOutput set) or read (if the port
+ * has the PortFlag IsInput set).
+ *
+ * The return value is untyped because buffers containing different data
+ * depending on the port type.
+ */
+ virtual void* get_buffer (PortHandle, pframes_t) = 0;
+
+ /* MIDI ports (the ones in libmidi++) need this to be able to correctly
+ * schedule MIDI events within their buffers. It is a bit odd that we
+ * expose this here, because it is also exposed by AudioBackend, but they
+ * only have access to a PortEngine object, not an AudioBackend.
+ *
+ * Return the time according to the sample clock in use when the current
+ * buffer process cycle began.
+ *
+ * XXX to be removed after some more design cleanup.
+ */
+ virtual pframes_t sample_time_at_cycle_start () = 0;
+
+ protected:
+ PortManager& manager;
+};
+
+}
+
+#endif /* __libardour_port_engine_h__ */
diff --git a/libs/ardour/ardour/port_manager.h b/libs/ardour/ardour/port_manager.h
new file mode 100644
index 0000000000..6d45597a41
--- /dev/null
+++ b/libs/ardour/ardour/port_manager.h
@@ -0,0 +1,169 @@
+/*
+ Copyright (C) 2013 Paul Davis
+
+ 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.
+
+*/
+
+#ifndef __libardour_port_manager_h__
+#define __libardour_port_manager_h__
+
+#include <vector>
+#include <string>
+#include <exception>
+#include <map>
+
+#include <stdint.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include "pbd/rcu.h"
+
+#include "ardour/chan_count.h"
+#include "ardour/midiport_manager.h"
+#include "ardour/port.h"
+#include "ardour/port_engine.h"
+
+namespace ARDOUR {
+
+class PortManager
+{
+ public:
+ typedef std::map<std::string,boost::shared_ptr<Port> > Ports;
+ typedef std::list<boost::shared_ptr<Port> > PortList;
+
+ PortManager ();
+ virtual ~PortManager() {}
+
+ void set_port_engine (PortEngine& pe);
+ PortEngine& port_engine() { return *_impl; }
+
+ uint32_t port_name_size() const;
+ std::string my_name() const;
+
+ /* Port registration */
+
+ boost::shared_ptr<Port> register_input_port (DataType, const std::string& portname, bool async = false);
+ boost::shared_ptr<Port> register_output_port (DataType, const std::string& portname, bool async = false);
+ int unregister_port (boost::shared_ptr<Port>);
+
+ /* Port connectivity */
+
+ int connect (const std::string& source, const std::string& destination);
+ int disconnect (const std::string& source, const std::string& destination);
+ int disconnect (boost::shared_ptr<Port>);
+ int reestablish_ports ();
+ int reconnect_ports ();
+
+ bool connected (const std::string&);
+ bool connected_to (const std::string&, const std::string&);
+ bool physically_connected (const std::string&);
+ int get_connections (const std::string&, std::vector<std::string>&);
+
+ /* Naming */
+
+ boost::shared_ptr<Port> get_port_by_name (const std::string &);
+ void port_renamed (const std::string&, const std::string&);
+ std::string make_port_name_relative (const std::string& name) const;
+ std::string make_port_name_non_relative (const std::string& name) const;
+ bool port_is_mine (const std::string& fullname) const;
+
+ /* other Port management */
+
+ bool port_is_physical (const std::string&) const;
+ void get_physical_outputs (DataType type, std::vector<std::string>&);
+ void get_physical_inputs (DataType type, std::vector<std::string>&);
+ ChanCount n_physical_outputs () const;
+ ChanCount n_physical_inputs () const;
+
+ int get_ports (const std::string& port_name_pattern, DataType type, PortFlags flags, std::vector<std::string>&);
+ int get_ports (DataType, PortList&);
+
+ void remove_all_ports ();
+
+ /* per-Port monitoring */
+
+ bool can_request_input_monitoring () const;
+ void request_input_monitoring (const std::string&, bool) const;
+ void ensure_input_monitoring (const std::string&, bool) const;
+
+ class PortRegistrationFailure : public std::exception {
+ public:
+ PortRegistrationFailure (std::string const & why = "")
+ : reason (why) {}
+
+ ~PortRegistrationFailure () throw () {}
+
+ const char *what() const throw () { return reason.c_str(); }
+
+ private:
+ std::string reason;
+ };
+
+ /* the port engine will invoke these callbacks when the time is right */
+
+ void registration_callback ();
+ int graph_order_callback ();
+ void connect_callback (const std::string&, const std::string&, bool connection);
+
+ bool port_remove_in_progress() const { return _port_remove_in_progress; }
+
+ /** Emitted if the backend notifies us of a graph order event */
+ PBD::Signal0<void> GraphReordered;
+
+ /** Emitted if a Port is registered or unregistered */
+ PBD::Signal0<void> PortRegisteredOrUnregistered;
+
+ /** Emitted if a Port is connected or disconnected.
+ * The Port parameters are the ports being connected / disconnected, or 0 if they are not known to Ardour.
+ * The std::string parameters are the (long) port names.
+ * The bool parameter is true if ports were connected, or false for disconnected.
+ */
+ PBD::Signal5<void, boost::weak_ptr<Port>, std::string, boost::weak_ptr<Port>, std::string, bool> PortConnectedOrDisconnected;
+
+ protected:
+ boost::shared_ptr<PortEngine> _impl;
+ SerializedRCUManager<Ports> ports;
+ bool _port_remove_in_progress;
+
+ boost::shared_ptr<Port> register_port (DataType type, const std::string& portname, bool input, bool async = false);
+ void port_registration_failure (const std::string& portname);
+
+ /** List of ports to be used between ::cycle_start() and ::cycle_end()
+ */
+ boost::shared_ptr<Ports> _cycle_ports;
+
+ void fade_out (gain_t, gain_t, pframes_t);
+ void silence (pframes_t nframes);
+ void check_monitoring ();
+ /** Signal the start of an audio cycle.
+ * This MUST be called before any reading/writing for this cycle.
+ * Realtime safe.
+ */
+ void cycle_start (pframes_t nframes);
+
+ /** Signal the end of an audio cycle.
+ * This signifies that the cycle began with @ref cycle_start has ended.
+ * This MUST be called at the end of each cycle.
+ * Realtime safe.
+ */
+ void cycle_end (pframes_t nframes);
+};
+
+
+
+} // namespace
+
+#endif /* __libardour_port_manager_h__ */
diff --git a/libs/ardour/ardour/public_diskstream.h b/libs/ardour/ardour/public_diskstream.h
index da481a6dee..125e1a21ce 100644
--- a/libs/ardour/ardour/public_diskstream.h
+++ b/libs/ardour/ardour/public_diskstream.h
@@ -33,8 +33,8 @@ public:
virtual ~PublicDiskstream() {}
virtual boost::shared_ptr<Playlist> playlist () = 0;
- virtual void request_jack_monitors_input (bool) = 0;
- virtual void ensure_jack_monitors_input (bool) = 0;
+ virtual void request_input_monitoring (bool) = 0;
+ virtual void ensure_input_monitoring (bool) = 0;
virtual bool destructive () const = 0;
virtual std::list<boost::shared_ptr<Source> > & last_capture_sources () = 0;
virtual void set_capture_offset () = 0;
diff --git a/libs/ardour/ardour/rc_configuration.h b/libs/ardour/ardour/rc_configuration.h
index ff1c5f035c..e2f68477a1 100644
--- a/libs/ardour/ardour/rc_configuration.h
+++ b/libs/ardour/ardour/rc_configuration.h
@@ -52,7 +52,6 @@ class RCConfiguration : public Configuration
XMLNode * instant_xml (const std::string& str);
XMLNode* control_protocol_state () { return _control_protocol_state; }
- std::list<XMLNode*> midi_port_states () { return _midi_port_states; }
/* define accessor methods */
@@ -81,11 +80,6 @@ class RCConfiguration : public Configuration
#undef CONFIG_VARIABLE_SPECIAL
XMLNode* _control_protocol_state;
-
- /** MIDI port nodes from the RC configuration. We store them so that we can set their
- state once the audio engine and hence ports are up.
- */
- std::list<XMLNode*> _midi_port_states;
};
/* XXX: rename this */
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index 525dfbd849..43660d41cc 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -110,6 +110,8 @@ class IOProcessor;
class ImportStatus;
class MidiClockTicker;
class MidiControlUI;
+class MidiPortManager;
+class MidiPort;
class MidiRegion;
class MidiSource;
class MidiTrack;
@@ -381,9 +383,6 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
framecnt_t worst_track_latency () const { return _worst_track_latency; }
framecnt_t worst_playback_latency () const { return _worst_output_latency + _worst_track_latency; }
-#ifdef HAVE_JACK_SESSION
- void jack_session_event (jack_session_event_t* event);
-#endif
int save_state (std::string snapshot_name, bool pending = false, bool switch_to_snapshot = false);
int restore_state (std::string snapshot_name);
int save_template (std::string template_name);
@@ -556,6 +555,11 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
int remove_last_capture ();
+ /** handlers should return 0 for "everything OK", and any other value for
+ * "cannot setup audioengine".
+ */
+ static PBD::Signal1<int,uint32_t> AudioEngineSetupRequired;
+
/** handlers should return -1 for "stop cleanup",
0 for "yes, delete this playlist",
1 for "no, don't delete this playlist".
@@ -813,8 +817,8 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
boost::shared_ptr<SessionPlaylists> playlists;
void send_mmc_locate (framepos_t);
- int send_full_time_code (framepos_t);
- void send_song_position_pointer (framepos_t);
+ void queue_full_time_code () { _send_timecode_update = true; }
+ void queue_song_position_pointer () { /* currently does nothing */ }
bool step_editing() const { return (_step_editors > 0); }
@@ -863,6 +867,27 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
boost::shared_ptr<IO> ltc_input_io() { return _ltc_input; }
boost::shared_ptr<IO> ltc_output_io() { return _ltc_output; }
+ MIDI::Port* midi_input_port () const;
+ MIDI::Port* midi_output_port () const;
+ MIDI::Port* mmc_output_port () const;
+ MIDI::Port* mmc_input_port () const;
+
+ boost::shared_ptr<MidiPort> midi_clock_output_port () const;
+ boost::shared_ptr<MidiPort> midi_clock_input_port () const;
+ boost::shared_ptr<MidiPort> mtc_output_port () const;
+ boost::shared_ptr<MidiPort> mtc_input_port () const;
+
+ MIDI::MachineControl& mmc() { return *_mmc; }
+
+ /* Callbacks specifically related to JACK, and called directly
+ * from the JACK audio backend.
+ */
+
+#ifdef HAVE_JACK_SESSION
+ void jack_session_event (jack_session_event_t* event);
+#endif
+ void jack_timebase_callback (jack_transport_state_t, pframes_t, jack_position_t*, int);
+
protected:
friend class AudioEngine;
void set_block_size (pframes_t nframes);
@@ -1046,7 +1071,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
boost::scoped_ptr<SessionDirectory> _session_dir;
void hookup_io ();
- void when_engine_running ();
+ int when_engine_running ();
void graph_reordered ();
/** current snapshot name, without the .ardour suffix */
@@ -1112,8 +1137,8 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
void auto_loop_changed (Location *);
void auto_loop_declick_range (Location *, framepos_t &, framepos_t &);
- void first_stage_init (std::string path, std::string snapshot_name);
- int second_stage_init ();
+ void pre_engine_init (std::string path);
+ int post_engine_init ();
void remove_empty_sounds ();
void setup_midi_control ();
@@ -1216,7 +1241,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
framepos_t ltc_timecode_offset;
bool ltc_timecode_negative_offset;
- jack_latency_range_t ltc_out_latency;
+ LatencyRange ltc_out_latency;
void ltc_tx_initialize();
void ltc_tx_cleanup();
@@ -1261,6 +1286,13 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
void engine_halted ();
void xrun_recovery ();
+ /* These are synchronous and so can only be called from within the process
+ * cycle
+ */
+
+ int send_full_time_code (framepos_t, pframes_t nframes);
+ void send_song_position_pointer (framepos_t);
+
TempoMap *_tempo_map;
void tempo_map_changed (const PBD::PropertyChange&);
@@ -1429,9 +1461,8 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
*/
std::list<GQuark> _current_trans_quarks;
- void jack_timebase_callback (jack_transport_state_t, pframes_t, jack_position_t*, int);
- int jack_sync_callback (jack_transport_state_t, jack_position_t*);
- void reset_jack_connection (jack_client_t* jack);
+ int backend_sync_callback (TransportState, framepos_t);
+
void process_rtop (SessionEvent*);
void update_latency (bool playback);
@@ -1585,6 +1616,14 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
void reconnect_ltc_input ();
void reconnect_ltc_output ();
+
+ /* persistent, non-track related MIDI ports */
+ MidiPortManager* _midi_ports;
+ MIDI::MachineControl* _mmc;
+
+ void setup_ltc ();
+ void setup_click ();
+ void setup_bundles ();
};
} // namespace ARDOUR
diff --git a/libs/ardour/ardour/slave.h b/libs/ardour/ardour/slave.h
index 9862824da9..c60acb1df7 100644
--- a/libs/ardour/ardour/slave.h
+++ b/libs/ardour/ardour/slave.h
@@ -40,14 +40,12 @@
#define PLUSMINUS(A) ( ((A)<0) ? "-" : (((A)>0) ? "+" : "\u00B1") )
#define LEADINGZERO(A) ( (A)<10 ? " " : (A)<100 ? " " : (A)<1000 ? " " : "" )
-namespace MIDI {
- class Port;
-}
-
namespace ARDOUR {
class TempoMap;
class Session;
+class AudioEngine;
+class MidiPort;
/**
* @class Slave
@@ -252,10 +250,10 @@ class TimecodeSlave : public Slave {
class MTC_Slave : public TimecodeSlave {
public:
- MTC_Slave (Session&, MIDI::Port&);
+ MTC_Slave (Session&, MidiPort&);
~MTC_Slave ();
- void rebind (MIDI::Port&);
+ void rebind (MidiPort&);
bool speed_and_position (double&, framepos_t&);
bool locked() const;
@@ -273,7 +271,7 @@ class MTC_Slave : public TimecodeSlave {
private:
Session& session;
- MIDI::Port* port;
+ MidiPort* port;
PBD::ScopedConnectionList port_connections;
PBD::ScopedConnection config_connection;
bool can_notify_on_unknown_rate;
@@ -354,7 +352,7 @@ public:
std::string approximate_current_delta() const;
private:
- void parse_ltc(const jack_nframes_t, const jack_default_audio_sample_t * const, const framecnt_t);
+ void parse_ltc(const pframes_t, const Sample* const, const framecnt_t);
void process_ltc(framepos_t const);
void init_engine_dll (framepos_t, int32_t);
bool detect_discontinuity(LTCFrameExt *, int, bool);
@@ -391,7 +389,7 @@ public:
PBD::ScopedConnectionList port_connections;
PBD::ScopedConnection config_connection;
- jack_latency_range_t ltc_slave_latency;
+ LatencyRange ltc_slave_latency;
/* DLL - chase LTC */
int transport_direction;
@@ -404,13 +402,13 @@ public:
class MIDIClock_Slave : public Slave {
public:
- MIDIClock_Slave (Session&, MIDI::Port&, int ppqn = 24);
+ MIDIClock_Slave (Session&, MidiPort&, int ppqn = 24);
/// Constructor for unit tests
MIDIClock_Slave (ISlaveSessionProxy* session_proxy = 0, int ppqn = 24);
~MIDIClock_Slave ();
- void rebind (MIDI::Port&);
+ void rebind (MidiPort&);
bool speed_and_position (double&, framepos_t&);
bool locked() const;
@@ -426,7 +424,6 @@ class MIDIClock_Slave : public Slave {
protected:
ISlaveSessionProxy* session;
- MIDI::Port* port;
PBD::ScopedConnectionList port_connections;
/// pulses per quarter note for one MIDI clock frame (default 24)
@@ -492,7 +489,7 @@ class MIDIClock_Slave : public Slave {
class JACK_Slave : public Slave
{
public:
- JACK_Slave (jack_client_t*);
+ JACK_Slave (AudioEngine&);
~JACK_Slave ();
bool speed_and_position (double& speed, framepos_t& pos);
@@ -502,11 +499,10 @@ class JACK_Slave : public Slave
bool ok() const;
framecnt_t resolution () const { return 1; }
bool requires_seekahead () const { return false; }
- void reset_client (jack_client_t* jack);
bool is_always_synced() const { return true; }
private:
- jack_client_t* jack;
+ AudioEngine& engine;
double speed;
bool _starting;
};
diff --git a/libs/ardour/ardour/ticker.h b/libs/ardour/ardour/ticker.h
index b6e5376c12..7f0d1987fc 100644
--- a/libs/ardour/ardour/ticker.h
+++ b/libs/ardour/ardour/ticker.h
@@ -27,17 +27,13 @@
#include "ardour/session_handle.h"
-#ifndef TICKER_H_
-#define TICKER_H_
+#ifndef __libardour_ticker_h__
+#define __libardour_ticker_h__
-namespace MIDI {
- class Port;
-}
-
-namespace ARDOUR
-{
+namespace ARDOUR {
class Session;
+class MidiPort;
class MidiClockTicker : public SessionHandlePtr, boost::noncopyable
{
@@ -45,7 +41,7 @@ public:
MidiClockTicker ();
virtual ~MidiClockTicker();
- void tick (const framepos_t& transport_frames);
+ void tick (const framepos_t& transport_frames, pframes_t nframes);
bool has_midi_port() const { return _midi_port != 0; }
@@ -58,9 +54,6 @@ public:
/// slot for the signal session::TransportStateChange
void transport_state_changed();
- /// slot for the signal session::PositionChanged
- void position_changed (framepos_t position);
-
/// slot for the signal session::TransportLooped
void transport_looped();
@@ -70,23 +63,25 @@ public:
/// pulses per quarter note (default 24)
void set_ppqn(int ppqn) { _ppqn = ppqn; }
-private:
- MIDI::Port* _midi_port;
- int _ppqn;
- double _last_tick;
-
- class Position;
- boost::scoped_ptr<Position> _pos;
-
- double one_ppqn_in_frames (framepos_t transport_position);
-
- void send_midi_clock_event (pframes_t offset);
- void send_start_event (pframes_t offset);
- void send_continue_event (pframes_t offset);
- void send_stop_event (pframes_t offset);
- void send_position_event (uint32_t midi_clocks, pframes_t offset);
+ private:
+ boost::shared_ptr<MidiPort> _midi_port;
+ int _ppqn;
+ double _last_tick;
+ bool _send_pos;
+ bool _send_state;
+
+ class Position;
+ boost::scoped_ptr<Position> _pos;
+
+ double one_ppqn_in_frames (framepos_t transport_position);
+
+ void send_midi_clock_event (pframes_t offset, pframes_t nframes);
+ void send_start_event (pframes_t offset, pframes_t nframes);
+ void send_continue_event (pframes_t offset, pframes_t nframes);
+ void send_stop_event (pframes_t offset, pframes_t nframes);
+ void send_position_event (uint32_t midi_clocks, pframes_t offset, pframes_t nframes);
};
-
}
+ // namespace
-#endif /* TICKER_H_ */
+#endif /* __libardour_ticker_h__ */
diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h
index 7159261b51..bdf420763b 100644
--- a/libs/ardour/ardour/track.h
+++ b/libs/ardour/ardour/track.h
@@ -115,8 +115,8 @@ class Track : public Route, public PublicDiskstream
/* PublicDiskstream interface */
boost::shared_ptr<Playlist> playlist ();
- void request_jack_monitors_input (bool);
- void ensure_jack_monitors_input (bool);
+ void request_input_monitoring (bool);
+ void ensure_input_monitoring (bool);
bool destructive () const;
std::list<boost::shared_ptr<Source> > & last_capture_sources ();
void set_capture_offset ();
diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h
index 2115149872..17bffc20e4 100644
--- a/libs/ardour/ardour/types.h
+++ b/libs/ardour/ardour/types.h
@@ -28,8 +28,6 @@
#include <stdint.h>
#include <inttypes.h>
-#include <jack/types.h>
-#include <jack/midiport.h>
#include "timecode/bbt_time.h"
#include "timecode/time.h"
@@ -53,12 +51,12 @@ namespace ARDOUR {
class Route;
class Region;
- typedef jack_default_audio_sample_t Sample;
- typedef float pan_t;
- typedef float gain_t;
- typedef uint32_t layer_t;
- typedef uint64_t microseconds_t;
- typedef jack_nframes_t pframes_t;
+ typedef float Sample;
+ typedef float pan_t;
+ typedef float gain_t;
+ typedef uint32_t layer_t;
+ typedef uint64_t microseconds_t;
+ typedef uint32_t pframes_t;
/* Any position measured in audio frames.
Assumed to be non-negative but not enforced.
@@ -585,6 +583,32 @@ namespace ARDOUR {
FadeSymmetric,
};
+ enum TransportState {
+ /* these values happen to match the constants used by JACK but
+ this equality cannot be assumed.
+ */
+ TransportStopped = 0,
+ TransportRolling = 1,
+ TransportLooping = 2,
+ TransportStarting = 3,
+ };
+
+ enum PortFlags {
+ /* these values happen to match the constants used by JACK but
+ this equality cannot be assumed.
+ */
+ IsInput = 0x1,
+ IsOutput = 0x2,
+ IsPhysical = 0x4,
+ CanMonitor = 0x8,
+ IsTerminal = 0x10
+ };
+
+ struct LatencyRange {
+ uint32_t min; //< samples
+ uint32_t max; //< samples
+ };
+
} // namespace ARDOUR
diff --git a/libs/ardour/async_midi_port.cc b/libs/ardour/async_midi_port.cc
new file mode 100644
index 0000000000..77ca62961e
--- /dev/null
+++ b/libs/ardour/async_midi_port.cc
@@ -0,0 +1,303 @@
+/*
+ Copyright (C) 1998 Paul Barton-Davis
+
+ 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.
+
+ $Id$
+*/
+
+#include <iostream>
+
+#include "pbd/error.h"
+#include "pbd/stacktrace.h"
+
+#include "midi++/types.h"
+
+#include "ardour/async_midi_port.h"
+#include "ardour/audioengine.h"
+#include "ardour/midi_buffer.h"
+
+using namespace MIDI;
+using namespace ARDOUR;
+using namespace std;
+using namespace PBD;
+
+namespace Evoral {
+ template class EventRingBuffer<MIDI::timestamp_t>;
+}
+
+pthread_t AsyncMIDIPort::_process_thread;
+
+#define port_engine AudioEngine::instance()->port_engine()
+
+AsyncMIDIPort::AsyncMIDIPort (string const & name, PortFlags flags)
+ : MidiPort (name, flags)
+ , MIDI::Port (name, MIDI::Port::Flags (0))
+ , _currently_in_cycle (false)
+ , _last_write_timestamp (0)
+ , output_fifo (512)
+ , input_fifo (1024)
+ , xthread (true)
+{
+}
+
+AsyncMIDIPort::~AsyncMIDIPort ()
+{
+}
+
+void
+AsyncMIDIPort::flush_output_fifo (pframes_t nframes)
+{
+ RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0 } };
+ size_t written;
+
+ output_fifo.get_read_vector (&vec);
+
+ MidiBuffer& mb (get_midi_buffer (nframes));
+
+ if (vec.len[0]) {
+ Evoral::Event<double>* evp = vec.buf[0];
+
+ for (size_t n = 0; n < vec.len[0]; ++n, ++evp) {
+ mb.push_back (evp->time(), evp->size(), evp->buffer());
+ }
+ }
+
+ if (vec.len[1]) {
+ Evoral::Event<double>* evp = vec.buf[1];
+
+ for (size_t n = 0; n < vec.len[1]; ++n, ++evp) {
+ mb.push_back (evp->time(), evp->size(), evp->buffer());
+ }
+ }
+
+ if ((written = vec.len[0] + vec.len[1]) != 0) {
+ output_fifo.increment_read_idx (written);
+ }
+}
+
+void
+AsyncMIDIPort::cycle_start (pframes_t nframes)
+{
+ _currently_in_cycle = true;
+ MidiPort::cycle_start (nframes);
+
+ /* dump anything waiting in the output FIFO at the start of the port
+ * buffer
+ */
+
+ if (ARDOUR::Port::sends_output()) {
+ flush_output_fifo (nframes);
+ }
+
+ /* copy incoming data from the port buffer into the input FIFO
+ and if necessary wakeup the reader
+ */
+
+ if (ARDOUR::Port::receives_input()) {
+ MidiBuffer& mb (get_midi_buffer (nframes));
+ pframes_t when = AudioEngine::instance()->sample_time_at_cycle_start();
+
+ for (MidiBuffer::iterator b = mb.begin(); b != mb.end(); ++b) {
+ input_fifo.write (when, (Evoral::EventType) 0, (*b).size(), (*b).buffer());
+ }
+
+ if (!mb.empty()) {
+ xthread.wakeup ();
+ }
+ }
+
+}
+
+void
+AsyncMIDIPort::cycle_end (pframes_t nframes)
+{
+ if (ARDOUR::Port::sends_output()) {
+ /* move any additional data from output FIFO into the port
+ buffer.
+ */
+ flush_output_fifo (nframes);
+ }
+
+ MidiPort::cycle_end (nframes);
+
+ _currently_in_cycle = false;
+}
+
+/** wait for the output FIFO to be emptied by successive process() callbacks.
+ *
+ * Cannot be called from a processing thread.
+ */
+void
+AsyncMIDIPort::drain (int check_interval_usecs)
+{
+ RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
+
+ if (!AudioEngine::instance()->running() || AudioEngine::instance()->session() == 0) {
+ /* no more process calls - it will never drain */
+ return;
+ }
+
+
+ if (is_process_thread()) {
+ error << "Process thread called MIDI::AsyncMIDIPort::drain() - this cannot work" << endmsg;
+ return;
+ }
+
+ while (1) {
+ output_fifo.get_write_vector (&vec);
+ if (vec.len[0] + vec.len[1] >= output_fifo.bufsize() - 1) {
+ break;
+ }
+ usleep (check_interval_usecs);
+ }
+}
+
+int
+AsyncMIDIPort::write (const MIDI::byte * msg, size_t msglen, MIDI::timestamp_t timestamp)
+{
+ int ret = 0;
+
+ if (!ARDOUR::Port::sends_output()) {
+ return ret;
+ }
+
+ if (!is_process_thread()) {
+
+ /* this is the best estimate of "when" this MIDI data is being
+ * delivered
+ */
+
+ _parser->set_timestamp (AudioEngine::instance()->sample_time() + timestamp);
+ for (size_t n = 0; n < msglen; ++n) {
+ _parser->scanner (msg[n]);
+ }
+
+ Glib::Threads::Mutex::Lock lm (output_fifo_lock);
+ RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
+
+ output_fifo.get_write_vector (&vec);
+
+ if (vec.len[0] + vec.len[1] < 1) {
+ error << "no space in FIFO for non-process thread MIDI write" << endmsg;
+ return 0;
+ }
+
+ if (vec.len[0]) {
+ if (!vec.buf[0]->owns_buffer()) {
+ vec.buf[0]->set_buffer (0, 0, true);
+ }
+ vec.buf[0]->set (msg, msglen, timestamp);
+ } else {
+ if (!vec.buf[1]->owns_buffer()) {
+ vec.buf[1]->set_buffer (0, 0, true);
+ }
+ vec.buf[1]->set (msg, msglen, timestamp);
+ }
+
+ output_fifo.increment_write_idx (1);
+
+ ret = msglen;
+
+ } else {
+
+ _parser->set_timestamp (AudioEngine::instance()->sample_time_at_cycle_start() + timestamp);
+ for (size_t n = 0; n < msglen; ++n) {
+ _parser->scanner (msg[n]);
+ }
+
+ if (timestamp >= _cycle_nframes) {
+ std::cerr << "attempting to write MIDI event of " << msglen << " MIDI::bytes at time "
+ << timestamp << " of " << _cycle_nframes
+ << " (this will not work - needs a code fix)"
+ << std::endl;
+ }
+
+ /* This is the process thread, which makes checking
+ * _currently_in_cycle atomic and safe, since it is only
+ * set from cycle_start() and cycle_end(), also called
+ * only from the process thread.
+ */
+
+ if (_currently_in_cycle) {
+
+ MidiBuffer& mb (get_midi_buffer (_cycle_nframes));
+
+ if (timestamp == 0) {
+ timestamp = _last_write_timestamp;
+ }
+
+ if (mb.push_back (timestamp, msglen, msg)) {
+ ret = msglen;
+ _last_write_timestamp = timestamp;
+
+ } else {
+ cerr << "AsyncMIDIPort (" << ARDOUR::Port::name() << "): write of " << msglen << " @ " << timestamp << " failed\n" << endl;
+ PBD::stacktrace (cerr, 20);
+ ret = 0;
+ }
+ } else {
+ cerr << "write to JACK midi port failed: not currently in a process cycle." << endl;
+ PBD::stacktrace (cerr, 20);
+ }
+ }
+
+ return ret;
+}
+
+
+int
+AsyncMIDIPort::read (MIDI::byte *, size_t)
+{
+ if (!ARDOUR::Port::receives_input()) {
+ return 0;
+ }
+
+ timestamp_t time;
+ Evoral::EventType type;
+ uint32_t size;
+ MIDI::byte buffer[input_fifo.capacity()];
+
+ while (input_fifo.read (&time, &type, &size, buffer)) {
+ _parser->set_timestamp (time);
+ for (uint32_t i = 0; i < size; ++i) {
+ _parser->scanner (buffer[i]);
+ }
+ }
+
+ return 0;
+}
+
+void
+AsyncMIDIPort::parse (framecnt_t)
+{
+ MIDI::byte buf[1];
+
+ /* see ::read() to realize why buf is not used */
+ read (buf, sizeof (buf));
+}
+
+void
+AsyncMIDIPort::set_process_thread (pthread_t thr)
+{
+ _process_thread = thr;
+}
+
+bool
+AsyncMIDIPort::is_process_thread()
+{
+ return pthread_equal (pthread_self(), _process_thread);
+}
+
diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc
index 622a42edf2..f4d10c2043 100644
--- a/libs/ardour/audio_diskstream.cc
+++ b/libs/ardour/audio_diskstream.cc
@@ -1752,7 +1752,7 @@ AudioDiskstream::prep_record_enable ()
if (Config->get_monitoring_model() == HardwareMonitoring) {
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
- (*chan)->source.request_jack_monitors_input (!(_session.config.get_auto_input() && rolling));
+ (*chan)->source.request_input_monitoring (!(_session.config.get_auto_input() && rolling));
capturing_sources.push_back ((*chan)->write_source);
(*chan)->write_source->mark_streaming_write_started ();
}
@@ -1773,7 +1773,7 @@ AudioDiskstream::prep_record_disable ()
boost::shared_ptr<ChannelList> c = channels.reader();
if (Config->get_monitoring_model() == HardwareMonitoring) {
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
- (*chan)->source.request_jack_monitors_input (false);
+ (*chan)->source.request_input_monitoring (false);
}
}
capturing_sources.clear ();
@@ -2039,12 +2039,12 @@ AudioDiskstream::allocate_temporary_buffers ()
}
void
-AudioDiskstream::request_jack_monitors_input (bool yn)
+AudioDiskstream::request_input_monitoring (bool yn)
{
boost::shared_ptr<ChannelList> c = channels.reader();
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
- (*chan)->source.request_jack_monitors_input (yn);
+ (*chan)->source.request_input_monitoring (yn);
}
}
@@ -2367,13 +2367,13 @@ AudioDiskstream::ChannelSource::is_physical () const
}
void
-AudioDiskstream::ChannelSource::request_jack_monitors_input (bool yn) const
+AudioDiskstream::ChannelSource::request_input_monitoring (bool yn) const
{
if (name.empty()) {
return;
}
- return AudioEngine::instance()->request_jack_monitors_input (name, yn);
+ return AudioEngine::instance()->request_input_monitoring (name, yn);
}
AudioDiskstream::ChannelInfo::ChannelInfo (framecnt_t playback_bufsize, framecnt_t capture_bufsize, framecnt_t speed_size, framecnt_t wrap_size)
diff --git a/libs/ardour/audio_port.cc b/libs/ardour/audio_port.cc
index 240224ea5e..6a86360b69 100644
--- a/libs/ardour/audio_port.cc
+++ b/libs/ardour/audio_port.cc
@@ -21,13 +21,17 @@
#include "pbd/stacktrace.h"
#include "ardour/audio_buffer.h"
+#include "ardour/audioengine.h"
#include "ardour/audio_port.h"
#include "ardour/data_type.h"
+#include "ardour/port_engine.h"
using namespace ARDOUR;
using namespace std;
-AudioPort::AudioPort (const std::string& name, Flags flags)
+#define port_engine AudioEngine::instance()->port_engine()
+
+AudioPort::AudioPort (const std::string& name, PortFlags flags)
: Port (name, DataType::AUDIO, flags)
, _buffer (new AudioBuffer (0))
{
@@ -73,7 +77,7 @@ AudioBuffer&
AudioPort::get_audio_buffer (pframes_t nframes)
{
/* caller must hold process lock */
- _buffer->set_data ((Sample *) jack_port_get_buffer (_jack_port, _cycle_nframes) +
+ _buffer->set_data ((Sample *) port_engine.get_buffer (_port_handle, _cycle_nframes) +
_global_port_buffer_offset + _port_buffer_offset, nframes);
return *_buffer;
}
@@ -82,7 +86,7 @@ Sample*
AudioPort::engine_get_whole_audio_buffer ()
{
/* caller must hold process lock */
- return (Sample *) jack_port_get_buffer (_jack_port, _cycle_nframes);
+ return (Sample *) port_engine.get_buffer (_port_handle, _cycle_nframes);
}
diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc
index 0530dbfce9..a82c109cba 100644
--- a/libs/ardour/audio_track.cc
+++ b/libs/ardour/audio_track.cc
@@ -96,7 +96,7 @@ AudioTrack::set_diskstream (boost::shared_ptr<Diskstream> ds)
}
_diskstream->set_record_enabled (false);
- _diskstream->request_jack_monitors_input (false);
+ _diskstream->request_input_monitoring (false);
DiskstreamChanged (); /* EMIT SIGNAL */
}
diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc
index 5e3e5ba9e2..0ff8cd89ef 100644
--- a/libs/ardour/audioengine.cc
+++ b/libs/ardour/audioengine.cc
@@ -25,24 +25,30 @@
#include <sstream>
#include <glibmm/timer.h>
+#include <glibmm/pattern.h>
+#include <glibmm/module.h>
+#include "pbd/epa.h"
+#include "pbd/file_utils.h"
#include "pbd/pthread_utils.h"
#include "pbd/stacktrace.h"
#include "pbd/unknown_type.h"
-#include "pbd/epa.h"
#include "midi++/port.h"
-#include "midi++/jack_midi_port.h"
#include "midi++/mmc.h"
-#include "midi++/manager.h"
+#include "ardour/async_midi_port.h"
#include "ardour/audio_port.h"
+#include "ardour/audio_backend.h"
#include "ardour/audioengine.h"
+#include "ardour/backend_search_path.h"
#include "ardour/buffer.h"
#include "ardour/cycle_timer.h"
#include "ardour/internal_send.h"
#include "ardour/meter.h"
#include "ardour/midi_port.h"
+#include "ardour/midiport_manager.h"
+#include "ardour/mtdm.h"
#include "ardour/port.h"
#include "ardour/process_thread.h"
#include "ardour/session.h"
@@ -56,60 +62,50 @@ using namespace PBD;
gint AudioEngine::m_meter_exit;
AudioEngine* AudioEngine::_instance = 0;
-#define GET_PRIVATE_JACK_POINTER(j) jack_client_t* _priv_jack = (jack_client_t*) (j); if (!_priv_jack) { return; }
-#define GET_PRIVATE_JACK_POINTER_RET(j,r) jack_client_t* _priv_jack = (jack_client_t*) (j); if (!_priv_jack) { return r; }
-
-AudioEngine::AudioEngine (string client_name, string session_uuid)
- : _jack (0)
- , session_remove_pending (false)
+AudioEngine::AudioEngine ()
+ : session_remove_pending (false)
, session_removal_countdown (-1)
, _running (false)
- , _has_run (false)
- , _buffer_size (0)
- , _frame_rate (0)
+ , _freewheeling (false)
, monitor_check_interval (INT32_MAX)
, last_monitor_check (0)
, _processed_frames (0)
- , _freewheeling (false)
- , _pre_freewheel_mmc_enabled (false)
- , _usecs_per_cycle (0)
- , port_remove_in_progress (false)
, m_meter_thread (0)
, _main_thread (0)
- , ports (new Ports)
+ , _mtdm (0)
+ , _measuring_latency (false)
+ , _latency_input_port (0)
+ , _latency_output_port (0)
+ , _latency_flush_frames (0)
+ , _latency_signal_latency (0)
{
- _instance = this; /* singleton */
-
g_atomic_int_set (&m_meter_exit, 0);
-
- if (connect_to_jack (client_name, session_uuid)) {
- throw NoBackendAvailable ();
- }
-
- Port::set_engine (this);
+ discover_backends ();
}
AudioEngine::~AudioEngine ()
{
+ drop_backend ();
+
config_connection.disconnect ();
{
Glib::Threads::Mutex::Lock tm (_process_lock);
session_removed.signal ();
-
- if (_running) {
- jack_client_close (_jack);
- _jack = 0;
- }
-
stop_metering_thread ();
}
}
-jack_client_t*
-AudioEngine::jack() const
+AudioEngine*
+AudioEngine::create ()
{
- return _jack;
+ if (_instance) {
+ return _instance;
+ }
+
+ _instance = new AudioEngine ();
+
+ return _instance;
}
void
@@ -126,290 +122,7 @@ _thread_init_callback (void * /*arg*/)
SessionEvent::create_per_thread_pool (X_("Audioengine"), 512);
- MIDI::JackMIDIPort::set_process_thread (pthread_self());
-}
-
-static void
-ardour_jack_error (const char* msg)
-{
- error << "JACK: " << msg << endmsg;
-}
-
-void
-AudioEngine::set_jack_callbacks ()
-{
- GET_PRIVATE_JACK_POINTER (_jack);
-
- if (jack_on_info_shutdown) {
- jack_on_info_shutdown (_priv_jack, halted_info, this);
- } else {
- jack_on_shutdown (_priv_jack, halted, this);
- }
-
- jack_set_thread_init_callback (_priv_jack, _thread_init_callback, this);
- jack_set_process_thread (_priv_jack, _process_thread, this);
- jack_set_sample_rate_callback (_priv_jack, _sample_rate_callback, this);
- jack_set_buffer_size_callback (_priv_jack, _bufsize_callback, this);
- jack_set_graph_order_callback (_priv_jack, _graph_order_callback, this);
- jack_set_port_registration_callback (_priv_jack, _registration_callback, this);
- jack_set_port_connect_callback (_priv_jack, _connect_callback, this);
- jack_set_xrun_callback (_priv_jack, _xrun_callback, this);
- jack_set_sync_callback (_priv_jack, _jack_sync_callback, this);
- jack_set_freewheel_callback (_priv_jack, _freewheel_callback, this);
-
- if (_session && _session->config.get_jack_time_master()) {
- jack_set_timebase_callback (_priv_jack, 0, _jack_timebase_callback, this);
- }
-
-#ifdef HAVE_JACK_SESSION
- if( jack_set_session_callback)
- jack_set_session_callback (_priv_jack, _session_callback, this);
-#endif
-
- if (jack_set_latency_callback) {
- jack_set_latency_callback (_priv_jack, _latency_callback, this);
- }
-
- jack_set_error_function (ardour_jack_error);
-}
-
-int
-AudioEngine::start ()
-{
- GET_PRIVATE_JACK_POINTER_RET (_jack, -1);
-
- if (!_running) {
-
- if (!jack_port_type_get_buffer_size) {
- warning << _("This version of JACK is old - you should upgrade to a newer version that supports jack_port_type_get_buffer_size()") << endmsg;
- }
-
- if (_session) {
- BootMessage (_("Connect session to engine"));
- _session->set_frame_rate (jack_get_sample_rate (_priv_jack));
- }
-
- /* a proxy for whether jack_activate() will definitely call the buffer size
- * callback. with older versions of JACK, this function symbol will be null.
- * this is reliable, but not clean.
- */
-
- if (!jack_port_type_get_buffer_size) {
- jack_bufsize_callback (jack_get_buffer_size (_priv_jack));
- }
-
- _processed_frames = 0;
- last_monitor_check = 0;
-
- set_jack_callbacks ();
-
- if (jack_activate (_priv_jack) == 0) {
- _running = true;
- _has_run = true;
- Running(); /* EMIT SIGNAL */
- } else {
- // error << _("cannot activate JACK client") << endmsg;
- }
- }
-
- return _running ? 0 : -1;
-}
-
-int
-AudioEngine::stop (bool forever)
-{
- GET_PRIVATE_JACK_POINTER_RET (_jack, -1);
-
- if (_priv_jack) {
- if (forever) {
- disconnect_from_jack ();
- } else {
- jack_deactivate (_priv_jack);
- MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */
- Stopped(); /* EMIT SIGNAL */
- }
- }
-
- if (forever) {
- stop_metering_thread ();
- }
-
- return _running ? -1 : 0;
-}
-
-
-bool
-AudioEngine::get_sync_offset (pframes_t& offset) const
-{
-
-#ifdef HAVE_JACK_VIDEO_SUPPORT
-
- GET_PRIVATE_JACK_POINTER_RET (_jack, false);
-
- jack_position_t pos;
-
- if (_priv_jack) {
- (void) jack_transport_query (_priv_jack, &pos);
-
- if (pos.valid & JackVideoFrameOffset) {
- offset = pos.video_offset;
- return true;
- }
- }
-#else
- /* keep gcc happy */
- offset = 0;
-#endif
-
- return false;
-}
-
-void
-AudioEngine::_jack_timebase_callback (jack_transport_state_t state, pframes_t nframes,
- jack_position_t* pos, int new_position, void *arg)
-{
- static_cast<AudioEngine*> (arg)->jack_timebase_callback (state, nframes, pos, new_position);
-}
-
-void
-AudioEngine::jack_timebase_callback (jack_transport_state_t state, pframes_t nframes,
- jack_position_t* pos, int new_position)
-{
- if (_jack && _session && _session->synced_to_jack()) {
- _session->jack_timebase_callback (state, nframes, pos, new_position);
- }
-}
-
-int
-AudioEngine::_jack_sync_callback (jack_transport_state_t state, jack_position_t* pos, void* arg)
-{
- return static_cast<AudioEngine*> (arg)->jack_sync_callback (state, pos);
-}
-
-int
-AudioEngine::jack_sync_callback (jack_transport_state_t state, jack_position_t* pos)
-{
- if (_jack && _session) {
- return _session->jack_sync_callback (state, pos);
- }
-
- return true;
-}
-
-int
-AudioEngine::_xrun_callback (void *arg)
-{
- AudioEngine* ae = static_cast<AudioEngine*> (arg);
- if (ae->connected()) {
- ae->Xrun (); /* EMIT SIGNAL */
- }
- return 0;
-}
-
-#ifdef HAVE_JACK_SESSION
-void
-AudioEngine::_session_callback (jack_session_event_t *event, void *arg)
-{
- AudioEngine* ae = static_cast<AudioEngine*> (arg);
- if (ae->connected()) {
- ae->JackSessionEvent ( event ); /* EMIT SIGNAL */
- }
-}
-#endif
-
-int
-AudioEngine::_graph_order_callback (void *arg)
-{
- AudioEngine* ae = static_cast<AudioEngine*> (arg);
-
- if (ae->connected() && !ae->port_remove_in_progress) {
- ae->GraphReordered (); /* EMIT SIGNAL */
- }
-
- return 0;
-}
-
-void*
-AudioEngine::_process_thread (void *arg)
-{
- return static_cast<AudioEngine *> (arg)->process_thread ();
-}
-
-void
-AudioEngine::_freewheel_callback (int onoff, void *arg)
-{
- static_cast<AudioEngine*>(arg)->freewheel_callback (onoff);
-}
-
-void
-AudioEngine::freewheel_callback (int onoff)
-{
- _freewheeling = onoff;
-
- if (onoff) {
- _pre_freewheel_mmc_enabled = MIDI::Manager::instance()->mmc()->send_enabled ();
- MIDI::Manager::instance()->mmc()->enable_send (false);
- } else {
- MIDI::Manager::instance()->mmc()->enable_send (_pre_freewheel_mmc_enabled);
- }
-}
-
-void
-AudioEngine::_registration_callback (jack_port_id_t /*id*/, int /*reg*/, void* arg)
-{
- AudioEngine* ae = static_cast<AudioEngine*> (arg);
-
- if (!ae->port_remove_in_progress) {
- ae->PortRegisteredOrUnregistered (); /* EMIT SIGNAL */
- }
-}
-
-void
-AudioEngine::_latency_callback (jack_latency_callback_mode_t mode, void* arg)
-{
- return static_cast<AudioEngine *> (arg)->jack_latency_callback (mode);
-}
-
-void
-AudioEngine::_connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn, void* arg)
-{
- AudioEngine* ae = static_cast<AudioEngine*> (arg);
- ae->connect_callback (id_a, id_b, conn);
-}
-
-void
-AudioEngine::connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn)
-{
- if (port_remove_in_progress) {
- return;
- }
-
- GET_PRIVATE_JACK_POINTER (_jack);
-
- jack_port_t* jack_port_a = jack_port_by_id (_priv_jack, id_a);
- jack_port_t* jack_port_b = jack_port_by_id (_priv_jack, id_b);
-
- boost::shared_ptr<Port> port_a;
- boost::shared_ptr<Port> port_b;
- Ports::iterator x;
- boost::shared_ptr<Ports> pr = ports.reader ();
-
-
- x = pr->find (make_port_name_relative (jack_port_name (jack_port_a)));
- if (x != pr->end()) {
- port_a = x->second;
- }
-
- x = pr->find (make_port_name_relative (jack_port_name (jack_port_b)));
- if (x != pr->end()) {
- port_b = x->second;
- }
-
- PortConnectedOrDisconnected (
- port_a, jack_port_name (jack_port_a),
- port_b, jack_port_name (jack_port_b),
- conn == 0 ? false : true
- ); /* EMIT SIGNAL */
+ AsyncMIDIPort::set_process_thread (pthread_self());
}
void
@@ -428,29 +141,32 @@ AudioEngine::split_cycle (pframes_t offset)
}
}
-void*
-AudioEngine::process_thread ()
+int
+AudioEngine::sample_rate_change (pframes_t nframes)
{
- /* JACK doesn't do this for us when we use the wait API
- */
-
- _thread_init_callback (0);
+ /* check for monitor input change every 1/10th of second */
- _main_thread = new ProcessThread;
+ monitor_check_interval = nframes / 10;
+ last_monitor_check = 0;
- while (1) {
- GET_PRIVATE_JACK_POINTER_RET(_jack,0);
+ if (_session) {
+ _session->set_frame_rate (nframes);
+ }
- pframes_t nframes = jack_cycle_wait (_priv_jack);
+ SampleRateChanged (nframes); /* EMIT SIGNAL */
- if (process_callback (nframes)) {
- return 0;
- }
+ return 0;
+}
- jack_cycle_signal (_priv_jack, 0);
- }
+int
+AudioEngine::buffer_size_change (pframes_t bufsiz)
+{
+ if (_session) {
+ _session->set_block_size (bufsiz);
+ last_monitor_check = 0;
+ }
- return 0;
+ return 0;
}
/** Method called by our ::process_thread when there is work to be done.
@@ -459,7 +175,6 @@ AudioEngine::process_thread ()
int
AudioEngine::process_callback (pframes_t nframes)
{
- GET_PRIVATE_JACK_POINTER_RET(_jack,0);
Glib::Threads::Mutex::Lock tm (_process_lock, Glib::Threads::TRY_LOCK);
PT_TIMING_REF;
@@ -482,6 +197,43 @@ AudioEngine::process_callback (pframes_t nframes)
return 0;
}
+ bool return_after_remove_check = false;
+
+ if (_measuring_latency && _mtdm) {
+ PortManager::cycle_start (nframes);
+ PortManager::silence (nframes);
+
+ if (_latency_input_port && _latency_output_port) {
+ PortEngine& pe (port_engine());
+
+ Sample* in = (Sample*) pe.get_buffer (_latency_input_port, nframes);
+ Sample* out = (Sample*) pe.get_buffer (_latency_output_port, nframes);
+
+ _mtdm->process (nframes, in, out);
+ }
+
+ PortManager::cycle_end (nframes);
+ return_after_remove_check = true;
+
+ } else if (_latency_flush_frames) {
+
+ /* wait for the appropriate duration for the MTDM signal to
+ * drain from the ports before we revert to normal behaviour.
+ */
+
+ PortManager::cycle_start (nframes);
+ PortManager::silence (nframes);
+ PortManager::cycle_end (nframes);
+
+ if (_latency_flush_frames > nframes) {
+ _latency_flush_frames -= nframes;
+ } else {
+ _latency_flush_frames = 0;
+ }
+
+ return_after_remove_check = true;
+ }
+
if (session_remove_pending) {
/* perform the actual session removal */
@@ -489,7 +241,7 @@ AudioEngine::process_callback (pframes_t nframes)
if (session_removal_countdown < 0) {
/* fade out over 1 second */
- session_removal_countdown = _frame_rate/2;
+ session_removal_countdown = sample_rate()/2;
session_removal_gain = 1.0;
session_removal_gain_step = 1.0/session_removal_countdown;
@@ -518,11 +270,15 @@ AudioEngine::process_callback (pframes_t nframes)
}
}
+ if (return_after_remove_check) {
+ return 0;
+ }
+
if (_session == 0) {
if (!_freewheeling) {
- MIDI::Manager::instance()->cycle_start(nframes);
- MIDI::Manager::instance()->cycle_end();
+ PortManager::cycle_start (nframes);
+ PortManager::cycle_end (nframes);
}
_processed_frames = next_processed_frames;
@@ -533,34 +289,22 @@ AudioEngine::process_callback (pframes_t nframes)
/* tell all relevant objects that we're starting a new cycle */
InternalSend::CycleStart (nframes);
- Port::set_global_port_buffer_offset (0);
- Port::set_cycle_framecnt (nframes);
/* tell all Ports that we're starting a new cycle */
- boost::shared_ptr<Ports> p = ports.reader();
-
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
- i->second->cycle_start (nframes);
- }
+ PortManager::cycle_start (nframes);
/* test if we are freewheeling and there are freewheel signals connected.
ardour should act normally even when freewheeling unless /it/ is
- exporting
+ exporting (which is what Freewheel.empty() tests for).
*/
if (_freewheeling && !Freewheel.empty()) {
-
Freewheel (nframes);
-
} else {
- MIDI::Manager::instance()->cycle_start(nframes);
-
if (_session) {
_session->process (nframes);
}
-
- MIDI::Manager::instance()->cycle_end();
}
if (_freewheeling) {
@@ -573,52 +317,18 @@ AudioEngine::process_callback (pframes_t nframes)
}
if (last_monitor_check + monitor_check_interval < next_processed_frames) {
-
- boost::shared_ptr<Ports> p = ports.reader();
-
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
-
- bool x;
-
- if (i->second->last_monitor() != (x = i->second->jack_monitoring_input ())) {
- i->second->set_last_monitor (x);
- /* XXX I think this is dangerous, due to
- a likely mutex in the signal handlers ...
- */
- i->second->MonitorInputChanged (x); /* EMIT SIGNAL */
- }
- }
+
+ PortManager::check_monitoring ();
last_monitor_check = next_processed_frames;
}
if (_session->silent()) {
-
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
-
- if (i->second->sends_output()) {
- i->second->get_buffer(nframes).silence(nframes);
- }
- }
+ PortManager::silence (nframes);
}
if (session_remove_pending && session_removal_countdown) {
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
-
- if (i->second->sends_output()) {
-
- boost::shared_ptr<AudioPort> ap = boost::dynamic_pointer_cast<AudioPort> (i->second);
- if (ap) {
- Sample* s = ap->engine_get_whole_audio_buffer ();
- gain_t g = session_removal_gain;
-
- for (pframes_t n = 0; n < nframes; ++n) {
- *s++ *= g;
- g -= session_removal_gain_step;
- }
- }
- }
- }
+ PortManager::fade_out (session_removal_gain, session_removal_gain_step, nframes);
if (session_removal_countdown > nframes) {
session_removal_countdown -= nframes;
@@ -629,11 +339,7 @@ AudioEngine::process_callback (pframes_t nframes)
session_removal_gain -= (nframes * session_removal_gain_step);
}
- // Finalize ports
-
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
- i->second->cycle_end (nframes);
- }
+ PortManager::cycle_end (nframes);
_processed_frames = next_processed_frames;
@@ -642,97 +348,6 @@ AudioEngine::process_callback (pframes_t nframes)
return 0;
}
-int
-AudioEngine::_sample_rate_callback (pframes_t nframes, void *arg)
-{
- return static_cast<AudioEngine *> (arg)->jack_sample_rate_callback (nframes);
-}
-
-int
-AudioEngine::jack_sample_rate_callback (pframes_t nframes)
-{
- _frame_rate = nframes;
- _usecs_per_cycle = (int) floor ((((double) frames_per_cycle() / nframes)) * 1000000.0);
-
- /* check for monitor input change every 1/10th of second */
-
- monitor_check_interval = nframes / 10;
- last_monitor_check = 0;
-
- if (_session) {
- _session->set_frame_rate (nframes);
- }
-
- SampleRateChanged (nframes); /* EMIT SIGNAL */
-
- return 0;
-}
-
-void
-AudioEngine::jack_latency_callback (jack_latency_callback_mode_t mode)
-{
- if (_session) {
- _session->update_latency (mode == JackPlaybackLatency);
- }
-}
-
-int
-AudioEngine::_bufsize_callback (pframes_t nframes, void *arg)
-{
- return static_cast<AudioEngine *> (arg)->jack_bufsize_callback (nframes);
-}
-
-int
-AudioEngine::jack_bufsize_callback (pframes_t nframes)
-{
- /* if the size has not changed, this should be a no-op */
-
- if (nframes == _buffer_size) {
- return 0;
- }
-
- GET_PRIVATE_JACK_POINTER_RET (_jack, 1);
-
- _buffer_size = nframes;
- _usecs_per_cycle = (int) floor ((((double) nframes / frame_rate())) * 1000000.0);
- last_monitor_check = 0;
-
- if (jack_port_type_get_buffer_size) {
- _raw_buffer_sizes[DataType::AUDIO] = jack_port_type_get_buffer_size (_priv_jack, JACK_DEFAULT_AUDIO_TYPE);
- _raw_buffer_sizes[DataType::MIDI] = jack_port_type_get_buffer_size (_priv_jack, JACK_DEFAULT_MIDI_TYPE);
- } else {
-
- /* Old version of JACK.
-
- These crude guesses, see below where we try to get the right answers.
-
- Note that our guess for MIDI deliberatey tries to overestimate
- by a little. It would be nicer if we could get the actual
- size from a port, but we have to use this estimate in the
- event that there are no MIDI ports currently. If there are
- the value will be adjusted below.
- */
-
- _raw_buffer_sizes[DataType::AUDIO] = nframes * sizeof (Sample);
- _raw_buffer_sizes[DataType::MIDI] = nframes * 4 - (nframes/2);
- }
-
- {
- Glib::Threads::Mutex::Lock lm (_process_lock);
-
- boost::shared_ptr<Ports> p = ports.reader();
-
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
- i->second->reset();
- }
- }
-
- if (_session) {
- _session->set_block_size (_buffer_size);
- }
-
- return 0;
-}
void
AudioEngine::stop_metering_thread ()
@@ -757,8 +372,9 @@ void
AudioEngine::meter_thread ()
{
pthread_set_name (X_("meter"));
+
while (true) {
- Glib::usleep (10000);
+ Glib::usleep (10000); /* 1/100th sec interval */
if (g_atomic_int_get(&m_meter_exit)) {
break;
}
@@ -775,19 +391,9 @@ AudioEngine::set_session (Session *s)
if (_session) {
- start_metering_thread ();
-
- pframes_t blocksize = jack_get_buffer_size (_jack);
+ pframes_t blocksize = samples_per_cycle ();
- /* page in as much of the session process code as we
- can before we really start running.
- */
-
- boost::shared_ptr<Ports> p = ports.reader();
-
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
- i->second->cycle_start (blocksize);
- }
+ PortManager::cycle_start (blocksize);
_session->process (blocksize);
_session->process (blocksize);
@@ -798,9 +404,7 @@ AudioEngine::set_session (Session *s)
_session->process (blocksize);
_session->process (blocksize);
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
- i->second->cycle_end (blocksize);
- }
+ PortManager::cycle_end (blocksize);
}
}
@@ -815,6 +419,7 @@ AudioEngine::remove_session ()
if (_session) {
session_remove_pending = true;
+ session_removal_countdown = 0;
session_removed.wait(_process_lock);
}
@@ -825,813 +430,622 @@ AudioEngine::remove_session ()
remove_all_ports ();
}
+
void
-AudioEngine::port_registration_failure (const std::string& portname)
+AudioEngine::died ()
{
- GET_PRIVATE_JACK_POINTER (_jack);
- string full_portname = jack_client_name;
- full_portname += ':';
- full_portname += portname;
-
-
- jack_port_t* p = jack_port_by_name (_priv_jack, full_portname.c_str());
- string reason;
+ /* called from a signal handler for SIGPIPE */
- if (p) {
- reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname);
- } else {
- reason = string_compose (_("No more JACK ports are available. You will need to stop %1 and restart JACK with more ports if you need this many tracks."), PROGRAM_NAME);
- }
+ stop_metering_thread ();
- throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
+ _running = false;
}
-boost::shared_ptr<Port>
-AudioEngine::register_port (DataType dtype, const string& portname, bool input)
+int
+AudioEngine::reset_timebase ()
{
- boost::shared_ptr<Port> newport;
-
- try {
- if (dtype == DataType::AUDIO) {
- newport.reset (new AudioPort (portname, (input ? Port::IsInput : Port::IsOutput)));
- } else if (dtype == DataType::MIDI) {
- newport.reset (new MidiPort (portname, (input ? Port::IsInput : Port::IsOutput)));
+ if (_session) {
+ if (_session->config.get_jack_time_master()) {
+ _backend->set_time_master (true);
} else {
- throw PortRegistrationFailure("unable to create port (unknown type)");
+ _backend->set_time_master (false);
}
-
- RCUWriter<Ports> writer (ports);
- boost::shared_ptr<Ports> ps = writer.get_copy ();
- ps->insert (make_pair (make_port_name_relative (portname), newport));
-
- /* writer goes out of scope, forces update */
-
- return newport;
- }
-
- catch (PortRegistrationFailure& err) {
- throw err;
- } catch (std::exception& e) {
- throw PortRegistrationFailure(string_compose(
- _("unable to create port: %1"), e.what()).c_str());
- } catch (...) {
- throw PortRegistrationFailure("unable to create port (unknown error)");
}
+ return 0;
}
-boost::shared_ptr<Port>
-AudioEngine::register_input_port (DataType type, const string& portname)
-{
- return register_port (type, portname, true);
-}
-boost::shared_ptr<Port>
-AudioEngine::register_output_port (DataType type, const string& portname)
+void
+AudioEngine::destroy ()
{
- return register_port (type, portname, false);
+ delete _instance;
+ _instance = 0;
}
int
-AudioEngine::unregister_port (boost::shared_ptr<Port> port)
+AudioEngine::discover_backends ()
{
- /* caller must hold process lock */
+ vector<std::string> backend_modules;
- if (!_running) {
- /* probably happening when the engine has been halted by JACK,
- in which case, there is nothing we can do here.
- */
- return 0;
- }
+ _backends.clear ();
- {
- RCUWriter<Ports> writer (ports);
- boost::shared_ptr<Ports> ps = writer.get_copy ();
- Ports::iterator x = ps->find (make_port_name_relative (port->name()));
+ Glib::PatternSpec so_extension_pattern("*backend.so");
+ Glib::PatternSpec dylib_extension_pattern("*backend.dylib");
- if (x != ps->end()) {
- ps->erase (x);
- }
+ find_matching_files_in_search_path (backend_search_path (),
+ so_extension_pattern, backend_modules);
- /* writer goes out of scope, forces update */
- }
+ find_matching_files_in_search_path (backend_search_path (),
+ dylib_extension_pattern, backend_modules);
- ports.flush ();
+ DEBUG_TRACE (DEBUG::Panning, string_compose (_("looking for backends in %1\n"), backend_search_path().to_string()));
- return 0;
-}
+ for (vector<std::string>::iterator i = backend_modules.begin(); i != backend_modules.end(); ++i) {
-int
-AudioEngine::connect (const string& source, const string& destination)
-{
- int ret;
+ AudioBackendInfo* info;
- if (!_running) {
- if (!_has_run) {
- fatal << _("connect called before engine was started") << endmsg;
- /*NOTREACHED*/
- } else {
- return -1;
+ if ((info = backend_discover (*i)) != 0) {
+ _backends.insert (make_pair (info->name, info));
}
}
- string s = make_port_name_non_relative (source);
- string d = make_port_name_non_relative (destination);
-
+ return _backends.size();
+}
- boost::shared_ptr<Port> src = get_port_by_name (s);
- boost::shared_ptr<Port> dst = get_port_by_name (d);
+AudioBackendInfo*
+AudioEngine::backend_discover (const string& path)
+{
+ Glib::Module module (path);
+ AudioBackendInfo* info;
+ void* sym = 0;
- if (src) {
- ret = src->connect (d);
- } else if (dst) {
- ret = dst->connect (s);
- } else {
- /* neither port is known to us, and this API isn't intended for use as a general patch bay */
- ret = -1;
+ if (!module) {
+ error << string_compose(_("AudioEngine: cannot load module \"%1\" (%2)"), path,
+ Glib::Module::get_last_error()) << endmsg;
+ return 0;
}
-
- if (ret > 0) {
- /* already exists - no error, no warning */
- } else if (ret < 0) {
- error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"),
- source, s, destination, d)
- << endmsg;
+
+ if (!module.get_symbol ("descriptor", sym)) {
+ error << string_compose(_("AudioEngine: backend at \"%1\" has no descriptor."), path) << endmsg;
+ error << Glib::Module::get_last_error() << endmsg;
+ return 0;
}
- return ret;
+ module.make_resident ();
+
+ info = (AudioBackendInfo*) sym;
+
+ return info;
}
-int
-AudioEngine::disconnect (const string& source, const string& destination)
+vector<const AudioBackendInfo*>
+AudioEngine::available_backends() const
{
- int ret;
-
- if (!_running) {
- if (!_has_run) {
- fatal << _("disconnect called before engine was started") << endmsg;
- /*NOTREACHED*/
- } else {
- return -1;
- }
+ vector<const AudioBackendInfo*> r;
+
+ for (BackendMap::const_iterator i = _backends.begin(); i != _backends.end(); ++i) {
+ r.push_back (i->second);
}
- string s = make_port_name_non_relative (source);
- string d = make_port_name_non_relative (destination);
-
- boost::shared_ptr<Port> src = get_port_by_name (s);
- boost::shared_ptr<Port> dst = get_port_by_name (d);
-
- if (src) {
- ret = src->disconnect (d);
- } else if (dst) {
- ret = dst->disconnect (s);
- } else {
- /* neither port is known to us, and this API isn't intended for use as a general patch bay */
- ret = -1;
- }
- return ret;
+ return r;
}
-int
-AudioEngine::disconnect (boost::shared_ptr<Port> port)
+string
+AudioEngine::current_backend_name() const
{
- GET_PRIVATE_JACK_POINTER_RET (_jack,-1);
-
- if (!_running) {
- if (!_has_run) {
- fatal << _("disconnect called before engine was started") << endmsg;
- /*NOTREACHED*/
- } else {
- return -1;
- }
- }
-
- return port->disconnect_all ();
+ if (_backend) {
+ return _backend->name();
+ }
+ return string();
}
-ARDOUR::framecnt_t
-AudioEngine::frame_rate () const
+void
+AudioEngine::drop_backend ()
{
- GET_PRIVATE_JACK_POINTER_RET (_jack, 0);
- if (_frame_rate == 0) {
- return (_frame_rate = jack_get_sample_rate (_priv_jack));
- } else {
- return _frame_rate;
+ if (_backend) {
+ _backend->stop ();
+ _backend.reset ();
}
}
-size_t
-AudioEngine::raw_buffer_size (DataType t)
+boost::shared_ptr<AudioBackend>
+AudioEngine::set_backend (const std::string& name, const std::string& arg1, const std::string& arg2)
{
- std::map<DataType,size_t>::const_iterator s = _raw_buffer_sizes.find(t);
- return (s != _raw_buffer_sizes.end()) ? s->second : 0;
-}
+ BackendMap::iterator b = _backends.find (name);
-ARDOUR::pframes_t
-AudioEngine::frames_per_cycle () const
-{
- GET_PRIVATE_JACK_POINTER_RET (_jack,0);
- if (_buffer_size == 0) {
- return jack_get_buffer_size (_jack);
- } else {
- return _buffer_size;
+ if (b == _backends.end()) {
+ return boost::shared_ptr<AudioBackend>();
}
-}
-
-/** @param name Full or short name of port
- * @return Corresponding Port or 0.
- */
-boost::shared_ptr<Port>
-AudioEngine::get_port_by_name (const string& portname)
-{
- if (!_running) {
- if (!_has_run) {
- fatal << _("get_port_by_name() called before engine was started") << endmsg;
- /*NOTREACHED*/
- } else {
- boost::shared_ptr<Port> ();
+ drop_backend ();
+
+ try {
+ if (b->second->instantiate (arg1, arg2)) {
+ throw failed_constructor ();
}
- }
- if (!port_is_mine (portname)) {
- /* not an ardour port */
- return boost::shared_ptr<Port> ();
- }
+ _backend = b->second->backend_factory (*this);
+ _impl = b->second->portengine_factory (*this);
- boost::shared_ptr<Ports> pr = ports.reader();
- std::string rel = make_port_name_relative (portname);
- Ports::iterator x = pr->find (rel);
-
- if (x != pr->end()) {
- /* its possible that the port was renamed by some 3rd party and
- we don't know about it. check for this (the check is quick
- and cheap), and if so, rename the port (which will alter
- the port map as a side effect).
- */
- const std::string check = make_port_name_relative (jack_port_name (x->second->jack_port()));
- if (check != rel) {
- x->second->set_name (check);
- }
- return x->second;
+ } catch (exception& e) {
+ error << string_compose (_("Could not create backend for %1: %2"), name, e.what()) << endmsg;
+ return boost::shared_ptr<AudioBackend>();
}
- return boost::shared_ptr<Port> ();
+ return _backend;
}
-void
-AudioEngine::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name)
+/* BACKEND PROXY WRAPPERS */
+
+int
+AudioEngine::start ()
{
- RCUWriter<Ports> writer (ports);
- boost::shared_ptr<Ports> p = writer.get_copy();
- Ports::iterator x = p->find (old_relative_name);
+ if (!_backend) {
+ return -1;
+ }
+
+ if (_running) {
+ return 0;
+ }
+
+ _processed_frames = 0;
+ last_monitor_check = 0;
- if (x != p->end()) {
- boost::shared_ptr<Port> port = x->second;
- p->erase (x);
- p->insert (make_pair (new_relative_name, port));
+ if (_backend->start()) {
+ return -1;
}
-}
-const char **
-AudioEngine::get_ports (const string& port_name_pattern, const string& type_name_pattern, uint32_t flags)
-{
- GET_PRIVATE_JACK_POINTER_RET (_jack,0);
- if (!_running) {
- if (!_has_run) {
- fatal << _("get_ports called before engine was started") << endmsg;
- /*NOTREACHED*/
- } else {
- return 0;
+ _running = true;
+
+ if (_session) {
+ _session->set_frame_rate (_backend->sample_rate());
+
+ if (_session->config.get_jack_time_master()) {
+ _backend->set_time_master (true);
}
}
- return jack_get_ports (_priv_jack, port_name_pattern.c_str(), type_name_pattern.c_str(), flags);
+
+ start_metering_thread ();
+
+ Running(); /* EMIT SIGNAL */
+
+ return 0;
}
-void
-AudioEngine::halted_info (jack_status_t code, const char* reason, void *arg)
+int
+AudioEngine::stop ()
{
- /* called from jack shutdown handler */
-
- AudioEngine* ae = static_cast<AudioEngine *> (arg);
- bool was_running = ae->_running;
-
- ae->stop_metering_thread ();
+ if (!_backend) {
+ return 0;
+ }
- ae->_running = false;
- ae->_buffer_size = 0;
- ae->_frame_rate = 0;
- ae->_jack = 0;
+ Glib::Threads::Mutex::Lock lm (_process_lock);
- if (was_running) {
- MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */
-#ifdef HAVE_JACK_ON_INFO_SHUTDOWN
- switch (code) {
- case JackBackendError:
- ae->Halted(reason); /* EMIT SIGNAL */
- break;
- default:
- ae->Halted(""); /* EMIT SIGNAL */
- }
-#else
- ae->Halted(""); /* EMIT SIGNAL */
-#endif
- }
+ if (_backend->stop ()) {
+ return -1;
+ }
+
+ _running = false;
+ _processed_frames = 0;
+ _measuring_latency = false;
+ _latency_output_port = 0;
+ _latency_input_port = 0;
+ stop_metering_thread ();
+
+ Port::PortDrop ();
+ Stopped (); /* EMIT SIGNAL */
+
+ return 0;
}
-void
-AudioEngine::halted (void *arg)
+int
+AudioEngine::pause ()
{
- cerr << "HALTED by JACK\n";
+ if (!_backend) {
+ return 0;
+ }
+
+ if (_backend->pause ()) {
+ return -1;
+ }
- /* called from jack shutdown handler */
+ _running = false;
+
+ Stopped(); /* EMIT SIGNAL */
+ return 0;
+}
- AudioEngine* ae = static_cast<AudioEngine *> (arg);
- bool was_running = ae->_running;
+int
+AudioEngine::freewheel (bool start_stop)
+{
+ if (!_backend) {
+ return -1;
+ }
- ae->stop_metering_thread ();
+ /* _freewheeling will be set when first Freewheel signal occurs */
- ae->_running = false;
- ae->_buffer_size = 0;
- ae->_frame_rate = 0;
- ae->_jack = 0;
+ return _backend->freewheel (start_stop);
+}
- if (was_running) {
- MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */
- ae->Halted(""); /* EMIT SIGNAL */
+float
+AudioEngine::get_cpu_load() const
+{
+ if (!_backend) {
+ return 0.0;
}
+ return _backend->cpu_load ();
}
-void
-AudioEngine::died ()
+bool
+AudioEngine::is_realtime() const
{
- /* called from a signal handler for SIGPIPE */
-
- stop_metering_thread ();
+ if (!_backend) {
+ return false;
+ }
- _running = false;
- _buffer_size = 0;
- _frame_rate = 0;
- _jack = 0;
+ return _backend->is_realtime();
}
bool
-AudioEngine::can_request_hardware_monitoring ()
+AudioEngine::connected() const
{
- GET_PRIVATE_JACK_POINTER_RET (_jack,false);
- const char ** ports;
-
- if ((ports = jack_get_ports (_priv_jack, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortCanMonitor)) == 0) {
+ if (!_backend) {
return false;
}
- free (ports);
-
- return true;
+ return _backend->connected();
}
-ChanCount
-AudioEngine::n_physical (unsigned long flags) const
+void
+AudioEngine::transport_start ()
{
- ChanCount c;
-
- GET_PRIVATE_JACK_POINTER_RET (_jack, c);
-
- const char ** ports = jack_get_ports (_priv_jack, NULL, NULL, JackPortIsPhysical | flags);
- if (ports == 0) {
- return c;
- }
-
- for (uint32_t i = 0; ports[i]; ++i) {
- if (!strstr (ports[i], "Midi-Through")) {
- DataType t (jack_port_type (jack_port_by_name (_jack, ports[i])));
- c.set (t, c.get (t) + 1);
- }
+ if (!_backend) {
+ return;
}
-
- free (ports);
-
- return c;
+ return _backend->transport_start ();
}
-ChanCount
-AudioEngine::n_physical_inputs () const
+void
+AudioEngine::transport_stop ()
{
- return n_physical (JackPortIsInput);
+ if (!_backend) {
+ return;
+ }
+ return _backend->transport_stop ();
}
-ChanCount
-AudioEngine::n_physical_outputs () const
+TransportState
+AudioEngine::transport_state ()
{
- return n_physical (JackPortIsOutput);
+ if (!_backend) {
+ return TransportStopped;
+ }
+ return _backend->transport_state ();
}
void
-AudioEngine::get_physical (DataType type, unsigned long flags, vector<string>& phy)
+AudioEngine::transport_locate (framepos_t pos)
{
- GET_PRIVATE_JACK_POINTER (_jack);
- const char ** ports;
-
- if ((ports = jack_get_ports (_priv_jack, NULL, type.to_jack_type(), JackPortIsPhysical | flags)) == 0) {
+ if (!_backend) {
return;
}
-
- if (ports) {
- for (uint32_t i = 0; ports[i]; ++i) {
- if (strstr (ports[i], "Midi-Through")) {
- continue;
- }
- phy.push_back (ports[i]);
- }
- free (ports);
- }
+ return _backend->transport_locate (pos);
}
-/** Get physical ports for which JackPortIsOutput is set; ie those that correspond to
- * a physical input connector.
- */
-void
-AudioEngine::get_physical_inputs (DataType type, vector<string>& ins)
+framepos_t
+AudioEngine::transport_frame()
{
- get_physical (type, JackPortIsOutput, ins);
+ if (!_backend) {
+ return 0;
+ }
+ return _backend->transport_frame ();
}
-/** Get physical ports for which JackPortIsInput is set; ie those that correspond to
- * a physical output connector.
- */
-void
-AudioEngine::get_physical_outputs (DataType type, vector<string>& outs)
+framecnt_t
+AudioEngine::sample_rate () const
{
- get_physical (type, JackPortIsInput, outs);
+ if (!_backend) {
+ return 0;
+ }
+ return _backend->sample_rate ();
}
-void
-AudioEngine::transport_stop ()
+pframes_t
+AudioEngine::samples_per_cycle () const
{
- GET_PRIVATE_JACK_POINTER (_jack);
- jack_transport_stop (_priv_jack);
+ if (!_backend) {
+ return 0;
+ }
+ return _backend->buffer_size ();
}
-void
-AudioEngine::transport_start ()
+int
+AudioEngine::usecs_per_cycle () const
{
- GET_PRIVATE_JACK_POINTER (_jack);
- jack_transport_start (_priv_jack);
+ if (!_backend) {
+ return -1;
+ }
+ return _backend->usecs_per_cycle ();
}
-void
-AudioEngine::transport_locate (framepos_t where)
+size_t
+AudioEngine::raw_buffer_size (DataType t)
{
- GET_PRIVATE_JACK_POINTER (_jack);
- jack_transport_locate (_priv_jack, where);
+ if (!_backend) {
+ return -1;
+ }
+ return _backend->raw_buffer_size (t);
}
-AudioEngine::TransportState
-AudioEngine::transport_state ()
+pframes_t
+AudioEngine::sample_time ()
{
- GET_PRIVATE_JACK_POINTER_RET (_jack, ((TransportState) JackTransportStopped));
- jack_position_t pos;
- return (TransportState) jack_transport_query (_priv_jack, &pos);
+ if (!_backend) {
+ return 0;
+ }
+ return _backend->sample_time ();
}
-int
-AudioEngine::reset_timebase ()
+pframes_t
+AudioEngine::sample_time_at_cycle_start ()
{
- GET_PRIVATE_JACK_POINTER_RET (_jack, -1);
- if (_session) {
- if (_session->config.get_jack_time_master()) {
- return jack_set_timebase_callback (_priv_jack, 0, _jack_timebase_callback, this);
- } else {
- return jack_release_timebase (_jack);
- }
+ if (!_backend) {
+ return 0;
}
- return 0;
+ return _backend->sample_time_at_cycle_start ();
}
-int
-AudioEngine::freewheel (bool onoff)
+pframes_t
+AudioEngine::samples_since_cycle_start ()
{
- GET_PRIVATE_JACK_POINTER_RET (_jack, -1);
-
- if (onoff != _freewheeling) {
- return jack_set_freewheel (_priv_jack, onoff);
-
- } else {
- /* already doing what has been asked for */
- return 0;
+ if (!_backend) {
+ return 0;
}
+ return _backend->samples_since_cycle_start ();
}
-void
-AudioEngine::remove_all_ports ()
+bool
+AudioEngine::get_sync_offset (pframes_t& offset) const
{
- /* make sure that JACK callbacks that will be invoked as we cleanup
- * ports know that they have nothing to do.
- */
-
- port_remove_in_progress = true;
-
- /* process lock MUST be held by caller
- */
-
- {
- RCUWriter<Ports> writer (ports);
- boost::shared_ptr<Ports> ps = writer.get_copy ();
- ps->clear ();
+ if (!_backend) {
+ return false;
}
-
- /* clear dead wood list in RCU */
-
- ports.flush ();
-
- port_remove_in_progress = false;
+ return _backend->get_sync_offset (offset);
}
int
-AudioEngine::connect_to_jack (string client_name, string session_uuid)
+AudioEngine::create_process_thread (boost::function<void()> func, pthread_t* thr, size_t stacksize)
{
- EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa ();
- boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa;
- jack_status_t status;
-
- /* revert all environment settings back to whatever they were when ardour started
- */
-
- if (global_epa) {
- current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */
- global_epa->restore ();
- }
-
- jack_client_name = client_name; /* might be reset below */
-#ifdef HAVE_JACK_SESSION
- if (! session_uuid.empty())
- _jack = jack_client_open (jack_client_name.c_str(), JackSessionID, &status, session_uuid.c_str());
- else
-#endif
- _jack = jack_client_open (jack_client_name.c_str(), JackNullOption, &status, 0);
-
- if (_jack == NULL) {
- // error message is not useful here
+ if (!_backend) {
return -1;
}
+ return _backend->create_process_thread (func, thr, stacksize);
+}
- GET_PRIVATE_JACK_POINTER_RET (_jack, -1);
- if (status & JackNameNotUnique) {
- jack_client_name = jack_get_client_name (_priv_jack);
+int
+AudioEngine::set_device_name (const std::string& name)
+{
+ if (!_backend) {
+ return -1;
}
-
- return 0;
+ return _backend->set_device_name (name);
}
int
-AudioEngine::disconnect_from_jack ()
+AudioEngine::set_sample_rate (float sr)
{
- GET_PRIVATE_JACK_POINTER_RET (_jack, 0);
-
- if (_running) {
- stop_metering_thread ();
- }
-
- {
- Glib::Threads::Mutex::Lock lm (_process_lock);
- jack_client_close (_priv_jack);
- _jack = 0;
- }
-
- _buffer_size = 0;
- _frame_rate = 0;
- _raw_buffer_sizes.clear();
-
- if (_running) {
- _running = false;
- MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */
- Stopped(); /* EMIT SIGNAL */
+ if (!_backend) {
+ return -1;
}
-
- return 0;
+ return _backend->set_sample_rate (sr);
}
int
-AudioEngine::reconnect_to_jack ()
+AudioEngine::set_buffer_size (uint32_t bufsiz)
{
- if (_running) {
- disconnect_from_jack ();
- /* XXX give jackd a chance */
- Glib::usleep (250000);
- }
-
- if (connect_to_jack (jack_client_name, "")) {
- error << _("failed to connect to JACK") << endmsg;
+ if (!_backend) {
return -1;
}
+ return _backend->set_buffer_size (bufsiz);
+}
- Ports::iterator i;
-
- boost::shared_ptr<Ports> p = ports.reader ();
-
- for (i = p->begin(); i != p->end(); ++i) {
- if (i->second->reestablish ()) {
- break;
- }
+int
+AudioEngine::set_sample_format (SampleFormat sf)
+{
+ if (!_backend) {
+ return -1;
}
+ return _backend->set_sample_format (sf);
+}
- if (i != p->end()) {
- /* failed */
- remove_all_ports ();
+int
+AudioEngine::set_interleaved (bool yn)
+{
+ if (!_backend) {
return -1;
}
+ return _backend->set_interleaved (yn);
+}
- GET_PRIVATE_JACK_POINTER_RET (_jack,-1);
-
- MIDI::Manager::instance()->reestablish (_priv_jack);
-
- if (_session) {
- _session->reset_jack_connection (_priv_jack);
- jack_bufsize_callback (jack_get_buffer_size (_priv_jack));
- _session->set_frame_rate (jack_get_sample_rate (_priv_jack));
+int
+AudioEngine::set_input_channels (uint32_t ic)
+{
+ if (!_backend) {
+ return -1;
}
+ return _backend->set_input_channels (ic);
+}
- last_monitor_check = 0;
-
- set_jack_callbacks ();
-
- if (jack_activate (_priv_jack) == 0) {
- _running = true;
- _has_run = true;
- } else {
+int
+AudioEngine::set_output_channels (uint32_t oc)
+{
+ if (!_backend) {
return -1;
}
+ return _backend->set_output_channels (oc);
+}
- /* re-establish connections */
-
- for (i = p->begin(); i != p->end(); ++i) {
- i->second->reconnect ();
+int
+AudioEngine::set_systemic_input_latency (uint32_t il)
+{
+ if (!_backend) {
+ return -1;
}
-
- MIDI::Manager::instance()->reconnect ();
-
- Running (); /* EMIT SIGNAL*/
-
- start_metering_thread ();
-
- return 0;
+ return _backend->set_systemic_input_latency (il);
}
int
-AudioEngine::request_buffer_size (pframes_t nframes)
+AudioEngine::set_systemic_output_latency (uint32_t ol)
{
- GET_PRIVATE_JACK_POINTER_RET (_jack, -1);
-
- if (nframes == jack_get_buffer_size (_priv_jack)) {
- return 0;
+ if (!_backend) {
+ return -1;
}
-
- return jack_set_buffer_size (_priv_jack, nframes);
+ return _backend->set_systemic_output_latency (ol);
}
-string
-AudioEngine::make_port_name_relative (string portname) const
+/* END OF BACKEND PROXY API */
+
+void
+AudioEngine::thread_init_callback (void* arg)
{
- string::size_type len;
- string::size_type n;
+ /* make sure that anybody who needs to know about this thread
+ knows about it.
+ */
- len = portname.length();
+ pthread_set_name (X_("audioengine"));
- for (n = 0; n < len; ++n) {
- if (portname[n] == ':') {
- break;
- }
- }
+ PBD::notify_gui_about_thread_creation ("gui", pthread_self(), X_("AudioEngine"), 4096);
+ PBD::notify_gui_about_thread_creation ("midiui", pthread_self(), X_("AudioEngine"), 128);
- if ((n != len) && (portname.substr (0, n) == jack_client_name)) {
- return portname.substr (n+1);
- }
+ SessionEvent::create_per_thread_pool (X_("AudioEngine"), 512);
- return portname;
+ AsyncMIDIPort::set_process_thread (pthread_self());
+
+ if (arg) {
+ /* the special thread created/managed by the backend */
+ AudioEngine::instance()->_main_thread = new ProcessThread;
+ }
}
-string
-AudioEngine::make_port_name_non_relative (string portname) const
+int
+AudioEngine::sync_callback (TransportState state, framepos_t position)
{
- string str;
-
- if (portname.find_first_of (':') != string::npos) {
- return portname;
+ if (_session) {
+ return _session->backend_sync_callback (state, position);
}
+ return 0;
+}
- str = jack_client_name;
- str += ':';
- str += portname;
-
- return str;
+void
+AudioEngine::freewheel_callback (bool onoff)
+{
+ _freewheeling = onoff;
}
-bool
-AudioEngine::port_is_mine (const string& portname) const
+void
+AudioEngine::latency_callback (bool for_playback)
{
- if (portname.find_first_of (':') != string::npos) {
- if (portname.substr (0, jack_client_name.length ()) != jack_client_name) {
- return false;
- }
+ if (_session) {
+ _session->update_latency (for_playback);
}
- return true;
}
-bool
-AudioEngine::is_realtime () const
+void
+AudioEngine::update_latencies ()
{
- GET_PRIVATE_JACK_POINTER_RET (_jack,false);
- return jack_is_realtime (_priv_jack);
+ if (_backend) {
+ _backend->update_latencies ();
+ }
}
-int
-AudioEngine::create_process_thread (boost::function<void()> f, jack_native_thread_t* thread, size_t stacksize)
+void
+AudioEngine::halted_callback (const char* why)
{
- GET_PRIVATE_JACK_POINTER_RET (_jack, 0);
- ThreadData* td = new ThreadData (this, f, stacksize);
+ stop_metering_thread ();
+ _running = false;
- if (jack_client_create_thread (_priv_jack, thread, jack_client_real_time_priority (_priv_jack),
- jack_is_realtime (_priv_jack), _start_process_thread, td)) {
- return -1;
- }
-
- return 0;
+ Port::PortDrop (); /* EMIT SIGNAL */
+ Halted (why); /* EMIT SIGNAL */
}
bool
-AudioEngine::stop_process_thread (jack_native_thread_t thread)
+AudioEngine::setup_required () const
{
- /**
- * can't use GET_PRIVATE_JACK_POINTER_RET (_jack, 0) here
- * because _jack is 0 when this is called. At least for
- * Jack 2 _jack arg is not used so it should be OK
- */
-
-#if defined(USING_JACK2_EXPANSION_OF_JACK_API) || defined(PLATFORM_WINDOWS)
- if (jack_client_stop_thread (_jack, thread) != 0) {
- error << "AudioEngine: cannot stop process thread" << endmsg;
+ if (_backends.size() == 1 && _backends.begin()->second->already_configured()) {
return false;
}
-#else
- void* status;
- pthread_join (thread, &status);
-#endif
return true;
}
-void*
-AudioEngine::_start_process_thread (void* arg)
+MTDM*
+AudioEngine::mtdm()
{
- ThreadData* td = reinterpret_cast<ThreadData*> (arg);
- boost::function<void()> f = td->f;
- delete td;
-
- f ();
-
- return 0;
+ return _mtdm;
}
-bool
-AudioEngine::port_is_physical (const std::string& portname) const
+void
+AudioEngine::start_latency_detection ()
{
- GET_PRIVATE_JACK_POINTER_RET(_jack, false);
+ PortEngine& pe (port_engine());
- jack_port_t *port = jack_port_by_name (_priv_jack, portname.c_str());
+ delete _mtdm;
+ _mtdm = 0;
- if (!port) {
- return false;
- }
+ /* create the ports we will use to read/write data */
+
+ if ((_latency_output_port = pe.register_port ("latency_out", DataType::AUDIO, IsOutput)) == 0) {
+ return;
+ }
+ if (pe.connect (_latency_output_port, _latency_output_name)) {
+ return;
+ }
- return jack_port_flags (port) & JackPortIsPhysical;
-}
+ const string portname ("latency_in");
+ if ((_latency_input_port = pe.register_port (portname, DataType::AUDIO, IsInput)) == 0) {
+ pe.unregister_port (_latency_output_port);
+ return;
+ }
+ if (pe.connect (_latency_input_name, make_port_name_non_relative (portname))) {
+ pe.unregister_port (_latency_output_port);
+ return;
+ }
-void
-AudioEngine::request_jack_monitors_input (const std::string& portname, bool yn) const
-{
- GET_PRIVATE_JACK_POINTER(_jack);
+ LatencyRange lr;
+ _latency_signal_latency = 0;
+ lr = pe.get_latency_range (_latency_input_port, false);
+ _latency_signal_latency = lr.max;
+ lr = pe.get_latency_range (_latency_output_port, true);
+ _latency_signal_latency += lr.max;
- jack_port_t *port = jack_port_by_name (_priv_jack, portname.c_str());
+ cerr << "latency signal pathway = " << _latency_signal_latency << endl;
+
+ /* all created and connected, lets go */
+
+ _mtdm = new MTDM (sample_rate());
+ _measuring_latency = true;
+ _latency_flush_frames = samples_per_cycle();
- if (!port) {
- return;
- }
- jack_port_request_monitor (port, yn);
}
void
-AudioEngine::update_latencies ()
+AudioEngine::stop_latency_detection ()
{
- GET_PRIVATE_JACK_POINTER (_jack);
- jack_recompute_total_latencies (_priv_jack);
+ port_engine().unregister_port (_latency_output_port);
+ port_engine().unregister_port (_latency_input_port);
+ _measuring_latency = false;
}
void
-AudioEngine::destroy ()
+AudioEngine::set_latency_output_port (const string& name)
{
- delete _instance;
- _instance = 0;
+ _latency_output_name = name;
}
+void
+AudioEngine::set_latency_input_port (const string& name)
+{
+ _latency_input_name = name;
+}
diff --git a/libs/ardour/backend_search_path.cc b/libs/ardour/backend_search_path.cc
new file mode 100644
index 0000000000..4c5ff40e7d
--- /dev/null
+++ b/libs/ardour/backend_search_path.cc
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 2013 Paul Davis
+
+ 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 <glibmm/miscutils.h>
+
+#include "ardour/backend_search_path.h"
+#include "ardour/directory_names.h"
+#include "ardour/filesystem_paths.h"
+
+namespace {
+ const char * const backend_env_variable_name = "ARDOUR_BACKEND_PATH";
+} // anonymous
+
+using namespace PBD;
+
+namespace ARDOUR {
+
+Searchpath
+backend_search_path ()
+{
+ Searchpath spath(user_config_directory ());
+ spath += ardour_dll_directory ();
+ spath.add_subdirectory_to_paths(backend_dir_name);
+
+ spath += Searchpath(Glib::getenv(backend_env_variable_name));
+ return spath;
+}
+
+} // namespace ARDOUR
diff --git a/libs/ardour/buffer_set.cc b/libs/ardour/buffer_set.cc
index 184e23d1af..e67aee6be7 100644
--- a/libs/ardour/buffer_set.cc
+++ b/libs/ardour/buffer_set.cc
@@ -90,7 +90,7 @@ BufferSet::clear()
/** Set up this BufferSet so that its data structures mirror a PortSet's buffers.
* This is quite expensive and not RT-safe, so it should not be called in a process context;
- * get_jack_port_addresses() will fill in a structure set up by this method.
+ * get_backend_port_addresses() will fill in a structure set up by this method.
*
* XXX: this *is* called in a process context; I'm not sure quite what `should not' means above.
*/
@@ -114,13 +114,13 @@ BufferSet::attach_buffers (PortSet& ports)
_is_mirror = true;
}
-/** Write the JACK port addresses from a PortSet into our data structures. This
+/** Write the backend port addresses from a PortSet into our data structures. This
* call assumes that attach_buffers() has already been called for the same PortSet.
* Does not allocate, so RT-safe BUT you can only call Port::get_buffer() from
* the process() callback tree anyway, so this has to be called in RT context.
*/
void
-BufferSet::get_jack_port_addresses (PortSet& ports, framecnt_t nframes)
+BufferSet::get_backend_port_addresses (PortSet& ports, framecnt_t nframes)
{
assert (_count == ports.count ());
assert (_available == ports.count ());
diff --git a/libs/ardour/bundle.cc b/libs/ardour/bundle.cc
index 0ac62d7076..834a98d347 100644
--- a/libs/ardour/bundle.cc
+++ b/libs/ardour/bundle.cc
@@ -443,27 +443,26 @@ Bundle::connected_to (boost::shared_ptr<Bundle> other, AudioEngine & engine)
return true;
}
-/** This must not be called in code executed as a response to a JACK event,
- * as it uses jack_port_get_all_connections().
+/** This must not be called in code executed as a response to a backend event,
+ * as it uses the backend port_get_all_connections().
* @return true if any of this bundle's channels are connected to anything.
*/
bool
Bundle::connected_to_anything (AudioEngine& engine)
{
+ PortManager& pm (engine);
+
for (uint32_t i = 0; i < nchannels().n_total(); ++i) {
Bundle::PortList const & ports = channel_ports (i);
for (uint32_t j = 0; j < ports.size(); ++j) {
- /* ports[j] may not be an Ardour port, so use JACK directly
+
+ /* ports[j] may not be an Ardour port, so use the port manager directly
rather than doing it with Port.
*/
- jack_port_t* jp = jack_port_by_name (engine.jack(), ports[j].c_str());
- if (jp) {
- const char ** c = jack_port_get_all_connections (engine.jack(), jp);
- if (c) {
- jack_free (c);
- return true;
- }
+
+ if (pm.connected (ports[j])) {
+ return true;
}
}
}
diff --git a/libs/ardour/capturing_processor.cc b/libs/ardour/capturing_processor.cc
index 4a31d92cc8..ce4a546fb4 100644
--- a/libs/ardour/capturing_processor.cc
+++ b/libs/ardour/capturing_processor.cc
@@ -28,7 +28,7 @@ namespace ARDOUR {
CapturingProcessor::CapturingProcessor (Session & session)
: Processor (session, X_("capture point"))
- , block_size (session.engine().frames_per_cycle())
+ , block_size (AudioEngine::instance()->samples_per_cycle())
{
realloc_buffers ();
}
diff --git a/libs/ardour/debug.cc b/libs/ardour/debug.cc
index 51115001cb..dc762299e9 100644
--- a/libs/ardour/debug.cc
+++ b/libs/ardour/debug.cc
@@ -61,5 +61,6 @@ uint64_t PBD::DEBUG::TempoMap = PBD::new_debug_bit ("tempomap");
uint64_t PBD::DEBUG::OrderKeys = PBD::new_debug_bit ("orderkeys");
uint64_t PBD::DEBUG::Automation = PBD::new_debug_bit ("automation");
uint64_t PBD::DEBUG::WiimoteControl = PBD::new_debug_bit ("wiimotecontrol");
+uint64_t PBD::DEBUG::Ports = PBD::new_debug_bit ("Ports");
diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc
index dfbe4c960a..79c44ce94a 100644
--- a/libs/ardour/delivery.cc
+++ b/libs/ardour/delivery.cc
@@ -245,7 +245,7 @@ Delivery::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pf
processing pathway that wants to use this->output_buffers() for some reason.
*/
- output_buffers().get_jack_port_addresses (ports, nframes);
+ output_buffers().get_backend_port_addresses (ports, nframes);
// this Delivery processor is not a derived type, and thus we assume
// we really can modify the buffers passed in (it is almost certainly
diff --git a/libs/ardour/directory_names.cc b/libs/ardour/directory_names.cc
index 6fb15eaabd..f01c024435 100644
--- a/libs/ardour/directory_names.cc
+++ b/libs/ardour/directory_names.cc
@@ -38,6 +38,7 @@ const char* const route_templates_dir_name = X_("route_templates");
const char* const surfaces_dir_name = X_("surfaces");
const char* const ladspa_dir_name = X_("ladspa");
const char* const panner_dir_name = X_("panners");
+const char* const backend_dir_name = X_("backends");
/* these should end up using variants of PROGRAM_NAME */
#ifdef __APPLE__
diff --git a/libs/ardour/export_channel.cc b/libs/ardour/export_channel.cc
index 9b3f50e85d..82e5d80244 100644
--- a/libs/ardour/export_channel.cc
+++ b/libs/ardour/export_channel.cc
@@ -117,7 +117,7 @@ RegionExportChannelFactory::RegionExportChannelFactory (Session * session, Audio
: region (region)
, track (track)
, type (type)
- , frames_per_cycle (session->engine().frames_per_cycle ())
+ , frames_per_cycle (session->engine().samples_per_cycle ())
, buffers_up_to_date (false)
, region_start (region.position())
, position (region_start)
diff --git a/libs/ardour/export_graph_builder.cc b/libs/ardour/export_graph_builder.cc
index 127546e8fc..2c0c44033d 100644
--- a/libs/ardour/export_graph_builder.cc
+++ b/libs/ardour/export_graph_builder.cc
@@ -56,7 +56,7 @@ ExportGraphBuilder::ExportGraphBuilder (Session const & session)
: session (session)
, thread_pool (hardware_concurrency())
{
- process_buffer_frames = session.engine().frames_per_cycle();
+ process_buffer_frames = session.engine().samples_per_cycle();
}
ExportGraphBuilder::~ExportGraphBuilder ()
@@ -507,7 +507,7 @@ ExportGraphBuilder::ChannelConfig::ChannelConfig (ExportGraphBuilder & parent, F
config = new_config;
- framecnt_t max_frames = parent.session.engine().frames_per_cycle();
+ framecnt_t max_frames = parent.session.engine().samples_per_cycle();
interleaver.reset (new Interleaver<Sample> ());
interleaver->init (new_config.channel_config->get_n_chans(), max_frames);
diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc
index 1406bbd43a..8646b819f3 100644
--- a/libs/ardour/globals.cc
+++ b/libs/ardour/globals.cc
@@ -35,6 +35,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
+#include <time.h>
#ifdef WINDOWS_VST_SUPPORT
#include <fst.h>
@@ -74,11 +75,11 @@
#include "pbd/basename.h"
#include "midi++/port.h"
-#include "midi++/manager.h"
#include "midi++/mmc.h"
#include "ardour/analyser.h"
#include "ardour/audio_library.h"
+#include "ardour/audio_backend.h"
#include "ardour/audioengine.h"
#include "ardour/audioplaylist.h"
#include "ardour/audioregion.h"
@@ -86,6 +87,7 @@
#include "ardour/control_protocol_manager.h"
#include "ardour/filesystem_paths.h"
#include "ardour/midi_region.h"
+#include "ardour/midiport_manager.h"
#include "ardour/mix.h"
#include "ardour/panner_manager.h"
#include "ardour/plugin_manager.h"
@@ -342,6 +344,8 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
EventTypeMap::instance().new_parameter(EnvelopeAutomation);
EventTypeMap::instance().new_parameter(MidiCCAutomation);
+ ARDOUR::AudioEngine::create ();
+
libardour_initialized = true;
return true;
@@ -350,9 +354,6 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
void
ARDOUR::init_post_engine ()
{
- /* the MIDI Manager is needed by the ControlProtocolManager */
- MIDI::Manager::create (AudioEngine::instance()->jack());
-
ControlProtocolManager::instance().discover_control_protocols ();
XMLNode* node;
@@ -550,3 +551,43 @@ ARDOUR::get_available_sync_options ()
return ret;
}
+
+/** Return a monotonic value for the number of microseconds that have elapsed
+ * since an arbitrary zero origin.
+ */
+
+#ifdef __MACH__
+/* Thanks Apple for not implementing this basic SUSv2, POSIX.1-2001 function
+ */
+#include <mach/mach_time.h>
+#define CLOCK_REALTIME 0
+#define CLOCK_MONOTONIC 0
+int
+clock_gettime (int /*clk_id*/, struct timespec *t)
+{
+ static bool initialized = false;
+ static mach_timebase_info_data_t timebase;
+ if (!initialized) {
+ mach_timebase_info(&timebase);
+ initialized = true;
+ }
+ uint64_t time;
+ time = mach_absolute_time();
+ double nseconds = ((double)time * (double)timebase.numer)/((double)timebase.denom);
+ double seconds = ((double)time * (double)timebase.numer)/((double)timebase.denom * 1e9);
+ t->tv_sec = seconds;
+ t->tv_nsec = nseconds;
+ return 0;
+}
+#endif
+
+microseconds_t
+ARDOUR::get_microseconds ()
+{
+ struct timespec ts;
+ if (clock_gettime (CLOCK_MONOTONIC, &ts) != 0) {
+ /* EEEK! */
+ return 0;
+ }
+ return (microseconds_t) ts.tv_sec * 1000000 + (ts.tv_nsec/1000);
+}
diff --git a/libs/ardour/internal_send.cc b/libs/ardour/internal_send.cc
index 029a46ce53..8136985e1f 100644
--- a/libs/ardour/internal_send.cc
+++ b/libs/ardour/internal_send.cc
@@ -310,7 +310,7 @@ bool
InternalSend::configure_io (ChanCount in, ChanCount out)
{
bool ret = Send::configure_io (in, out);
- set_block_size (_session.engine().frames_per_cycle());
+ set_block_size (_session.engine().samples_per_cycle());
return ret;
}
diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc
index a9d37d4315..ebd295411e 100644
--- a/libs/ardour/io.cc
+++ b/libs/ardour/io.cc
@@ -1344,7 +1344,7 @@ IO::bundle_changed (Bundle::Change /*c*/)
string
IO::build_legal_port_name (DataType type)
{
- const int name_size = jack_port_name_size();
+ const int name_size = AudioEngine::instance()->port_name_size();
int limit;
string suffix;
@@ -1378,7 +1378,7 @@ IO::build_legal_port_name (DataType type)
// allow up to 4 digits for the output port number, plus the slash, suffix and extra space
- limit = name_size - _session.engine().client_name().length() - (suffix.length() + 5);
+ limit = name_size - AudioEngine::instance()->my_name().length() - (suffix.length() + 5);
std::vector<char> buf1(name_size+1);
std::vector<char> buf2(name_size+1);
@@ -1411,7 +1411,7 @@ IO::find_port_hole (const char* base)
*/
for (n = 1; n < 9999; ++n) {
- std::vector<char> buf(jack_port_name_size());
+ std::vector<char> buf (AudioEngine::instance()->port_name_size());
PortSet::iterator i = _ports.begin();
snprintf (&buf[0], jack_port_name_size(), _("%s %u"), base, n);
@@ -1645,7 +1645,7 @@ IO::process_input (boost::shared_ptr<Processor> proc, framepos_t start_frame, fr
return;
}
- _buffers.get_jack_port_addresses (_ports, nframes);
+ _buffers.get_backend_port_addresses (_ports, nframes);
if (proc) {
proc->run (_buffers, start_frame, end_frame, nframes, true);
}
diff --git a/libs/ardour/jack_slave.cc b/libs/ardour/jack_slave.cc
index 4c2da4c6c4..4b2f3b1860 100644
--- a/libs/ardour/jack_slave.cc
+++ b/libs/ardour/jack_slave.cc
@@ -20,16 +20,14 @@
#include <iostream>
#include <cerrno>
-#include <jack/jack.h>
-#include <jack/transport.h>
-
+#include "ardour/audioengine.h"
#include "ardour/slave.h"
using namespace std;
using namespace ARDOUR;
-JACK_Slave::JACK_Slave (jack_client_t* j)
- : jack (j)
+JACK_Slave::JACK_Slave (AudioEngine& e)
+ : engine (e)
{
double x;
framepos_t p;
@@ -41,12 +39,6 @@ JACK_Slave::~JACK_Slave ()
{
}
-void
-JACK_Slave::reset_client (jack_client_t* j)
-{
- jack = j;
-}
-
bool
JACK_Slave::locked() const
{
@@ -62,33 +54,26 @@ JACK_Slave::ok() const
bool
JACK_Slave::speed_and_position (double& sp, framepos_t& position)
{
- jack_position_t pos;
- jack_transport_state_t state;
-
- state = jack_transport_query (jack, &pos);
-
- switch (state) {
- case JackTransportStopped:
+ switch (engine.transport_state()) {
+ case TransportStopped:
speed = 0;
_starting = false;
break;
- case JackTransportRolling:
+ case TransportRolling:
speed = 1.0;
_starting = false;
break;
- case JackTransportLooping:
+ case TransportLooping:
speed = 1.0;
_starting = false;
break;
- case JackTransportStarting:
+ case TransportStarting:
_starting = true;
// don't adjust speed here, just leave it as it was
break;
- default:
- cerr << "WARNING: Unknown JACK transport state: " << state << endl;
}
sp = speed;
- position = pos.frame;
+ position = engine.transport_frame();
return true;
}
diff --git a/libs/ardour/ladspa_plugin.cc b/libs/ardour/ladspa_plugin.cc
index 78e16f9941..8b089929b5 100644
--- a/libs/ardour/ladspa_plugin.cc
+++ b/libs/ardour/ladspa_plugin.cc
@@ -44,8 +44,6 @@
#include "pbd/xml++.h"
#include "pbd/stacktrace.h"
-#include "midi++/manager.h"
-
#include "ardour/session.h"
#include "ardour/ladspa_plugin.h"
#include "ardour/buffer_set.h"
@@ -129,7 +127,9 @@ LadspaPlugin::init (string module_path, uint32_t index, framecnt_t rate)
port_cnt = parameter_count();
_control_data = new LADSPA_Data[port_cnt];
+ memset (_control_data, 0, sizeof (LADSPA_Data) * port_cnt);
_shadow_data = new LADSPA_Data[port_cnt];
+ memset (_shadow_data, 0, sizeof (LADSPA_Data) * port_cnt);
for (i = 0; i < port_cnt; ++i) {
if (LADSPA_IS_PORT_CONTROL(port_descriptor (i))) {
diff --git a/libs/ardour/ltc_slave.cc b/libs/ardour/ltc_slave.cc
index ac93ccdb40..eb168c9554 100644
--- a/libs/ardour/ltc_slave.cc
+++ b/libs/ardour/ltc_slave.cc
@@ -134,7 +134,7 @@ LTC_Slave::resync_latency()
if (!session.deletion_in_progress() && session.ltc_output_io()) { /* check if Port exits */
boost::shared_ptr<Port> ltcport = session.ltc_input_port();
- ltcport->get_connected_latency_range(ltc_slave_latency, false);
+ ltcport->get_connected_latency_range (ltc_slave_latency, false);
}
}
@@ -150,9 +150,9 @@ LTC_Slave::reset()
}
void
-LTC_Slave::parse_ltc(const jack_nframes_t nframes, const jack_default_audio_sample_t * const in, const ARDOUR::framecnt_t posinfo)
+LTC_Slave::parse_ltc(const pframes_t nframes, const Sample* const in, const framecnt_t posinfo)
{
- jack_nframes_t i;
+ pframes_t i;
unsigned char sound[8192];
if (nframes > 8192) {
/* TODO warn once or wrap, loop conversion below
@@ -413,22 +413,22 @@ LTC_Slave::init_engine_dll (framepos_t pos, int32_t inc)
}
/* main entry point from session_process.cc
- * called from jack_process callback context
- * so it is OK to use jack_port_get_buffer()
+ * called from process callback context
+ * so it is OK to use get_buffer()
*/
bool
LTC_Slave::speed_and_position (double& speed, framepos_t& pos)
{
bool engine_init_called = false;
- framepos_t now = session.engine().frame_time_at_cycle_start();
+ framepos_t now = session.engine().sample_time_at_cycle_start();
framepos_t sess_pos = session.transport_frame(); // corresponds to now
- framecnt_t nframes = session.engine().frames_per_cycle();
+ framecnt_t nframes = session.engine().samples_per_cycle();
- jack_default_audio_sample_t *in;
+ Sample* in;
boost::shared_ptr<Port> ltcport = session.ltc_input_port();
- in = (jack_default_audio_sample_t*) jack_port_get_buffer (ltcport->jack_port(), nframes);
+ in = (Sample*) AudioEngine::instance()->port_engine().get_buffer (ltcport->port_handle(), nframes);
frameoffset_t skip = now - (monotonic_cnt + nframes);
monotonic_cnt = now;
@@ -441,7 +441,7 @@ LTC_Slave::speed_and_position (double& speed, framepos_t& pos)
else if (engine_dll_initstate != transport_direction && ltc_speed != 0) {
engine_dll_initstate = transport_direction;
init_engine_dll(last_ltc_frame + rint(ltc_speed * double(2 * nframes + now - last_timestamp)),
- session.engine().frames_per_cycle());
+ session.engine().samples_per_cycle());
engine_init_called = true;
}
@@ -521,8 +521,8 @@ LTC_Slave::speed_and_position (double& speed, framepos_t& pos)
t0 = t1;
t1 += b * e + e2;
e2 += c * e;
- speed_flt = (t1 - t0) / double(session.engine().frames_per_cycle());
- DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC engine DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", t0, t1, e, speed_flt, e2 - session.engine().frames_per_cycle() ));
+ speed_flt = (t1 - t0) / double(session.engine().samples_per_cycle());
+ DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC engine DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", t0, t1, e, speed_flt, e2 - session.engine().samples_per_cycle() ));
} else {
DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC adjusting elapsed (no DLL) from %1 by %2\n", elapsed, (2 * nframes * ltc_speed)));
speed_flt = 0;
diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc
index 30134a5dbb..6288616824 100644
--- a/libs/ardour/lv2_plugin.cc
+++ b/libs/ardour/lv2_plugin.cc
@@ -291,7 +291,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
_latency_control_port = 0;
_next_cycle_start = std::numeric_limits<framepos_t>::max();
_next_cycle_speed = 1.0;
- _block_length = _engine.frames_per_cycle();
+ _block_length = _engine.samples_per_cycle();
_seq_size = _engine.raw_buffer_size(DataType::MIDI);
_state_version = 0;
_was_activated = false;
@@ -1925,7 +1925,7 @@ LV2Plugin::Impl::designated_input (const char* uri, void** bufptrs[], void** buf
return port;
}
-static bool lv2_filter (const string& str, void * /* arg*/)
+static bool lv2_filter (const string& str, void* /*arg*/)
{
/* Not a dotfile, has a prefix before a period, suffix is "lv2" */
diff --git a/libs/ardour/midi_buffer.cc b/libs/ardour/midi_buffer.cc
index b47c3ca152..d75b861ea1 100644
--- a/libs/ardour/midi_buffer.cc
+++ b/libs/ardour/midi_buffer.cc
@@ -22,6 +22,7 @@
#include "pbd/malign.h"
#include "pbd/compose.h"
#include "pbd/debug.h"
+#include "pbd/stacktrace.h"
#include "ardour/debug.h"
#include "ardour/midi_buffer.h"
@@ -133,6 +134,7 @@ MidiBuffer::push_back(const Evoral::MIDIEvent<TimeType>& ev)
if (_size + stamp_size + ev.size() >= _capacity) {
cerr << "MidiBuffer::push_back failed (buffer is full)" << endl;
+ PBD::stacktrace (cerr, 20);
return false;
}
@@ -171,7 +173,9 @@ MidiBuffer::push_back(TimeType time, size_t size, const uint8_t* data)
#endif
if (_size + stamp_size + size >= _capacity) {
- cerr << "MidiBuffer::push_back failed (buffer is full)" << endl;
+ cerr << "MidiBuffer::push_back2 failed (buffer is full; _size = " << _size << " capacity "
+ << _capacity << " stamp " << stamp_size << " size = " << size << ")" << endl;
+ PBD::stacktrace (cerr, 20);
return false;
}
@@ -190,55 +194,6 @@ MidiBuffer::push_back(TimeType time, size_t size, const uint8_t* data)
return true;
}
-
-/** Push an event into the buffer.
- *
- * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
- * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
- * Realtime safe.
- * @return false if operation failed (not enough room)
- */
-bool
-MidiBuffer::push_back(const jack_midi_event_t& ev)
-{
- const size_t stamp_size = sizeof(TimeType);
-
- if (_size + stamp_size + ev.size >= _capacity) {
- cerr << "MidiBuffer::push_back failed (buffer is full)" << endl;
- return false;
- }
-
- if (!Evoral::midi_event_is_valid(ev.buffer, ev.size)) {
- cerr << "WARNING: MidiBuffer ignoring illegal MIDI event" << endl;
- return false;
- }
-
-#ifndef NDEBUG
- if (DEBUG::MidiIO & PBD::debug_bits) {
- DEBUG_STR_DECL(a);
- DEBUG_STR_APPEND(a, string_compose ("midibuffer %1 push jack event @ %2 sz %3 ", this, ev.time, ev.size));
- for (size_t i=0; i < ev.size; ++i) {
- DEBUG_STR_APPEND(a,hex);
- DEBUG_STR_APPEND(a,"0x");
- DEBUG_STR_APPEND(a,(int)ev.buffer[i]);
- DEBUG_STR_APPEND(a,' ');
- }
- DEBUG_STR_APPEND(a,'\n');
- DEBUG_TRACE (DEBUG::MidiIO, DEBUG_STR(a).str());
- }
-#endif
-
- uint8_t* const write_loc = _data + _size;
- *((TimeType*)write_loc) = ev.time;
- memcpy(write_loc + stamp_size, ev.buffer, ev.size);
-
- _size += stamp_size + ev.size;
- _silent = false;
-
- return true;
-}
-
-
/** Reserve space for a new event in the buffer.
*
* This call is for copying MIDI directly into the buffer, the data location
diff --git a/libs/ardour/midi_clock_slave.cc b/libs/ardour/midi_clock_slave.cc
index 75f3c13f1c..a52854d954 100644
--- a/libs/ardour/midi_clock_slave.cc
+++ b/libs/ardour/midi_clock_slave.cc
@@ -30,6 +30,8 @@
#include "midi++/port.h"
#include "ardour/debug.h"
+#include "ardour/midi_buffer.h"
+#include "ardour/midi_port.h"
#include "ardour/slave.h"
#include "ardour/tempo.h"
@@ -40,7 +42,7 @@ using namespace ARDOUR;
using namespace MIDI;
using namespace PBD;
-MIDIClock_Slave::MIDIClock_Slave (Session& s, MIDI::Port& p, int ppqn)
+MIDIClock_Slave::MIDIClock_Slave (Session& s, MidiPort& p, int ppqn)
: ppqn (ppqn)
, bandwidth (10.0 / 60.0) // 1 BpM = 1 / 60 Hz
{
@@ -63,19 +65,18 @@ MIDIClock_Slave::~MIDIClock_Slave()
}
void
-MIDIClock_Slave::rebind (MIDI::Port& p)
+MIDIClock_Slave::rebind (MidiPort& port)
{
- port_connections.drop_connections();
+ DEBUG_TRACE (DEBUG::MidiClock, string_compose ("MIDIClock_Slave: connecting to port %1\n", port.name()));
- port = &p;
+ port_connections.drop_connections ();
- DEBUG_TRACE (DEBUG::MidiClock, string_compose ("MIDIClock_Slave: connecting to port %1\n", port->name()));
+ port.self_parser().timing.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::update_midi_clock, this, _1, _2));
+ port.self_parser().start.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::start, this, _1, _2));
+ port.self_parser().contineu.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::contineu, this, _1, _2));
+ port.self_parser().stop.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::stop, this, _1, _2));
+ port.self_parser().position.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::position, this, _1, _2, 3));
- port->parser()->timing.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::update_midi_clock, this, _1, _2));
- port->parser()->start.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::start, this, _1, _2));
- port->parser()->contineu.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::contineu, this, _1, _2));
- port->parser()->stop.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::stop, this, _1, _2));
- port->parser()->position.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::position, this, _1, _2, 3));
}
void
diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc
index cf143bf595..39932db8bf 100644
--- a/libs/ardour/midi_diskstream.cc
+++ b/libs/ardour/midi_diskstream.cc
@@ -201,8 +201,8 @@ MidiDiskstream::non_realtime_input_change ()
seek (_session.transport_frame());
}
- g_atomic_int_set(&_frames_pending_write, 0);
- g_atomic_int_set(&_num_captured_loops, 0);
+ g_atomic_int_set(const_cast<gint*> (&_frames_pending_write), 0);
+ g_atomic_int_set(const_cast<gint*> (&_num_captured_loops), 0);
}
int
@@ -375,8 +375,8 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
}
_write_source->mark_write_starting_now(
capture_start_frame, capture_captured, loop_length);
- g_atomic_int_set(&_frames_pending_write, 0);
- g_atomic_int_set(&_num_captured_loops, 0);
+ g_atomic_int_set(const_cast<gint*> (&_frames_pending_write), 0);
+ g_atomic_int_set(const_cast<gint*> (&_num_captured_loops), 0);
was_recording = true;
}
}
@@ -445,7 +445,7 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
break;
}
}
- g_atomic_int_add(&_frames_pending_write, nframes);
+ g_atomic_int_add(const_cast<gint*> (&_frames_pending_write), nframes);
if (buf.size() != 0) {
Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex, Glib::Threads::TRY_LOCK);
@@ -798,7 +798,7 @@ MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush)
return 0;
}
- const framecnt_t total = g_atomic_int_get(&_frames_pending_write);
+ const framecnt_t total = g_atomic_int_get(const_cast<gint*> (&_frames_pending_write));
if (total == 0 ||
_capture_buf->read_space() == 0 ||
@@ -833,7 +833,7 @@ MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush)
error << string_compose(_("MidiDiskstream %1: cannot write to disk"), id()) << endmsg;
return -1;
}
- g_atomic_int_add(&_frames_pending_write, -to_write);
+ g_atomic_int_add(const_cast<gint*> (&_frames_pending_write), -to_write);
}
out:
@@ -1043,7 +1043,7 @@ MidiDiskstream::transport_looped (framepos_t)
the Source and/or entirely after the capture is finished.
*/
if (was_recording) {
- g_atomic_int_add(&_num_captured_loops, 1);
+ g_atomic_int_add(const_cast<gint*> (&_num_captured_loops), 1);
}
}
@@ -1110,7 +1110,7 @@ MidiDiskstream::prep_record_enable ()
boost::shared_ptr<MidiPort> sp = _source_port.lock ();
if (sp && Config->get_monitoring_model() == HardwareMonitoring) {
- sp->request_jack_monitors_input (!(_session.config.get_auto_input() && rolling));
+ sp->request_input_monitoring (!(_session.config.get_auto_input() && rolling));
}
return true;
@@ -1258,12 +1258,12 @@ MidiDiskstream::allocate_temporary_buffers ()
}
void
-MidiDiskstream::ensure_jack_monitors_input (bool yn)
+MidiDiskstream::ensure_input_monitoring (bool yn)
{
boost::shared_ptr<MidiPort> sp = _source_port.lock ();
if (sp) {
- sp->ensure_jack_monitors_input (yn);
+ sp->ensure_input_monitoring (yn);
}
}
diff --git a/libs/ardour/midi_port.cc b/libs/ardour/midi_port.cc
index 72150f8cfc..de2263fad6 100644
--- a/libs/ardour/midi_port.cc
+++ b/libs/ardour/midi_port.cc
@@ -26,11 +26,14 @@
using namespace ARDOUR;
using namespace std;
-MidiPort::MidiPort (const std::string& name, Flags flags)
+#define port_engine AudioEngine::instance()->port_engine()
+
+MidiPort::MidiPort (const std::string& name, PortFlags flags)
: Port (name, DataType::MIDI, flags)
, _has_been_mixed_down (false)
, _resolve_required (false)
, _input_active (true)
+ , _always_parse (false)
{
_buffer = new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI));
}
@@ -43,12 +46,32 @@ MidiPort::~MidiPort()
void
MidiPort::cycle_start (pframes_t nframes)
{
+ framepos_t now = AudioEngine::instance()->sample_time_at_cycle_start();
+
Port::cycle_start (nframes);
_buffer->clear ();
if (sends_output ()) {
- jack_midi_clear_buffer (jack_port_get_buffer (_jack_port, nframes));
+ port_engine.midi_clear (port_engine.get_buffer (_port_handle, nframes));
+ }
+
+ if (_always_parse) {
+ MidiBuffer& mb (get_midi_buffer (nframes));
+
+ /* dump incoming MIDI to parser */
+
+ for (MidiBuffer::iterator b = mb.begin(); b != mb.end(); ++b) {
+ uint8_t* buf = (*b).buffer();
+
+ _self_parser.set_timestamp (now + (*b).time());
+
+ uint32_t limit = (*b).size();
+
+ for (size_t n = 0; n < limit; ++n) {
+ _self_parser.scanner (buf[n]);
+ }
+ }
}
}
@@ -63,31 +86,33 @@ MidiPort::get_midi_buffer (pframes_t nframes)
if (_input_active) {
- void* jack_buffer = jack_port_get_buffer (_jack_port, nframes);
- const pframes_t event_count = jack_midi_get_event_count (jack_buffer);
-
- /* suck all relevant MIDI events from the JACK MIDI port buffer
+ void* buffer = port_engine.get_buffer (_port_handle, nframes);
+ const pframes_t event_count = port_engine.get_midi_event_count (buffer);
+
+ /* suck all relevant MIDI events from the MIDI port buffer
into our MidiBuffer
*/
-
+
for (pframes_t i = 0; i < event_count; ++i) {
- jack_midi_event_t ev;
+ pframes_t timestamp;
+ size_t size;
+ uint8_t* buf;
- jack_midi_event_get (&ev, jack_buffer, i);
+ port_engine.midi_event_get (timestamp, size, &buf, buffer, i);
- if (ev.buffer[0] == 0xfe) {
+ if (buf[0] == 0xfe) {
/* throw away active sensing */
continue;
}
/* check that the event is in the acceptable time range */
- if ((ev.time >= (_global_port_buffer_offset + _port_buffer_offset)) &&
- (ev.time < (_global_port_buffer_offset + _port_buffer_offset + nframes))) {
- _buffer->push_back (ev);
+ if ((timestamp >= (_global_port_buffer_offset + _port_buffer_offset)) &&
+ (timestamp < (_global_port_buffer_offset + _port_buffer_offset + nframes))) {
+ _buffer->push_back (timestamp, size, buf);
} else {
- cerr << "Dropping incoming MIDI at time " << ev.time << "; offset="
+ cerr << "Dropping incoming MIDI at time " << timestamp << "; offset="
<< _global_port_buffer_offset << " limit="
<< (_global_port_buffer_offset + _port_buffer_offset + nframes) << "\n";
}
@@ -121,7 +146,7 @@ MidiPort::cycle_split ()
}
void
-MidiPort::resolve_notes (void* jack_buffer, MidiBuffer::TimeType when)
+MidiPort::resolve_notes (void* port_buffer, MidiBuffer::TimeType when)
{
for (uint8_t channel = 0; channel <= 0xF; channel++) {
@@ -132,13 +157,13 @@ MidiPort::resolve_notes (void* jack_buffer, MidiBuffer::TimeType when)
* that prioritize sustain over AllNotesOff
*/
- if (jack_midi_event_write (jack_buffer, when, ev, 3) != 0) {
+ if (port_engine.midi_event_put (port_buffer, when, ev, 3) != 0) {
cerr << "failed to deliver sustain-zero on channel " << channel << " on port " << name() << endl;
}
ev[1] = MIDI_CTL_ALL_NOTES_OFF;
- if (jack_midi_event_write (jack_buffer, 0, ev, 3) != 0) {
+ if (port_engine.midi_event_put (port_buffer, 0, ev, 3) != 0) {
cerr << "failed to deliver ALL NOTES OFF on channel " << channel << " on port " << name() << endl;
}
}
@@ -149,11 +174,11 @@ MidiPort::flush_buffers (pframes_t nframes)
{
if (sends_output ()) {
- void* jack_buffer = jack_port_get_buffer (_jack_port, nframes);
-
+ void* port_buffer = port_engine.get_buffer (_port_handle, nframes);
+
if (_resolve_required) {
/* resolve all notes at the start of the buffer */
- resolve_notes (jack_buffer, 0);
+ resolve_notes (port_buffer, 0);
_resolve_required = false;
}
@@ -166,7 +191,7 @@ MidiPort::flush_buffers (pframes_t nframes)
assert (ev.time() < (nframes + _global_port_buffer_offset + _port_buffer_offset));
if (ev.time() >= _global_port_buffer_offset + _port_buffer_offset) {
- if (jack_midi_event_write (jack_buffer, (jack_nframes_t) ev.time(), ev.buffer(), ev.size()) != 0) {
+ if (port_engine.midi_event_put (port_buffer, (pframes_t) ev.time(), ev.buffer(), ev.size()) != 0) {
cerr << "write failed, drop flushed note off on the floor, time "
<< ev.time() << " > " << _global_port_buffer_offset + _port_buffer_offset << endl;
}
@@ -202,6 +227,7 @@ MidiPort::reset ()
{
Port::reset ();
delete _buffer;
+ cerr << name() << " new MIDI buffer of size " << AudioEngine::instance()->raw_buffer_size (DataType::MIDI) << endl;
_buffer = new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI));
}
@@ -210,3 +236,9 @@ MidiPort::set_input_active (bool yn)
{
_input_active = yn;
}
+
+void
+MidiPort::set_always_parse (bool yn)
+{
+ _always_parse = yn;
+}
diff --git a/libs/ardour/midi_ui.cc b/libs/ardour/midi_ui.cc
index 5822cc509e..e6d49175bd 100644
--- a/libs/ardour/midi_ui.cc
+++ b/libs/ardour/midi_ui.cc
@@ -22,11 +22,11 @@
#include "pbd/pthread_utils.h"
-#include "midi++/manager.h"
-#include "midi++/port.h"
-
+#include "ardour/async_midi_port.h"
#include "ardour/debug.h"
#include "ardour/audioengine.h"
+#include "ardour/midi_port.h"
+#include "ardour/midiport_manager.h"
#include "ardour/midi_ui.h"
#include "ardour/session.h"
#include "ardour/session_event.h"
@@ -48,7 +48,6 @@ MidiControlUI::MidiControlUI (Session& s)
: AbstractUI<MidiUIRequest> (X_("midiui"))
, _session (s)
{
- MIDI::Manager::instance()->PortsChanged.connect_same_thread (rebind_connection, boost::bind (&MidiControlUI::change_midi_ports, this));
_instance = this;
}
@@ -83,20 +82,10 @@ MidiControlUI::do_request (MidiUIRequest* req)
}
}
-void
-MidiControlUI::change_midi_ports ()
-{
- MidiUIRequest* req = get_request (PortChange);
- if (req == 0) {
- return;
- }
- send_request (req);
-}
-
bool
-MidiControlUI::midi_input_handler (IOCondition ioc, MIDI::Port* port)
+MidiControlUI::midi_input_handler (IOCondition ioc, AsyncMIDIPort* port)
{
- DEBUG_TRACE (DEBUG::MidiIO, string_compose ("something happend on %1\n", port->name()));
+ DEBUG_TRACE (DEBUG::MidiIO, string_compose ("something happend on %1\n", ((ARDOUR::Port*)port)->name()));
if (ioc & ~IO_IN) {
return false;
@@ -108,8 +97,8 @@ MidiControlUI::midi_input_handler (IOCondition ioc, MIDI::Port* port)
CrossThreadChannel::drain (port->selectable());
#endif
- DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", port->name()));
- framepos_t now = _session.engine().frame_time();
+ DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", ((ARDOUR::Port*)port)->name()));
+ framepos_t now = _session.engine().sample_time();
port->parse (now);
}
@@ -130,22 +119,19 @@ MidiControlUI::clear_ports ()
void
MidiControlUI::reset_ports ()
{
- clear_ports ();
-
- boost::shared_ptr<const MIDI::Manager::PortList> plist = MIDI::Manager::instance()->get_midi_ports ();
+ if (port_sources.empty()) {
+ AsyncMIDIPort* async = dynamic_cast<AsyncMIDIPort*> (_session.midi_input_port());
- for (MIDI::Manager::PortList::const_iterator i = plist->begin(); i != plist->end(); ++i) {
-
- if (!(*i)->centrally_parsed()) {
- continue;
+ if (!async) {
+ return;
}
int fd;
- if ((fd = (*i)->selectable ()) >= 0) {
+ if ((fd = async->selectable ()) >= 0) {
Glib::RefPtr<IOSource> psrc = IOSource::create (fd, IO_IN|IO_HUP|IO_ERR);
-
- psrc->connect (sigc::bind (sigc::mem_fun (this, &MidiControlUI::midi_input_handler), *i));
+
+ psrc->connect (sigc::bind (sigc::mem_fun (this, &MidiControlUI::midi_input_handler), async));
psrc->attach (_main_loop->get_context());
// glibmm hack: for now, store only the GSource*
diff --git a/libs/ardour/midiport_manager.cc b/libs/ardour/midiport_manager.cc
new file mode 100644
index 0000000000..d17401cee8
--- /dev/null
+++ b/libs/ardour/midiport_manager.cc
@@ -0,0 +1,170 @@
+/*
+ Copyright (C) 1998-99 Paul Barton-Davis
+ 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.
+
+ $Id$
+*/
+
+#include "ardour/audioengine.h"
+#include "ardour/async_midi_port.h"
+#include "ardour/midiport_manager.h"
+#include "ardour/rc_configuration.h"
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace std;
+using namespace MIDI;
+using namespace PBD;
+
+
+MidiPortManager::MidiPortManager ()
+{
+ create_ports ();
+}
+
+MidiPortManager::~MidiPortManager ()
+{
+ if (_midi_in) {
+ AudioEngine::instance()->unregister_port (_midi_in);
+ }
+ if (_midi_in) {
+ AudioEngine::instance()->unregister_port (_midi_in);
+ }
+ if (_mtc_input_port) {
+ AudioEngine::instance()->unregister_port (_mtc_input_port);
+ }
+ if (_mtc_output_port) {
+ AudioEngine::instance()->unregister_port (_mtc_output_port);
+ }
+ if (_midi_clock_input_port) {
+ AudioEngine::instance()->unregister_port (_midi_clock_input_port);
+ }
+ if (_midi_clock_output_port) {
+ AudioEngine::instance()->unregister_port (_midi_clock_output_port);
+ }
+
+}
+
+void
+MidiPortManager::create_ports ()
+{
+ /* this method is idempotent
+ */
+
+ if (_midi_in) {
+ return;
+ }
+
+ _midi_in = AudioEngine::instance()->register_input_port (DataType::MIDI, _("MIDI control in"), true);
+ _midi_out = AudioEngine::instance()->register_output_port (DataType::MIDI, _("MIDI control out"), true);
+
+ _mmc_in = AudioEngine::instance()->register_input_port (DataType::MIDI, _("MMC in"), true);
+ _mmc_out = AudioEngine::instance()->register_output_port (DataType::MIDI, _("MMC out"), true);
+
+ /* XXX nasty type conversion needed because of the mixed inheritance
+ * required to integrate MIDI::IPMidiPort and ARDOUR::AsyncMIDIPort.
+ *
+ * At some point, we'll move IPMidiPort into Ardour and make it
+ * inherit from ARDOUR::MidiPort not MIDI::Port, and then this
+ * mess can go away
+ */
+
+ _midi_input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_midi_in).get();
+ _midi_output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_midi_out).get();
+
+ _mmc_input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_mmc_in).get();
+ _mmc_output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_mmc_out).get();
+
+ /* Now register ports used for sync (MTC and MIDI Clock)
+ */
+
+ boost::shared_ptr<ARDOUR::Port> p;
+
+ p = AudioEngine::instance()->register_input_port (DataType::MIDI, _("MTC in"));
+ _mtc_input_port = boost::dynamic_pointer_cast<MidiPort> (p);
+ p = AudioEngine::instance()->register_output_port (DataType::MIDI, _("MTC out"));
+ _mtc_output_port= boost::dynamic_pointer_cast<MidiPort> (p);
+
+ p = AudioEngine::instance()->register_input_port (DataType::MIDI, _("MIDI Clock in"));
+ _midi_clock_input_port = boost::dynamic_pointer_cast<MidiPort> (p);
+ p = AudioEngine::instance()->register_output_port (DataType::MIDI, _("MIDI Clock out"));
+ _midi_clock_output_port= boost::dynamic_pointer_cast<MidiPort> (p);
+
+ /* These ports all need their incoming data handled in
+ * Port::cycle_start() and so ...
+ */
+
+ _mtc_input_port->set_always_parse (true);
+ _mtc_output_port->set_always_parse (true);
+ _midi_clock_input_port->set_always_parse (true);
+ _midi_clock_output_port->set_always_parse (true);
+}
+
+void
+MidiPortManager::set_midi_port_states (const XMLNodeList&nodes)
+{
+ XMLProperty* prop;
+ typedef map<std::string,boost::shared_ptr<Port> > PortMap;
+ PortMap ports;
+ const int version = 0;
+
+ ports.insert (make_pair (_mtc_input_port->name(), _mtc_input_port));
+ ports.insert (make_pair (_mtc_output_port->name(), _mtc_output_port));
+ ports.insert (make_pair (_midi_clock_input_port->name(), _midi_clock_input_port));
+ ports.insert (make_pair (_midi_clock_output_port->name(), _midi_clock_output_port));
+ ports.insert (make_pair (_midi_input_port->name(), _midi_in));
+ ports.insert (make_pair (_midi_output_port->name(), _midi_out));
+ ports.insert (make_pair (_mmc_input_port->name(), _mmc_in));
+ ports.insert (make_pair (_mmc_output_port->name(), _mmc_out));
+
+ for (XMLNodeList::const_iterator n = nodes.begin(); n != nodes.end(); ++n) {
+ if ((prop = (*n)->property (X_("name"))) == 0) {
+ continue;
+ }
+
+ PortMap::iterator p = ports.find (prop->value());
+ if (p == ports.end()) {
+ continue;
+ }
+
+ p->second->set_state (**n, version);
+ }
+}
+
+list<XMLNode*>
+MidiPortManager::get_midi_port_states () const
+{
+ typedef map<std::string,boost::shared_ptr<Port> > PortMap;
+ PortMap ports;
+ list<XMLNode*> s;
+
+ ports.insert (make_pair (_mtc_input_port->name(), _mtc_input_port));
+ ports.insert (make_pair (_mtc_output_port->name(), _mtc_output_port));
+ ports.insert (make_pair (_midi_clock_input_port->name(), _midi_clock_input_port));
+ ports.insert (make_pair (_midi_clock_output_port->name(), _midi_clock_output_port));
+ ports.insert (make_pair (_midi_input_port->name(), _midi_in));
+ ports.insert (make_pair (_midi_output_port->name(), _midi_out));
+ ports.insert (make_pair (_mmc_input_port->name(), _mmc_in));
+ ports.insert (make_pair (_mmc_output_port->name(), _mmc_out));
+
+ for (PortMap::const_iterator p = ports.begin(); p != ports.end(); ++p) {
+ s.push_back (&p->second->get_state());
+ }
+
+ return s;
+}
+
+
diff --git a/libs/ardour/mtc_slave.cc b/libs/ardour/mtc_slave.cc
index c41c6d6c94..2c351ab68a 100644
--- a/libs/ardour/mtc_slave.cc
+++ b/libs/ardour/mtc_slave.cc
@@ -25,11 +25,12 @@
#include "pbd/error.h"
#include "pbd/pthread_utils.h"
-#include "midi++/port.h"
+#include "ardour/audioengine.h"
#include "ardour/debug.h"
-#include "ardour/slave.h"
+#include "ardour/midi_buffer.h"
+#include "ardour/midi_port.h"
#include "ardour/session.h"
-#include "ardour/audioengine.h"
+#include "ardour/slave.h"
#include <glibmm/timer.h>
@@ -50,8 +51,9 @@ using namespace Timecode;
*/
const int MTC_Slave::frame_tolerance = 2;
-MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
+MTC_Slave::MTC_Slave (Session& s, MidiPort& p)
: session (s)
+ , port (&p)
{
can_notify_on_unknown_rate = true;
did_reset_tc_format = false;
@@ -72,7 +74,10 @@ MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
session.config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&MTC_Slave::parameter_changed, this, _1));
parse_timecode_offset();
reset (true);
- rebind (p);
+
+ port->self_parser().mtc_time.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_time, this, _1, _2, _3));
+ port->self_parser().mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_qtr, this, _1, _2, _3));
+ port->self_parser().mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_status, this, _1));
}
MTC_Slave::~MTC_Slave()
@@ -96,15 +101,12 @@ MTC_Slave::~MTC_Slave()
}
void
-MTC_Slave::rebind (MIDI::Port& p)
+MTC_Slave::rebind (MidiPort& p)
{
port_connections.drop_connections ();
port = &p;
- port->parser()->mtc_time.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_time, this, _1, _2, _3));
- port->parser()->mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_qtr, this, _1, _2, _3));
- port->parser()->mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_status, this, _1));
}
void
@@ -156,7 +158,8 @@ MTC_Slave::outside_window (framepos_t pos) const
bool
MTC_Slave::locked () const
{
- return port->parser()->mtc_locked() && last_inbound_frame !=0 && engine_dll_initstate !=0;
+ DEBUG_TRACE (DEBUG::MTC, string_compose ("locked ? %1 last %2 initstate %3\n", port->self_parser().mtc_locked(), last_inbound_frame, engine_dll_initstate));
+ return port->self_parser().mtc_locked() && last_inbound_frame !=0 && engine_dll_initstate !=0;
}
bool
@@ -257,7 +260,6 @@ MTC_Slave::init_mtc_dll(framepos_t tme, double qtr)
DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2));
}
-
/* called from MIDI parser */
void
MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, framepos_t now)
@@ -308,7 +310,7 @@ MTC_Slave::update_mtc_time (const MIDI::byte *msg, bool was_full, framepos_t now
a locate command via MMC.
*/
- //DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", pthread_name()));
+ DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", ::pthread_self()));
TimecodeFormat tc_format;
bool reset_tc = true;
@@ -424,7 +426,7 @@ MTC_Slave::update_mtc_time (const MIDI::byte *msg, bool was_full, framepos_t now
now, timecode, mtc_frame, was_full, speedup_due_to_tc_mismatch));
if (was_full || outside_window (mtc_frame)) {
- DEBUG_TRACE (DEBUG::MTC, string_compose ("update_mtc_time: full TC or outside window. - TID:%1\n", pthread_name()));
+ DEBUG_TRACE (DEBUG::MTC, string_compose ("update_mtc_time: full TC %1 or outside window %2\n", was_full, outside_window (mtc_frame)));
session.request_locate (mtc_frame, false);
session.request_transport_speed (0);
update_mtc_status (MIDI::MTC_Stopped);
@@ -448,7 +450,7 @@ MTC_Slave::update_mtc_time (const MIDI::byte *msg, bool was_full, framepos_t now
DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame: %1 | MTC-FpT: %2 A3-FpT:%3\n",
mtc_frame, (4.0*qtr), session.frames_per_timecode_frame()));
- switch (port->parser()->mtc_running()) {
+ switch (port->self_parser().mtc_running()) {
case MTC_Backward:
mtc_frame -= mtc_off;
qtr *= -1.0;
@@ -528,9 +530,10 @@ MTC_Slave::reset_window (framepos_t root)
of acceptable MTC frames wide open. otherwise, shrink it down to just 2 video frames
ahead of the window root (taking direction into account).
*/
+
framecnt_t const d = (quarter_frame_duration * 4 * frame_tolerance);
- switch (port->parser()->mtc_running()) {
+ switch (port->self_parser().mtc_running()) {
case MTC_Forward:
window_begin = root;
transport_direction = 1;
@@ -553,7 +556,7 @@ MTC_Slave::reset_window (framepos_t root)
break;
}
- DEBUG_TRACE (DEBUG::MTC, string_compose ("legal MTC window now %1 .. %2\n", window_begin, window_end));
+ DEBUG_TRACE (DEBUG::MTC, string_compose ("reset MTC window @ %3, now %1 .. %2\n", window_begin, window_end, root));
}
void
@@ -577,11 +580,11 @@ MTC_Slave::init_engine_dll (framepos_t pos, framepos_t inc)
}
/* main entry point from session_process.cc
- * in jack_process callback context */
+xo * in process callback context */
bool
MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
{
- framepos_t now = session.engine().frame_time_at_cycle_start();
+ framepos_t now = session.engine().sample_time_at_cycle_start();
framepos_t sess_pos = session.transport_frame(); // corresponds to now
//sess_pos -= session.engine().frames_since_cycle_start();
@@ -591,11 +594,21 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
read_current (&last);
+ DEBUG_TRACE (DEBUG::MTC, string_compose ("speed&pos: timestamp %1 speed %2 initstate %3 dir %4 tpos %5 now %6 last-in %7\n",
+ last.timestamp,
+ last.speed,
+ engine_dll_initstate,
+ transport_direction,
+ sess_pos,
+ now,
+ last_inbound_frame));
+
/* re-init engine DLL here when state changed (direction, first_mtc_timestamp) */
- if (last.timestamp == 0) { engine_dll_initstate = 0; }
- else if (engine_dll_initstate != transport_direction && last.speed != 0) {
+ if (last.timestamp == 0) {
+ engine_dll_initstate = 0;
+ } else if (engine_dll_initstate != transport_direction && last.speed != 0) {
engine_dll_initstate = transport_direction;
- init_engine_dll(last.position, session.engine().frames_per_cycle());
+ init_engine_dll(last.position, session.engine().samples_per_cycle());
engine_dll_reinitialized = true;
}
@@ -627,9 +640,7 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
/* interpolate position according to speed and time since last quarter-frame*/
if (speed_flt == 0.0f) {
elapsed = 0;
- }
- else
- {
+ } else {
/* scale elapsed time by the current MTC speed */
elapsed = (framecnt_t) rint (speed_flt * (now - last.timestamp));
if (give_slave_full_control_over_transport_speed() && !engine_dll_reinitialized) {
@@ -645,8 +656,8 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
te0 = te1;
te1 += be * e + ee2;
ee2 += ce * e;
- speed_flt = (te1 - te0) / double(session.engine().frames_per_cycle());
- DEBUG_TRACE (DEBUG::MTC, string_compose ("engine DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", te0, te1, e, speed_flt, ee2 - session.engine().frames_per_cycle() ));
+ speed_flt = (te1 - te0) / double(session.engine().samples_per_cycle());
+ DEBUG_TRACE (DEBUG::MTC, string_compose ("engine DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", te0, te1, e, speed_flt, ee2 - session.engine().samples_per_cycle() ));
}
}
@@ -659,14 +670,13 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
*/
if (!session.actively_recording()
&& speed != 0
- && ( (pos < 0) || (labs(pos - sess_pos) > 3 * session.frame_rate()) )
- ) {
+ && ((pos < 0) || (labs(pos - sess_pos) > 3 * session.frame_rate()))) {
engine_dll_initstate = 0;
queue_reset (false);
}
/* provide a .1% deadzone to lock the speed */
- if (fabs(speed - 1.0) <= 0.001)
+ if (fabs (speed - 1.0) <= 0.001)
speed = 1.0;
DEBUG_TRACE (DEBUG::MTC, string_compose ("MTCsync spd: %1 pos: %2 | last-pos: %3 elapsed: %4 delta: %5\n",
diff --git a/libs/ardour/panner_manager.cc b/libs/ardour/panner_manager.cc
index f757279d29..8611c7bc86 100644
--- a/libs/ardour/panner_manager.cc
+++ b/libs/ardour/panner_manager.cc
@@ -84,6 +84,7 @@ PannerManager::discover_panners ()
panner_discover (*i);
}
}
+
int
PannerManager::panner_discover (string path)
{
diff --git a/libs/ardour/port.cc b/libs/ardour/port.cc
index 0f28b31b57..d8a32e3467 100644
--- a/libs/ardour/port.cc
+++ b/libs/ardour/port.cc
@@ -32,6 +32,7 @@
#include "ardour/audioengine.h"
#include "ardour/debug.h"
#include "ardour/port.h"
+#include "ardour/port_engine.h"
#include "i18n.h"
@@ -42,13 +43,19 @@ using namespace PBD;
PBD::Signal2<void,boost::shared_ptr<Port>, boost::shared_ptr<Port> > Port::PostDisconnect;
PBD::Signal0<void> Port::PortDrop;
-AudioEngine* Port::_engine = 0;
bool Port::_connecting_blocked = false;
pframes_t Port::_global_port_buffer_offset = 0;
pframes_t Port::_cycle_nframes = 0;
+std::string Port::state_node_name = X_("Port");
+
+/* a handy define to shorten what would otherwise be a needlessly verbose
+ * repeated phrase
+ */
+#define port_engine AudioEngine::instance()->port_engine()
+#define port_manager AudioEngine::instance()
/** @param n Port short name */
-Port::Port (std::string const & n, DataType t, Flags f)
+Port::Port (std::string const & n, DataType t, PortFlags f)
: _port_buffer_offset (0)
, _name (n)
, _flags (f)
@@ -60,21 +67,17 @@ Port::Port (std::string const & n, DataType t, Flags f)
_private_capture_latency.max = 0;
/* Unfortunately we have to pass the DataType into this constructor so that
- we can create the right kind of JACK port; aside from this we'll use the
+ we can create the right kind of port; aside from this we'll use the
virtual function type () to establish type.
*/
assert (_name.find_first_of (':') == std::string::npos);
- if (!_engine->connected()) {
+ if ((_port_handle = port_engine.register_port (_name, t, _flags)) == 0) {
+ cerr << "Failed to register port \"" << _name << "\", reason is unknown from here\n";
throw failed_constructor ();
}
-
- if ((_jack_port = jack_port_register (_engine->jack (), _name.c_str (), t.to_jack_type (), _flags, 0)) == 0) {
- cerr << "Failed to register JACK port \"" << _name << "\", reason is unknown from here\n";
- throw failed_constructor ();
- }
-
+
PortDrop.connect_same_thread (drop_connection, boost::bind (&Port::drop, this));
}
@@ -87,11 +90,10 @@ Port::~Port ()
void
Port::drop ()
{
- if (_jack_port) {
- if (_engine->jack ()) {
- jack_port_unregister (_engine->jack (), _jack_port);
- }
- _jack_port = 0;
+ if (_port_handle) {
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
+ port_engine.unregister_port (_port_handle);
+ _port_handle = 0;
}
}
@@ -99,18 +101,18 @@ Port::drop ()
bool
Port::connected () const
{
- return (jack_port_connected (_jack_port) != 0);
+ return (port_engine.connected (_port_handle) != 0);
}
int
Port::disconnect_all ()
{
- jack_port_disconnect (_engine->jack(), _jack_port);
+ port_engine.disconnect_all (_port_handle);
_connections.clear ();
/* a cheaper, less hacky way to do boost::shared_from_this() ...
*/
- boost::shared_ptr<Port> pself = _engine->get_port_by_name (name());
+ boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
PostDisconnect (pself, boost::shared_ptr<Port>()); // emit signal
return 0;
@@ -122,48 +124,29 @@ Port::disconnect_all ()
bool
Port::connected_to (std::string const & o) const
{
- if (!_engine->connected()) {
- /* in some senses, this answer isn't the right one all the time,
- because we know about our connections and will re-establish
- them when we reconnect to JACK.
- */
+ if (!port_engine.connected()) {
return false;
}
- return jack_port_connected_to (_jack_port,
- _engine->make_port_name_non_relative(o).c_str ());
+ return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
}
-/** @param o Filled in with port full names of ports that we are connected to */
int
Port::get_connections (std::vector<std::string> & c) const
{
- int n = 0;
-
- if (_engine->connected()) {
- const char** jc = jack_port_get_connections (_jack_port);
- if (jc) {
- for (int i = 0; jc[i]; ++i) {
- c.push_back (jc[i]);
- ++n;
- }
-
- if (jack_free) {
- jack_free (jc);
- } else {
- free (jc);
- }
- }
+ if (!port_engine.connected()) {
+ c.insert (c.end(), _connections.begin(), _connections.end());
+ return c.size();
}
- return n;
+ return port_engine.get_connections (_port_handle, c);
}
int
Port::connect (std::string const & other)
{
- std::string const other_shrt = _engine->make_port_name_non_relative (other);
- std::string const this_shrt = _engine->make_port_name_non_relative (_name);
+ std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
+ std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
int r = 0;
@@ -172,9 +155,11 @@ Port::connect (std::string const & other)
}
if (sends_output ()) {
- r = jack_connect (_engine->jack (), this_shrt.c_str (), other_shrt.c_str ());
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
+ r = port_engine.connect (our_name, other_name);
} else {
- r = jack_connect (_engine->jack (), other_shrt.c_str (), this_shrt.c_str());
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
+ r = port_engine.connect (other_name, our_name);
}
if (r == 0) {
@@ -187,15 +172,15 @@ Port::connect (std::string const & other)
int
Port::disconnect (std::string const & other)
{
- std::string const other_fullname = _engine->make_port_name_non_relative (other);
- std::string const this_fullname = _engine->make_port_name_non_relative (_name);
+ std::string const other_fullname = port_manager->make_port_name_non_relative (other);
+ std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
int r = 0;
if (sends_output ()) {
- r = jack_disconnect (_engine->jack (), this_fullname.c_str (), other_fullname.c_str ());
+ r = port_engine.disconnect (this_fullname, other_fullname);
} else {
- r = jack_disconnect (_engine->jack (), other_fullname.c_str (), this_fullname.c_str ());
+ r = port_engine.disconnect (other_fullname, this_fullname);
}
if (r == 0) {
@@ -204,8 +189,8 @@ Port::disconnect (std::string const & other)
/* a cheaper, less hacky way to do boost::shared_from_this() ...
*/
- boost::shared_ptr<Port> pself = _engine->get_port_by_name (name());
- boost::shared_ptr<Port> pother = _engine->get_port_by_name (other);
+ boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
+ boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
if (pself && pother) {
/* Disconnecting from another Ardour port: need to allow
@@ -238,21 +223,22 @@ Port::disconnect (Port* o)
}
void
-Port::set_engine (AudioEngine* e)
+Port::request_input_monitoring (bool yn)
{
- _engine = e;
+ port_engine.request_input_monitoring (_port_handle, yn);
}
void
-Port::ensure_jack_monitors_input (bool yn)
+Port::ensure_input_monitoring (bool yn)
{
- jack_port_ensure_monitor (_jack_port, yn);
+ port_engine.ensure_input_monitoring (_port_handle, yn);
}
bool
-Port::jack_monitoring_input () const
+Port::monitoring_input () const
{
- return jack_port_monitoring_input (_jack_port);
+
+ return port_engine.monitoring_input (_port_handle);
}
void
@@ -274,28 +260,23 @@ Port::increment_port_buffer_offset (pframes_t nframes)
}
void
-Port::set_public_latency_range (jack_latency_range_t& range, bool playback) const
+Port::set_public_latency_range (LatencyRange& range, bool playback) const
{
- /* this sets the visible latency that the rest of JACK sees. because we do latency
- compensation, all (most) of our visible port latency values are identical.
+ /* this sets the visible latency that the rest of the port system
+ sees. because we do latency compensation, all (most) of our visible
+ port latency values are identical.
*/
- if (!jack_port_set_latency_range) {
- return;
- }
-
DEBUG_TRACE (DEBUG::Latency,
string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
name(), range.min, range.max,
(playback ? "PLAYBACK" : "CAPTURE")));;
- jack_port_set_latency_range (_jack_port,
- (playback ? JackPlaybackLatency : JackCaptureLatency),
- &range);
+ port_engine.set_latency_range (_port_handle, playback, range);
}
void
-Port::set_private_latency_range (jack_latency_range_t& range, bool playback)
+Port::set_private_latency_range (LatencyRange& range, bool playback)
{
if (playback) {
_private_playback_latency = range;
@@ -313,12 +294,12 @@ Port::set_private_latency_range (jack_latency_range_t& range, bool playback)
_private_capture_latency.max));
}
- /* push to public (JACK) location so that everyone else can see it */
+ /* push to public (port system) location so that everyone else can see it */
set_public_latency_range (range, playback);
}
-const jack_latency_range_t&
+const LatencyRange&
Port::private_latency_range (bool playback) const
{
if (playback) {
@@ -338,14 +319,13 @@ Port::private_latency_range (bool playback) const
}
}
-jack_latency_range_t
+LatencyRange
Port::public_latency_range (bool /*playback*/) const
{
- jack_latency_range_t r;
+ LatencyRange r;
+
+ r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
- jack_port_get_latency_range (_jack_port,
- sends_output() ? JackPlaybackLatency : JackCaptureLatency,
- &r);
DEBUG_TRACE (DEBUG::Latency, string_compose (
"GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
name(), r.min, r.max,
@@ -354,27 +334,15 @@ Port::public_latency_range (bool /*playback*/) const
}
void
-Port::get_connected_latency_range (jack_latency_range_t& range, bool playback) const
+Port::get_connected_latency_range (LatencyRange& range, bool playback) const
{
- if (!jack_port_get_latency_range) {
- return;
- }
-
vector<string> connections;
- jack_client_t* jack = _engine->jack();
-
- if (!jack) {
- range.min = 0;
- range.max = 0;
- PBD::warning << _("get_connected_latency_range() called while disconnected from JACK") << endmsg;
- return;
- }
get_connections (connections);
if (!connections.empty()) {
- range.min = ~((jack_nframes_t) 0);
+ range.min = ~((pframes_t) 0);
range.max = 0;
DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
@@ -382,21 +350,18 @@ Port::get_connected_latency_range (jack_latency_range_t& range, bool playback) c
for (vector<string>::const_iterator c = connections.begin();
c != connections.end(); ++c) {
- jack_latency_range_t lr;
+ LatencyRange lr;
if (!AudioEngine::instance()->port_is_mine (*c)) {
- /* port belongs to some other JACK client, use
- * JACK to lookup its latency information.
+ /* port belongs to some other port-system client, use
+ * the port engine to lookup its latency information.
*/
- jack_port_t* remote_port = jack_port_by_name (_engine->jack(), (*c).c_str());
+ PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
if (remote_port) {
- jack_port_get_latency_range (
- remote_port,
- (playback ? JackPlaybackLatency : JackCaptureLatency),
- &lr);
+ lr = port_engine.get_latency_range (remote_port, playback);
DEBUG_TRACE (DEBUG::Latency, string_compose (
"\t%1 <-> %2 : latter has latency range %3 .. %4\n",
@@ -440,15 +405,10 @@ Port::get_connected_latency_range (jack_latency_range_t& range, bool playback) c
int
Port::reestablish ()
{
- jack_client_t* jack = _engine->jack();
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
+ _port_handle = port_engine.register_port (_name, type(), _flags);
- if (!jack) {
- return -1;
- }
-
- _jack_port = jack_port_register (jack, _name.c_str(), type().to_jack_type(), _flags, 0);
-
- if (_jack_port == 0) {
+ if (_port_handle == 0) {
PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
return -1;
}
@@ -464,6 +424,8 @@ Port::reconnect ()
{
/* caller must hold process lock; intended to be used only after reestablish() */
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
+
for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
if (connect (*i)) {
return -1;
@@ -473,7 +435,7 @@ Port::reconnect ()
return 0;
}
-/** @param n Short port name (no JACK client name) */
+/** @param n Short port name (no port-system client name) */
int
Port::set_name (std::string const & n)
{
@@ -481,10 +443,10 @@ Port::set_name (std::string const & n)
return 0;
}
- int const r = jack_port_set_name (_jack_port, n.c_str());
+ int const r = port_engine.set_port_name (_port_handle, n);
if (r == 0) {
- _engine->port_renamed (_name, n);
+ AudioEngine::instance()->port_renamed (_name, n);
_name = n;
}
@@ -492,38 +454,67 @@ Port::set_name (std::string const & n)
return r;
}
-void
-Port::request_jack_monitors_input (bool yn)
+bool
+Port::physically_connected () const
{
- jack_port_request_monitor (_jack_port, yn);
+ return port_engine.physically_connected (_port_handle);
}
-bool
-Port::physically_connected () const
+XMLNode&
+Port::get_state () const
{
- const char** jc = jack_port_get_connections (_jack_port);
+ XMLNode* root = new XMLNode (state_node_name);
- if (jc) {
- for (int i = 0; jc[i]; ++i) {
+ root->add_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
- jack_port_t* port = jack_port_by_name (_engine->jack(), jc[i]);
+ if (receives_input()) {
+ root->add_property (X_("direction"), X_("input"));
+ } else {
+ root->add_property (X_("direction"), X_("output"));
+ }
- if (port && (jack_port_flags (port) & JackPortIsPhysical)) {
- if (jack_free) {
- jack_free (jc);
- } else {
- free (jc);
- }
- return true;
- }
- }
- if (jack_free) {
- jack_free (jc);
- } else {
- free (jc);
- }
+ vector<string> c;
+
+ get_connections (c);
+
+ for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
+ XMLNode* child = new XMLNode (X_("Connection"));
+ child->add_property (X_("other"), *i);
+ root->add_child_nocopy (*child);
}
- return false;
+ return *root;
}
+int
+Port::set_state (const XMLNode& node, int)
+{
+ const XMLProperty* prop;
+
+ if (node.name() != state_node_name) {
+ return -1;
+ }
+
+ if ((prop = node.property (X_("name"))) != 0) {
+ set_name (prop->value());
+ }
+
+ const XMLNodeList& children (node.children());
+
+ _connections.clear ();
+
+ for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
+
+ if ((*c)->name() != X_("Connection")) {
+ continue;
+ }
+
+ if ((prop = (*c)->property (X_("other"))) == 0) {
+ continue;
+ }
+
+ _connections.insert (prop->value());
+ }
+
+ return 0;
+}
diff --git a/libs/ardour/port_insert.cc b/libs/ardour/port_insert.cc
index b2758b2d57..2519451b89 100644
--- a/libs/ardour/port_insert.cc
+++ b/libs/ardour/port_insert.cc
@@ -49,7 +49,7 @@ PortInsert::PortInsert (Session& s, boost::shared_ptr<Pannable> pannable, boost:
{
_mtdm = 0;
_latency_detect = false;
- _latency_flush_frames = false;
+ _latency_flush_frames = 0;
_measured_latency = 0;
}
@@ -64,7 +64,7 @@ PortInsert::start_latency_detection ()
{
delete _mtdm;
_mtdm = new MTDM (_session.frame_rate());
- _latency_flush_frames = false;
+ _latency_flush_frames = 0;
_latency_detect = true;
_measured_latency = 0;
}
@@ -72,7 +72,7 @@ PortInsert::start_latency_detection ()
void
PortInsert::stop_latency_detection ()
{
- _latency_flush_frames = signal_latency() + _session.engine().frames_per_cycle();
+ _latency_flush_frames = signal_latency() + _session.engine().samples_per_cycle();
_latency_detect = false;
}
@@ -93,7 +93,7 @@ PortInsert::latency() const
*/
if (_measured_latency == 0) {
- return _session.engine().frames_per_cycle() + _input->latency();
+ return _session.engine().samples_per_cycle() + _input->latency();
} else {
return _measured_latency;
}
@@ -240,7 +240,7 @@ PortInsert::signal_latency() const
*/
if (_measured_latency == 0) {
- return _session.engine().frames_per_cycle() + _input->signal_latency();
+ return _session.engine().samples_per_cycle() + _input->signal_latency();
} else {
return _measured_latency;
}
diff --git a/libs/ardour/port_manager.cc b/libs/ardour/port_manager.cc
new file mode 100644
index 0000000000..1758ea4a3f
--- /dev/null
+++ b/libs/ardour/port_manager.cc
@@ -0,0 +1,659 @@
+/*
+ Copyright (C) 2013 Paul Davis
+
+ 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 "pbd/error.h"
+
+#include "ardour/async_midi_port.h"
+#include "ardour/debug.h"
+#include "ardour/port_manager.h"
+#include "ardour/audio_port.h"
+#include "ardour/midi_port.h"
+#include "ardour/midiport_manager.h"
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+using std::string;
+using std::vector;
+
+PortManager::PortManager ()
+ : ports (new Ports)
+ , _port_remove_in_progress (false)
+{
+}
+
+void
+PortManager::remove_all_ports ()
+{
+ /* make sure that JACK callbacks that will be invoked as we cleanup
+ * ports know that they have nothing to do.
+ */
+
+ _port_remove_in_progress = true;
+
+ /* process lock MUST be held by caller
+ */
+
+ {
+ RCUWriter<Ports> writer (ports);
+ boost::shared_ptr<Ports> ps = writer.get_copy ();
+ ps->clear ();
+ }
+
+ /* clear dead wood list in RCU */
+
+ ports.flush ();
+
+ _port_remove_in_progress = false;
+}
+
+
+string
+PortManager::make_port_name_relative (const string& portname) const
+{
+ if (!_impl) {
+ return portname;
+ }
+
+ string::size_type len;
+ string::size_type n;
+ string self = _impl->my_name();
+
+ len = portname.length();
+
+ for (n = 0; n < len; ++n) {
+ if (portname[n] == ':') {
+ break;
+ }
+ }
+
+ if ((n != len) && (portname.substr (0, n) == self)) {
+ return portname.substr (n+1);
+ }
+
+ return portname;
+}
+
+string
+PortManager::make_port_name_non_relative (const string& portname) const
+{
+ string str;
+
+ if (portname.find_first_of (':') != string::npos) {
+ return portname;
+ }
+
+ str = _impl->my_name();
+ str += ':';
+ str += portname;
+
+ return str;
+}
+
+bool
+PortManager::port_is_mine (const string& portname) const
+{
+ if (!_impl) {
+ return true;
+ }
+
+ string self = _impl->my_name();
+
+ if (portname.find_first_of (':') != string::npos) {
+ if (portname.substr (0, self.length ()) != self) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+PortManager::port_is_physical (const std::string& portname) const
+{
+ if (!_impl) {
+ return false;
+ }
+
+ PortEngine::PortHandle ph = _impl->get_port_by_name (portname);
+ if (!ph) {
+ return false;
+ }
+
+ return _impl->port_is_physical (ph);
+}
+
+void
+PortManager::get_physical_outputs (DataType type, std::vector<std::string>& s)
+{
+ if (!_impl) {
+ return;
+ }
+ _impl->get_physical_outputs (type, s);
+}
+
+void
+PortManager::get_physical_inputs (DataType type, std::vector<std::string>& s)
+{
+ if (!_impl) {
+ return;
+ }
+
+ _impl->get_physical_inputs (type, s);
+}
+
+ChanCount
+PortManager::n_physical_outputs () const
+{
+ if (!_impl) {
+ return ChanCount::ZERO;
+ }
+
+ return _impl->n_physical_outputs ();
+}
+
+ChanCount
+PortManager::n_physical_inputs () const
+{
+ if (!_impl) {
+ return ChanCount::ZERO;
+ }
+ return _impl->n_physical_inputs ();
+}
+
+/** @param name Full or short name of port
+ * @return Corresponding Port or 0.
+ */
+
+boost::shared_ptr<Port>
+PortManager::get_port_by_name (const string& portname)
+{
+ if (!_impl) {
+ return boost::shared_ptr<Port>();
+ }
+
+ if (!port_is_mine (portname)) {
+ /* not an ardour port */
+ return boost::shared_ptr<Port> ();
+ }
+
+ boost::shared_ptr<Ports> pr = ports.reader();
+ std::string rel = make_port_name_relative (portname);
+ Ports::iterator x = pr->find (rel);
+
+ if (x != pr->end()) {
+ /* its possible that the port was renamed by some 3rd party and
+ we don't know about it. check for this (the check is quick
+ and cheap), and if so, rename the port (which will alter
+ the port map as a side effect).
+ */
+ const std::string check = make_port_name_relative (_impl->get_port_name (x->second->port_handle()));
+ if (check != rel) {
+ x->second->set_name (check);
+ }
+ return x->second;
+ }
+
+ return boost::shared_ptr<Port> ();
+}
+
+void
+PortManager::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name)
+{
+ RCUWriter<Ports> writer (ports);
+ boost::shared_ptr<Ports> p = writer.get_copy();
+ Ports::iterator x = p->find (old_relative_name);
+
+ if (x != p->end()) {
+ boost::shared_ptr<Port> port = x->second;
+ p->erase (x);
+ p->insert (make_pair (new_relative_name, port));
+ }
+}
+
+int
+PortManager::get_ports (DataType type, PortList& pl)
+{
+ boost::shared_ptr<Ports> plist = ports.reader();
+ for (Ports::iterator p = plist->begin(); p != plist->end(); ++p) {
+ if (p->second->type() == type) {
+ pl.push_back (p->second);
+ }
+ }
+ return pl.size();
+}
+
+int
+PortManager::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector<string>& s)
+{
+ if (!_impl) {
+ return 0;
+ }
+
+ return _impl->get_ports (port_name_pattern, type, flags, s);
+}
+
+void
+PortManager::port_registration_failure (const std::string& portname)
+{
+ if (!_impl) {
+ return;
+ }
+
+ string full_portname = _impl->my_name();
+ full_portname += ':';
+ full_portname += portname;
+
+
+ PortEngine::PortHandle p = _impl->get_port_by_name (full_portname);
+ string reason;
+
+ if (p) {
+ reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname);
+ } else {
+ reason = string_compose (_("No more ports are available. You will need to stop %1 and restart with more ports if you need this many tracks."), PROGRAM_NAME);
+ }
+
+ throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
+}
+
+boost::shared_ptr<Port>
+PortManager::register_port (DataType dtype, const string& portname, bool input, bool async)
+{
+ boost::shared_ptr<Port> newport;
+
+ try {
+ if (dtype == DataType::AUDIO) {
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("registering AUDIO port %1, input %2\n",
+ portname, input));
+ newport.reset (new AudioPort (portname, (input ? IsInput : IsOutput)));
+ } else if (dtype == DataType::MIDI) {
+ if (async) {
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("registering ASYNC MIDI port %1, input %2\n",
+ portname, input));
+ newport.reset (new AsyncMIDIPort (portname, (input ? IsInput : IsOutput)));
+ } else {
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("registering MIDI port %1, input %2\n",
+ portname, input));
+ newport.reset (new MidiPort (portname, (input ? IsInput : IsOutput)));
+ }
+ } else {
+ throw PortRegistrationFailure("unable to create port (unknown type)");
+ }
+
+ RCUWriter<Ports> writer (ports);
+ boost::shared_ptr<Ports> ps = writer.get_copy ();
+ ps->insert (make_pair (make_port_name_relative (portname), newport));
+
+ /* writer goes out of scope, forces update */
+
+ }
+
+ catch (PortRegistrationFailure& err) {
+ throw err;
+ } catch (std::exception& e) {
+ throw PortRegistrationFailure(string_compose(
+ _("unable to create port: %1"), e.what()).c_str());
+ } catch (...) {
+ throw PortRegistrationFailure("unable to create port (unknown error)");
+ }
+
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("\t%2 port registration success, ports now = %1\n", ports.reader()->size(), this));
+ return newport;
+}
+
+boost::shared_ptr<Port>
+PortManager::register_input_port (DataType type, const string& portname, bool async)
+{
+ return register_port (type, portname, true, async);
+}
+
+boost::shared_ptr<Port>
+PortManager::register_output_port (DataType type, const string& portname, bool async)
+{
+ return register_port (type, portname, false, async);
+}
+
+int
+PortManager::unregister_port (boost::shared_ptr<Port> port)
+{
+ /* caller must hold process lock */
+
+ {
+ RCUWriter<Ports> writer (ports);
+ boost::shared_ptr<Ports> ps = writer.get_copy ();
+ Ports::iterator x = ps->find (make_port_name_relative (port->name()));
+
+ if (x != ps->end()) {
+ ps->erase (x);
+ }
+
+ /* writer goes out of scope, forces update */
+ }
+
+ ports.flush ();
+
+ return 0;
+}
+
+bool
+PortManager::connected (const string& port_name)
+{
+ if (!_impl) {
+ return false;
+ }
+
+ PortEngine::PortHandle handle = _impl->get_port_by_name (port_name);
+
+ if (!handle) {
+ return false;
+ }
+
+ return _impl->connected (handle);
+}
+
+int
+PortManager::connect (const string& source, const string& destination)
+{
+ int ret;
+
+ string s = make_port_name_non_relative (source);
+ string d = make_port_name_non_relative (destination);
+
+ boost::shared_ptr<Port> src = get_port_by_name (s);
+ boost::shared_ptr<Port> dst = get_port_by_name (d);
+
+ if (src) {
+ ret = src->connect (d);
+ } else if (dst) {
+ ret = dst->connect (s);
+ } else {
+ /* neither port is known to us ...hand-off to the PortEngine
+ */
+ if (_impl) {
+ ret = _impl->connect (s, d);
+ } else {
+ ret = -1;
+ }
+ }
+
+ if (ret > 0) {
+ /* already exists - no error, no warning */
+ } else if (ret < 0) {
+ error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"),
+ source, s, destination, d)
+ << endmsg;
+ }
+
+ return ret;
+}
+
+int
+PortManager::disconnect (const string& source, const string& destination)
+{
+ int ret;
+
+ string s = make_port_name_non_relative (source);
+ string d = make_port_name_non_relative (destination);
+
+ boost::shared_ptr<Port> src = get_port_by_name (s);
+ boost::shared_ptr<Port> dst = get_port_by_name (d);
+
+ if (src) {
+ ret = src->disconnect (d);
+ } else if (dst) {
+ ret = dst->disconnect (s);
+ } else {
+ /* neither port is known to us ...hand-off to the PortEngine
+ */
+ if (_impl) {
+ ret = _impl->disconnect (s, d);
+ } else {
+ ret = -1;
+ }
+ }
+ return ret;
+}
+
+int
+PortManager::disconnect (boost::shared_ptr<Port> port)
+{
+ return port->disconnect_all ();
+}
+
+int
+PortManager::reestablish_ports ()
+{
+ Ports::iterator i;
+
+ boost::shared_ptr<Ports> p = ports.reader ();
+
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("reestablish %1 ports\n", p->size()));
+
+ for (i = p->begin(); i != p->end(); ++i) {
+ if (i->second->reestablish ()) {
+ error << string_compose (_("Re-establising port %1 failed"), i->second->name()) << endmsg;
+ cerr << string_compose (_("Re-establising port %1 failed"), i->second->name()) << endl;
+ break;
+ }
+ }
+
+ if (i != p->end()) {
+ /* failed */
+ remove_all_ports ();
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+PortManager::reconnect_ports ()
+{
+ boost::shared_ptr<Ports> p = ports.reader ();
+
+ /* re-establish connections */
+
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("reconnect %1 ports\n", p->size()));
+
+ for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
+ i->second->reconnect ();
+ }
+
+ return 0;
+}
+
+void
+PortManager::connect_callback (const string& a, const string& b, bool conn)
+{
+ boost::shared_ptr<Port> port_a;
+ boost::shared_ptr<Port> port_b;
+ Ports::iterator x;
+ boost::shared_ptr<Ports> pr = ports.reader ();
+
+ x = pr->find (make_port_name_relative (a));
+ if (x != pr->end()) {
+ port_a = x->second;
+ }
+
+ x = pr->find (make_port_name_relative (b));
+ if (x != pr->end()) {
+ port_b = x->second;
+ }
+
+ PortConnectedOrDisconnected (
+ port_a, a,
+ port_b, b,
+ conn
+ ); /* EMIT SIGNAL */
+}
+
+void
+PortManager::registration_callback ()
+{
+ if (!_port_remove_in_progress) {
+ PortRegisteredOrUnregistered (); /* EMIT SIGNAL */
+ }
+}
+
+bool
+PortManager::can_request_input_monitoring () const
+{
+ if (!_impl) {
+ return false;
+ }
+
+ return _impl->can_monitor_input ();
+}
+
+void
+PortManager::request_input_monitoring (const string& name, bool yn) const
+{
+ if (!_impl) {
+ return;
+ }
+
+ PortEngine::PortHandle ph = _impl->get_port_by_name (name);
+
+ if (ph) {
+ _impl->request_input_monitoring (ph, yn);
+ }
+}
+
+void
+PortManager::ensure_input_monitoring (const string& name, bool yn) const
+{
+ if (!_impl) {
+ return;
+ }
+
+ PortEngine::PortHandle ph = _impl->get_port_by_name (name);
+
+ if (ph) {
+ _impl->ensure_input_monitoring (ph, yn);
+ }
+}
+
+uint32_t
+PortManager::port_name_size() const
+{
+ if (!_impl) {
+ return 0;
+ }
+
+ return _impl->port_name_size ();
+}
+
+string
+PortManager::my_name() const
+{
+ if (!_impl) {
+ return string();
+ }
+
+ return _impl->my_name();
+}
+
+int
+PortManager::graph_order_callback ()
+{
+ if (!_port_remove_in_progress) {
+ GraphReordered(); /* EMIT SIGNAL */
+ }
+
+ return 0;
+}
+
+void
+PortManager::cycle_start (pframes_t nframes)
+{
+ Port::set_global_port_buffer_offset (0);
+ Port::set_cycle_framecnt (nframes);
+
+ _cycle_ports = ports.reader ();
+
+ for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
+ p->second->cycle_start (nframes);
+ }
+}
+
+void
+PortManager::cycle_end (pframes_t nframes)
+{
+ for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
+ p->second->cycle_end (nframes);
+ }
+
+ for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
+ p->second->flush_buffers (nframes);
+ }
+
+ _cycle_ports.reset ();
+
+ /* we are done */
+}
+
+void
+PortManager::silence (pframes_t nframes)
+{
+ for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
+ if (i->second->sends_output()) {
+ i->second->get_buffer(nframes).silence(nframes);
+ }
+ }
+}
+
+void
+PortManager::check_monitoring ()
+{
+ for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
+
+ bool x;
+
+ if (i->second->last_monitor() != (x = i->second->monitoring_input ())) {
+ i->second->set_last_monitor (x);
+ /* XXX I think this is dangerous, due to
+ a likely mutex in the signal handlers ...
+ */
+ i->second->MonitorInputChanged (x); /* EMIT SIGNAL */
+ }
+ }
+}
+
+void
+PortManager::fade_out (gain_t base_gain, gain_t gain_step, pframes_t nframes)
+{
+ for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
+
+ if (i->second->sends_output()) {
+
+ boost::shared_ptr<AudioPort> ap = boost::dynamic_pointer_cast<AudioPort> (i->second);
+ if (ap) {
+ Sample* s = ap->engine_get_whole_audio_buffer ();
+ gain_t g = base_gain;
+
+ for (pframes_t n = 0; n < nframes; ++n) {
+ *s++ *= g;
+ g -= gain_step;
+ }
+ }
+ }
+ }
+}
diff --git a/libs/ardour/rc_configuration.cc b/libs/ardour/rc_configuration.cc
index 4733587654..7fe030fb7e 100644
--- a/libs/ardour/rc_configuration.cc
+++ b/libs/ardour/rc_configuration.cc
@@ -27,11 +27,11 @@
#include "pbd/xml++.h"
#include "pbd/file_utils.h"
-#include "midi++/manager.h"
-
+#include "ardour/audioengine.h"
#include "ardour/control_protocol_manager.h"
#include "ardour/diskstream.h"
#include "ardour/filesystem_paths.h"
+#include "ardour/port.h"
#include "ardour/rc_configuration.h"
#include "ardour/session_metadata.h"
@@ -63,13 +63,8 @@ RCConfiguration::RCConfiguration ()
{
}
-
RCConfiguration::~RCConfiguration ()
{
- for (list<XMLNode*>::iterator i = _midi_port_states.begin(); i != _midi_port_states.end(); ++i) {
- delete *i;
- }
-
delete _control_protocol_state;
}
@@ -177,16 +172,6 @@ RCConfiguration::get_state ()
root = new XMLNode("Ardour");
- MIDI::Manager* mm = MIDI::Manager::instance();
-
- if (mm) {
- boost::shared_ptr<const MIDI::Manager::PortList> ports = mm->get_midi_ports();
-
- for (MIDI::Manager::PortList::const_iterator i = ports->begin(); i != ports->end(); ++i) {
- root->add_child_nocopy((*i)->get_state());
- }
- }
-
root->add_child_nocopy (get_variables ());
root->add_child_nocopy (SessionMetadata::Metadata()->get_user_state());
@@ -232,12 +217,6 @@ RCConfiguration::set_state (const XMLNode& root, int version)
XMLNodeConstIterator niter;
XMLNode *node;
- for (list<XMLNode*>::iterator i = _midi_port_states.begin(); i != _midi_port_states.end(); ++i) {
- delete *i;
- }
-
- _midi_port_states.clear ();
-
Stateful::save_extra_xml (root);
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
@@ -250,8 +229,6 @@ RCConfiguration::set_state (const XMLNode& root, int version)
SessionMetadata::Metadata()->set_state (*node, version);
} else if (node->name() == ControlProtocolManager::state_node_name) {
_control_protocol_state = new XMLNode (*node);
- } else if (node->name() == MIDI::Port::state_node_name) {
- _midi_port_states.push_back (new XMLNode (*node));
}
}
diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc
index 232f295df6..026ee4ec5c 100644
--- a/libs/ardour/route.cc
+++ b/libs/ardour/route.cc
@@ -971,7 +971,7 @@ Route::add_processor (boost::shared_ptr<Processor> processor, boost::shared_ptr<
DEBUG_TRACE (DEBUG::Processors, string_compose (
"%1 adding processor %2\n", name(), processor->name()));
- if (!_session.engine().connected() || !processor) {
+ if (!AudioEngine::instance()->connected() || !processor) {
return 1;
}
@@ -3818,13 +3818,13 @@ Route::update_port_latencies (PortSet& from, PortSet& to, bool playback, framecn
universally true, but the alternative is way too corner-case to worry about.
*/
- jack_latency_range_t all_connections;
+ LatencyRange all_connections;
if (from.empty()) {
all_connections.min = 0;
all_connections.max = 0;
} else {
- all_connections.min = ~((jack_nframes_t) 0);
+ all_connections.min = ~((pframes_t) 0);
all_connections.max = 0;
/* iterate over all "from" ports and determine the latency range for all of their
@@ -3833,7 +3833,7 @@ Route::update_port_latencies (PortSet& from, PortSet& to, bool playback, framecn
for (PortSet::iterator p = from.begin(); p != from.end(); ++p) {
- jack_latency_range_t range;
+ LatencyRange range;
p->get_connected_latency_range (range, playback);
@@ -3898,7 +3898,7 @@ Route::set_public_port_latencies (framecnt_t value, bool playback) const
latency compensation into account.
*/
- jack_latency_range_t range;
+ LatencyRange range;
range.min = value;
range.max = value;
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index 428a5e5274..135df7c2cd 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -49,6 +49,7 @@
#include "ardour/amp.h"
#include "ardour/analyser.h"
+#include "ardour/async_midi_port.h"
#include "ardour/audio_buffer.h"
#include "ardour/audio_diskstream.h"
#include "ardour/audio_port.h"
@@ -66,6 +67,7 @@
#include "ardour/debug.h"
#include "ardour/filename_extensions.h"
#include "ardour/graph.h"
+#include "ardour/midiport_manager.h"
#include "ardour/midi_track.h"
#include "ardour/midi_ui.h"
#include "ardour/operations.h"
@@ -85,12 +87,11 @@
#include "ardour/session_playlists.h"
#include "ardour/smf_source.h"
#include "ardour/source_factory.h"
+#include "ardour/speakers.h"
#include "ardour/utils.h"
#include "midi++/port.h"
-#include "midi++/jack_midi_port.h"
#include "midi++/mmc.h"
-#include "midi++/manager.h"
#include "i18n.h"
@@ -106,6 +107,7 @@ using namespace PBD;
bool Session::_disable_all_loaded_plugins = false;
+PBD::Signal1<int,uint32_t> Session::AudioEngineSetupRequired;
PBD::Signal1<void,std::string> Session::Dialog;
PBD::Signal0<int> Session::AskAboutPendingState;
PBD::Signal2<int, framecnt_t, framecnt_t> Session::AskAboutSampleRateMismatch;
@@ -130,70 +132,184 @@ Session::Session (AudioEngine &eng,
const string& snapshot_name,
BusProfile* bus_profile,
string mix_template)
- : _engine (eng)
+ : playlists (new SessionPlaylists)
+ , _engine (eng)
+ , process_function (&Session::process_with_events)
+ , waiting_for_sync_offset (false)
+ , _base_frame_rate (0)
+ , _current_frame_rate (0)
+ , _nominal_frame_rate (0)
+ , transport_sub_state (0)
+ , _record_status (Disabled)
+ , _transport_frame (0)
+ , _session_range_location (0)
+ , _slave (0)
+ , _silent (false)
+ , _transport_speed (0)
+ , _default_transport_speed (1.0)
+ , _last_transport_speed (0)
, _target_transport_speed (0.0)
+ , auto_play_legal (false)
+ , _last_slave_transport_frame (0)
+ , maximum_output_latency (0)
, _requested_return_frame (-1)
+ , current_block_size (0)
+ , _worst_output_latency (0)
+ , _worst_input_latency (0)
+ , _worst_track_latency (0)
+ , _have_captured (false)
+ , _meter_hold (0)
+ , _meter_falloff (0)
+ , _non_soloed_outs_muted (false)
+ , _listen_cnt (0)
+ , _solo_isolated_cnt (0)
+ , _writable (false)
+ , _was_seamless (Config->get_seamless_loop ())
, _under_nsm_control (false)
- , _session_dir (new SessionDirectory(fullpath))
+ , delta_accumulator_cnt (0)
+ , average_slave_delta (1800) // !!! why 1800 ???
+ , average_dir (0)
+ , have_first_delta_accumulator (false)
+ , _slave_state (Stopped)
+ , post_export_sync (false)
+ , post_export_position (0)
+ , _exporting (false)
+ , _export_started (false)
+ , _export_rolling (false)
+ , _pre_export_mmc_enabled (false)
+ , _name (snapshot_name)
+ , _is_new (true)
+ , _send_qf_mtc (false)
+ , _pframes_since_last_mtc (0)
+ , session_midi_feedback (0)
+ , play_loop (false)
+ , loop_changing (false)
+ , last_loopend (0)
+ , _session_dir (new SessionDirectory (fullpath))
+ , _current_snapshot_name (snapshot_name)
, state_tree (0)
- , _state_of_the_state (Clean)
+ , state_was_pending (false)
+ , _state_of_the_state (StateOfTheState(CannotSave|InitialConnecting|Loading))
+ , _last_roll_location (0)
+ , _last_roll_or_reversal_location (0)
+ , _last_record_location (0)
+ , pending_locate_roll (false)
+ , pending_locate_frame (0)
+ , pending_locate_flush (false)
+ , pending_abort (false)
+ , pending_auto_loop (false)
, _butler (new Butler (*this))
, _post_transport_work (0)
+ , cumulative_rf_motion (0)
+ , rf_scale (1.0)
+ , _locations (new Locations (*this))
+ , step_speed (0)
+ , outbound_mtc_timecode_frame (0)
+ , next_quarter_frame_to_send (-1)
+ , _frames_per_timecode_frame (0)
+ , _frames_per_hour (0)
+ , _timecode_frames_per_hour (0)
+ , last_timecode_valid (false)
+ , last_timecode_when (0)
, _send_timecode_update (false)
+ , ltc_encoder (0)
, ltc_enc_buf(0)
+ , ltc_buf_off (0)
+ , ltc_buf_len (0)
+ , ltc_speed (0)
+ , ltc_enc_byte (0)
+ , ltc_enc_pos (0)
+ , ltc_enc_cnt (0)
+ , ltc_enc_off (0)
+ , restarting (false)
+ , ltc_prev_cycle (0)
+ , ltc_timecode_offset (0)
+ , ltc_timecode_negative_offset (false)
+ , midi_control_ui (0)
+ , _tempo_map (0)
, _all_route_group (new RouteGroup (*this, "all"))
, routes (new RouteList)
+ , _adding_routes_in_progress (false)
+ , destructive_index (0)
+ , solo_update_disabled (false)
+ , default_fade_steepness (0)
+ , default_fade_msecs (0)
, _total_free_4k_blocks (0)
, _total_free_4k_blocks_uncertain (false)
+ , no_questions_about_missing_files (false)
+ , _playback_load (0)
+ , _capture_load (0)
, _bundles (new BundleList)
, _bundle_xml_node (0)
, _current_trans (0)
+ , _clicking (false)
, click_data (0)
, click_emphasis_data (0)
+ , click_length (0)
+ , click_emphasis_length (0)
+ , _clicks_cleared (0)
+ , _play_range (false)
, main_outs (0)
+ , first_file_data_format_reset (true)
+ , first_file_header_format_reset (true)
+ , have_looped (false)
, _have_rec_enabled_track (false)
+ , _step_editors (0)
, _suspend_timecode_transmission (0)
+ , _speakers (new Speakers)
+ , ignore_route_processor_changes (false)
{
- _locations = new Locations (*this);
- ltc_encoder = NULL;
-
- if (how_many_dsp_threads () > 1) {
- /* For now, only create the graph if we are using >1 DSP threads, as
- it is a bit slower than the old code with 1 thread.
- */
- _process_graph.reset (new Graph (*this));
- }
-
- playlists.reset (new SessionPlaylists);
-
- _all_route_group->set_active (true, this);
-
- interpolation.add_channel_to (0, 0);
-
- if (!eng.connected()) {
- throw failed_constructor();
- }
-
- n_physical_outputs = _engine.n_physical_outputs ();
- n_physical_inputs = _engine.n_physical_inputs ();
-
- first_stage_init (fullpath, snapshot_name);
-
- _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
+ uint32_t sr = 0;
+ pre_engine_init (fullpath);
+
if (_is_new) {
if (create (mix_template, bus_profile)) {
destroy ();
throw failed_constructor ();
}
+ } else {
+ if (load_state (_current_snapshot_name)) {
+ throw failed_constructor ();
+ }
+
+ /* try to get sample rate from XML state so that we
+ * can influence the SR if we set up the audio
+ * engine.
+ */
+
+ if (state_tree) {
+ const XMLProperty* prop;
+ if ((prop = state_tree->root()->property (X_("sample-rate"))) != 0) {
+ sr = atoi (prop->value());
+ }
+ }
+ }
+
+ if (_engine.current_backend() == 0 || _engine.setup_required()) {
+ boost::optional<int> r = AudioEngineSetupRequired (sr);
+ if (r.get_value_or (-1) != 0) {
+ destroy ();
+ throw failed_constructor();
+ }
+ }
+
+ /* at this point the engine should be connected (i.e. interacting
+ with a backend device (or psuedo-device) and available to us
+ for determinining sample rates and other settings.
+ */
+
+ if (!_engine.connected()) {
+ destroy ();
+ throw failed_constructor();
}
- if (second_stage_init ()) {
+ if (post_engine_init ()) {
destroy ();
throw failed_constructor ();
}
- store_recent_sessions(_name, _path);
+ store_recent_sessions (_name, _path);
bool was_dirty = dirty();
@@ -210,6 +326,16 @@ Session::Session (AudioEngine &eng,
EndTimeChanged.connect_same_thread (*this, boost::bind (&Session::end_time_changed, this, _1));
_is_new = false;
+
+ /* hook us up to the engine since we are now completely constructed */
+
+ BootMessage (_("Connect to engine"));
+
+ _engine.set_session (this);
+ _engine.reset_timebase ();
+
+ BootMessage (_("Session loading complete"));
+
}
Session::~Session ()
@@ -330,6 +456,8 @@ Session::destroy ()
/* not strictly necessary, but doing it here allows the shared_ptr debugging to work */
playlists.reset ();
+ delete _mmc;
+ delete _midi_ports;
delete _locations;
DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
@@ -340,135 +468,103 @@ Session::destroy ()
}
void
-Session::when_engine_running ()
+Session::setup_ltc ()
{
- string first_physical_output;
-
- BootMessage (_("Set block size and sample rate"));
-
- set_block_size (_engine.frames_per_cycle());
- set_frame_rate (_engine.frame_rate());
-
- BootMessage (_("Using configuration"));
-
- boost::function<void (std::string)> ff (boost::bind (&Session::config_changed, this, _1, false));
- boost::function<void (std::string)> ft (boost::bind (&Session::config_changed, this, _1, true));
-
- Config->map_parameters (ff);
- config.map_parameters (ft);
-
- /* every time we reconnect, recompute worst case output latencies */
-
- _engine.Running.connect_same_thread (*this, boost::bind (&Session::initialize_latencies, this));
-
- if (synced_to_jack()) {
- _engine.transport_stop ();
+ XMLNode* child = 0;
+
+ _ltc_input.reset (new IO (*this, _("LTC In"), IO::Input));
+ _ltc_output.reset (new IO (*this, _("LTC Out"), IO::Output));
+
+ if (state_tree && (child = find_named_node (*state_tree->root(), "LTC-In")) != 0) {
+ _ltc_input->set_state (*(child->children().front()), Stateful::loading_state_version);
+ } else {
+ {
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ _ltc_input->ensure_io (ChanCount (DataType::AUDIO, 1), true, this);
+ }
+ reconnect_ltc_input ();
}
-
- if (config.get_jack_time_master()) {
- _engine.transport_locate (_transport_frame);
+
+ if (state_tree && (child = find_named_node (*state_tree->root(), "LTC-Out")) != 0) {
+ _ltc_output->set_state (*(child->children().front()), Stateful::loading_state_version);
+ } else {
+ {
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ _ltc_output->ensure_io (ChanCount (DataType::AUDIO, 1), true, this);
+ }
+ reconnect_ltc_output ();
}
+
+ /* fix up names of LTC ports because we don't want the normal
+ * IO style of NAME/TYPE-{in,out}N
+ */
+
+ _ltc_input->nth (0)->set_name (_("LTC-in"));
+ _ltc_output->nth (0)->set_name (_("LTC-out"));
+}
- _clicking = false;
+void
+Session::setup_click ()
+{
+ XMLNode* child = 0;
- try {
- XMLNode* child = 0;
+ _clicking = false;
+ _click_io.reset (new ClickIO (*this, "click"));
+ _click_gain.reset (new Amp (*this));
+ _click_gain->activate ();
+
+ if (state_tree && (child = find_named_node (*state_tree->root(), "Click")) != 0) {
- _ltc_input.reset (new IO (*this, _("LTC In"), IO::Input));
- _ltc_output.reset (new IO (*this, _("LTC Out"), IO::Output));
-
- if (state_tree && (child = find_named_node (*state_tree->root(), "LTC-In")) != 0) {
- _ltc_input->set_state (*(child->children().front()), Stateful::loading_state_version);
- } else {
- {
- Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
- _ltc_input->ensure_io (ChanCount (DataType::AUDIO, 1), true, this);
- }
- reconnect_ltc_input ();
- }
+ /* existing state for Click */
+ int c = 0;
- if (state_tree && (child = find_named_node (*state_tree->root(), "LTC-Out")) != 0) {
- _ltc_output->set_state (*(child->children().front()), Stateful::loading_state_version);
+ if (Stateful::loading_state_version < 3000) {
+ c = _click_io->set_state_2X (*child->children().front(), Stateful::loading_state_version, false);
} else {
- {
- Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
- _ltc_output->ensure_io (ChanCount (DataType::AUDIO, 1), true, this);
- }
- reconnect_ltc_output ();
- }
-
- /* fix up names of LTC ports because we don't want the normal
- * IO style of NAME/TYPE-{in,out}N
- */
-
- _ltc_input->nth (0)->set_name (_("LTC-in"));
- _ltc_output->nth (0)->set_name (_("LTC-out"));
-
- _click_io.reset (new ClickIO (*this, "click"));
- _click_gain.reset (new Amp (*this));
- _click_gain->activate ();
-
- if (state_tree && (child = find_named_node (*state_tree->root(), "Click")) != 0) {
-
- /* existing state for Click */
- int c = 0;
-
- if (Stateful::loading_state_version < 3000) {
- c = _click_io->set_state_2X (*child->children().front(), Stateful::loading_state_version, false);
- } else {
- const XMLNodeList& children (child->children());
- XMLNodeList::const_iterator i = children.begin();
- if ((c = _click_io->set_state (**i, Stateful::loading_state_version)) == 0) {
- ++i;
- if (i != children.end()) {
- c = _click_gain->set_state (**i, Stateful::loading_state_version);
- }
+ const XMLNodeList& children (child->children());
+ XMLNodeList::const_iterator i = children.begin();
+ if ((c = _click_io->set_state (**i, Stateful::loading_state_version)) == 0) {
+ ++i;
+ if (i != children.end()) {
+ c = _click_gain->set_state (**i, Stateful::loading_state_version);
}
}
+ }
- if (c == 0) {
- _clicking = Config->get_clicking ();
+ if (c == 0) {
+ _clicking = Config->get_clicking ();
- } else {
+ } else {
- error << _("could not setup Click I/O") << endmsg;
- _clicking = false;
- }
+ error << _("could not setup Click I/O") << endmsg;
+ _clicking = false;
+ }
- } else {
+ } else {
- /* default state for Click: dual-mono to first 2 physical outputs */
+ /* default state for Click: dual-mono to first 2 physical outputs */
- vector<string> outs;
- _engine.get_physical_outputs (DataType::AUDIO, outs);
+ vector<string> outs;
+ _engine.get_physical_outputs (DataType::AUDIO, outs);
- for (uint32_t physport = 0; physport < 2; ++physport) {
- if (outs.size() > physport) {
- if (_click_io->add_port (outs[physport], this)) {
- // relax, even though its an error
- }
+ for (uint32_t physport = 0; physport < 2; ++physport) {
+ if (outs.size() > physport) {
+ if (_click_io->add_port (outs[physport], this)) {
+ // relax, even though its an error
}
}
-
- if (_click_io->n_ports () > ChanCount::ZERO) {
- _clicking = Config->get_clicking ();
- }
}
- }
- catch (failed_constructor& err) {
- error << _("cannot setup Click I/O") << endmsg;
- }
-
- BootMessage (_("Compute I/O Latencies"));
-
- if (_clicking) {
- // XXX HOW TO ALERT UI TO THIS ? DO WE NEED TO?
+ if (_click_io->n_ports () > ChanCount::ZERO) {
+ _clicking = Config->get_clicking ();
+ }
}
+}
- BootMessage (_("Set up standard connections"));
-
+void
+Session::setup_bundles ()
+{
vector<string> inputs[DataType::num_types];
vector<string> outputs[DataType::num_types];
for (uint32_t i = 0; i < DataType::num_types; ++i) {
@@ -567,6 +663,37 @@ Session::when_engine_running ()
add_bundle (c);
}
+}
+
+int
+Session::when_engine_running ()
+{
+ /* every time we reconnect, recompute worst case output latencies */
+
+ _engine.Running.connect_same_thread (*this, boost::bind (&Session::initialize_latencies, this));
+
+ if (synced_to_jack()) {
+ _engine.transport_stop ();
+ }
+
+ if (config.get_jack_time_master()) {
+ _engine.transport_locate (_transport_frame);
+ }
+
+
+ try {
+ BootMessage (_("Set up LTC"));
+ setup_ltc ();
+ BootMessage (_("Set up Click"));
+ setup_click ();
+ BootMessage (_("Set up standard connections"));
+ setup_bundles ();
+ }
+
+ catch (failed_constructor& err) {
+ return -1;
+ }
+
BootMessage (_("Setup signal flow and plugins"));
/* Reset all panners */
@@ -584,7 +711,8 @@ Session::when_engine_running ()
as it will set states for ports which the ControlProtocolManager creates.
*/
- MIDI::Manager::instance()->set_port_states (Config->midi_port_states ());
+ // XXX set state of MIDI::Port's
+ // MidiPortManager::instance()->set_port_states (Config->midi_port_states ());
/* And this must be done after the MIDI::Manager::set_port_states as
* it will try to make connections whose details are loaded by set_port_states.
@@ -609,11 +737,7 @@ Session::when_engine_running ()
initialize_latencies ();
- /* hook us up to the engine */
-
- BootMessage (_("Connect to engine"));
- _engine.set_session (this);
- _engine.reset_timebase ();
+ return 0;
}
void
@@ -874,7 +998,12 @@ Session::hookup_io ()
/* Tell all IO objects to connect themselves together */
IO::enable_connecting ();
- MIDI::JackMIDIPort::MakeConnections ();
+
+ /* Now tell all "floating" ports to connect to whatever
+ they should be connected to.
+ */
+
+ AudioEngine::instance()->reconnect_ports ();
/* Anyone who cares about input state, wake up and do something */
@@ -936,7 +1065,7 @@ Session::set_track_monitor_input_status (bool yn)
boost::shared_ptr<AudioTrack> tr = boost::dynamic_pointer_cast<AudioTrack> (*i);
if (tr && tr->record_enabled ()) {
//cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl;
- tr->request_jack_monitors_input (yn);
+ tr->request_input_monitoring (yn);
}
}
}
@@ -1169,7 +1298,7 @@ Session::enable_record ()
if (g_atomic_int_compare_and_exchange (&_record_status, rs, Recording)) {
_last_record_location = _transport_frame;
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe));
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe));
if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
set_track_monitor_input_status (true);
@@ -1190,7 +1319,7 @@ Session::disable_record (bool rt_context, bool force)
if ((!Config->get_latched_record_enable () && !play_loop) || force) {
g_atomic_int_set (&_record_status, Disabled);
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit));
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit));
} else {
if (rs == Recording) {
g_atomic_int_set (&_record_status, Enabled);
@@ -1244,7 +1373,7 @@ Session::maybe_enable_record ()
enable_record ();
}
} else {
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause));
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause));
RecordStateChanged (); /* EMIT SIGNAL */
}
@@ -1341,6 +1470,7 @@ Session::set_frame_rate (framecnt_t frames_per_second)
*/
_base_frame_rate = frames_per_second;
+ _nominal_frame_rate = frames_per_second;
sync_time_vars();
diff --git a/libs/ardour/session_export.cc b/libs/ardour/session_export.cc
index 0db4fc33bb..62eb61ab83 100644
--- a/libs/ardour/session_export.cc
+++ b/libs/ardour/session_export.cc
@@ -21,7 +21,6 @@
#include "pbd/error.h"
#include <glibmm/threads.h>
-#include <midi++/manager.h>
#include <midi++/mmc.h>
#include "ardour/audioengine.h"
@@ -93,8 +92,8 @@ Session::pre_export ()
/* disable MMC output early */
- _pre_export_mmc_enabled = MIDI::Manager::instance()->mmc()->send_enabled ();
- MIDI::Manager::instance()->mmc()->enable_send (false);
+ _pre_export_mmc_enabled = _mmc->send_enabled ();
+ _mmc->enable_send (false);
return 0;
}
@@ -237,7 +236,7 @@ Session::finalize_audio_export ()
export_freewheel_connection.disconnect();
- MIDI::Manager::instance()->mmc()->enable_send (_pre_export_mmc_enabled);
+ _mmc->enable_send (_pre_export_mmc_enabled);
/* maybe write CUE/TOC */
diff --git a/libs/ardour/session_jack.cc b/libs/ardour/session_jack.cc
new file mode 100644
index 0000000000..af8a93fec3
--- /dev/null
+++ b/libs/ardour/session_jack.cc
@@ -0,0 +1,185 @@
+/*
+ Copyright (C) 1999-2013 Paul Davis
+
+ 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.
+
+*/
+
+
+#ifdef WAF_BUILD
+#include "libardour-config.h"
+#endif
+
+#include <time.h>
+
+#include <glibmm/miscutils.h>
+
+#include "jack/jack.h"
+#include "jack/session.h"
+
+#include "ardour/audioengine.h"
+#include "ardour/filename_extensions.h"
+#include "ardour/session.h"
+#include "ardour/session_directory.h"
+#include "ardour/tempo.h"
+
+using namespace ARDOUR;
+using std::string;
+
+#ifdef HAVE_JACK_SESSION
+void
+Session::jack_session_event (jack_session_event_t* event)
+{
+ char timebuf[128], *tmp;
+ time_t n;
+ struct tm local_time;
+
+ time (&n);
+ localtime_r (&n, &local_time);
+ strftime (timebuf, sizeof(timebuf), "JS_%FT%T", &local_time);
+
+ while ((tmp = strchr(timebuf, ':'))) { *tmp = '.'; }
+
+ if (event->type == JackSessionSaveTemplate)
+ {
+ if (save_template( timebuf )) {
+ event->flags = JackSessionSaveError;
+ } else {
+ string cmd ("ardour3 -P -U ");
+ cmd += event->client_uuid;
+ cmd += " -T ";
+ cmd += timebuf;
+
+ event->command_line = strdup (cmd.c_str());
+ }
+ }
+ else
+ {
+ if (save_state (timebuf)) {
+ event->flags = JackSessionSaveError;
+ } else {
+ std::string xml_path (_session_dir->root_path());
+ std::string legalized_filename = legalize_for_path (timebuf) + statefile_suffix;
+ xml_path = Glib::build_filename (xml_path, legalized_filename);
+
+ string cmd ("ardour3 -P -U ");
+ cmd += event->client_uuid;
+ cmd += " \"";
+ cmd += xml_path;
+ cmd += '\"';
+
+ event->command_line = strdup (cmd.c_str());
+ }
+ }
+
+ /* this won't be called if the port engine in use is not JACK, so we do
+ not have to worry about the type of PortEngine::private_handle()
+ */
+
+ jack_client_t* jack_client = (jack_client_t*) AudioEngine::instance()->port_engine().private_handle();
+
+ if (jack_client) {
+ jack_session_reply (jack_client, event);
+ }
+
+ if (event->type == JackSessionSaveAndQuit) {
+ Quit (); /* EMIT SIGNAL */
+ }
+
+ jack_session_event_free( event );
+}
+#endif
+
+void
+Session::jack_timebase_callback (jack_transport_state_t /*state*/,
+ pframes_t /*nframes*/,
+ jack_position_t* pos,
+ int /*new_position*/)
+{
+ Timecode::BBT_Time bbt;
+
+ /* BBT info */
+
+ if (_tempo_map) {
+
+ TempoMetric metric (_tempo_map->metric_at (_transport_frame));
+
+ try {
+ _tempo_map->bbt_time_rt (_transport_frame, bbt);
+
+ pos->bar = bbt.bars;
+ pos->beat = bbt.beats;
+ pos->tick = bbt.ticks;
+
+ // XXX still need to set bar_start_tick
+
+ pos->beats_per_bar = metric.meter().divisions_per_bar();
+ pos->beat_type = metric.meter().note_divisor();
+ pos->ticks_per_beat = Timecode::BBT_Time::ticks_per_beat;
+ pos->beats_per_minute = metric.tempo().beats_per_minute();
+
+ pos->valid = jack_position_bits_t (pos->valid | JackPositionBBT);
+
+ } catch (...) {
+ /* no message */
+ }
+ }
+
+#ifdef HAVE_JACK_VIDEO_SUPPORT
+ //poke audio video ratio so Ardour can track Video Sync
+ pos->audio_frames_per_video_frame = frame_rate() / timecode_frames_per_second();
+ pos->valid = jack_position_bits_t (pos->valid | JackAudioVideoRatio);
+#endif
+
+#if 0
+ /* Timecode info */
+
+ pos->timecode_offset = config.get_timecode_offset();
+ t.timecode_frame_rate = timecode_frames_per_second();
+ pos->valid = jack_position_bits_t (pos->valid | JackPositionTimecode;
+
+ if (_transport_speed) {
+
+ if (play_loop) {
+
+ Location* location = _locations.auto_loop_location();
+
+ if (location) {
+
+ t.transport_state = JackTransportLooping;
+ t.loop_start = location->start();
+ t.loop_end = location->end();
+ t.valid = jack_transport_bits_t (t.valid | JackTransportLoop);
+
+ } else {
+
+ t.loop_start = 0;
+ t.loop_end = 0;
+ t.transport_state = JackTransportRolling;
+
+ }
+
+ } else {
+
+ t.loop_start = 0;
+ t.loop_end = 0;
+ t.transport_state = JackTransportRolling;
+
+ }
+
+ }
+#endif
+}
+
diff --git a/libs/ardour/session_ltc.cc b/libs/ardour/session_ltc.cc
index 2671bfccf4..2ce25b5a48 100644
--- a/libs/ardour/session_ltc.cc
+++ b/libs/ardour/session_ltc.cc
@@ -55,7 +55,7 @@ using namespace Timecode;
* This filter is adaptive so that fast vari-speed signals
* will not be affected by it.
*/
-#define LTC_RISE_TIME(speed) MIN (100, MAX(40, (4000000 / ((speed==0)?1:speed) / engine().frame_rate())))
+#define LTC_RISE_TIME(speed) MIN (100, MAX(40, (4000000 / ((speed==0)?1:speed) / engine().sample_rate())))
#define TV_STANDARD(tcf) \
(timecode_to_frames_per_second(tcf)==25.0 ? LTC_TV_625_50 : \
@@ -564,7 +564,7 @@ Session::ltc_tx_send_time_code_for_cycle (framepos_t start_frame, framepos_t end
* To do better than this, resampling (or a rewrite of the
* encoder) is required.
*/
- ltc_speed -= ((ltc_enc_pos + ltc_enc_cnt - poff) - cycle_start_frame) / engine().frame_rate();
+ ltc_speed -= ((ltc_enc_pos + ltc_enc_cnt - poff) - cycle_start_frame) / engine().sample_rate();
}
diff --git a/libs/ardour/session_midi.cc b/libs/ardour/session_midi.cc
index 9a7ca5b734..63cba68df1 100644
--- a/libs/ardour/session_midi.cc
+++ b/libs/ardour/session_midi.cc
@@ -29,7 +29,6 @@
#include "midi++/mmc.h"
#include "midi++/port.h"
-#include "midi++/manager.h"
#include "pbd/error.h"
#include "pbd/pthread_utils.h"
@@ -40,6 +39,7 @@
#include "ardour/audio_track.h"
#include "ardour/audioengine.h"
#include "ardour/debug.h"
+#include "ardour/midi_port.h"
#include "ardour/midi_track.h"
#include "ardour/midi_ui.h"
#include "ardour/session.h"
@@ -348,7 +348,7 @@ Session::mmc_record_enable (MIDI::MachineControl &mmc, size_t trk, bool enabled)
* @param t time to send.
*/
int
-Session::send_full_time_code (framepos_t const t)
+Session::send_full_time_code (framepos_t const t, pframes_t nframes)
{
/* This function could easily send at a given frame offset, but would
* that be useful? Does ardour do sub-block accurate locating? [DR] */
@@ -423,10 +423,9 @@ Session::send_full_time_code (framepos_t const t)
msg[8] = timecode.frames;
// Send message at offset 0, sent time is for the start of this cycle
- if (MIDI::Manager::instance()->mtc_output_port()->midimsg (msg, sizeof (msg), 0)) {
- error << _("Session: could not send full MIDI time code") << endmsg;
- return -1;
- }
+
+ MidiBuffer& mb (_midi_ports->mtc_output_port()->get_midi_buffer (nframes));
+ mb.push_back (0, sizeof (msg), msg);
_pframes_since_last_mtc = 0;
return 0;
@@ -469,7 +468,7 @@ Session::send_midi_time_code_for_cycle (framepos_t start_frame, framepos_t end_f
next_quarter_frame_to_send, quarter_frame_duration));
if (rint(outbound_mtc_timecode_frame + (next_quarter_frame_to_send * quarter_frame_duration)) < _transport_frame) {
- send_full_time_code (_transport_frame);
+ send_full_time_code (_transport_frame, nframes);
return 0;
}
@@ -515,9 +514,10 @@ Session::send_midi_time_code_for_cycle (framepos_t start_frame, framepos_t end_f
ARDOUR::pframes_t const out_stamp = (msg_time - start_frame) / _transport_speed;
assert (out_stamp < nframes);
- if (MIDI::Manager::instance()->mtc_output_port()->midimsg (mtc_msg, 2, out_stamp)) {
+ MidiBuffer& mb (_midi_ports->mtc_output_port()->get_midi_buffer(nframes));
+ if (!mb.push_back (out_stamp, 2, mtc_msg)) {
error << string_compose(_("Session: cannot send quarter-frame MTC message (%1)"), strerror (errno))
- << endmsg;
+ << endmsg;
return -1;
}
@@ -602,3 +602,45 @@ Session::start_midi_thread ()
return 0;
}
+MIDI::Port*
+Session::midi_input_port () const
+{
+ return _midi_ports->midi_input_port ();
+}
+MIDI::Port*
+Session::midi_output_port () const
+{
+ return _midi_ports->midi_output_port ();
+}
+boost::shared_ptr<MidiPort>
+Session::midi_clock_output_port () const
+{
+ return _midi_ports->midi_clock_output_port ();
+}
+boost::shared_ptr<MidiPort>
+Session::midi_clock_input_port () const
+{
+ return _midi_ports->midi_clock_input_port ();
+}
+boost::shared_ptr<MidiPort>
+Session::mtc_output_port () const
+{
+ return _midi_ports->mtc_output_port ();
+}
+boost::shared_ptr<MidiPort>
+Session::mtc_input_port () const
+{
+ return _midi_ports->mtc_input_port ();
+}
+
+MIDI::Port*
+Session::mmc_output_port () const
+{
+ return _midi_ports->mmc_output_port ();
+}
+
+MIDI::Port*
+Session::mmc_input_port () const
+{
+ return _midi_ports->mmc_input_port ();
+}
diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc
index cecbd88f65..3c46a2e0a6 100644
--- a/libs/ardour/session_process.cc
+++ b/libs/ardour/session_process.cc
@@ -40,7 +40,6 @@
#include "ardour/ticker.h"
#include "ardour/types.h"
-#include "midi++/manager.h"
#include "midi++/mmc.h"
#include "i18n.h"
@@ -85,7 +84,7 @@ Session::process (pframes_t nframes)
try {
if (!_engine.freewheeling() && Config->get_send_midi_clock() && transport_speed() == 1.0f && midi_clock->has_midi_port()) {
- midi_clock->tick (transport_at_start);
+ midi_clock->tick (transport_at_start, nframes);
}
} catch (...) {
/* don't bother with a message */
@@ -325,7 +324,7 @@ Session::process_with_events (pframes_t nframes)
* and prepare for rolling)
*/
if (_send_timecode_update) {
- send_full_time_code (_transport_frame);
+ send_full_time_code (_transport_frame, nframes);
}
if (!process_can_proceed()) {
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index 9699a7c37f..64f8e387be 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -1,5 +1,5 @@
/*
- Copyright (C) 1999-2002 Paul Davis
+ Copyright (C) 1999-2013 Paul Davis
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
@@ -59,7 +59,6 @@
#include "midi++/mmc.h"
#include "midi++/port.h"
-#include "midi++/manager.h"
#include "evoral/SMF.hpp"
@@ -88,6 +87,7 @@
#include "ardour/control_protocol_manager.h"
#include "ardour/directory_names.h"
#include "ardour/filename_extensions.h"
+#include "ardour/graph.h"
#include "ardour/location.h"
#include "ardour/midi_model.h"
#include "ardour/midi_patch_manager.h"
@@ -126,112 +126,47 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
-/** @param snapshot_name Snapshot name, without the .ardour prefix */
void
-Session::first_stage_init (string fullpath, string snapshot_name)
+Session::pre_engine_init (string fullpath)
{
- if (fullpath.length() == 0) {
+ if (fullpath.empty()) {
destroy ();
throw failed_constructor();
}
- _path = canonical_path (fullpath);
+ /* discover canonical fullpath */
- if (_path[_path.length()-1] != G_DIR_SEPARATOR) {
- _path += G_DIR_SEPARATOR;
+ char buf[PATH_MAX+1];
+ if (!realpath (fullpath.c_str(), buf) && (errno != ENOENT)) {
+ error << string_compose(_("Could not use path %1 (%2)"), buf, strerror(errno)) << endmsg;
+ destroy ();
+ throw failed_constructor();
}
- /* these two are just provisional settings. set_state()
- will likely override them.
- */
-
- _name = _current_snapshot_name = snapshot_name;
+ _path = string(buf);
+
+ /* we require _path to end with a dir separator */
- set_history_depth (Config->get_history_depth());
+ if (_path[_path.length()-1] != G_DIR_SEPARATOR) {
+ _path += G_DIR_SEPARATOR;
+ }
- _current_frame_rate = _engine.frame_rate ();
- _nominal_frame_rate = _current_frame_rate;
- _base_frame_rate = _current_frame_rate;
+ /* is it new ? */
- _tempo_map = new TempoMap (_current_frame_rate);
- _tempo_map->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1));
+ _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
+ /* finish initialization that can't be done in a normal C++ constructor
+ definition.
+ */
- _non_soloed_outs_muted = false;
- _listen_cnt = 0;
- _solo_isolated_cnt = 0;
+ timerclear (&last_mmc_step);
g_atomic_int_set (&processing_prohibited, 0);
- _transport_speed = 0;
- _default_transport_speed = 1.0;
- _last_transport_speed = 0;
- _target_transport_speed = 0;
- auto_play_legal = false;
- transport_sub_state = 0;
- _transport_frame = 0;
- _requested_return_frame = -1;
- _session_range_location = 0;
g_atomic_int_set (&_record_status, Disabled);
- loop_changing = false;
- play_loop = false;
- have_looped = false;
- _last_roll_location = 0;
- _last_roll_or_reversal_location = 0;
- _last_record_location = 0;
- pending_locate_frame = 0;
- pending_locate_roll = false;
- pending_locate_flush = false;
- state_was_pending = false;
- set_next_event ();
- outbound_mtc_timecode_frame = 0;
- next_quarter_frame_to_send = -1;
- current_block_size = 0;
- solo_update_disabled = false;
- _have_captured = false;
- _worst_output_latency = 0;
- _worst_input_latency = 0;
- _worst_track_latency = 0;
- _state_of_the_state = StateOfTheState(CannotSave|InitialConnecting|Loading);
- _was_seamless = Config->get_seamless_loop ();
- _slave = 0;
- _send_qf_mtc = false;
- _pframes_since_last_mtc = 0;
g_atomic_int_set (&_playback_load, 100);
g_atomic_int_set (&_capture_load, 100);
- _play_range = false;
- _exporting = false;
- pending_abort = false;
- _adding_routes_in_progress = false;
- destructive_index = 0;
- first_file_data_format_reset = true;
- first_file_header_format_reset = true;
- post_export_sync = false;
- midi_control_ui = 0;
- _step_editors = 0;
- no_questions_about_missing_files = false;
- _speakers.reset (new Speakers);
- _clicks_cleared = 0;
- ignore_route_processor_changes = false;
- _pre_export_mmc_enabled = false;
-
- AudioDiskstream::allocate_working_buffers();
-
- /* default short fade = 15ms */
-
- SndFileSource::setup_standard_crossfades (*this, frame_rate());
-
- last_mmc_step.tv_sec = 0;
- last_mmc_step.tv_usec = 0;
- step_speed = 0.0;
-
- /* click sounds are unset by default, which causes us to internal
- waveforms for clicks.
- */
-
- click_length = 0;
- click_emphasis_length = 0;
- _clicking = false;
-
- process_function = &Session::process_with_events;
+ set_next_event ();
+ _all_route_group->set_active (true, this);
+ interpolation.add_channel_to (0, 0);
if (config.get_use_video_sync()) {
waiting_for_sync_offset = true;
@@ -239,32 +174,26 @@ Session::first_stage_init (string fullpath, string snapshot_name)
waiting_for_sync_offset = false;
}
- last_timecode_when = 0;
- last_timecode_valid = false;
-
- sync_time_vars ();
-
last_rr_session_dir = session_dirs.begin();
- refresh_disk_space ();
+
+ set_history_depth (Config->get_history_depth());
+
+ if (how_many_dsp_threads () > 1) {
+ /* For now, only create the graph if we are using >1 DSP threads, as
+ it is a bit slower than the old code with 1 thread.
+ */
+ _process_graph.reset (new Graph (*this));
+ }
/* default: assume simple stereo speaker configuration */
_speakers->setup_default_speakers (2);
- /* slave stuff */
-
- average_slave_delta = 1800; // !!! why 1800 ????
- have_first_delta_accumulator = false;
- delta_accumulator_cnt = 0;
- _slave_state = Stopped;
-
_solo_cut_control.reset (new ProxyControllable (_("solo cut control (dB)"), PBD::Controllable::GainLike,
boost::bind (&RCConfiguration::set_solo_mute_gain, Config, _1),
boost::bind (&RCConfiguration::get_solo_mute_gain, Config)));
add_controllable (_solo_cut_control);
- _engine.GraphReordered.connect_same_thread (*this, boost::bind (&Session::graph_reordered, this));
-
/* These are all static "per-class" signals */
SourceFactory::SourceCreated.connect_same_thread (*this, boost::bind (&Session::add_source, this, _1));
@@ -277,87 +206,101 @@ Session::first_stage_init (string fullpath, string snapshot_name)
Delivery::disable_panners ();
IO::disable_connecting ();
+
+ AudioFileSource::set_peak_dir (_session_dir->peak_path());
}
int
-Session::second_stage_init ()
+Session::post_engine_init ()
{
- AudioFileSource::set_peak_dir (_session_dir->peak_path());
+ BootMessage (_("Set block size and sample rate"));
- if (!_is_new) {
- if (load_state (_current_snapshot_name)) {
- return -1;
- }
- }
+ set_block_size (_engine.samples_per_cycle());
+ set_frame_rate (_engine.sample_rate());
+
+ n_physical_outputs = _engine.n_physical_outputs ();
+ n_physical_inputs = _engine.n_physical_inputs ();
+ BootMessage (_("Using configuration"));
+
+ _midi_ports = new MidiPortManager;
+ setup_midi_machine_control ();
+
if (_butler->start_thread()) {
return -1;
}
-
+
if (start_midi_thread ()) {
return -1;
}
+
+ setup_click_sounds (0);
+ setup_midi_control ();
- setup_midi_machine_control ();
-
- // set_state() will call setup_raid_path(), but if it's a new session we need
- // to call setup_raid_path() here.
-
- if (state_tree) {
- if (set_state (*state_tree->root(), Stateful::loading_state_version)) {
- return -1;
- }
- } else {
- setup_raid_path(_path);
- }
+ _engine.Halted.connect_same_thread (*this, boost::bind (&Session::engine_halted, this));
+ _engine.Xrun.connect_same_thread (*this, boost::bind (&Session::xrun_recovery, this));
- /* we can't save till after ::when_engine_running() is called,
- because otherwise we save state with no connections made.
- therefore, we reset _state_of_the_state because ::set_state()
- will have cleared it.
+ try {
+ /* tempo map requires sample rate knowledge */
- we also have to include Loading so that any events that get
- generated between here and the end of ::when_engine_running()
- will be processed directly rather than queued.
- */
+ _tempo_map = new TempoMap (_current_frame_rate);
+ _tempo_map->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1));
+
+ /* MidiClock requires a tempo map */
- _state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave|Loading);
+ midi_clock = new MidiClockTicker ();
+ midi_clock->set_session (this);
- _locations->changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this));
- _locations->added.connect_same_thread (*this, boost::bind (&Session::locations_added, this, _1));
- setup_click_sounds (0);
- setup_midi_control ();
+ /* crossfades require sample rate knowledge */
- /* Pay attention ... */
+ SndFileSource::setup_standard_crossfades (*this, frame_rate());
+ _engine.GraphReordered.connect_same_thread (*this, boost::bind (&Session::graph_reordered, this));
+
+ AudioDiskstream::allocate_working_buffers();
+ refresh_disk_space ();
+
+ /* we're finally ready to call set_state() ... all objects have
+ * been created, the engine is running.
+ */
+
+ if (state_tree) {
+ if (set_state (*state_tree->root(), Stateful::loading_state_version)) {
+ return -1;
+ }
+ } else {
+ // set_state() will call setup_raid_path(), but if it's a new session we need
+ // to call setup_raid_path() here.
+ setup_raid_path (_path);
+ }
- _engine.Halted.connect_same_thread (*this, boost::bind (&Session::engine_halted, this));
- _engine.Xrun.connect_same_thread (*this, boost::bind (&Session::xrun_recovery, this));
+ /* ENGINE */
- midi_clock = new MidiClockTicker ();
- midi_clock->set_session (this);
+ boost::function<void (std::string)> ff (boost::bind (&Session::config_changed, this, _1, false));
+ boost::function<void (std::string)> ft (boost::bind (&Session::config_changed, this, _1, true));
+
+ Config->map_parameters (ff);
+ config.map_parameters (ft);
- try {
when_engine_running ();
- }
-
- /* handle this one in a different way than all others, so that its clear what happened */
- catch (AudioEngine::PortRegistrationFailure& err) {
+ _locations->changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this));
+ _locations->added.connect_same_thread (*this, boost::bind (&Session::locations_added, this, _1));
+
+ } catch (AudioEngine::PortRegistrationFailure& err) {
+ /* handle this one in a different way than all others, so that its clear what happened */
error << err.what() << endmsg;
return -1;
- }
-
- catch (...) {
+ } catch (...) {
return -1;
}
BootMessage (_("Reset Remote Controls"));
- send_full_time_code (0);
+ // send_full_time_code (0);
_engine.transport_locate (0);
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (Timecode::Time ()));
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
+ _mmc->send (MIDI::MachineControlCommand (Timecode::Time ()));
MIDI::Name::MidiPatchManager::instance().set_session (this);
@@ -366,18 +309,16 @@ Session::second_stage_init ()
_state_of_the_state = Clean;
- Port::set_connecting_blocked (false);
-
DirtyChanged (); /* EMIT SIGNAL */
- if (state_was_pending) {
- save_state (_current_snapshot_name);
+ if (_is_new) {
+ save_state ("");
+ } else if (state_was_pending) {
+ save_state ("");
remove_pending_capture_state ();
state_was_pending = false;
}
- BootMessage (_("Session loading complete"));
-
return 0;
}
@@ -604,8 +545,6 @@ Session::create (const string& session_template, BusProfile* bus_profile)
add_monitor_section ();
}
- save_state ("");
-
return 0;
}
@@ -684,62 +623,6 @@ Session::remove_state (string snapshot_name)
}
}
-#ifdef HAVE_JACK_SESSION
-void
-Session::jack_session_event (jack_session_event_t * event)
-{
- char timebuf[128], *tmp;
- time_t n;
- struct tm local_time;
-
- time (&n);
- localtime_r (&n, &local_time);
- strftime (timebuf, sizeof(timebuf), "JS_%FT%T", &local_time);
-
- while ((tmp = strchr(timebuf, ':'))) { *tmp = '.'; }
-
- if (event->type == JackSessionSaveTemplate)
- {
- if (save_template( timebuf )) {
- event->flags = JackSessionSaveError;
- } else {
- string cmd ("ardour3 -P -U ");
- cmd += event->client_uuid;
- cmd += " -T ";
- cmd += timebuf;
-
- event->command_line = strdup (cmd.c_str());
- }
- }
- else
- {
- if (save_state (timebuf)) {
- event->flags = JackSessionSaveError;
- } else {
- std::string xml_path (_session_dir->root_path());
- std::string legalized_filename = legalize_for_path (timebuf) + statefile_suffix;
- xml_path = Glib::build_filename (xml_path, legalized_filename);
-
- string cmd ("ardour3 -P -U ");
- cmd += event->client_uuid;
- cmd += " \"";
- cmd += xml_path;
- cmd += '\"';
-
- event->command_line = strdup (cmd.c_str());
- }
- }
-
- jack_session_reply (_engine.jack(), event);
-
- if (event->type == JackSessionSaveAndQuit) {
- Quit (); /* EMIT SIGNAL */
- }
-
- jack_session_event_free( event );
-}
-#endif
-
/** @param snapshot_name Name to save under, without .ardour / .pending prefix */
int
Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot)
@@ -1034,6 +917,15 @@ Session::state (bool full_state)
/* various options */
+ list<XMLNode*> midi_port_nodes = _midi_ports->get_midi_port_states();
+ if (!midi_port_nodes.empty()) {
+ XMLNode* midi_port_stuff = new XMLNode ("MIDIPorts");
+ for (list<XMLNode*>::const_iterator n = midi_port_nodes.begin(); n != midi_port_nodes.end(); ++n) {
+ midi_port_stuff->add_child_nocopy (**n);
+ }
+ node->add_child_nocopy (*midi_port_stuff);
+ }
+
node->add_child_nocopy (config.get_variables ());
node->add_child_nocopy (ARDOUR::SessionMetadata::Metadata()->get_state());
@@ -1239,6 +1131,11 @@ Session::set_state (const XMLNode& node, int version)
Evoral::init_event_id_counter (atoi (prop->value()));
}
+
+ if ((child = find_named_node (node, "MIDIPorts")) != 0) {
+ _midi_ports->set_midi_port_states (child->children());
+ }
+
IO::disable_connecting ();
Stateful::save_extra_xml (node);
@@ -3453,11 +3350,11 @@ Session::config_changed (std::string p, bool ours)
} else if (p == "mmc-device-id" || p == "mmc-receive-id" || p == "mmc-receive-device-id") {
- MIDI::Manager::instance()->mmc()->set_receive_device_id (Config->get_mmc_receive_device_id());
+ _mmc->set_receive_device_id (Config->get_mmc_receive_device_id());
} else if (p == "mmc-send-id" || p == "mmc-send-device-id") {
- MIDI::Manager::instance()->mmc()->set_send_device_id (Config->get_mmc_send_device_id());
+ _mmc->set_send_device_id (Config->get_mmc_send_device_id());
} else if (p == "midi-control") {
@@ -3520,7 +3417,7 @@ Session::config_changed (std::string p, bool ours)
} else if (p == "send-mmc") {
- MIDI::Manager::instance()->mmc()->enable_send (Config->get_send_mmc ());
+ _mmc->enable_send (Config->get_send_mmc ());
} else if (p == "midi-feedback") {
@@ -3578,13 +3475,13 @@ Session::config_changed (std::string p, bool ours)
} else if (p == "initial-program-change") {
- if (MIDI::Manager::instance()->mmc()->output_port() && Config->get_initial_program_change() >= 0) {
+ if (_mmc->output_port() && Config->get_initial_program_change() >= 0) {
MIDI::byte buf[2];
buf[0] = MIDI::program; // channel zero by default
buf[1] = (Config->get_initial_program_change() & 0x7f);
- MIDI::Manager::instance()->mmc()->output_port()->midimsg (buf, sizeof (buf), 0);
+ _mmc->output_port()->midimsg (buf, sizeof (buf), 0);
}
} else if (p == "solo-mute-override") {
// catch_up_on_solo_mute_override ();
@@ -3648,27 +3545,28 @@ Session::load_diskstreams_2X (XMLNode const & node, int)
void
Session::setup_midi_machine_control ()
{
- MIDI::MachineControl* mmc = MIDI::Manager::instance()->mmc ();
-
- mmc->Play.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
- mmc->DeferredPlay.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
- mmc->Stop.connect_same_thread (*this, boost::bind (&Session::mmc_stop, this, _1));
- mmc->FastForward.connect_same_thread (*this, boost::bind (&Session::mmc_fast_forward, this, _1));
- mmc->Rewind.connect_same_thread (*this, boost::bind (&Session::mmc_rewind, this, _1));
- mmc->Pause.connect_same_thread (*this, boost::bind (&Session::mmc_pause, this, _1));
- mmc->RecordPause.connect_same_thread (*this, boost::bind (&Session::mmc_record_pause, this, _1));
- mmc->RecordStrobe.connect_same_thread (*this, boost::bind (&Session::mmc_record_strobe, this, _1));
- mmc->RecordExit.connect_same_thread (*this, boost::bind (&Session::mmc_record_exit, this, _1));
- mmc->Locate.connect_same_thread (*this, boost::bind (&Session::mmc_locate, this, _1, _2));
- mmc->Step.connect_same_thread (*this, boost::bind (&Session::mmc_step, this, _1, _2));
- mmc->Shuttle.connect_same_thread (*this, boost::bind (&Session::mmc_shuttle, this, _1, _2, _3));
- mmc->TrackRecordStatusChange.connect_same_thread (*this, boost::bind (&Session::mmc_record_enable, this, _1, _2, _3));
+ _mmc = new MIDI::MachineControl;
+ _mmc->set_ports (_midi_ports->mmc_input_port(), _midi_ports->mmc_output_port());
+
+ _mmc->Play.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
+ _mmc->DeferredPlay.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
+ _mmc->Stop.connect_same_thread (*this, boost::bind (&Session::mmc_stop, this, _1));
+ _mmc->FastForward.connect_same_thread (*this, boost::bind (&Session::mmc_fast_forward, this, _1));
+ _mmc->Rewind.connect_same_thread (*this, boost::bind (&Session::mmc_rewind, this, _1));
+ _mmc->Pause.connect_same_thread (*this, boost::bind (&Session::mmc_pause, this, _1));
+ _mmc->RecordPause.connect_same_thread (*this, boost::bind (&Session::mmc_record_pause, this, _1));
+ _mmc->RecordStrobe.connect_same_thread (*this, boost::bind (&Session::mmc_record_strobe, this, _1));
+ _mmc->RecordExit.connect_same_thread (*this, boost::bind (&Session::mmc_record_exit, this, _1));
+ _mmc->Locate.connect_same_thread (*this, boost::bind (&Session::mmc_locate, this, _1, _2));
+ _mmc->Step.connect_same_thread (*this, boost::bind (&Session::mmc_step, this, _1, _2));
+ _mmc->Shuttle.connect_same_thread (*this, boost::bind (&Session::mmc_shuttle, this, _1, _2, _3));
+ _mmc->TrackRecordStatusChange.connect_same_thread (*this, boost::bind (&Session::mmc_record_enable, this, _1, _2, _3));
/* also handle MIDI SPP because its so common */
- mmc->SPPStart.connect_same_thread (*this, boost::bind (&Session::spp_start, this));
- mmc->SPPContinue.connect_same_thread (*this, boost::bind (&Session::spp_continue, this));
- mmc->SPPStop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this));
+ _mmc->SPPStart.connect_same_thread (*this, boost::bind (&Session::spp_start, this));
+ _mmc->SPPContinue.connect_same_thread (*this, boost::bind (&Session::spp_continue, this));
+ _mmc->SPPStop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this));
}
boost::shared_ptr<Controllable>
diff --git a/libs/ardour/session_time.cc b/libs/ardour/session_time.cc
index 18805afa90..0f2186c09b 100644
--- a/libs/ardour/session_time.cc
+++ b/libs/ardour/session_time.cc
@@ -180,31 +180,30 @@ Session::timecode_time (Timecode::Time &t)
}
int
-Session::jack_sync_callback (jack_transport_state_t state,
- jack_position_t* pos)
+Session::backend_sync_callback (TransportState state, framepos_t pos)
{
bool slave = synced_to_jack();
switch (state) {
- case JackTransportStopped:
- if (slave && _transport_frame != pos->frame && post_transport_work() == 0) {
- request_locate (pos->frame, false);
+ case TransportStopped:
+ if (slave && _transport_frame != pos && post_transport_work() == 0) {
+ request_locate (pos, false);
// cerr << "SYNC: stopped, locate to " << pos->frame << " from " << _transport_frame << endl;
return false;
} else {
return true;
}
- case JackTransportStarting:
+ case TransportStarting:
// cerr << "SYNC: starting @ " << pos->frame << " a@ " << _transport_frame << " our work = " << post_transport_work() << " pos matches ? " << (_transport_frame == pos->frame) << endl;
if (slave) {
- return _transport_frame == pos->frame && post_transport_work() == 0;
+ return _transport_frame == pos && post_transport_work() == 0;
} else {
return true;
}
break;
- case JackTransportRolling:
+ case TransportRolling:
// cerr << "SYNC: rolling slave = " << slave << endl;
if (slave) {
start_transport ();
@@ -212,93 +211,13 @@ Session::jack_sync_callback (jack_transport_state_t state,
break;
default:
- error << string_compose (_("Unknown JACK transport state %1 in sync callback"), state)
+ error << string_compose (_("Unknown transport state %1 in sync callback"), state)
<< endmsg;
}
return true;
}
-void
-Session::jack_timebase_callback (jack_transport_state_t /*state*/,
- pframes_t /*nframes*/,
- jack_position_t* pos,
- int /*new_position*/)
-{
- Timecode::BBT_Time bbt;
-
- /* BBT info */
-
- if (_tempo_map) {
-
- TempoMetric metric (_tempo_map->metric_at (_transport_frame));
-
- try {
- _tempo_map->bbt_time_rt (_transport_frame, bbt);
-
- pos->bar = bbt.bars;
- pos->beat = bbt.beats;
- pos->tick = bbt.ticks;
-
- // XXX still need to set bar_start_tick
-
- pos->beats_per_bar = metric.meter().divisions_per_bar();
- pos->beat_type = metric.meter().note_divisor();
- pos->ticks_per_beat = Timecode::BBT_Time::ticks_per_beat;
- pos->beats_per_minute = metric.tempo().beats_per_minute();
-
- pos->valid = jack_position_bits_t (pos->valid | JackPositionBBT);
-
- } catch (...) {
- /* no message */
- }
- }
-
-#ifdef HAVE_JACK_VIDEO_SUPPORT
- //poke audio video ratio so Ardour can track Video Sync
- pos->audio_frames_per_video_frame = frame_rate() / timecode_frames_per_second();
- pos->valid = jack_position_bits_t (pos->valid | JackAudioVideoRatio);
-#endif
-
-#if 0
- /* Timecode info */
-
- pos->timecode_offset = config.get_timecode_offset();
- t.timecode_frame_rate = timecode_frames_per_second();
- pos->valid = jack_position_bits_t (pos->valid | JackPositionTimecode;
-
- if (_transport_speed) {
-
- if (play_loop) {
-
- Location* location = _locations.auto_loop_location();
-
- if (location) {
-
- t.transport_state = JackTransportLooping;
- t.loop_start = location->start();
- t.loop_end = location->end();
- t.valid = jack_transport_bits_t (t.valid | JackTransportLoop);
-
- } else {
-
- t.loop_start = 0;
- t.loop_end = 0;
- t.transport_state = JackTransportRolling;
-
- }
-
- } else {
-
- t.loop_start = 0;
- t.loop_end = 0;
- t.transport_state = JackTransportRolling;
-
- }
-
- }
-#endif
-}
ARDOUR::framecnt_t
Session::convert_to_frames (AnyTime const & position)
diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc
index 51d8d2c369..ab6d68fd67 100644
--- a/libs/ardour/session_transport.cc
+++ b/libs/ardour/session_transport.cc
@@ -33,7 +33,6 @@
#include "midi++/mmc.h"
#include "midi++/port.h"
-#include "midi++/manager.h"
#include "ardour/audioengine.h"
#include "ardour/auditioner.h"
@@ -610,11 +609,23 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
have_looped = false;
- if (!_engine.freewheeling()) {
- send_full_time_code (_transport_frame);
+ /* don't bother with this stuff if we're disconnected from the engine,
+ because there will be no process callbacks to deliver stuff from
+ */
+
+ if (_engine.connected() && !_engine.freewheeling()) {
+ // need to queue this in the next RT cycle
+ _send_timecode_update = true;
if (!dynamic_cast<MTC_Slave*>(_slave)) {
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop));
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop));
+
+ /* This (::non_realtime_stop()) gets called by main
+ process thread, which will lead to confusion
+ when calling AsyncMIDIPort::write().
+
+ Something must be done. XXX
+ */
send_mmc_locate (_transport_frame);
}
}
@@ -1260,7 +1271,7 @@ Session::start_transport ()
Timecode::Time time;
timecode_time_subframes (_transport_frame, time);
if (!dynamic_cast<MTC_Slave*>(_slave)) {
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay));
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay));
}
}
@@ -1338,8 +1349,9 @@ Session::use_sync_source (Slave* new_slave)
_slave = new_slave;
DEBUG_TRACE (DEBUG::Slave, string_compose ("set new slave to %1\n", _slave));
-
- send_full_time_code (_transport_frame);
+
+ // need to queue this for next process() cycle
+ _send_timecode_update = true;
boost::shared_ptr<RouteList> rl = routes.reader();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
@@ -1380,7 +1392,7 @@ Session::switch_to_sync_source (SyncSource src)
}
try {
- new_slave = new MTC_Slave (*this, *MIDI::Manager::instance()->mtc_input_port());
+ new_slave = new MTC_Slave (*this, *_midi_ports->mtc_input_port());
}
catch (failed_constructor& err) {
@@ -1409,7 +1421,7 @@ Session::switch_to_sync_source (SyncSource src)
}
try {
- new_slave = new MIDIClock_Slave (*this, *MIDI::Manager::instance()->midi_clock_input_port(), 24);
+ new_slave = new MIDIClock_Slave (*this, *_midi_ports->midi_clock_input_port(), 24);
}
catch (failed_constructor& err) {
@@ -1426,7 +1438,7 @@ Session::switch_to_sync_source (SyncSource src)
return;
}
- new_slave = new JACK_Slave (_engine.jack());
+ new_slave = new JACK_Slave (*AudioEngine::instance());
break;
default:
@@ -1616,16 +1628,6 @@ Session::allow_auto_play (bool yn)
auto_play_legal = yn;
}
-void
-Session::reset_jack_connection (jack_client_t* jack)
-{
- JACK_Slave* js;
-
- if (_slave && ((js = dynamic_cast<JACK_Slave*> (_slave)) != 0)) {
- js->reset_client (jack);
- }
-}
-
bool
Session::maybe_stop (framepos_t limit)
{
@@ -1646,7 +1648,7 @@ Session::send_mmc_locate (framepos_t t)
if (!_engine.freewheeling()) {
Timecode::Time time;
timecode_time_subframes (t, time);
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (time));
+ _mmc->send (MIDI::MachineControlCommand (time));
}
}
diff --git a/libs/ardour/slave.cc b/libs/ardour/slave.cc
index 1cbcba8ee6..e8c63b43c1 100644
--- a/libs/ardour/slave.cc
+++ b/libs/ardour/slave.cc
@@ -50,13 +50,13 @@ SlaveSessionProxy::transport_frame() const
pframes_t
SlaveSessionProxy::frames_since_cycle_start() const
{
- return session.engine().frames_since_cycle_start();
+ return session.engine().samples_since_cycle_start();
}
framepos_t
SlaveSessionProxy::frame_time() const
{
- return session.engine().frame_time();
+ return session.engine().sample_time();
}
void
diff --git a/libs/ardour/ticker.cc b/libs/ardour/ticker.cc
index f32cdf9415..9a29df2641 100644
--- a/libs/ardour/ticker.cc
+++ b/libs/ardour/ticker.cc
@@ -19,13 +19,12 @@
#include "pbd/compose.h"
#include "pbd/stacktrace.h"
-#include "midi++/port.h"
-#include "midi++/jack_midi_port.h"
-#include "midi++/manager.h"
-
#include "evoral/midi_events.h"
+#include "ardour/async_midi_port.h"
#include "ardour/audioengine.h"
+#include "ardour/midi_buffer.h"
+#include "ardour/midi_port.h"
#include "ardour/ticker.h"
#include "ardour/session.h"
#include "ardour/tempo.h"
@@ -95,16 +94,16 @@ public:
MidiClockTicker::MidiClockTicker ()
- : _midi_port (0)
- , _ppqn (24)
+ : _ppqn (24)
, _last_tick (0.0)
+ , _send_pos (false)
+ , _send_state (false)
{
_pos.reset (new Position());
}
MidiClockTicker::~MidiClockTicker()
{
- _midi_port = 0;
_pos.reset (0);
}
@@ -115,7 +114,6 @@ MidiClockTicker::set_session (Session* s)
if (_session) {
_session->TransportStateChange.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::transport_state_changed, this));
- _session->PositionChanged.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::position_changed, this, _1));
_session->TransportLooped.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::transport_looped, this));
_session->Located.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::session_located, this));
@@ -139,41 +137,20 @@ MidiClockTicker::session_located()
return;
}
- if (_pos->speed == 0.0f) {
- uint32_t where = llrint (_pos->midi_beats);
- send_position_event (where, 0);
- } else if (_pos->speed == 1.0f) {
-#if 1
- /* Experimental. To really do this and have accuracy, the
- stop/locate/continue sequence would need queued to send immediately
- before the next midi clock. */
-
- send_stop_event (0);
-
- if (_pos->frame == 0) {
- send_start_event (0);
- } else {
- uint32_t where = llrint (_pos->midi_beats);
- send_position_event (where, 0);
- send_continue_event (0);
- }
-#endif
- } else {
- /* Varispeed not supported */
- }
+ _send_pos = true;
}
void
MidiClockTicker::session_going_away ()
{
SessionHandlePtr::session_going_away();
- _midi_port = 0;
+ _midi_port.reset ();
}
void
MidiClockTicker::update_midi_clock_port()
{
- _midi_port = MIDI::Manager::instance()->midi_clock_output_port();
+ _midi_port = _session->midi_clock_output_port();
}
void
@@ -204,49 +181,12 @@ MidiClockTicker::transport_state_changed()
return;
}
- if (_pos->speed == 1.0f) {
-
- if (_session->get_play_loop()) {
- assert(_session->locations()->auto_loop_location());
-
- if (_pos->frame == _session->locations()->auto_loop_location()->start()) {
- send_start_event(0);
- } else {
- send_continue_event(0);
- }
-
- } else if (_pos->frame == 0) {
- send_start_event(0);
- } else {
- send_continue_event(0);
- }
-
- // send_midi_clock_event (0);
-
- } else if (_pos->speed == 0.0f) {
- send_stop_event (0);
- send_position_event (llrint (_pos->midi_beats), 0);
- }
+ _send_state = true;
// tick (_pos->frame);
}
void
-MidiClockTicker::position_changed (framepos_t)
-{
-#if 0
- const double speed = _session->transport_speed();
- DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Transport Position Change: %1, speed: %2\n", position, speed));
-
- if (speed == 0.0f && Config->get_send_midi_clock()) {
- send_position_event (position, 0);
- }
-
- _last_tick = position;
-#endif
-}
-
-void
MidiClockTicker::transport_looped()
{
Location* loop_location = _session->locations()->auto_loop_location();
@@ -270,18 +210,68 @@ MidiClockTicker::transport_looped()
}
void
-MidiClockTicker::tick (const framepos_t& /* transport_frame */)
+MidiClockTicker::tick (const framepos_t& /* transport_frame */, pframes_t nframes)
{
if (!Config->get_send_midi_clock() || _session == 0 || _session->transport_speed() != 1.0f || _midi_port == 0) {
return;
}
- MIDI::JackMIDIPort* mp = dynamic_cast<MIDI::JackMIDIPort*> (_midi_port);
- if (! mp) {
- return;
+ if (_send_pos) {
+ if (_pos->speed == 0.0f) {
+ uint32_t where = llrint (_pos->midi_beats);
+ send_position_event (where, 0, nframes);
+ } else if (_pos->speed == 1.0f) {
+#if 1
+ /* Experimental. To really do this and have accuracy, the
+ stop/locate/continue sequence would need queued to send immediately
+ before the next midi clock. */
+
+ send_stop_event (0, nframes);
+
+ if (_pos->frame == 0) {
+ send_start_event (0, nframes);
+ } else {
+ uint32_t where = llrint (_pos->midi_beats);
+ send_position_event (where, 0, nframes);
+ send_continue_event (0, nframes);
+ }
+#endif
+ } else {
+ /* Varispeed not supported */
+ }
+
+ _send_pos = true;
}
- const framepos_t end = _pos->frame + mp->nframes_this_cycle();
+ if (_send_state) {
+ if (_pos->speed == 1.0f) {
+ if (_session->get_play_loop()) {
+ assert(_session->locations()->auto_loop_location());
+
+ if (_pos->frame == _session->locations()->auto_loop_location()->start()) {
+ send_start_event (0, nframes);
+ } else {
+ send_continue_event (0, nframes);
+ }
+
+ } else if (_pos->frame == 0) {
+ send_start_event (0, nframes);
+ } else {
+ send_continue_event (0, nframes);
+ }
+
+ // send_midi_clock_event (0);
+
+ } else if (_pos->speed == 0.0f) {
+ send_stop_event (0, nframes);
+ send_position_event (llrint (_pos->midi_beats), 0, nframes);
+ }
+
+ _send_state = false;
+ }
+
+
+ const framepos_t end = _pos->frame + nframes;
double iter = _last_tick;
while (true) {
@@ -291,14 +281,14 @@ MidiClockTicker::tick (const framepos_t& /* transport_frame */)
DEBUG_TRACE (DEBUG::MidiClock,
string_compose ("Tick: iter: %1, last tick time: %2, next tick time: %3, offset: %4, cycle length: %5\n",
- iter, _last_tick, next_tick, next_tick_offset, mp ? mp->nframes_this_cycle() : 0));
+ iter, _last_tick, next_tick, next_tick_offset, nframes));
- if (!mp || (next_tick_offset >= mp->nframes_this_cycle())) {
+ if (next_tick_offset >= nframes) {
break;
}
if (next_tick_offset >= 0) {
- send_midi_clock_event (next_tick_offset);
+ send_midi_clock_event (next_tick_offset, nframes);
}
iter = next_tick;
@@ -308,7 +298,6 @@ MidiClockTicker::tick (const framepos_t& /* transport_frame */)
_pos->frame = end;
}
-
double
MidiClockTicker::one_ppqn_in_frames (framepos_t transport_position)
{
@@ -322,7 +311,7 @@ MidiClockTicker::one_ppqn_in_frames (framepos_t transport_position)
}
void
-MidiClockTicker::send_midi_clock_event (pframes_t offset)
+MidiClockTicker::send_midi_clock_event (pframes_t offset, pframes_t nframes)
{
if (!_midi_port) {
return;
@@ -330,12 +319,13 @@ MidiClockTicker::send_midi_clock_event (pframes_t offset)
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Tick with offset %1\n", offset));
- static uint8_t _midi_clock_tick[1] = { MIDI_CMD_COMMON_CLOCK };
- _midi_port->write (_midi_clock_tick, 1, offset);
+ static uint8_t tick_byte = { MIDI_CMD_COMMON_CLOCK };
+ MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
+ mb.push_back (offset, 1, &tick_byte);
}
void
-MidiClockTicker::send_start_event (pframes_t offset)
+MidiClockTicker::send_start_event (pframes_t offset, pframes_t nframes)
{
if (!_midi_port) {
return;
@@ -343,12 +333,13 @@ MidiClockTicker::send_start_event (pframes_t offset)
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Start %1\n", _last_tick));
- static uint8_t _midi_clock_tick[1] = { MIDI_CMD_COMMON_START };
- _midi_port->write (_midi_clock_tick, 1, offset);
+ static uint8_t tick_byte = { MIDI_CMD_COMMON_START };
+ MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
+ mb.push_back (offset, 1, &tick_byte);
}
void
-MidiClockTicker::send_continue_event (pframes_t offset)
+MidiClockTicker::send_continue_event (pframes_t offset, pframes_t nframes)
{
if (!_midi_port) {
return;
@@ -356,12 +347,13 @@ MidiClockTicker::send_continue_event (pframes_t offset)
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Continue %1\n", _last_tick));
- static uint8_t _midi_clock_tick[1] = { MIDI_CMD_COMMON_CONTINUE };
- _midi_port->write (_midi_clock_tick, 1, offset);
+ static uint8_t tick_byte = { MIDI_CMD_COMMON_CONTINUE };
+ MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
+ mb.push_back (offset, 1, &tick_byte);
}
void
-MidiClockTicker::send_stop_event (pframes_t offset)
+MidiClockTicker::send_stop_event (pframes_t offset, pframes_t nframes)
{
if (!_midi_port) {
return;
@@ -369,12 +361,13 @@ MidiClockTicker::send_stop_event (pframes_t offset)
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Stop %1\n", _last_tick));
- static uint8_t _midi_clock_tick[1] = { MIDI_CMD_COMMON_STOP };
- _midi_port->write (_midi_clock_tick, 1, offset);
+ static uint8_t tick_byte = { MIDI_CMD_COMMON_STOP };
+ MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
+ mb.push_back (offset, 1, &tick_byte);
}
void
-MidiClockTicker::send_position_event (uint32_t midi_beats, pframes_t offset)
+MidiClockTicker::send_position_event (uint32_t midi_beats, pframes_t offset, pframes_t nframes)
{
if (!_midi_port) {
return;
@@ -391,7 +384,8 @@ MidiClockTicker::send_position_event (uint32_t midi_beats, pframes_t offset)
msg[1] = midi_beats & 0x007f;
msg[2] = midi_beats >> 7;
- _midi_port->midimsg (msg, sizeof (msg), offset);
+ MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
+ mb.push_back (offset, 3, &msg[0]);
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Song Position Sent: %1\n", midi_beats));
}
diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc
index f02863393e..57239cb841 100644
--- a/libs/ardour/track.cc
+++ b/libs/ardour/track.cc
@@ -550,15 +550,15 @@ Track::playlist ()
}
void
-Track::request_jack_monitors_input (bool m)
+Track::request_input_monitoring (bool m)
{
- _diskstream->request_jack_monitors_input (m);
+ _diskstream->request_input_monitoring (m);
}
void
-Track::ensure_jack_monitors_input (bool m)
+Track::ensure_input_monitoring (bool m)
{
- _diskstream->ensure_jack_monitors_input (m);
+ _diskstream->ensure_input_monitoring (m);
}
bool
diff --git a/libs/ardour/wscript b/libs/ardour/wscript
index 87dce06a2a..485a2284bc 100644
--- a/libs/ardour/wscript
+++ b/libs/ardour/wscript
@@ -1,10 +1,11 @@
-#!/usr/bin/env python
+1#!/usr/bin/env python
from waflib.extras import autowaf as autowaf
from waflib import Options
import os
import sys
import re
import subprocess
+import sys
# default state file version for this build
CURRENT_SESSION_FILE_VERSION = 3001
@@ -22,6 +23,7 @@ path_prefix = 'libs/ardour/'
libardour_sources = [
'amp.cc',
'analyser.cc',
+ 'async_midi_port.cc',
'audio_buffer.cc',
'audio_diskstream.cc',
'audio_library.cc',
@@ -44,6 +46,7 @@ libardour_sources = [
'automation_control.cc',
'automation_list.cc',
'automation_watch.cc',
+ 'backend_search_path.cc',
'beats_frames_converter.cc',
'broadcast_info.cc',
'buffer.cc',
@@ -128,6 +131,7 @@ libardour_sources = [
'midi_stretch.cc',
'midi_track.cc',
'midi_ui.cc',
+ 'midiport_manager.cc',
'mix.cc',
'monitor_processor.cc',
'mtc_slave.cc',
@@ -151,6 +155,7 @@ libardour_sources = [
'plugin_manager.cc',
'port.cc',
'port_insert.cc',
+ 'port_manager.cc',
'port_set.cc',
'process_thread.cc',
'processor.cc',
@@ -178,6 +183,7 @@ libardour_sources = [
'session_events.cc',
'session_export.cc',
'session_handle.cc',
+ 'session_jack.cc',
'session_ltc.cc',
'session_metadata.cc',
'session_midi.cc',
@@ -259,6 +265,10 @@ def configure(conf):
atleast_version='0.1.0')
autowaf.check_pkg(conf, 'sigc++-2.0', uselib_store='SIGCPP',
atleast_version='2.0')
+
+ if re.search ("linux", sys.platform) != None:
+ autowaf.check_pkg(conf, 'alsa', uselib_store='ALSA')
+
if Options.options.lv2:
autowaf.check_pkg(conf, 'lv2', uselib_store='LV2',
atleast_version='1.0.0', mandatory=True)
@@ -303,60 +313,6 @@ def configure(conf):
conf.check(header_name='unistd.h', define_name='HAVE_UNISTD',mandatory=False)
- conf.check_cc(fragment = '''
-#include <jack/jack.h>
-void callback(jack_status_t code, const char* reason, void* arg) { return; }
-int main(int argc, char **argv) {
- jack_client_t* c;
- jack_on_info_shutdown(c, callback, (void*) 0);
- return 0;
-}''',
- uselib= [ 'JACK' ],
- msg = 'Checking for jack_on_info_shutdown',
- define_name = 'HAVE_JACK_ON_INFO_SHUTDOWN',
- okmsg = 'present')
-
- missing_jack_message = 'missing - a version of JACK that supports jack_port_set_latency_range() is required to compile Ardour3.'
-
- conf.check_cc(fragment = '''
-#include <jack/jack.h>
-int main(int argc, char **argv) {
- jack_port_t* p;
- jack_latency_range_t r;
- jack_port_set_latency_range(p, JackCaptureLatency, &r);
- return 0;
-}''',
- uselib = [ 'JACK' ],
- msg = 'Checking for new JACK latency API',
- okmsg = 'present',
- mandatory = True,
- errmsg = missing_jack_message)
-
- conf.check_cc(fragment = '''
-#include <jack/jack.h>
-int main(int argc, char **argv) {
- jack_port_type_get_buffer_size((jack_client_t*)0, "");
- return 0;
-}''',
- uselib = [ 'JACK' ],
- msg = 'Checking for new jack_port_type_get_buffer_size',
- okmsg = 'present',
- mandatory = True,
- errmsg = missing_jack_message)
-
- conf.check_cc(fragment = '''
-#include <jack/transport.h>
-int main(int argc, char** argv) {
- jack_position_t pos;
- pos.valid & JackVideoFrameOffset;
- return 0;
-}''',
- uselib= [ 'JACK' ],
- msg = 'Checking for JackVideoFrameOffset',
- define_name = 'HAVE_JACK_VIDEO_SUPPORT',
- mandatory = False,
- okmsg = 'present')
-
if flac_supported():
conf.define ('HAVE_FLAC', 1)
if ogg_supported():
diff --git a/libs/backends/jack/jack_api.cc b/libs/backends/jack/jack_api.cc
new file mode 100644
index 0000000000..0136161181
--- /dev/null
+++ b/libs/backends/jack/jack_api.cc
@@ -0,0 +1,102 @@
+/*
+ Copyright (C) 2013 Paul Davis
+
+ 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 "jack_connection.h"
+#include "jack_audiobackend.h"
+#include "jack_portengine.h"
+
+using namespace ARDOUR;
+
+static boost::shared_ptr<JACKAudioBackend> backend;
+static boost::shared_ptr<JACKPortEngine> port_engine;
+static boost::shared_ptr<JackConnection> jack_connection;
+
+static boost::shared_ptr<AudioBackend>
+backend_factory (AudioEngine& ae)
+{
+ if (!jack_connection) {
+ return boost::shared_ptr<AudioBackend>();
+ }
+
+ if (!backend) {
+ backend.reset (new JACKAudioBackend (ae, jack_connection));
+ }
+
+ return backend;
+}
+
+static boost::shared_ptr<PortEngine>
+portengine_factory (PortManager& pm)
+{
+ if (!jack_connection) {
+ return boost::shared_ptr<PortEngine>();
+ }
+
+ if (!port_engine) {
+ port_engine.reset (new JACKPortEngine (pm, jack_connection));
+ }
+
+ return port_engine;
+}
+
+static int
+instantiate (const std::string& arg1, const std::string& arg2)
+{
+ try {
+ jack_connection.reset (new JackConnection (arg1, arg2));
+ } catch (...) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+deinstantiate ()
+{
+ port_engine.reset ();
+ backend.reset ();
+ jack_connection.reset ();
+
+ return 0;
+}
+
+static bool
+already_configured ()
+{
+ return JackConnection::server_running ();
+}
+
+extern "C" {
+
+
+ /* functions looked up using dlopen-and-cousins, and so naming scope
+ * must be non-mangled.
+ */
+
+ ARDOUR::AudioBackendInfo descriptor = {
+ "JACK",
+ instantiate,
+ deinstantiate,
+ backend_factory,
+ portengine_factory,
+ already_configured,
+ };
+}
+
diff --git a/libs/backends/jack/jack_audiobackend.cc b/libs/backends/jack/jack_audiobackend.cc
new file mode 100644
index 0000000000..e004849896
--- /dev/null
+++ b/libs/backends/jack/jack_audiobackend.cc
@@ -0,0 +1,1002 @@
+/*
+ Copyright (C) 2013 Paul Davis
+
+ 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 <string>
+#include <list>
+#include <math.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <glibmm/timer.h>
+#include <glibmm/spawn.h>
+
+#include "pbd/error.h"
+
+#include "jack/jack.h"
+#include "jack/thread.h"
+
+#include "ardour/audioengine.h"
+#include "ardour/session.h"
+#include "ardour/types.h"
+
+#include "jack_audiobackend.h"
+#include "jack_connection.h"
+#include "jack_portengine.h"
+#include "jack_utils.h"
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+using std::string;
+using std::vector;
+using std::cerr;
+using std::endl;
+
+#define GET_PRIVATE_JACK_POINTER(localvar) jack_client_t* localvar = _jack_connection->jack(); if (!(localvar)) { return; }
+#define GET_PRIVATE_JACK_POINTER_RET(localvar,r) jack_client_t* localvar = _jack_connection->jack(); if (!(localvar)) { return r; }
+
+JACKAudioBackend::JACKAudioBackend (AudioEngine& e, boost::shared_ptr<JackConnection> jc)
+ : AudioBackend (e)
+ , _jack_connection (jc)
+ , _running (false)
+ , _freewheeling (false)
+ , _target_sample_rate (48000)
+ , _target_buffer_size (1024)
+ , _target_sample_format (FormatFloat)
+ , _target_interleaved (false)
+ , _target_input_channels (-1)
+ , _target_output_channels (-1)
+ , _target_systemic_input_latency (0)
+ , _target_systemic_output_latency (0)
+ , _current_sample_rate (0)
+ , _current_buffer_size (0)
+{
+ _jack_connection->Disconnected.connect_same_thread (disconnect_connection, boost::bind (&JACKAudioBackend::disconnected, this, _1));
+}
+
+JACKAudioBackend::~JACKAudioBackend()
+{
+}
+
+string
+JACKAudioBackend::name() const
+{
+ return X_("JACK");
+}
+
+void*
+JACKAudioBackend::private_handle() const
+{
+ return _jack_connection->jack();
+}
+
+bool
+JACKAudioBackend::connected() const
+{
+ return (private_handle() != 0);
+}
+
+bool
+JACKAudioBackend::is_realtime () const
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack,false);
+ return jack_is_realtime (_priv_jack);
+}
+
+bool
+JACKAudioBackend::requires_driver_selection() const
+{
+ return true;
+}
+
+vector<string>
+JACKAudioBackend::enumerate_drivers () const
+{
+ vector<string> currently_available;
+ get_jack_audio_driver_names (currently_available);
+ return currently_available;
+}
+
+int
+JACKAudioBackend::set_driver (const std::string& name)
+{
+ _target_driver = name;
+ return 0;
+}
+
+vector<AudioBackend::DeviceStatus>
+JACKAudioBackend::enumerate_devices () const
+{
+ vector<string> currently_available = get_jack_device_names_for_audio_driver (_target_driver);
+ vector<DeviceStatus> statuses;
+
+ if (all_devices.find (_target_driver) == all_devices.end()) {
+ all_devices.insert (make_pair (_target_driver, std::set<string>()));
+ }
+
+ /* store every device we've found, by driver name.
+ *
+ * This is so we do not confuse ALSA, FFADO, netjack etc. devices
+ * with each other.
+ */
+
+ DeviceList& all (all_devices[_target_driver]);
+
+ for (vector<string>::const_iterator d = currently_available.begin(); d != currently_available.end(); ++d) {
+ all.insert (*d);
+ }
+
+ for (DeviceList::const_iterator d = all.begin(); d != all.end(); ++d) {
+ if (find (currently_available.begin(), currently_available.end(), *d) == currently_available.end()) {
+ statuses.push_back (DeviceStatus (*d, false));
+ } else {
+ statuses.push_back (DeviceStatus (*d, false));
+ }
+ }
+
+ return statuses;
+}
+
+vector<float>
+JACKAudioBackend::available_sample_rates (const string& /*device*/) const
+{
+ vector<float> f;
+
+ if (connected()) {
+ f.push_back (sample_rate());
+ return f;
+ }
+
+ /* if JACK is not already running, just list a bunch of reasonable
+ values and let the future sort it all out.
+ */
+
+ f.push_back (8000.0);
+ f.push_back (16000.0);
+ f.push_back (24000.0);
+ f.push_back (32000.0);
+ f.push_back (44100.0);
+ f.push_back (48000.0);
+ f.push_back (88200.0);
+ f.push_back (96000.0);
+ f.push_back (192000.0);
+ f.push_back (384000.0);
+
+ return f;
+}
+
+vector<uint32_t>
+JACKAudioBackend::available_buffer_sizes (const string& /*device*/) const
+{
+ vector<uint32_t> s;
+
+ if (connected()) {
+ s.push_back (buffer_size());
+ return s;
+ }
+
+ s.push_back (8);
+ s.push_back (16);
+ s.push_back (32);
+ s.push_back (64);
+ s.push_back (128);
+ s.push_back (256);
+ s.push_back (512);
+ s.push_back (1024);
+ s.push_back (2048);
+ s.push_back (4096);
+ s.push_back (8192);
+
+ return s;
+}
+
+uint32_t
+JACKAudioBackend::available_input_channel_count (const string& /*device*/) const
+{
+ return 128;
+}
+
+uint32_t
+JACKAudioBackend::available_output_channel_count (const string& /*device*/) const
+{
+ return 128;
+}
+
+/* -- parameter setting -- */
+
+int
+JACKAudioBackend::set_device_name (const string& dev)
+{
+ if (connected()) {
+ /* need to stop and restart JACK for this to work, at present */
+ return -1;
+ }
+
+ _target_device = dev;
+ return 0;
+}
+
+int
+JACKAudioBackend::set_sample_rate (float sr)
+{
+ if (!connected()) {
+ _target_sample_rate = sr;
+ return 0;
+ }
+
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+
+ if (sr == jack_get_sample_rate (_priv_jack)) {
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+JACKAudioBackend::set_buffer_size (uint32_t nframes)
+{
+ if (!connected()) {
+ _target_buffer_size = nframes;
+ return 0;
+ }
+
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+
+ if (nframes == jack_get_buffer_size (_priv_jack)) {
+ return 0;
+ }
+
+ return jack_set_buffer_size (_priv_jack, nframes);
+}
+
+int
+JACKAudioBackend::set_sample_format (SampleFormat sf)
+{
+ /* as far as JACK clients are concerned, the hardware is always
+ * floating point format.
+ */
+ if (sf == FormatFloat) {
+ return 0;
+ }
+ return -1;
+}
+
+int
+JACKAudioBackend::set_interleaved (bool yn)
+{
+ /* as far as JACK clients are concerned, the hardware is always
+ * non-interleaved
+ */
+ if (!yn) {
+ return 0;
+ }
+ return -1;
+}
+
+int
+JACKAudioBackend::set_input_channels (uint32_t cnt)
+{
+ if (connected()) {
+ return -1;
+ }
+
+ _target_input_channels = cnt;
+
+ return 0;
+}
+
+int
+JACKAudioBackend::set_output_channels (uint32_t cnt)
+{
+ if (connected()) {
+ return -1;
+ }
+
+ _target_output_channels = cnt;
+
+ return 0;
+}
+
+int
+JACKAudioBackend::set_systemic_input_latency (uint32_t l)
+{
+ if (connected()) {
+ return -1;
+ }
+
+ _target_systemic_input_latency = l;
+
+ return 0;
+}
+
+int
+JACKAudioBackend::set_systemic_output_latency (uint32_t l)
+{
+ if (connected()) {
+ return -1;
+ }
+
+ _target_systemic_output_latency = l;
+
+ return 0;
+}
+
+/* --- Parameter retrieval --- */
+
+std::string
+JACKAudioBackend::device_name () const
+{
+ if (connected()) {
+ return "???";
+ }
+
+ return _target_device;
+}
+
+float
+JACKAudioBackend::sample_rate () const
+{
+ if (connected()) {
+ return _current_sample_rate;
+ }
+ return _target_sample_rate;
+}
+
+uint32_t
+JACKAudioBackend::buffer_size () const
+{
+ if (connected()) {
+ return _current_buffer_size;
+ }
+ return _target_buffer_size;
+}
+
+SampleFormat
+JACKAudioBackend::sample_format () const
+{
+ return FormatFloat;
+}
+
+bool
+JACKAudioBackend::interleaved () const
+{
+ return false;
+}
+
+uint32_t
+JACKAudioBackend::input_channels () const
+{
+ if (connected()) {
+ return n_physical (JackPortIsInput).n_audio();
+ }
+ return _target_input_channels;
+}
+
+uint32_t
+JACKAudioBackend::output_channels () const
+{
+ if (connected()) {
+ return n_physical (JackPortIsOutput).n_audio();
+ }
+ return _target_output_channels;
+}
+
+uint32_t
+JACKAudioBackend::systemic_input_latency () const
+{
+ return _target_systemic_output_latency;
+}
+
+uint32_t
+JACKAudioBackend::systemic_output_latency () const
+{
+ return _target_systemic_output_latency;
+}
+
+size_t
+JACKAudioBackend::raw_buffer_size(DataType t)
+{
+ std::map<DataType,size_t>::const_iterator s = _raw_buffer_sizes.find(t);
+ return (s != _raw_buffer_sizes.end()) ? s->second : 0;
+}
+
+void
+JACKAudioBackend::setup_jack_startup_command ()
+{
+ /* first we map the parameters that have been set onto a
+ * JackCommandLineOptions object.
+ */
+
+ JackCommandLineOptions options;
+
+ get_jack_default_server_path (options.server_path);
+ options.driver = _target_driver;
+ options.samplerate = _target_sample_rate;
+ options.period_size = _target_buffer_size;
+ options.num_periods = 2;
+ options.input_device = _target_device;
+ options.output_device = _target_device;
+ options.input_latency = _target_systemic_input_latency;
+ options.output_latency = _target_systemic_output_latency;
+ options.input_channels = _target_input_channels;
+ options.output_channels = _target_output_channels;
+ if (_target_sample_format == FormatInt16) {
+ options.force16_bit = _target_sample_format;
+ }
+ options.realtime = true;
+ options.ports_max = 2048;
+
+ /* this must always be true for any server instance we start ourselves
+ */
+
+ options.temporary = true;
+
+ string cmdline;
+
+ if (!get_jack_command_line_string (options, cmdline)) {
+ /* error, somehow */
+ return;
+ }
+
+ std::cerr << "JACK command line will be: " << cmdline << std::endl;
+
+ write_jack_config_file (get_jack_server_user_config_file_path(), cmdline);
+}
+
+/* ---- BASIC STATE CONTROL API: start/stop/pause/freewheel --- */
+
+int
+JACKAudioBackend::start ()
+{
+ if (!connected()) {
+
+ if (!_jack_connection->server_running()) {
+ setup_jack_startup_command ();
+ }
+
+ if (_jack_connection->open ()) {
+ return -1;
+ }
+ }
+
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+
+ /* get the buffer size and sample rates established */
+
+ jack_sample_rate_callback (jack_get_sample_rate (_priv_jack));
+ jack_bufsize_callback (jack_get_buffer_size (_priv_jack));
+
+ /* Now that we have buffer size and sample rate established, the engine
+ can go ahead and do its stuff
+ */
+
+ engine.reestablish_ports ();
+
+ if (!jack_port_type_get_buffer_size) {
+ warning << _("This version of JACK is old - you should upgrade to a newer version that supports jack_port_type_get_buffer_size()") << endmsg;
+ }
+
+ set_jack_callbacks ();
+
+ if (jack_activate (_priv_jack) == 0) {
+ _running = true;
+ } else {
+ // error << _("cannot activate JACK client") << endmsg;
+ }
+
+ engine.reconnect_ports ();
+
+ return 0;
+}
+
+int
+JACKAudioBackend::stop ()
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+
+ _jack_connection->close ();
+
+ _current_buffer_size = 0;
+ _current_sample_rate = 0;
+
+ _raw_buffer_sizes.clear();
+
+ return 0;
+}
+
+int
+JACKAudioBackend::pause ()
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+
+ if (_priv_jack) {
+ jack_deactivate (_priv_jack);
+ }
+
+ return 0;
+}
+
+int
+JACKAudioBackend::freewheel (bool onoff)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+
+ if (onoff == _freewheeling) {
+ /* already doing what has been asked for */
+
+ return 0;
+ }
+
+ if (jack_set_freewheel (_priv_jack, onoff) == 0) {
+ _freewheeling = true;
+ return 0;
+ }
+
+ return -1;
+}
+
+/* --- TRANSPORT STATE MANAGEMENT --- */
+
+void
+JACKAudioBackend::transport_stop ()
+{
+ GET_PRIVATE_JACK_POINTER (_priv_jack);
+ jack_transport_stop (_priv_jack);
+}
+
+void
+JACKAudioBackend::transport_start ()
+{
+ GET_PRIVATE_JACK_POINTER (_priv_jack);
+ jack_transport_start (_priv_jack);
+}
+
+void
+JACKAudioBackend::transport_locate (framepos_t where)
+{
+ GET_PRIVATE_JACK_POINTER (_priv_jack);
+ jack_transport_locate (_priv_jack, where);
+}
+
+framepos_t
+JACKAudioBackend::transport_frame () const
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ return jack_get_current_transport_frame (_priv_jack);
+}
+
+TransportState
+JACKAudioBackend::transport_state () const
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, ((TransportState) JackTransportStopped));
+ jack_position_t pos;
+ return (TransportState) jack_transport_query (_priv_jack, &pos);
+}
+
+int
+JACKAudioBackend::set_time_master (bool yn)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+ if (yn) {
+ return jack_set_timebase_callback (_priv_jack, 0, _jack_timebase_callback, this);
+ } else {
+ return jack_release_timebase (_priv_jack);
+ }
+}
+
+/* process-time */
+
+bool
+JACKAudioBackend::get_sync_offset (pframes_t& offset) const
+{
+
+#ifdef HAVE_JACK_VIDEO_SUPPORT
+
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, false);
+
+ jack_position_t pos;
+
+ if (_priv_jack) {
+ (void) jack_transport_query (_priv_jack, &pos);
+
+ if (pos.valid & JackVideoFrameOffset) {
+ offset = pos.video_offset;
+ return true;
+ }
+ }
+#else
+ /* keep gcc happy */
+ offset = 0;
+#endif
+
+ return false;
+}
+
+pframes_t
+JACKAudioBackend::sample_time ()
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ return jack_frame_time (_priv_jack);
+}
+
+pframes_t
+JACKAudioBackend::sample_time_at_cycle_start ()
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ return jack_last_frame_time (_priv_jack);
+}
+
+pframes_t
+JACKAudioBackend::samples_since_cycle_start ()
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ return jack_frames_since_cycle_start (_priv_jack);
+}
+
+/* JACK Callbacks */
+
+static void
+ardour_jack_error (const char* msg)
+{
+ error << "JACK: " << msg << endmsg;
+}
+
+void
+JACKAudioBackend::set_jack_callbacks ()
+{
+ GET_PRIVATE_JACK_POINTER (_priv_jack);
+
+ jack_set_thread_init_callback (_priv_jack, AudioEngine::thread_init_callback, 0);
+
+ jack_set_process_thread (_priv_jack, _process_thread, this);
+ jack_set_sample_rate_callback (_priv_jack, _sample_rate_callback, this);
+ jack_set_buffer_size_callback (_priv_jack, _bufsize_callback, this);
+ jack_set_xrun_callback (_priv_jack, _xrun_callback, this);
+ jack_set_sync_callback (_priv_jack, _jack_sync_callback, this);
+ jack_set_freewheel_callback (_priv_jack, _freewheel_callback, this);
+
+#ifdef HAVE_JACK_SESSION
+ if( jack_set_session_callback)
+ jack_set_session_callback (_priv_jack, _session_callback, this);
+#endif
+
+ if (jack_set_latency_callback) {
+ jack_set_latency_callback (_priv_jack, _latency_callback, this);
+ }
+
+ jack_set_error_function (ardour_jack_error);
+}
+
+void
+JACKAudioBackend::_jack_timebase_callback (jack_transport_state_t state, pframes_t nframes,
+ jack_position_t* pos, int new_position, void *arg)
+{
+ static_cast<JACKAudioBackend*> (arg)->jack_timebase_callback (state, nframes, pos, new_position);
+}
+
+void
+JACKAudioBackend::jack_timebase_callback (jack_transport_state_t state, pframes_t nframes,
+ jack_position_t* pos, int new_position)
+{
+ ARDOUR::Session* session = engine.session();
+
+ if (session) {
+ session->jack_timebase_callback (state, nframes, pos, new_position);
+ }
+}
+
+int
+JACKAudioBackend::_jack_sync_callback (jack_transport_state_t state, jack_position_t* pos, void* arg)
+{
+ return static_cast<JACKAudioBackend*> (arg)->jack_sync_callback (state, pos);
+}
+
+int
+JACKAudioBackend::jack_sync_callback (jack_transport_state_t state, jack_position_t* pos)
+{
+ TransportState tstate;
+
+ switch (state) {
+ case JackTransportStopped:
+ tstate = TransportStopped;
+ break;
+ case JackTransportRolling:
+ tstate = TransportRolling;
+ break;
+ case JackTransportLooping:
+ tstate = TransportLooping;
+ break;
+ case JackTransportStarting:
+ tstate = TransportStarting;
+ break;
+ }
+
+ return engine.sync_callback (tstate, pos->frame);
+
+ return true;
+}
+
+int
+JACKAudioBackend::_xrun_callback (void *arg)
+{
+ JACKAudioBackend* ae = static_cast<JACKAudioBackend*> (arg);
+ if (ae->connected()) {
+ ae->engine.Xrun (); /* EMIT SIGNAL */
+ }
+ return 0;
+}
+
+#ifdef HAVE_JACK_SESSION
+void
+JACKAudioBackend::_session_callback (jack_session_event_t *event, void *arg)
+{
+ JACKAudioBackend* ae = static_cast<JACKAudioBackend*> (arg);
+ ARDOUR::Session* session = ae->engine.session();
+
+ if (session) {
+ session->jack_session_event (event);
+ }
+}
+#endif
+
+void
+JACKAudioBackend::_freewheel_callback (int onoff, void *arg)
+{
+ static_cast<JACKAudioBackend*>(arg)->freewheel_callback (onoff);
+}
+
+void
+JACKAudioBackend::freewheel_callback (int onoff)
+{
+ _freewheeling = onoff;
+ engine.freewheel_callback (onoff);
+}
+
+void
+JACKAudioBackend::_latency_callback (jack_latency_callback_mode_t mode, void* arg)
+{
+ return static_cast<JACKAudioBackend*> (arg)->jack_latency_callback (mode);
+}
+
+int
+JACKAudioBackend::create_process_thread (boost::function<void()> f, pthread_t* thread, size_t stacksize)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ ThreadData* td = new ThreadData (this, f, stacksize);
+
+ if (jack_client_create_thread (_priv_jack, thread, jack_client_real_time_priority (_priv_jack),
+ jack_is_realtime (_priv_jack), _start_process_thread, td)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+void*
+JACKAudioBackend::_start_process_thread (void* arg)
+{
+ ThreadData* td = reinterpret_cast<ThreadData*> (arg);
+ boost::function<void()> f = td->f;
+ delete td;
+
+ f ();
+
+ return 0;
+}
+
+void*
+JACKAudioBackend::_process_thread (void *arg)
+{
+ return static_cast<JACKAudioBackend*> (arg)->process_thread ();
+}
+
+void*
+JACKAudioBackend::process_thread ()
+{
+ /* JACK doesn't do this for us when we use the wait API
+ */
+
+ AudioEngine::thread_init_callback (this);
+
+ while (1) {
+ GET_PRIVATE_JACK_POINTER_RET(_priv_jack,0);
+
+ pframes_t nframes = jack_cycle_wait (_priv_jack);
+
+ if (engine.process_callback (nframes)) {
+ return 0;
+ }
+
+ jack_cycle_signal (_priv_jack, 0);
+ }
+
+ return 0;
+}
+
+int
+JACKAudioBackend::_sample_rate_callback (pframes_t nframes, void *arg)
+{
+ return static_cast<JACKAudioBackend*> (arg)->jack_sample_rate_callback (nframes);
+}
+
+int
+JACKAudioBackend::jack_sample_rate_callback (pframes_t nframes)
+{
+ _current_sample_rate = nframes;
+ return engine.sample_rate_change (nframes);
+}
+
+void
+JACKAudioBackend::jack_latency_callback (jack_latency_callback_mode_t mode)
+{
+ engine.latency_callback (mode == JackPlaybackLatency);
+}
+
+int
+JACKAudioBackend::_bufsize_callback (pframes_t nframes, void *arg)
+{
+ return static_cast<JACKAudioBackend*> (arg)->jack_bufsize_callback (nframes);
+}
+
+int
+JACKAudioBackend::jack_bufsize_callback (pframes_t nframes)
+{
+ /* if the size has not changed, this should be a no-op */
+
+ if (nframes == _current_buffer_size) {
+ return 0;
+ }
+
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 1);
+
+ _current_buffer_size = nframes;
+
+ if (jack_port_type_get_buffer_size) {
+ _raw_buffer_sizes[DataType::AUDIO] = jack_port_type_get_buffer_size (_priv_jack, JACK_DEFAULT_AUDIO_TYPE);
+ _raw_buffer_sizes[DataType::MIDI] = jack_port_type_get_buffer_size (_priv_jack, JACK_DEFAULT_MIDI_TYPE);
+ } else {
+
+ /* Old version of JACK.
+
+ These crude guesses, see below where we try to get the right answers.
+
+ Note that our guess for MIDI deliberatey tries to overestimate
+ by a little. It would be nicer if we could get the actual
+ size from a port, but we have to use this estimate in the
+ event that there are no MIDI ports currently. If there are
+ the value will be adjusted below.
+ */
+
+ _raw_buffer_sizes[DataType::AUDIO] = nframes * sizeof (Sample);
+ _raw_buffer_sizes[DataType::MIDI] = nframes * 4 - (nframes/2);
+ }
+
+ engine.buffer_size_change (nframes);
+
+ return 0;
+}
+
+void
+JACKAudioBackend::disconnected (const char* why)
+{
+ bool was_running = _running;
+
+ _running = false;
+ _current_buffer_size = 0;
+ _current_sample_rate = 0;
+
+ if (was_running) {
+ engine.halted_callback (why); /* EMIT SIGNAL */
+ }
+}
+float
+JACKAudioBackend::cpu_load() const
+{
+ GET_PRIVATE_JACK_POINTER_RET(_priv_jack,0);
+ return jack_cpu_load (_priv_jack);
+}
+
+void
+JACKAudioBackend::update_latencies ()
+{
+ GET_PRIVATE_JACK_POINTER (_priv_jack);
+ jack_recompute_total_latencies (_priv_jack);
+}
+
+ChanCount
+JACKAudioBackend::n_physical (unsigned long flags) const
+{
+ ChanCount c;
+
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, c);
+
+ const char ** ports = jack_get_ports (_priv_jack, NULL, NULL, JackPortIsPhysical | flags);
+
+ if (ports) {
+ for (uint32_t i = 0; ports[i]; ++i) {
+ if (!strstr (ports[i], "Midi-Through")) {
+ DataType t (jack_port_type (jack_port_by_name (_priv_jack, ports[i])));
+ c.set (t, c.get (t) + 1);
+ }
+ }
+
+ jack_free (ports);
+ }
+
+ return c;
+}
+
+bool
+JACKAudioBackend::can_change_sample_rate_when_running () const
+{
+ return false;
+}
+
+bool
+JACKAudioBackend::can_change_buffer_size_when_running () const
+{
+ return true;
+}
+
+string
+JACKAudioBackend::control_app_name () const
+{
+ /* Since JACK/ALSA really don't provide particularly integrated support
+ for the idea of a control app to be used to control a device,
+ allow the user to take some control themselves if necessary.
+ */
+
+ const char* env_value = g_getenv ("ARDOUR_DEVICE_CONTROL_APP");
+ string appname;
+
+ if (!env_value) {
+ if (_target_driver.empty() || _target_device.empty()) {
+ return appname;
+ }
+
+ if (_target_driver == "ALSA") {
+
+ if (_target_device == "Hammerfall DSP") {
+ appname = "hdspconf";
+ } else if (_target_device == "M Audio Delta 1010") {
+ appname = "mudita24";
+ }
+ }
+ } else {
+ appname = env_value;
+ }
+
+ return appname;
+}
+
+void
+JACKAudioBackend::launch_control_app ()
+{
+ string appname = control_app_name();
+
+ if (appname.empty()) {
+ error << string_compose (_("There is no control application for the device \"%1\""), _target_device) << endmsg;
+ return;
+ }
+
+ std::list<string> args;
+ args.push_back (appname);
+ Glib::spawn_async ("", args, Glib::SPAWN_SEARCH_PATH);
+}
diff --git a/libs/backends/jack/jack_audiobackend.h b/libs/backends/jack/jack_audiobackend.h
new file mode 100644
index 0000000000..1389e15c4a
--- /dev/null
+++ b/libs/backends/jack/jack_audiobackend.h
@@ -0,0 +1,190 @@
+/*
+ Copyright (C) 2013 Paul Davis
+
+ 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.
+
+*/
+
+#ifndef __libardour_jack_audiobackend_h__
+#define __libardour_jack_audiobackend_h__
+
+#include <string>
+#include <vector>
+#include <map>
+#include <set>
+
+#include <stdint.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <jack/jack.h>
+#ifdef HAVE_JACK_SESSION
+#include <jack/session.h>
+#endif
+
+#include "ardour/audio_backend.h"
+
+namespace ARDOUR {
+
+class JackConnection;
+
+class JACKAudioBackend : public AudioBackend {
+ public:
+ JACKAudioBackend (AudioEngine& e, boost::shared_ptr<JackConnection>);
+ ~JACKAudioBackend ();
+
+ std::string name() const;
+ void* private_handle() const;
+ bool connected() const;
+ bool is_realtime () const;
+
+ bool requires_driver_selection() const;
+ std::vector<std::string> enumerate_drivers () const;
+ int set_driver (const std::string&);
+
+ std::vector<DeviceStatus> enumerate_devices () const;
+
+ std::vector<float> available_sample_rates (const std::string& device) const;
+ std::vector<uint32_t> available_buffer_sizes (const std::string& device) const;
+ uint32_t available_input_channel_count (const std::string& device) const;
+ uint32_t available_output_channel_count (const std::string& device) const;
+
+ bool can_change_sample_rate_when_running() const;
+ bool can_change_buffer_size_when_running() const;
+
+ int set_device_name (const std::string&);
+ int set_sample_rate (float);
+ int set_buffer_size (uint32_t);
+ int set_sample_format (SampleFormat);
+ int set_interleaved (bool yn);
+ int set_input_channels (uint32_t);
+ int set_output_channels (uint32_t);
+ int set_systemic_input_latency (uint32_t);
+ int set_systemic_output_latency (uint32_t);
+
+ std::string device_name () const;
+ float sample_rate () const;
+ uint32_t buffer_size () const;
+ SampleFormat sample_format () const;
+ bool interleaved () const;
+ uint32_t input_channels () const;
+ uint32_t output_channels () const;
+ uint32_t systemic_input_latency () const;
+ uint32_t systemic_output_latency () const;
+
+ std::string control_app_name () const;
+ void launch_control_app ();
+
+ int start ();
+ int stop ();
+ int pause ();
+ int freewheel (bool);
+
+ float cpu_load() const;
+
+ pframes_t sample_time ();
+ pframes_t sample_time_at_cycle_start ();
+ pframes_t samples_since_cycle_start ();
+
+ size_t raw_buffer_size (DataType t);
+
+ int create_process_thread (boost::function<void()> func, pthread_t*, size_t stacksize);
+
+ void transport_start ();
+ void transport_stop ();
+ void transport_locate (framepos_t /*pos*/);
+ TransportState transport_state () const;
+ framepos_t transport_frame() const;
+
+ int set_time_master (bool /*yn*/);
+ bool get_sync_offset (pframes_t& /*offset*/) const;
+
+ void update_latencies ();
+
+ static bool already_configured();
+
+ private:
+ boost::shared_ptr<JackConnection> _jack_connection; //< shared with JACKPortEngine
+ bool _running;
+ bool _freewheeling;
+ std::map<DataType,size_t> _raw_buffer_sizes;
+
+ static int _xrun_callback (void *arg);
+ static void* _process_thread (void *arg);
+ static int _sample_rate_callback (pframes_t nframes, void *arg);
+ static int _bufsize_callback (pframes_t nframes, void *arg);
+ static void _jack_timebase_callback (jack_transport_state_t, pframes_t, jack_position_t*, int, void*);
+ static int _jack_sync_callback (jack_transport_state_t, jack_position_t*, void *arg);
+ static void _freewheel_callback (int , void *arg);
+ static void _latency_callback (jack_latency_callback_mode_t, void*);
+#ifdef HAVE_JACK_SESSION
+ static void _session_callback (jack_session_event_t *event, void *arg);
+#endif
+
+ void jack_timebase_callback (jack_transport_state_t, pframes_t, jack_position_t*, int);
+ int jack_sync_callback (jack_transport_state_t, jack_position_t*);
+ int jack_bufsize_callback (pframes_t);
+ int jack_sample_rate_callback (pframes_t);
+ void freewheel_callback (int);
+ int process_callback (pframes_t nframes);
+ void jack_latency_callback (jack_latency_callback_mode_t);
+ void disconnected (const char*);
+
+ void set_jack_callbacks ();
+ int reconnect_to_jack ();
+
+ struct ThreadData {
+ JACKAudioBackend* engine;
+ boost::function<void()> f;
+ size_t stacksize;
+
+ ThreadData (JACKAudioBackend* e, boost::function<void()> fp, size_t stacksz)
+ : engine (e) , f (fp) , stacksize (stacksz) {}
+ };
+
+ void* process_thread ();
+ static void* _start_process_thread (void*);
+
+ ChanCount n_physical (unsigned long) const;
+
+ void setup_jack_startup_command ();
+
+ /* pffooo */
+
+ std::string _target_driver;
+ std::string _target_device;
+ float _target_sample_rate;
+ uint32_t _target_buffer_size;
+ SampleFormat _target_sample_format;
+ bool _target_interleaved;
+ uint32_t _target_input_channels;
+ uint32_t _target_output_channels;
+ uint32_t _target_systemic_input_latency;
+ uint32_t _target_systemic_output_latency;
+ uint32_t _current_sample_rate;
+ uint32_t _current_buffer_size;
+
+ typedef std::set<std::string> DeviceList;
+ typedef std::map<std::string,DeviceList> DriverDeviceMap;
+
+ mutable DriverDeviceMap all_devices;
+
+ PBD::ScopedConnection disconnect_connection;
+};
+
+} // namespace
+
+#endif /* __ardour_audiobackend_h__ */
+
diff --git a/libs/backends/jack/jack_connection.cc b/libs/backends/jack/jack_connection.cc
new file mode 100644
index 0000000000..da0127c584
--- /dev/null
+++ b/libs/backends/jack/jack_connection.cc
@@ -0,0 +1,164 @@
+/*
+ Copyright (C) 2013 Paul Davis
+
+ 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 <boost/scoped_ptr.hpp>
+#include <jack/session.h>
+
+#include "pbd/epa.h"
+
+#include "jack_connection.h"
+#include "jack_utils.h"
+
+#define GET_PRIVATE_JACK_POINTER(j) jack_client_t* _priv_jack = (jack_client_t*) (j); if (!_priv_jack) { return; }
+#define GET_PRIVATE_JACK_POINTER_RET(j,r) jack_client_t* _priv_jack = (jack_client_t*) (j); if (!_priv_jack) { return r; }
+
+using namespace ARDOUR;
+using namespace PBD;
+using std::string;
+using std::vector;
+
+static void jack_halted_callback (void* arg)
+{
+ JackConnection* jc = static_cast<JackConnection*> (arg);
+ jc->halted_callback ();
+}
+
+static void jack_halted_info_callback (jack_status_t code, const char* reason, void* arg)
+{
+ JackConnection* jc = static_cast<JackConnection*> (arg);
+ jc->halted_info_callback (code, reason);
+}
+
+
+JackConnection::JackConnection (const std::string& arg1, const std::string& arg2)
+ : _jack (0)
+ , _client_name (arg1)
+ , session_uuid (arg2)
+{
+}
+
+JackConnection::~JackConnection ()
+{
+ close ();
+}
+
+bool
+JackConnection::server_running ()
+{
+ EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa ();
+ boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa;
+
+ /* revert all environment settings back to whatever they were when
+ * ardour started, because ardour's startup script may have reset
+ * something in ways that interfere with finding/starting JACK.
+ */
+
+ if (global_epa) {
+ current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */
+ global_epa->restore ();
+ }
+
+ jack_status_t status;
+ jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
+
+ if (status == 0) {
+ jack_client_close (c);
+ return true;
+ }
+
+ return false;
+}
+
+int
+JackConnection::open ()
+{
+ EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa ();
+ boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa;
+ jack_status_t status;
+
+ close ();
+
+ /* revert all environment settings back to whatever they were when ardour started
+ */
+
+ if (global_epa) {
+ current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */
+ global_epa->restore ();
+ }
+
+ /* ensure that PATH or equivalent includes likely locations of the JACK
+ * server, in case the user's default does not.
+ */
+
+ vector<string> dirs;
+ get_jack_server_dir_paths (dirs);
+ set_path_env_for_jack_autostart (dirs);
+
+ if ((_jack = jack_client_open (_client_name.c_str(), JackSessionID, &status, session_uuid.c_str())) == 0) {
+ return -1;
+ }
+
+ if (status & JackNameNotUnique) {
+ _client_name = jack_get_client_name (_jack);
+ }
+
+ /* attach halted handler */
+
+ if (jack_on_info_shutdown) {
+ jack_on_info_shutdown (_jack, jack_halted_info_callback, this);
+ } else {
+ jack_on_shutdown (_jack, jack_halted_callback, this);
+ }
+
+
+ Connected(); /* EMIT SIGNAL */
+
+ return 0;
+}
+
+int
+JackConnection::close ()
+{
+ GET_PRIVATE_JACK_POINTER_RET (_jack, -1);
+
+ if (_priv_jack) {
+ int ret = jack_client_close (_priv_jack);
+ _jack = 0;
+ Disconnected (""); /* EMIT SIGNAL */
+ return ret;
+ }
+
+ return 0;
+}
+
+void
+JackConnection::halted_callback ()
+{
+ _jack = 0;
+ Disconnected ("");
+}
+
+void
+JackConnection::halted_info_callback (jack_status_t /*status*/, const char* reason)
+{
+ _jack = 0;
+ Disconnected (reason);
+}
+
+
diff --git a/libs/backends/jack/jack_connection.h b/libs/backends/jack/jack_connection.h
new file mode 100644
index 0000000000..cd45f3b9ba
--- /dev/null
+++ b/libs/backends/jack/jack_connection.h
@@ -0,0 +1,40 @@
+#ifndef __libardour_jack_connection_h__
+#define __libardour_jack_connection_h__
+
+#include <string>
+#include <jack/jack.h>
+
+#include "pbd/signals.h"
+
+namespace ARDOUR {
+
+class JackConnection {
+ public:
+ JackConnection (const std::string& client_name, const std::string& session_uuid);
+ ~JackConnection ();
+
+ const std::string& client_name() const { return _client_name; }
+
+ int open ();
+ int close ();
+ bool connected () const { return _jack != 0; }
+
+ jack_client_t* jack() const { return _jack; }
+
+ PBD::Signal0<void> Connected;
+ PBD::Signal1<void,const char*> Disconnected;
+
+ void halted_callback ();
+ void halted_info_callback (jack_status_t, const char*);
+
+ static bool server_running();
+
+ private:
+ jack_client_t* volatile _jack;
+ std::string _client_name;
+ std::string session_uuid;
+};
+
+} // namespace
+
+#endif /* __libardour_jack_connection_h__ */
diff --git a/libs/backends/jack/jack_portengine.cc b/libs/backends/jack/jack_portengine.cc
new file mode 100644
index 0000000000..bd352a2f11
--- /dev/null
+++ b/libs/backends/jack/jack_portengine.cc
@@ -0,0 +1,569 @@
+/*
+ Copyright (C) 2013 Paul Davis
+
+ 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 <string.h>
+#include <stdint.h>
+
+#include "pbd/error.h"
+
+#include "jack_portengine.h"
+#include "jack_connection.h"
+
+#include "ardour/port_manager.h"
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+using std::string;
+using std::vector;
+
+#define GET_PRIVATE_JACK_POINTER(localvar) jack_client_t* localvar = _jack_connection->jack(); if (!(localvar)) { return; }
+#define GET_PRIVATE_JACK_POINTER_RET(localvar,r) jack_client_t* localvar = _jack_connection->jack(); if (!(localvar)) { return r; }
+
+static uint32_t
+ardour_port_flags_to_jack_flags (PortFlags flags)
+{
+ uint32_t jack_flags = 0;
+
+ if (flags & IsInput) {
+ jack_flags |= JackPortIsInput;
+ }
+ if (flags & IsOutput) {
+ jack_flags |= JackPortIsOutput;
+ }
+ if (flags & IsTerminal) {
+ jack_flags |= JackPortIsTerminal;
+ }
+ if (flags & IsPhysical) {
+ jack_flags |= JackPortIsPhysical;
+ }
+ if (flags & CanMonitor) {
+ jack_flags |= JackPortCanMonitor;
+ }
+
+ return jack_flags;
+}
+
+static DataType
+jack_port_type_to_ardour_data_type (const char* jack_type)
+{
+ if (strcmp (jack_type, JACK_DEFAULT_AUDIO_TYPE) == 0) {
+ return DataType::AUDIO;
+ } else if (strcmp (jack_type, JACK_DEFAULT_MIDI_TYPE) == 0) {
+ return DataType::MIDI;
+ }
+ return DataType::NIL;
+}
+
+static const char*
+ardour_data_type_to_jack_port_type (DataType d)
+{
+ switch (d) {
+ case DataType::AUDIO:
+ return JACK_DEFAULT_AUDIO_TYPE;
+ case DataType::MIDI:
+ return JACK_DEFAULT_MIDI_TYPE;
+ }
+
+ return "";
+}
+
+JACKPortEngine::JACKPortEngine (PortManager& pm, boost::shared_ptr<JackConnection> jc)
+ : PortEngine (pm)
+ , _jack_connection (jc)
+{
+ _jack_connection->Connected.connect_same_thread (jack_connection_connection, boost::bind (&JACKPortEngine::connected_to_jack, this));
+}
+
+JACKPortEngine::~JACKPortEngine ()
+{
+ /* a default destructor would do this, and so would this one,
+ but we'll make it explicit in case we ever need to debug
+ the lifetime of the JACKConnection
+ */
+ _jack_connection.reset ();
+}
+
+void
+JACKPortEngine::connected_to_jack ()
+{
+ /* register callbacks for stuff that is our responsibility */
+
+ jack_client_t* client = _jack_connection->jack();
+
+ if (!client) {
+ /* how could this happen? it could ... */
+ error << _("Already disconnected from JACK before PortEngine could register callbacks") << endmsg;
+ return;
+ }
+
+ jack_set_port_registration_callback (client, _registration_callback, this);
+ jack_set_port_connect_callback (client, _connect_callback, this);
+ jack_set_graph_order_callback (client, _graph_order_callback, this);
+}
+
+void*
+JACKPortEngine::private_handle() const
+{
+ return _jack_connection->jack();
+}
+
+bool
+JACKPortEngine::connected() const
+{
+ return _jack_connection->connected();
+}
+
+int
+JACKPortEngine::set_port_name (PortHandle port, const std::string& name)
+{
+ return jack_port_set_name ((jack_port_t*) port, name.c_str());
+}
+
+string
+JACKPortEngine::get_port_name (PortHandle port) const
+{
+ return jack_port_name ((jack_port_t*) port);
+}
+
+PortEngine::PortHandle*
+JACKPortEngine:: get_port_by_name (const std::string& name) const
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ return (PortHandle*) jack_port_by_name (_priv_jack, name.c_str());
+}
+
+void
+JACKPortEngine::_registration_callback (jack_port_id_t /*id*/, int /*reg*/, void* arg)
+{
+ static_cast<JACKPortEngine*> (arg)->manager.registration_callback ();
+}
+
+int
+JACKPortEngine::_graph_order_callback (void *arg)
+{
+ return static_cast<JACKPortEngine*> (arg)->manager.graph_order_callback ();
+}
+
+void
+JACKPortEngine::_connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn, void* arg)
+{
+ static_cast<JACKPortEngine*> (arg)->connect_callback (id_a, id_b, conn);
+}
+
+void
+JACKPortEngine::connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn)
+{
+ if (manager.port_remove_in_progress()) {
+ return;
+ }
+
+ GET_PRIVATE_JACK_POINTER (_priv_jack);
+
+ jack_port_t* a = jack_port_by_id (_priv_jack, id_a);
+ jack_port_t* b = jack_port_by_id (_priv_jack, id_b);
+
+ manager.connect_callback (jack_port_name (a), jack_port_name (b), conn == 0 ? false : true);
+}
+
+bool
+JACKPortEngine::connected (PortHandle port, bool process_callback_safe)
+{
+ bool ret = false;
+
+ const char** ports;
+
+ if (process_callback_safe) {
+ ports = jack_port_get_connections ((jack_port_t*)port);
+ } else {
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, false);
+ ports = jack_port_get_all_connections (_priv_jack, (jack_port_t*)port);
+ }
+
+ if (ports) {
+ ret = true;
+ }
+
+ jack_free (ports);
+
+ return ret;
+}
+
+bool
+JACKPortEngine::connected_to (PortHandle port, const std::string& other, bool process_callback_safe)
+{
+ bool ret = false;
+ const char** ports;
+
+ if (process_callback_safe) {
+ ports = jack_port_get_connections ((jack_port_t*)port);
+ } else {
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, false);
+ ports = jack_port_get_all_connections (_priv_jack, (jack_port_t*)port);
+ }
+
+ if (ports) {
+ for (int i = 0; ports[i]; ++i) {
+ if (other == ports[i]) {
+ ret = true;
+ }
+ }
+ jack_free (ports);
+ }
+
+ return ret;
+}
+
+bool
+JACKPortEngine::physically_connected (PortHandle p, bool process_callback_safe)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, false);
+ jack_port_t* port = (jack_port_t*) p;
+
+ const char** ports;
+
+ if (process_callback_safe) {
+ ports = jack_port_get_connections ((jack_port_t*)port);
+ } else {
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, false);
+ ports = jack_port_get_all_connections (_priv_jack, (jack_port_t*)port);
+ }
+
+ if (ports) {
+ for (int i = 0; ports[i]; ++i) {
+
+ jack_port_t* other = jack_port_by_name (_priv_jack, ports[i]);
+
+ if (other && (jack_port_flags (other) & JackPortIsPhysical)) {
+ return true;
+ }
+ }
+ jack_free (ports);
+ }
+
+ return false;
+}
+
+int
+JACKPortEngine::get_connections (PortHandle port, vector<string>& s, bool process_callback_safe)
+{
+ const char** ports;
+
+ if (process_callback_safe) {
+ ports = jack_port_get_connections ((jack_port_t*)port);
+ } else {
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ ports = jack_port_get_all_connections (_priv_jack, (jack_port_t*)port);
+ }
+
+ if (ports) {
+ for (int i = 0; ports[i]; ++i) {
+ s.push_back (ports[i]);
+ }
+ jack_free (ports);
+ }
+
+ return s.size();
+}
+
+DataType
+JACKPortEngine::port_data_type (PortHandle p) const
+{
+ return jack_port_type_to_ardour_data_type (jack_port_type ((jack_port_t*) p));
+}
+
+const string&
+JACKPortEngine::my_name() const
+{
+ return _jack_connection->client_name();
+}
+
+bool
+JACKPortEngine::port_is_physical (PortHandle ph) const
+{
+ if (!ph) {
+ return false;
+ }
+
+ return jack_port_flags ((jack_port_t*) ph) & JackPortIsPhysical;
+}
+
+int
+JACKPortEngine::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector<string>& s) const
+{
+
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack,0);
+
+ const char** ports = jack_get_ports (_priv_jack, port_name_pattern.c_str(),
+ ardour_data_type_to_jack_port_type (type),
+ ardour_port_flags_to_jack_flags (flags));
+
+ if (ports == 0) {
+ return 0;
+ }
+
+ for (uint32_t i = 0; ports[i]; ++i) {
+ s.push_back (ports[i]);
+ }
+
+ jack_free (ports);
+
+ return s.size();
+}
+
+ChanCount
+JACKPortEngine::n_physical (unsigned long flags) const
+{
+ ChanCount c;
+
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, c);
+
+ const char ** ports = jack_get_ports (_priv_jack, NULL, NULL, JackPortIsPhysical | flags);
+
+ if (ports) {
+ for (uint32_t i = 0; ports[i]; ++i) {
+ if (!strstr (ports[i], "Midi-Through")) {
+ DataType t = port_data_type (jack_port_by_name (_priv_jack, ports[i]));
+ if (t != DataType::NIL) {
+ c.set (t, c.get (t) + 1);
+ }
+ }
+ }
+
+ jack_free (ports);
+ }
+
+ return c;
+}
+
+ChanCount
+JACKPortEngine::n_physical_inputs () const
+{
+ return n_physical (JackPortIsInput);
+}
+
+ChanCount
+JACKPortEngine::n_physical_outputs () const
+{
+ return n_physical (JackPortIsOutput);
+}
+
+void
+JACKPortEngine::get_physical (DataType type, unsigned long flags, vector<string>& phy) const
+{
+ GET_PRIVATE_JACK_POINTER (_priv_jack);
+ const char ** ports;
+
+ if ((ports = jack_get_ports (_priv_jack, NULL, ardour_data_type_to_jack_port_type (type), JackPortIsPhysical | flags)) == 0) {
+ return;
+ }
+
+ if (ports) {
+ for (uint32_t i = 0; ports[i]; ++i) {
+ if (strstr (ports[i], "Midi-Through")) {
+ continue;
+ }
+ phy.push_back (ports[i]);
+ }
+ jack_free (ports);
+ }
+}
+
+/** Get physical ports for which JackPortIsOutput is set; ie those that correspond to
+ * a physical input connector.
+ */
+void
+JACKPortEngine::get_physical_inputs (DataType type, vector<string>& ins)
+{
+ get_physical (type, JackPortIsOutput, ins);
+}
+
+/** Get physical ports for which JackPortIsInput is set; ie those that correspond to
+ * a physical output connector.
+ */
+void
+JACKPortEngine::get_physical_outputs (DataType type, vector<string>& outs)
+{
+ get_physical (type, JackPortIsInput, outs);
+}
+
+
+bool
+JACKPortEngine::can_monitor_input () const
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack,false);
+ const char ** ports;
+
+ if ((ports = jack_get_ports (_priv_jack, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortCanMonitor)) == 0) {
+ return false;
+ }
+
+ jack_free (ports);
+
+ return true;
+}
+
+int
+JACKPortEngine::request_input_monitoring (PortHandle port, bool yn)
+{
+ return jack_port_request_monitor ((jack_port_t*) port, yn);
+}
+int
+JACKPortEngine::ensure_input_monitoring (PortHandle port, bool yn)
+{
+ return jack_port_ensure_monitor ((jack_port_t*) port, yn);
+}
+bool
+JACKPortEngine::monitoring_input (PortHandle port)
+{
+ return jack_port_monitoring_input ((jack_port_t*) port);
+}
+
+
+pframes_t
+JACKPortEngine::sample_time_at_cycle_start ()
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ return jack_last_frame_time (_priv_jack);
+}
+
+
+PortEngine::PortHandle
+JACKPortEngine::register_port (const std::string& shortname, ARDOUR::DataType type, ARDOUR::PortFlags flags)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ return jack_port_register (_priv_jack, shortname.c_str(),
+ ardour_data_type_to_jack_port_type (type),
+ ardour_port_flags_to_jack_flags (flags),
+ 0);
+}
+
+void
+JACKPortEngine::unregister_port (PortHandle port)
+{
+ GET_PRIVATE_JACK_POINTER (_priv_jack);
+ (void) jack_port_unregister (_priv_jack, (jack_port_t*) port);
+}
+
+int
+JACKPortEngine::connect (PortHandle port, const std::string& other)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+ return jack_connect (_priv_jack, jack_port_name ((jack_port_t*) port), other.c_str());
+}
+int
+JACKPortEngine::connect (const std::string& src, const std::string& dst)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+
+ int r = jack_connect (_priv_jack, src.c_str(), dst.c_str());
+ return r;
+}
+
+int
+JACKPortEngine::disconnect (PortHandle port, const std::string& other)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+ return jack_disconnect (_priv_jack, jack_port_name ((jack_port_t*) port), other.c_str());
+}
+
+int
+JACKPortEngine::disconnect (const std::string& src, const std::string& dst)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+ return jack_disconnect (_priv_jack, src.c_str(), dst.c_str());
+}
+
+int
+JACKPortEngine::disconnect_all (PortHandle port)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+ return jack_port_disconnect (_priv_jack, (jack_port_t*) port);
+}
+
+int
+JACKPortEngine::midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** buf, void* port_buffer, uint32_t event_index)
+{
+ jack_midi_event_t ev;
+ int ret;
+
+ if ((ret = jack_midi_event_get (&ev, port_buffer, event_index)) == 0) {
+ timestamp = ev.time;
+ size = ev.size;
+ *buf = ev.buffer;
+ }
+
+ return ret;
+}
+
+int
+JACKPortEngine::midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size)
+{
+ return jack_midi_event_write (port_buffer, timestamp, buffer, size);
+}
+
+uint32_t
+JACKPortEngine::get_midi_event_count (void* port_buffer)
+{
+ return jack_midi_get_event_count (port_buffer);
+}
+
+void
+JACKPortEngine::midi_clear (void* port_buffer)
+{
+ jack_midi_clear_buffer (port_buffer);
+}
+
+void
+JACKPortEngine::set_latency_range (PortHandle port, bool for_playback, LatencyRange r)
+{
+ jack_latency_range_t range;
+
+ range.min = r.min;
+ range.max = r.max;
+
+ jack_port_set_latency_range ((jack_port_t*) port, for_playback ? JackPlaybackLatency : JackCaptureLatency, &range);
+}
+
+LatencyRange
+JACKPortEngine::get_latency_range (PortHandle port, bool for_playback)
+{
+ jack_latency_range_t range;
+ LatencyRange ret;
+
+ jack_port_get_latency_range ((jack_port_t*) port, for_playback ? JackPlaybackLatency : JackCaptureLatency, &range);
+
+ ret.min = range.min;
+ ret.max = range.max;
+
+ return ret;
+}
+
+void*
+JACKPortEngine::get_buffer (PortHandle port, pframes_t nframes)
+{
+ return jack_port_get_buffer ((jack_port_t*) port, nframes);
+}
+
+uint32_t
+JACKPortEngine::port_name_size() const
+{
+ return jack_port_name_size ();
+}
diff --git a/libs/backends/jack/jack_portengine.h b/libs/backends/jack/jack_portengine.h
new file mode 100644
index 0000000000..0e1eb48c5c
--- /dev/null
+++ b/libs/backends/jack/jack_portengine.h
@@ -0,0 +1,132 @@
+/*
+ Copyright (C) 2013 Paul Davis
+
+ 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.
+
+*/
+
+#ifndef __libardour_jack_portengine_h__
+#define __libardour_jack_portengine_h__
+
+#include <string>
+#include <vector>
+
+#include <stdint.h>
+
+#include <jack/types.h>
+#include <jack/midiport.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include "pbd/signals.h"
+
+#include "ardour/port_engine.h"
+#include "ardour/types.h"
+
+namespace ARDOUR {
+
+class JackConnection;
+
+class JACKPortEngine : public PortEngine
+{
+ public:
+ JACKPortEngine (PortManager&, boost::shared_ptr<JackConnection>);
+ ~JACKPortEngine();
+
+ void* private_handle() const;
+ bool connected() const;
+
+ const std::string& my_name() const;
+
+ uint32_t port_name_size() const;
+
+ int set_port_name (PortHandle, const std::string&);
+ std::string get_port_name (PortHandle) const;
+ PortHandle* get_port_by_name (const std::string&) const;
+
+ int get_ports (const std::string& port_name_pattern, DataType type, PortFlags flags, std::vector<std::string>&) const;
+
+ DataType port_data_type (PortHandle) const;
+
+ PortHandle register_port (const std::string& shortname, ARDOUR::DataType, ARDOUR::PortFlags);
+ void unregister_port (PortHandle);
+
+ bool connected (PortHandle, bool process_callback_safe);
+ bool connected_to (PortHandle, const std::string&, bool process_callback_safe);
+ bool physically_connected (PortHandle, bool process_callback_safe);
+ int get_connections (PortHandle, std::vector<std::string>&, bool process_callback_safe);
+ int connect (PortHandle, const std::string&);
+
+ int disconnect (PortHandle, const std::string&);
+ int disconnect_all (PortHandle);
+ int connect (const std::string& src, const std::string& dst);
+ int disconnect (const std::string& src, const std::string& dst);
+
+ /* MIDI */
+
+ int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** buf, void* port_buffer, uint32_t event_index);
+ int midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size);
+ uint32_t get_midi_event_count (void* port_buffer);
+ void midi_clear (void* port_buffer);
+
+ /* Monitoring */
+
+ bool can_monitor_input() const;
+ int request_input_monitoring (PortHandle, bool);
+ int ensure_input_monitoring (PortHandle, bool);
+ bool monitoring_input (PortHandle);
+
+ /* Latency management
+ */
+
+ void set_latency_range (PortHandle, bool for_playback, LatencyRange);
+ LatencyRange get_latency_range (PortHandle, bool for_playback);
+
+ /* Physical ports */
+
+ bool port_is_physical (PortHandle) const;
+ void get_physical_outputs (DataType type, std::vector<std::string>&);
+ void get_physical_inputs (DataType type, std::vector<std::string>&);
+ ChanCount n_physical_outputs () const;
+ ChanCount n_physical_inputs () const;
+
+ /* Getting access to the data buffer for a port */
+
+ void* get_buffer (PortHandle, pframes_t);
+
+ /* Miscellany */
+
+ pframes_t sample_time_at_cycle_start ();
+
+ private:
+ boost::shared_ptr<JackConnection> _jack_connection;
+
+ static int _graph_order_callback (void *arg);
+ static void _registration_callback (jack_port_id_t, int, void *);
+ static void _connect_callback (jack_port_id_t, jack_port_id_t, int, void *);
+
+ void connect_callback (jack_port_id_t, jack_port_id_t, int);
+
+ ChanCount n_physical (unsigned long flags) const;
+ void get_physical (DataType type, unsigned long flags, std::vector<std::string>& phy) const;
+
+ PBD::ScopedConnection jack_connection_connection;
+ void connected_to_jack ();
+
+};
+
+} // namespace
+
+#endif /* __libardour_jack_portengine_h__ */
diff --git a/libs/backends/jack/jack_utils.cc b/libs/backends/jack/jack_utils.cc
new file mode 100644
index 0000000000..77f3d95aa1
--- /dev/null
+++ b/libs/backends/jack/jack_utils.cc
@@ -0,0 +1,897 @@
+/*
+ Copyright (C) 2010 Paul Davis
+ Copyright (C) 2011 Tim Mayberry
+
+ 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.
+
+*/
+
+#ifdef HAVE_ALSA
+#include <alsa/asoundlib.h>
+#endif
+
+#ifdef __APPLE__
+#include <CoreAudio/CoreAudio.h>
+#include <CoreFoundation/CFString.h>
+#include <sys/param.h>
+#include <mach-o/dyld.h>
+#endif
+
+#ifdef HAVE_PORTAUDIO
+#include <portaudio.h>
+#endif
+
+#include <jack/jack.h>
+
+#include <fstream>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <glibmm/miscutils.h>
+
+#include "pbd/epa.h"
+#include "pbd/error.h"
+#include "pbd/convert.h"
+#include "pbd/file_utils.h"
+#include "pbd/search_path.h"
+
+#include "jack_utils.h"
+
+#ifdef __APPLE
+#include <CFBundle.h>
+#endif
+
+#include "i18n.h"
+
+using namespace std;
+using namespace PBD;
+
+namespace ARDOUR {
+ // The pretty driver names
+ const char * const portaudio_driver_name = X_("Portaudio");
+ const char * const coreaudio_driver_name = X_("CoreAudio");
+ const char * const alsa_driver_name = X_("ALSA");
+ const char * const oss_driver_name = X_("OSS");
+ const char * const freebob_driver_name = X_("FreeBoB");
+ const char * const ffado_driver_name = X_("FFADO");
+ const char * const netjack_driver_name = X_("NetJACK");
+ const char * const dummy_driver_name = X_("Dummy");
+}
+
+namespace {
+
+ // The real driver names
+ const char * const portaudio_driver_command_line_name = X_("portaudio");
+ const char * const coreaudio_driver_command_line_name = X_("coreaudio");
+ const char * const alsa_driver_command_line_name = X_("alsa");
+ const char * const oss_driver_command_line_name = X_("oss");
+ const char * const freebob_driver_command_line_name = X_("freebob");
+ const char * const ffado_driver_command_line_name = X_("firewire");
+ const char * const netjack_driver_command_line_name = X_("netjack");
+ const char * const dummy_driver_command_line_name = X_("dummy");
+
+ // should we provide more "pretty" names like above?
+ const char * const alsaseq_midi_driver_name = X_("seq");
+ const char * const alsaraw_midi_driver_name = X_("raw");
+ const char * const winmme_midi_driver_name = X_("winmme");
+ const char * const coremidi_midi_driver_name = X_("coremidi");
+
+ // this should probably be translated
+ const char * const default_device_name = X_("Default");
+}
+
+std::string
+get_none_string ()
+{
+ return _("None");
+}
+
+void
+ARDOUR::get_jack_audio_driver_names (vector<string>& audio_driver_names)
+{
+#ifdef WIN32
+ audio_driver_names.push_back (portaudio_driver_name);
+#elif __APPLE__
+ audio_driver_names.push_back (coreaudio_driver_name);
+#else
+#ifdef HAVE_ALSA
+ audio_driver_names.push_back (alsa_driver_name);
+#endif
+ audio_driver_names.push_back (oss_driver_name);
+ audio_driver_names.push_back (freebob_driver_name);
+ audio_driver_names.push_back (ffado_driver_name);
+#endif
+ audio_driver_names.push_back (netjack_driver_name);
+ audio_driver_names.push_back (dummy_driver_name);
+}
+
+void
+ARDOUR::get_jack_default_audio_driver_name (string& audio_driver_name)
+{
+ vector<string> drivers;
+ get_jack_audio_driver_names (drivers);
+ audio_driver_name = drivers.front ();
+}
+
+void
+ARDOUR::get_jack_sample_rate_strings (vector<string>& samplerates)
+{
+ // do these really need to be translated?
+ samplerates.push_back (_("8000Hz"));
+ samplerates.push_back (_("22050Hz"));
+ samplerates.push_back (_("44100Hz"));
+ samplerates.push_back (_("48000Hz"));
+ samplerates.push_back (_("88200Hz"));
+ samplerates.push_back (_("96000Hz"));
+ samplerates.push_back (_("192000Hz"));
+}
+
+string
+ARDOUR::get_jack_default_sample_rate ()
+{
+ return _("48000Hz");
+}
+
+void
+ARDOUR::get_jack_period_size_strings (std::vector<std::string>& period_sizes)
+{
+ period_sizes.push_back ("32");
+ period_sizes.push_back ("64");
+ period_sizes.push_back ("128");
+ period_sizes.push_back ("256");
+ period_sizes.push_back ("512");
+ period_sizes.push_back ("1024");
+ period_sizes.push_back ("2048");
+ period_sizes.push_back ("4096");
+ period_sizes.push_back ("8192");
+}
+
+string
+ARDOUR::get_jack_default_period_size ()
+{
+ return "1024";
+}
+
+void
+ARDOUR::get_jack_dither_mode_strings (const string& driver, vector<string>& dither_modes)
+{
+ dither_modes.push_back (get_none_string ());
+
+ if (driver == alsa_driver_name ) {
+ dither_modes.push_back (_("Triangular"));
+ dither_modes.push_back (_("Rectangular"));
+ dither_modes.push_back (_("Shaped"));
+ }
+}
+
+string
+ARDOUR::get_jack_default_dither_mode (const string& /*driver*/)
+{
+ return get_none_string ();
+}
+
+string
+ARDOUR::get_jack_latency_string (string samplerate, float periods, string period_size)
+{
+ uint32_t rate = atoi (samplerate);
+ float psize = atof (period_size);
+
+ char buf[32];
+ snprintf (buf, sizeof(buf), "%.1fmsec", (periods * psize) / (rate/1000.0));
+
+ return buf;
+}
+
+bool
+get_jack_command_line_audio_driver_name (const string& driver_name, string& command_line_name)
+{
+ using namespace ARDOUR;
+ if (driver_name == portaudio_driver_name) {
+ command_line_name = portaudio_driver_command_line_name;
+ return true;
+ } else if (driver_name == coreaudio_driver_name) {
+ command_line_name = coreaudio_driver_command_line_name;
+ return true;
+ } else if (driver_name == alsa_driver_name) {
+ command_line_name = alsa_driver_command_line_name;
+ return true;
+ } else if (driver_name == oss_driver_name) {
+ command_line_name = oss_driver_command_line_name;
+ return true;
+ } else if (driver_name == freebob_driver_name) {
+ command_line_name = freebob_driver_command_line_name;
+ return true;
+ } else if (driver_name == ffado_driver_name) {
+ command_line_name = ffado_driver_command_line_name;
+ return true;
+ } else if (driver_name == netjack_driver_name) {
+ command_line_name = netjack_driver_command_line_name;
+ return true;
+ } else if (driver_name == dummy_driver_name) {
+ command_line_name = dummy_driver_command_line_name;
+ return true;
+ }
+ return false;
+}
+
+bool
+get_jack_command_line_audio_device_name (const string& driver_name,
+ const string& device_name, string& command_line_device_name)
+{
+ using namespace ARDOUR;
+ device_map_t devices;
+
+ get_jack_device_names_for_audio_driver (driver_name, devices);
+
+ for (device_map_t::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+ if (i->first == device_name) {
+ command_line_device_name = i->second;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+get_jack_command_line_dither_mode (const string& dither_mode, string& command_line_dither_mode)
+{
+ using namespace ARDOUR;
+
+ if (dither_mode == _("Triangular")) {
+ command_line_dither_mode = "triangular";
+ return true;
+ } else if (dither_mode == _("Rectangular")) {
+ command_line_dither_mode = "rectangular";
+ return true;
+ } else if (dither_mode == _("Shaped")) {
+ command_line_dither_mode = "shaped";
+ return true;
+ }
+
+ return false;
+}
+
+void
+ARDOUR::get_jack_alsa_device_names (device_map_t& devices)
+{
+#ifdef HAVE_ALSA
+ snd_ctl_t *handle;
+ snd_ctl_card_info_t *info;
+ snd_pcm_info_t *pcminfo;
+ snd_ctl_card_info_alloca(&info);
+ snd_pcm_info_alloca(&pcminfo);
+ string devname;
+ int cardnum = -1;
+ int device = -1;
+
+ while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
+
+ devname = "hw:";
+ devname += PBD::to_string (cardnum, std::dec);
+
+ if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
+
+ if (snd_ctl_card_info (handle, info) < 0) {
+ continue;
+ }
+
+ string card_name = snd_ctl_card_info_get_name (info);
+
+ /* change devname to use ID, not number */
+
+ devname = "hw:";
+ devname += snd_ctl_card_info_get_id (info);
+
+ while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
+
+ /* only detect duplex devices here. more
+ * complex arrangements are beyond our scope
+ */
+
+ snd_pcm_info_set_device (pcminfo, device);
+ snd_pcm_info_set_subdevice (pcminfo, 0);
+ snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_CAPTURE);
+
+ if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
+
+ snd_pcm_info_set_device (pcminfo, device);
+ snd_pcm_info_set_subdevice (pcminfo, 0);
+ snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
+
+ if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
+ devname += ',';
+ devname += PBD::to_string (device, std::dec);
+ devices.insert (std::make_pair (card_name, devname));
+ }
+ }
+ }
+
+ snd_ctl_close(handle);
+ }
+ }
+#else
+ /* silence a compiler unused variable warning */
+ (void) devices;
+#endif
+}
+
+#ifdef __APPLE__
+static OSStatus
+getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
+{
+ UInt32 size = sizeof(CFStringRef);
+ CFStringRef UI;
+ OSStatus res = AudioDeviceGetProperty(id, 0, false,
+ kAudioDevicePropertyDeviceUID, &size, &UI);
+ if (res == noErr)
+ CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
+ CFRelease(UI);
+ return res;
+}
+#endif
+
+void
+ARDOUR::get_jack_coreaudio_device_names (device_map_t& devices)
+{
+#ifdef __APPLE__
+ // Find out how many Core Audio devices are there, if any...
+ // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
+ OSStatus err;
+ Boolean isWritable;
+ UInt32 outSize = sizeof(isWritable);
+
+ err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
+ &outSize, &isWritable);
+ if (err == noErr) {
+ // Calculate the number of device available...
+ int numCoreDevices = outSize / sizeof(AudioDeviceID);
+ // Make space for the devices we are about to get...
+ AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
+ err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
+ &outSize, (void *) coreDeviceIDs);
+ if (err == noErr) {
+ // Look for the CoreAudio device name...
+ char coreDeviceName[256];
+ UInt32 nameSize;
+
+ for (int i = 0; i < numCoreDevices; i++) {
+
+ nameSize = sizeof (coreDeviceName);
+
+ /* enforce duplex devices only */
+
+ err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
+ 0, true, kAudioDevicePropertyStreams,
+ &outSize, &isWritable);
+
+ if (err != noErr || outSize == 0) {
+ continue;
+ }
+
+ err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
+ 0, false, kAudioDevicePropertyStreams,
+ &outSize, &isWritable);
+
+ if (err != noErr || outSize == 0) {
+ continue;
+ }
+
+ err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
+ 0, true, kAudioDevicePropertyDeviceName,
+ &outSize, &isWritable);
+ if (err == noErr) {
+ err = AudioDeviceGetProperty(coreDeviceIDs[i],
+ 0, true, kAudioDevicePropertyDeviceName,
+ &nameSize, (void *) coreDeviceName);
+ if (err == noErr) {
+ char drivername[128];
+
+ // this returns the unique id for the device
+ // that must be used on the commandline for jack
+
+ if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
+ devices.insert (make_pair (coreDeviceName, drivername));
+ }
+ }
+ }
+ }
+ }
+ delete [] coreDeviceIDs;
+ }
+#else
+ /* silence a compiler unused variable warning */
+ (void) devices;
+#endif
+}
+
+void
+ARDOUR::get_jack_portaudio_device_names (device_map_t& devices)
+{
+#ifdef HAVE_PORTAUDIO
+ if (Pa_Initialize() != paNoError) {
+ return;
+ }
+
+ for (PaDeviceIndex i = 0; i < Pa_GetDeviceCount (); ++i) {
+ string api_name;
+ string readable_name;
+ string jack_device_name;
+ const PaDeviceInfo* device_info = Pa_GetDeviceInfo(i);
+
+ if (device_info != NULL) { // it should never be ?
+ api_name = Pa_GetHostApiInfo (device_info->hostApi)->name;
+ readable_name = api_name + " " + device_info->name;
+ jack_device_name = api_name + "::" + device_info->name;
+ devices.insert (make_pair (readable_name, jack_device_name));
+ }
+ }
+ Pa_Terminate();
+#else
+ /* silence a compiler unused variable warning */
+ (void) devices;
+#endif
+}
+
+void
+ARDOUR::get_jack_oss_device_names (device_map_t& devices)
+{
+ devices.insert (make_pair (default_device_name, default_device_name));
+}
+
+void
+ARDOUR::get_jack_freebob_device_names (device_map_t& devices)
+{
+ devices.insert (make_pair (default_device_name, default_device_name));
+}
+
+void
+ARDOUR::get_jack_ffado_device_names (device_map_t& devices)
+{
+ devices.insert (make_pair (default_device_name, default_device_name));
+}
+
+void
+ARDOUR::get_jack_netjack_device_names (device_map_t& devices)
+{
+ devices.insert (make_pair (default_device_name, default_device_name));
+}
+
+void
+ARDOUR::get_jack_dummy_device_names (device_map_t& devices)
+{
+ devices.insert (make_pair (default_device_name, default_device_name));
+}
+
+bool
+ARDOUR::get_jack_device_names_for_audio_driver (const string& driver_name, device_map_t& devices)
+{
+ devices.clear();
+
+ if (driver_name == portaudio_driver_name) {
+ get_jack_portaudio_device_names (devices);
+ } else if (driver_name == coreaudio_driver_name) {
+ get_jack_coreaudio_device_names (devices);
+ } else if (driver_name == alsa_driver_name) {
+ get_jack_alsa_device_names (devices);
+ } else if (driver_name == oss_driver_name) {
+ get_jack_oss_device_names (devices);
+ } else if (driver_name == freebob_driver_name) {
+ get_jack_freebob_device_names (devices);
+ } else if (driver_name == ffado_driver_name) {
+ get_jack_ffado_device_names (devices);
+ } else if (driver_name == netjack_driver_name) {
+ get_jack_netjack_device_names (devices);
+ } else if (driver_name == dummy_driver_name) {
+ get_jack_dummy_device_names (devices);
+ }
+
+ return !devices.empty();
+}
+
+
+std::vector<std::string>
+ARDOUR::get_jack_device_names_for_audio_driver (const string& driver_name)
+{
+ std::vector<std::string> readable_names;
+ device_map_t devices;
+
+ get_jack_device_names_for_audio_driver (driver_name, devices);
+
+ for (device_map_t::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+ readable_names.push_back (i->first);
+ }
+
+ return readable_names;
+}
+
+bool
+ARDOUR::get_jack_audio_driver_supports_two_devices (const string& driver)
+{
+ return (driver == alsa_driver_name || driver == oss_driver_name);
+}
+
+bool
+ARDOUR::get_jack_audio_driver_supports_latency_adjustment (const string& driver)
+{
+ return (driver == alsa_driver_name || driver == coreaudio_driver_name ||
+ driver == ffado_driver_name || driver == portaudio_driver_name);
+}
+
+bool
+ARDOUR::get_jack_audio_driver_supports_setting_period_count (const string& driver)
+{
+ return !(driver == dummy_driver_name || driver == coreaudio_driver_name ||
+ driver == portaudio_driver_name);
+}
+
+bool
+ARDOUR::get_jack_server_application_names (std::vector<std::string>& server_names)
+{
+#ifdef WIN32
+ server_names.push_back ("jackd.exe");
+#else
+ server_names.push_back ("jackd");
+ server_names.push_back ("jackdmp");
+#endif
+ return !server_names.empty();
+}
+
+void
+ARDOUR::set_path_env_for_jack_autostart (const vector<std::string>& dirs)
+{
+#ifdef __APPLE__
+ // push it back into the environment so that auto-started JACK can find it.
+ // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
+ setenv ("PATH", SearchPath(dirs).to_string().c_str(), 1);
+#else
+ /* silence a compiler unused variable warning */
+ (void) dirs;
+#endif
+}
+
+bool
+ARDOUR::get_jack_server_dir_paths (vector<std::string>& server_dir_paths)
+{
+#ifdef __APPLE__
+ /* this magic lets us finds the path to the OSX bundle, and then
+ we infer JACK's location from there
+ */
+
+ char execpath[MAXPATHLEN+1];
+ uint32_t pathsz = sizeof (execpath);
+
+ _NSGetExecutablePath (execpath, &pathsz);
+
+ server_dir_paths.push_back (Glib::path_get_dirname (execpath));
+#endif
+
+ SearchPath sp(string(g_getenv("PATH")));
+
+#ifdef WIN32
+ gchar *install_dir = g_win32_get_package_installation_directory_of_module (NULL);
+ if (install_dir) {
+ sp.push_back (install_dir);
+ g_free (install_dir);
+ }
+ // don't try and use a system wide JACK install yet.
+#else
+ if (sp.empty()) {
+ sp.push_back ("/usr/bin");
+ sp.push_back ("/bin");
+ sp.push_back ("/usr/local/bin");
+ sp.push_back ("/opt/local/bin");
+ }
+#endif
+
+ std::copy (sp.begin(), sp.end(), std::back_inserter(server_dir_paths));
+
+ return !server_dir_paths.empty();
+}
+
+bool
+ARDOUR::get_jack_server_paths (const vector<std::string>& server_dir_paths,
+ const vector<string>& server_names,
+ vector<std::string>& server_paths)
+{
+ for (vector<string>::const_iterator i = server_names.begin(); i != server_names.end(); ++i) {
+ Glib::PatternSpec ps (*i);
+ find_matching_files_in_directories (server_dir_paths, ps, server_paths);
+ }
+ return !server_paths.empty();
+}
+
+bool
+ARDOUR::get_jack_server_paths (vector<std::string>& server_paths)
+{
+ vector<std::string> server_dirs;
+
+ if (!get_jack_server_dir_paths (server_dirs)) {
+ return false;
+ }
+
+ vector<string> server_names;
+
+ if (!get_jack_server_application_names (server_names)) {
+ return false;
+ }
+
+ if (!get_jack_server_paths (server_dirs, server_names, server_paths)) {
+ return false;
+ }
+
+ return !server_paths.empty();
+}
+
+bool
+ARDOUR::get_jack_default_server_path (std::string& server_path)
+{
+ vector<std::string> server_paths;
+
+ if (!get_jack_server_paths (server_paths)) {
+ return false;
+ }
+
+ server_path = server_paths.front ();
+ return true;
+}
+
+string
+quote_string (const string& str)
+{
+ return "\"" + str + "\"";
+}
+
+ARDOUR::JackCommandLineOptions::JackCommandLineOptions ()
+ : server_path ()
+ , timeout(0)
+ , no_mlock(false)
+ , ports_max(128)
+ , realtime(true)
+ , priority(0)
+ , unlock_gui_libs(false)
+ , verbose(false)
+ , temporary(true)
+ , driver()
+ , input_device()
+ , output_device()
+ , num_periods(2)
+ , period_size(1024)
+ , samplerate(48000)
+ , input_latency(0)
+ , output_latency(0)
+ , hardware_metering(false)
+ , hardware_monitoring(false)
+ , dither_mode()
+ , force16_bit(false)
+ , soft_mode(false)
+ , midi_driver()
+{
+
+}
+
+bool
+ARDOUR::get_jack_command_line_string (const JackCommandLineOptions& options, string& command_line)
+{
+ vector<string> args;
+
+ args.push_back (options.server_path);
+
+#ifdef WIN32
+ // must use sync mode on windows
+ args.push_back ("-S");
+
+ // this needs to be added now on windows
+ if (!options.midi_driver.empty () && options.midi_driver != get_none_string ()) {
+ args.push_back ("-X");
+ args.push_back (options.midi_driver);
+ }
+#endif
+
+ if (options.timeout) {
+ args.push_back ("-t");
+ args.push_back (to_string (options.timeout, std::dec));
+ }
+
+ if (options.no_mlock) {
+ args.push_back ("-m");
+ }
+
+ args.push_back ("-p");
+ args.push_back (to_string(options.ports_max, std::dec));
+
+ if (options.realtime) {
+ args.push_back ("-R");
+ if (options.priority != 0) {
+ args.push_back ("-P");
+ args.push_back (to_string(options.priority, std::dec));
+ }
+ } else {
+ args.push_back ("-r");
+ }
+
+ if (options.unlock_gui_libs) {
+ args.push_back ("-u");
+ }
+
+ if (options.verbose) {
+ args.push_back ("-v");
+ }
+
+#ifndef WIN32
+ if (options.temporary) {
+ args.push_back ("-T");
+ }
+#endif
+
+ string command_line_driver_name;
+
+ if (!get_jack_command_line_audio_driver_name (options.driver, command_line_driver_name)) {
+ return false;
+ }
+
+ args.push_back ("-d");
+ args.push_back (command_line_driver_name);
+
+ if (options.output_device.empty() && options.input_device.empty()) {
+ return false;
+ }
+
+ string command_line_input_device_name;
+ string command_line_output_device_name;
+
+ if (!get_jack_command_line_audio_device_name (options.driver,
+ options.input_device, command_line_input_device_name)) {
+ return false;
+ }
+
+ if (!get_jack_command_line_audio_device_name (options.driver,
+ options.output_device, command_line_output_device_name)) {
+ return false;
+ }
+
+ if (options.input_device.empty()) {
+ // playback only
+ if (options.output_device.empty()) {
+ return false;
+ }
+ args.push_back ("-P");
+ } else if (options.output_device.empty()) {
+ // capture only
+ if (options.input_device.empty()) {
+ return false;
+ }
+ args.push_back ("-C");
+ } else if (options.input_device != options.output_device) {
+ // capture and playback on two devices if supported
+ if (get_jack_audio_driver_supports_two_devices (options.driver)) {
+ args.push_back ("-C");
+ args.push_back (command_line_input_device_name);
+ args.push_back ("-P");
+ args.push_back (command_line_output_device_name);
+ } else {
+ return false;
+ }
+ }
+
+ if (options.input_channels) {
+ args.push_back ("-i");
+ args.push_back (to_string (options.input_channels, std::dec));
+ }
+
+ if (options.output_channels) {
+ args.push_back ("-o");
+ args.push_back (to_string (options.output_channels, std::dec));
+ }
+
+ if (get_jack_audio_driver_supports_setting_period_count (options.driver)) {
+ args.push_back ("-n");
+ args.push_back (to_string (options.num_periods, std::dec));
+ }
+
+ args.push_back ("-r");
+ args.push_back (to_string (options.samplerate, std::dec));
+
+ args.push_back ("-p");
+ args.push_back (to_string (options.period_size, std::dec));
+
+ if (get_jack_audio_driver_supports_latency_adjustment (options.driver)) {
+ if (options.input_latency) {
+ args.push_back ("-I");
+ args.push_back (to_string (options.input_latency, std::dec));
+ }
+ if (options.output_latency) {
+ args.push_back ("-O");
+ args.push_back (to_string (options.output_latency, std::dec));
+ }
+ }
+
+ if (options.input_device == options.output_device && options.input_device != default_device_name) {
+ args.push_back ("-d");
+ args.push_back (command_line_input_device_name);
+ }
+
+ if (options.driver == alsa_driver_name) {
+ if (options.hardware_metering) {
+ args.push_back ("-M");
+ }
+ if (options.hardware_monitoring) {
+ args.push_back ("-H");
+ }
+
+ string command_line_dither_mode;
+ if (get_jack_command_line_dither_mode (options.dither_mode, command_line_dither_mode)) {
+ args.push_back ("-z");
+ args.push_back (command_line_dither_mode);
+ }
+ if (options.force16_bit) {
+ args.push_back ("-S");
+ }
+ if (options.soft_mode) {
+ args.push_back ("-s");
+ }
+
+ if (!options.midi_driver.empty() && options.midi_driver != get_none_string ()) {
+ args.push_back ("-X");
+ args.push_back (options.midi_driver);
+ }
+ }
+
+ ostringstream oss;
+
+ for (vector<string>::const_iterator i = args.begin(); i != args.end();) {
+#ifdef WIN32
+ oss << quote_string (*i);
+#else
+ oss << *i;
+#endif
+ if (++i != args.end()) oss << ' ';
+ }
+
+ command_line = oss.str();
+ return true;
+}
+
+string
+ARDOUR::get_jack_server_config_file_name ()
+{
+ return ".jackdrc";
+}
+
+std::string
+ARDOUR::get_jack_server_user_config_dir_path ()
+{
+ return Glib::get_home_dir ();
+}
+
+std::string
+ARDOUR::get_jack_server_user_config_file_path ()
+{
+ return Glib::build_filename (get_jack_server_user_config_dir_path (), get_jack_server_config_file_name ());
+}
+
+bool
+ARDOUR::write_jack_config_file (const std::string& config_file_path, const string& command_line)
+{
+ ofstream jackdrc (config_file_path.c_str());
+
+ if (!jackdrc) {
+ error << string_compose (_("cannot open JACK rc file %1 to store parameters"), config_file_path) << endmsg;
+ return false;
+ }
+
+ jackdrc << command_line << endl;
+ jackdrc.close ();
+ return true;
+}
diff --git a/libs/backends/jack/jack_utils.h b/libs/backends/jack/jack_utils.h
new file mode 100644
index 0000000000..7565353198
--- /dev/null
+++ b/libs/backends/jack/jack_utils.h
@@ -0,0 +1,235 @@
+/*
+ Copyright (C) 2011 Tim Mayberry
+
+ 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 <stdint.h>
+
+#include <vector>
+#include <map>
+#include <string>
+
+namespace ARDOUR {
+
+ // Names for the drivers on all possible systems
+ extern const char * const portaudio_driver_name;
+ extern const char * const coreaudio_driver_name;
+ extern const char * const alsa_driver_name;
+ extern const char * const oss_driver_name;
+ extern const char * const freebob_driver_name;
+ extern const char * const ffado_driver_name;
+ extern const char * const netjack_driver_name;
+ extern const char * const dummy_driver_name;
+
+ /**
+ * Get a list of possible JACK audio driver names based on platform
+ */
+ void get_jack_audio_driver_names (std::vector<std::string>& driver_names);
+
+ /**
+ * Get the default JACK audio driver based on platform
+ */
+ void get_jack_default_audio_driver_name (std::string& driver_name);
+
+ /**
+ * Get a list of possible samplerates supported be JACK
+ */
+ void get_jack_sample_rate_strings (std::vector<std::string>& sample_rates);
+
+ /**
+ * @return The default samplerate
+ */
+ std::string get_jack_default_sample_rate ();
+
+ /**
+ * @return true if sample rate string was able to be converted
+ */
+ bool get_jack_sample_rate_value_from_string (const std::string& srs, uint32_t& srv);
+
+ /**
+ * Get a list of possible period sizes supported be JACK
+ */
+ void get_jack_period_size_strings (std::vector<std::string>& samplerates);
+
+ /**
+ * @return The default period size
+ */
+ std::string get_jack_default_period_size ();
+
+ /**
+ * @return true if period size string was able to be converted
+ */
+ bool get_jack_period_size_value_from_string (const std::string& pss, uint32_t& psv);
+
+ /**
+ * These are driver specific I think, so it may require a driver arg
+ * in future
+ */
+ void get_jack_dither_mode_strings (const std::string& driver, std::vector<std::string>& dither_modes);
+
+ /**
+ * @return The default dither mode
+ */
+ std::string get_jack_default_dither_mode (const std::string& driver);
+
+ /**
+ * @return Estimate of latency
+ *
+ * API matches current use in GUI
+ */
+ std::string get_jack_latency_string (std::string samplerate, float periods, std::string period_size);
+
+ /**
+ * Key being a readable name to display in a GUI
+ * Value being name used in a jack commandline
+ */
+ typedef std::map<std::string, std::string> device_map_t;
+
+ /**
+ * Use library specific code to find out what what devices exist for a given
+ * driver that might work in JACK. There is no easy way to find out what
+ * modules the JACK server supports so guess based on platform. For instance
+ * portaudio is cross-platform but we only return devices if built for
+ * windows etc
+ */
+ void get_jack_alsa_device_names (device_map_t& devices);
+ void get_jack_portaudio_device_names (device_map_t& devices);
+ void get_jack_coreaudio_device_names (device_map_t& devices);
+ void get_jack_oss_device_names (device_map_t& devices);
+ void get_jack_freebob_device_names (device_map_t& devices);
+ void get_jack_ffado_device_names (device_map_t& devices);
+ void get_jack_netjack_device_names (device_map_t& devices);
+ void get_jack_dummy_device_names (device_map_t& devices);
+
+ /*
+ * @return true if there were devices found for the driver
+ *
+ * @param driver The driver name returned by get_jack_audio_driver_names
+ * @param devices The map used to insert the drivers into, devices will be cleared before
+ * adding the available drivers
+ */
+ bool get_jack_device_names_for_audio_driver (const std::string& driver, device_map_t& devices);
+
+ /*
+ * @return a list of readable device names for a specific driver.
+ */
+ std::vector<std::string> get_jack_device_names_for_audio_driver (const std::string& driver);
+
+ /**
+ * @return true if the driver supports playback and recording
+ * on separate devices
+ */
+ bool get_jack_audio_driver_supports_two_devices (const std::string& driver);
+
+ bool get_jack_audio_driver_supports_latency_adjustment (const std::string& driver);
+
+ bool get_jack_audio_driver_supports_setting_period_count (const std::string& driver);
+
+ /**
+ * The possible names to use to try and find servers, this includes
+ * any file extensions like .exe on Windows
+ *
+ * @return true if the JACK application names for this platform could be guessed
+ */
+ bool get_jack_server_application_names (std::vector<std::string>& server_names);
+
+ /**
+ * Sets the PATH environment variable to contain directories likely to contain
+ * JACK servers so that if the JACK server is auto-started it can find the server
+ * executable.
+ *
+ * This is only modifies PATH on the mac at the moment.
+ */
+ void set_path_env_for_jack_autostart (const std::vector<std::string>&);
+
+ /**
+ * Get absolute paths to directories that might contain JACK servers on the system
+ *
+ * @return true if !server_paths.empty()
+ */
+ bool get_jack_server_dir_paths (std::vector<std::string>& server_dir_paths);
+
+ /**
+ * Get absolute paths to JACK servers on the system
+ *
+ * @return true if a server was found
+ */
+ bool get_jack_server_paths (const std::vector<std::string>& server_dir_paths,
+ const std::vector<std::string>& server_names,
+ std::vector<std::string>& server_paths);
+
+
+ bool get_jack_server_paths (std::vector<std::string>& server_paths);
+
+ /**
+ * Get absolute path to default JACK server
+ */
+ bool get_jack_default_server_path (std::string& server_path);
+
+ /**
+ * @return The name of the jack server config file
+ */
+ std::string get_jack_server_config_file_name ();
+
+ std::string get_jack_server_user_config_dir_path ();
+
+ std::string get_jack_server_user_config_file_path ();
+
+ bool write_jack_config_file (const std::string& config_file_path, const std::string& command_line);
+
+ struct JackCommandLineOptions {
+
+ // see implementation for defaults
+ JackCommandLineOptions ();
+
+ //operator bool
+ //operator ostream
+
+ std::string server_path;
+ uint32_t timeout;
+ bool no_mlock;
+ uint32_t ports_max;
+ bool realtime;
+ uint32_t priority;
+ bool unlock_gui_libs;
+ bool verbose;
+ bool temporary;
+ bool playback_only;
+ bool capture_only;
+ std::string driver;
+ std::string input_device;
+ std::string output_device;
+ uint32_t num_periods;
+ uint32_t period_size;
+ uint32_t samplerate;
+ uint32_t input_channels;
+ uint32_t output_channels;
+ uint32_t input_latency;
+ uint32_t output_latency;
+ bool hardware_metering;
+ bool hardware_monitoring;
+ std::string dither_mode;
+ bool force16_bit;
+ bool soft_mode;
+ std::string midi_driver;
+ };
+
+ /**
+ * @return true if able to build a valid command line based on options
+ */
+ bool get_jack_command_line_string (const JackCommandLineOptions& options, std::string& command_line);
+}
diff --git a/libs/backends/jack/wscript b/libs/backends/jack/wscript
new file mode 100644
index 0000000000..3c47f3a652
--- /dev/null
+++ b/libs/backends/jack/wscript
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+import os
+import sys
+import re
+
+# Library version (UNIX style major, minor, micro)
+# major increment <=> incompatible changes
+# minor increment <=> compatible changes (additions)
+# micro increment <=> no interface changes
+JACKBACKEND_VERSION = '1.0.0'
+I18N_PACKAGE = 'jack-backend'
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(opt):
+ autowaf.set_options(opt)
+
+def configure(conf):
+ autowaf.configure(conf)
+
+def build(bld):
+ obj = bld(features = 'cxx cxxshlib')
+ obj.source = [
+ 'jack_api.cc',
+ 'jack_connection.cc',
+ 'jack_audiobackend.cc',
+ 'jack_portengine.cc',
+ 'jack_utils.cc'
+ ]
+ obj.includes = ['.']
+ obj.cxxflags = [ '-fPIC' ]
+ obj.name = 'jack_audiobackend'
+ obj.target = 'jack_audiobackend'
+ obj.uselib = [ 'JACK' ]
+ obj.use = 'ardour libpbd'
+ obj.vnum = JACKBACKEND_VERSION
+ obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'backends')
+ obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"']
+
+
+ #
+ # device discovery code in the jack backend needs ALSA
+ # on Linux.
+ #
+
+ if re.search ("linux", sys.platform) != None:
+ obj.uselib += [ 'ALSA' ]
+
diff --git a/libs/backends/wscript b/libs/backends/wscript
new file mode 100644
index 0000000000..b8779b7713
--- /dev/null
+++ b/libs/backends/wscript
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+import os
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+backends = [ 'jack' ]
+
+def options(opt):
+ autowaf.set_options(opt)
+
+def sub_config_and_use(conf, name, has_objects = True):
+ conf.recurse(name)
+ autowaf.set_local_lib(conf, name, has_objects)
+
+def configure(conf):
+ autowaf.set_recursive()
+ autowaf.configure(conf)
+
+ for i in backends:
+ sub_config_and_use(conf, i)
+
+def build(bld):
+ for i in backends:
+ bld.recurse(i)
diff --git a/libs/midi++2/jack_midi_port.cc b/libs/midi++2/jack_midi_port.cc
deleted file mode 100644
index 38ec4d35c7..0000000000
--- a/libs/midi++2/jack_midi_port.cc
+++ /dev/null
@@ -1,486 +0,0 @@
-/*
- Copyright (C) 1998 Paul Barton-Davis
-
- 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.
-
- $Id$
-*/
-#include <iostream>
-#include <cstdio>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#include <jack/jack.h>
-#include <jack/midiport.h>
-
-#include "pbd/xml++.h"
-#include "pbd/error.h"
-#include "pbd/failed_constructor.h"
-#include "pbd/convert.h"
-#include "pbd/strsplit.h"
-#include "pbd/stacktrace.h"
-
-#include "midi++/types.h"
-#include "midi++/jack_midi_port.h"
-#include "midi++/channel.h"
-
-using namespace MIDI;
-using namespace std;
-using namespace PBD;
-
-namespace Evoral {
-template class EventRingBuffer<timestamp_t>;
-}
-
-pthread_t JackMIDIPort::_process_thread;
-Signal0<void> JackMIDIPort::JackHalted;
-Signal0<void> JackMIDIPort::MakeConnections;
-
-JackMIDIPort::JackMIDIPort (string const & name, Flags flags, jack_client_t* jack_client)
- : Port (name, flags)
- , _currently_in_cycle (false)
- , _nframes_this_cycle (0)
- , _jack_client (jack_client)
- , _jack_port (0)
- , _last_write_timestamp (0)
- , output_fifo (512)
- , input_fifo (1024)
-#ifndef PLATFORM_WINDOWS
- , xthread (true)
-#endif
-{
- assert (jack_client);
- init (name, flags);
-}
-
-JackMIDIPort::JackMIDIPort (const XMLNode& node, jack_client_t* jack_client)
- : Port (node)
- , _currently_in_cycle (false)
- , _nframes_this_cycle (0)
- , _jack_client (jack_client)
- , _jack_port (0)
- , _last_write_timestamp (0)
- , output_fifo (512)
- , input_fifo (1024)
-#ifndef PLATFORM_WINDOWS
- , xthread (true)
-#endif
-{
- assert (jack_client);
-
- Descriptor desc (node);
- init (desc.tag, desc.flags);
- set_state (node);
-}
-
-void
-JackMIDIPort::init (const string& /*name*/, Flags /*flags*/)
-{
- if (!create_port ()) {
- _ok = true;
- }
-
- MakeConnections.connect_same_thread (connect_connection, boost::bind (&JackMIDIPort::make_connections, this));
- JackHalted.connect_same_thread (halt_connection, boost::bind (&JackMIDIPort::jack_halted, this));
-}
-
-
-JackMIDIPort::~JackMIDIPort ()
-{
- if (_jack_port) {
- if (_jack_client) {
- jack_port_unregister (_jack_client, _jack_port);
- _jack_port = 0;
- }
- }
-}
-
-void
-JackMIDIPort::parse (framecnt_t timestamp)
-{
- byte buf[512];
-
- /* NOTE: parsing is done (if at all) by initiating a read from
- the port. Each port implementation calls on the parser
- once it has data ready.
- */
-
- _parser->set_timestamp (timestamp);
-
- while (1) {
-
- // cerr << "+++ READ ON " << name() << endl;
-
- int nread = read (buf, sizeof (buf));
-
- // cerr << "-- READ (" << nread << " ON " << name() << endl;
-
- if (nread > 0) {
- if ((size_t) nread < sizeof (buf)) {
- break;
- } else {
- continue;
- }
- } else if (nread == 0) {
- break;
- } else if (errno == EAGAIN) {
- break;
- } else {
- fatal << "Error reading from MIDI port " << name() << endmsg;
- /*NOTREACHED*/
- }
- }
-}
-
-void
-JackMIDIPort::cycle_start (pframes_t nframes)
-{
- assert (_jack_port);
-
- _currently_in_cycle = true;
- _nframes_this_cycle = nframes;
-
- assert(_nframes_this_cycle == nframes);
-
- if (sends_output()) {
- void *buffer = jack_port_get_buffer (_jack_port, nframes);
- jack_midi_clear_buffer (buffer);
- flush (buffer);
- }
-
- if (receives_input()) {
- void* jack_buffer = jack_port_get_buffer(_jack_port, nframes);
- const pframes_t event_count = jack_midi_get_event_count(jack_buffer);
-
- jack_midi_event_t ev;
- timestamp_t cycle_start_frame = jack_last_frame_time (_jack_client);
-
- for (pframes_t i = 0; i < event_count; ++i) {
- jack_midi_event_get (&ev, jack_buffer, i);
- input_fifo.write (cycle_start_frame + ev.time, (Evoral::EventType) 0, ev.size, ev.buffer);
- }
-
- if (event_count) {
-#ifndef PLATFORM_WINDOWS
- xthread.wakeup ();
-#endif
- }
- }
-}
-
-void
-JackMIDIPort::cycle_end ()
-{
- if (sends_output()) {
- flush (jack_port_get_buffer (_jack_port, _nframes_this_cycle));
- }
-
- _currently_in_cycle = false;
- _nframes_this_cycle = 0;
-}
-
-void
-JackMIDIPort::jack_halted ()
-{
- _jack_client = 0;
- _jack_port = 0;
-}
-
-void
-JackMIDIPort::drain (int check_interval_usecs)
-{
- RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
-
- if (is_process_thread()) {
- error << "Process thread called MIDI::JackMIDIPort::drain() - this cannot work" << endmsg;
- return;
- }
-
- while (1) {
- output_fifo.get_write_vector (&vec);
- if (vec.len[0] + vec.len[1] >= output_fifo.bufsize() - 1) {
- break;
- }
- g_usleep (check_interval_usecs);
- }
-}
-
-int
-JackMIDIPort::write (const MIDI::byte * msg, size_t msglen, timestamp_t timestamp)
-{
- int ret = 0;
-
- if (!_jack_client || !_jack_port) {
- /* poof ! make it just vanish into thin air, since we are no
- longer connected to JACK.
- */
- return msglen;
- }
-
- if (!sends_output()) {
- return ret;
- }
-
- if (!is_process_thread()) {
-
- Glib::Threads::Mutex::Lock lm (output_fifo_lock);
- RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
-
- output_fifo.get_write_vector (&vec);
-
- if (vec.len[0] + vec.len[1] < 1) {
- error << "no space in FIFO for non-process thread MIDI write" << endmsg;
- return 0;
- }
-
- if (vec.len[0]) {
- if (!vec.buf[0]->owns_buffer()) {
- vec.buf[0]->set_buffer (0, 0, true);
- }
- vec.buf[0]->set (msg, msglen, timestamp);
- } else {
- if (!vec.buf[1]->owns_buffer()) {
- vec.buf[1]->set_buffer (0, 0, true);
- }
- vec.buf[1]->set (msg, msglen, timestamp);
- }
-
- output_fifo.increment_write_idx (1);
-
- ret = msglen;
-
- } else {
-
- if (timestamp >= _nframes_this_cycle) {
- std::cerr << "attempting to write MIDI event of " << msglen << " bytes at time "
- << timestamp << " of " << _nframes_this_cycle
- << " (this will not work - needs a code fix)"
- << std::endl;
- }
-
- if (_currently_in_cycle) {
- if (timestamp == 0) {
- timestamp = _last_write_timestamp;
- }
-
- if ((ret = jack_midi_event_write (jack_port_get_buffer (_jack_port, _nframes_this_cycle),
- timestamp, msg, msglen)) == 0) {
- ret = msglen;
- _last_write_timestamp = timestamp;
-
- } else {
- cerr << "write of " << msglen << " @ " << timestamp << " failed, port holds "
- << jack_midi_get_event_count (jack_port_get_buffer (_jack_port, _nframes_this_cycle))
- << " port is " << _jack_port
- << " ntf = " << _nframes_this_cycle
- << " buf = " << jack_port_get_buffer (_jack_port, _nframes_this_cycle)
- << " ret = " << ret
- << endl;
- PBD::stacktrace (cerr, 20);
- ret = 0;
- }
- } else {
- cerr << "write to JACK midi port failed: not currently in a process cycle." << endl;
- PBD::stacktrace (cerr, 20);
- }
- }
-
- if (ret > 0 && _parser) {
- // ardour doesn't care about this and neither should your app, probably
- // output_parser->raw_preparse (*output_parser, msg, ret);
- for (int i = 0; i < ret; i++) {
- _parser->scanner (msg[i]);
- }
- // ardour doesn't care about this and neither should your app, probably
- // output_parser->raw_postparse (*output_parser, msg, ret);
- }
-
- return ret;
-}
-
-void
-JackMIDIPort::flush (void* jack_port_buffer)
-{
- RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0 } };
- size_t written;
-
- output_fifo.get_read_vector (&vec);
-
- if (vec.len[0] + vec.len[1]) {
- // cerr << "Flush " << vec.len[0] + vec.len[1] << " events from non-process FIFO\n";
- }
-
- if (vec.len[0]) {
- Evoral::Event<double>* evp = vec.buf[0];
-
- for (size_t n = 0; n < vec.len[0]; ++n, ++evp) {
- jack_midi_event_write (jack_port_buffer,
- (timestamp_t) evp->time(), evp->buffer(), evp->size());
- }
- }
-
- if (vec.len[1]) {
- Evoral::Event<double>* evp = vec.buf[1];
-
- for (size_t n = 0; n < vec.len[1]; ++n, ++evp) {
- jack_midi_event_write (jack_port_buffer,
- (timestamp_t) evp->time(), evp->buffer(), evp->size());
- }
- }
-
- if ((written = vec.len[0] + vec.len[1]) != 0) {
- output_fifo.increment_read_idx (written);
- }
-}
-
-int
-JackMIDIPort::read (MIDI::byte *, size_t)
-{
- if (!receives_input()) {
- return 0;
- }
-
- timestamp_t time;
- Evoral::EventType type;
- uint32_t size;
- vector<MIDI::byte> buffer(input_fifo.capacity());
-
- while (input_fifo.read (&time, &type, &size, &buffer[0])) {
- _parser->set_timestamp (time);
- for (uint32_t i = 0; i < size; ++i) {
- _parser->scanner (buffer[i]);
- }
- }
-
- return 0;
-}
-
-int
-JackMIDIPort::create_port ()
-{
- _jack_port = jack_port_register(_jack_client, _tagname.c_str(), JACK_DEFAULT_MIDI_TYPE, _flags, 0);
- return _jack_port == 0 ? -1 : 0;
-}
-
-XMLNode&
-JackMIDIPort::get_state () const
-{
- XMLNode& root = Port::get_state ();
-
-#if 0
- byte device_inquiry[6];
-
- device_inquiry[0] = 0xf0;
- device_inquiry[0] = 0x7e;
- device_inquiry[0] = 0x7f;
- device_inquiry[0] = 0x06;
- device_inquiry[0] = 0x02;
- device_inquiry[0] = 0xf7;
-
- write (device_inquiry, sizeof (device_inquiry), 0);
-#endif
-
- if (_jack_port) {
-
- const char** jc = jack_port_get_connections (_jack_port);
- string connection_string;
- if (jc) {
- for (int i = 0; jc[i]; ++i) {
- if (i > 0) {
- connection_string += ',';
- }
- connection_string += jc[i];
- }
- free (jc);
- }
-
- if (!connection_string.empty()) {
- root.add_property ("connections", connection_string);
- }
- } else {
- if (!_connections.empty()) {
- root.add_property ("connections", _connections);
- }
- }
-
- return root;
-}
-
-void
-JackMIDIPort::set_state (const XMLNode& node)
-{
- const XMLProperty* prop;
-
- if ((prop = node.property ("tag")) == 0 || prop->value() != _tagname) {
- return;
- }
-
- Port::set_state (node);
-
- if ((prop = node.property ("connections")) != 0) {
- _connections = prop->value ();
- }
-}
-
-void
-JackMIDIPort::make_connections ()
-{
- if (!_connections.empty()) {
- vector<string> ports;
- split (_connections, ports, ',');
- for (vector<string>::iterator x = ports.begin(); x != ports.end(); ++x) {
- if (_jack_client) {
- if (receives_input()) {
- jack_connect (_jack_client, (*x).c_str(), jack_port_name (_jack_port));
- } else {
- jack_connect (_jack_client, jack_port_name (_jack_port), (*x).c_str());
- }
- /* ignore failures */
- }
- }
- }
-
- connect_connection.disconnect ();
-}
-
-void
-JackMIDIPort::set_process_thread (pthread_t thr)
-{
- _process_thread = thr;
-}
-
-bool
-JackMIDIPort::is_process_thread()
-{
- return (pthread_equal(pthread_self(), _process_thread));
-}
-
-void
-JackMIDIPort::reestablish (jack_client_t* jack)
-{
- _jack_client = jack;
- int const r = create_port ();
-
- if (r) {
- PBD::error << "could not reregister ports for " << name() << endmsg;
- }
-}
-
-void
-JackMIDIPort::reconnect ()
-{
- make_connections ();
-}
diff --git a/libs/midi++2/manager.cc b/libs/midi++2/manager.cc
deleted file mode 100644
index 822c74e125..0000000000
--- a/libs/midi++2/manager.cc
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- Copyright (C) 1998-99 Paul Barton-Davis
- 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.
-
- $Id$
-*/
-
-#include <fcntl.h>
-
-#include <glib.h>
-
-#include "pbd/error.h"
-
-#include "midi++/types.h"
-#include "midi++/manager.h"
-#include "midi++/channel.h"
-#include "midi++/port.h"
-#include "midi++/jack_midi_port.h"
-#include "midi++/mmc.h"
-
-using namespace std;
-using namespace MIDI;
-using namespace PBD;
-
-Manager *Manager::theManager = 0;
-
-Manager::Manager (jack_client_t* jack)
- : _ports (new PortList)
-{
- _mmc = new MachineControl (this, jack);
-
- _mtc_input_port = add_port (new MIDI::JackMIDIPort ("MTC in", Port::IsInput, jack));
- _mtc_output_port = add_port (new MIDI::JackMIDIPort ("MTC out", Port::IsOutput, jack));
- _midi_input_port = add_port (new MIDI::JackMIDIPort ("MIDI control in", Port::IsInput, jack));
- _midi_output_port = add_port (new MIDI::JackMIDIPort ("MIDI control out", Port::IsOutput, jack));
- _midi_clock_input_port = add_port (new MIDI::JackMIDIPort ("MIDI clock in", Port::IsInput, jack));
- _midi_clock_output_port = add_port (new MIDI::JackMIDIPort ("MIDI clock out", Port::IsOutput, jack));
-}
-
-Manager::~Manager ()
-{
- delete _mmc;
-
- /* This will delete our MTC etc. ports */
-
- boost::shared_ptr<PortList> pr = _ports.reader ();
- for (PortList::iterator p = pr->begin(); p != pr->end(); ++p) {
- delete *p;
- }
-
- if (theManager == this) {
- theManager = 0;
- }
-}
-
-Port *
-Manager::add_port (Port* p)
-{
- {
- RCUWriter<PortList> writer (_ports);
- boost::shared_ptr<PortList> pw = writer.get_copy ();
- pw->push_back (p);
- }
-
- PortsChanged (); /* EMIT SIGNAL */
-
- return p;
-}
-
-void
-Manager::remove_port (Port* p)
-{
- {
- RCUWriter<PortList> writer (_ports);
- boost::shared_ptr<PortList> pw = writer.get_copy ();
- pw->remove (p);
- }
-
- PortsChanged (); /* EMIT SIGNAL */
-}
-
-void
-Manager::cycle_start (pframes_t nframes)
-{
- boost::shared_ptr<PortList> pr = _ports.reader ();
-
- for (PortList::iterator p = pr->begin(); p != pr->end(); ++p) {
- (*p)->cycle_start (nframes);
- }
-}
-
-void
-Manager::cycle_end()
-{
- boost::shared_ptr<PortList> pr = _ports.reader ();
-
- for (PortList::iterator p = pr->begin(); p != pr->end(); ++p) {
- (*p)->cycle_end ();
- }
-}
-
-/** Re-register ports that disappear on JACK shutdown */
-void
-Manager::reestablish (jack_client_t* jack)
-{
- boost::shared_ptr<PortList> pr = _ports.reader ();
-
- for (PortList::const_iterator p = pr->begin(); p != pr->end(); ++p) {
- JackMIDIPort* pp = dynamic_cast<JackMIDIPort*> (*p);
- if (pp) {
- pp->reestablish (jack);
- }
- }
-}
-
-/** Re-connect ports after a reestablish () */
-void
-Manager::reconnect ()
-{
- boost::shared_ptr<PortList> pr = _ports.reader ();
-
- for (PortList::const_iterator p = pr->begin(); p != pr->end(); ++p) {
- JackMIDIPort* pp = dynamic_cast<JackMIDIPort*> (*p);
- if (pp) {
- pp->reconnect ();
- }
- }
-}
-
-Port*
-Manager::port (string const & n)
-{
- boost::shared_ptr<PortList> pr = _ports.reader ();
-
- PortList::const_iterator p = pr->begin();
- while (p != pr->end() && (*p)->name() != n) {
- ++p;
- }
-
- if (p == pr->end()) {
- return 0;
- }
-
- return *p;
-}
-
-void
-Manager::create (jack_client_t* jack)
-{
- assert (theManager == 0);
- theManager = new Manager (jack);
-}
-
-void
-Manager::set_port_states (list<XMLNode*> s)
-{
- boost::shared_ptr<PortList> pr = _ports.reader ();
-
- for (list<XMLNode*>::iterator i = s.begin(); i != s.end(); ++i) {
- for (PortList::const_iterator j = pr->begin(); j != pr->end(); ++j) {
- (*j)->set_state (**i);
- }
- }
-}
-
-void
-Manager::destroy ()
-{
- delete theManager;
- theManager = 0;
-}
diff --git a/libs/midi++2/midi++/manager.h b/libs/midi++2/midi++/manager.h
deleted file mode 100644
index c37ba392b4..0000000000
--- a/libs/midi++2/midi++/manager.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- Copyright (C) 1998 Paul Barton-Davis
-
- 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.
-
-*/
-
-#ifndef __midi_manager_h__
-#define __midi_manager_h__
-
-#include <list>
-
-#include <string>
-
-#include "pbd/rcu.h"
-
-#include "midi++/types.h"
-#include "midi++/port.h"
-
-namespace MIDI {
-
-class MachineControl;
-
-class Manager {
- public:
- ~Manager ();
-
- /** Signal the start of an audio cycle.
- * This MUST be called before any reading/writing for this cycle.
- * Realtime safe.
- */
- void cycle_start (pframes_t nframes);
-
- /** Signal the end of an audio cycle.
- * This signifies that the cycle began with @ref cycle_start has ended.
- * This MUST be called at the end of each cycle.
- * Realtime safe.
- */
- void cycle_end ();
-
- MachineControl* mmc () const { return _mmc; }
- Port *mtc_input_port() const { return _mtc_input_port; }
- Port *mtc_output_port() const { return _mtc_output_port; }
- Port *midi_input_port() const { return _midi_input_port; }
- Port *midi_output_port() const { return _midi_output_port; }
- Port *midi_clock_input_port() const { return _midi_clock_input_port; }
- Port *midi_clock_output_port() const { return _midi_clock_output_port; }
-
- Port* add_port (Port *);
- void remove_port (Port *);
-
- Port* port (std::string const &);
-
- void set_port_states (std::list<XMLNode*>);
-
- typedef std::list<Port *> PortList;
-
- boost::shared_ptr<const PortList> get_midi_ports() const { return _ports.reader (); }
-
- static void create (jack_client_t* jack);
-
- static Manager *instance () {
- return theManager;
- }
- static void destroy ();
-
- void reestablish (jack_client_t *);
- void reconnect ();
-
- PBD::Signal0<void> PortsChanged;
-
- private:
- /* This is a SINGLETON pattern */
-
- Manager (jack_client_t *);
- static Manager *theManager;
-
- MIDI::MachineControl* _mmc;
- MIDI::Port* _mtc_input_port;
- MIDI::Port* _mtc_output_port;
- MIDI::Port* _midi_input_port;
- MIDI::Port* _midi_output_port;
- MIDI::Port* _midi_clock_input_port;
- MIDI::Port* _midi_clock_output_port;
-
- SerializedRCUManager<PortList> _ports;
-};
-
-} // namespace MIDI
-
-#endif // __midi_manager_h__
diff --git a/libs/midi++2/midi++/mmc.h b/libs/midi++2/midi++/mmc.h
index 18204e3fa8..01f8bf3b8a 100644
--- a/libs/midi++2/midi++/mmc.h
+++ b/libs/midi++2/midi++/mmc.h
@@ -22,17 +22,22 @@
#include <jack/types.h>
#include "timecode/time.h"
+
#include "pbd/signals.h"
#include "pbd/ringbuffer.h"
+
#include "midi++/types.h"
#include "midi++/parser.h"
+namespace ARDOUR {
+ class PortEngine;
+}
+
namespace MIDI {
class Port;
class Parser;
class MachineControlCommand;
-class Manager;
/** Class to handle incoming and outgoing MIDI machine control messages */
class MachineControl
@@ -89,7 +94,9 @@ class MachineControl
cmdResume = 0x7F
};
- MachineControl (Manager *, jack_client_t *);
+ MachineControl ();
+
+ void set_ports (MIDI::Port* input, MIDI::Port* output);
Port* input_port() { return _input_port; }
Port* output_port() { return _output_port; }
diff --git a/libs/midi++2/midi++/parser.h b/libs/midi++2/midi++/parser.h
index f5f343e952..44897f9d8e 100644
--- a/libs/midi++2/midi++/parser.h
+++ b/libs/midi++2/midi++/parser.h
@@ -41,7 +41,7 @@ typedef PBD::Signal3<void,Parser &, byte *, size_t> Signal;
class Parser {
public:
- Parser (Port &p);
+ Parser ();
~Parser ();
/* sets the time that will be reported for any MTC or MIDI Clock
@@ -105,7 +105,6 @@ class Parser {
const char *midi_event_type_name (MIDI::eventType);
void trace (bool onoff, std::ostream *o, const std::string &prefix = "");
bool tracing() { return trace_stream != 0; }
- Port &port() { return _port; }
void set_offline (bool);
bool offline() const { return _offline; }
@@ -136,7 +135,6 @@ class Parser {
void reset_mtc_state ();
private:
- Port&_port;
/* tracing */
std::ostream *trace_stream;
diff --git a/libs/midi++2/midi++/port.h b/libs/midi++2/midi++/port.h
index 153cfb0529..a915320fa3 100644
--- a/libs/midi++2/midi++/port.h
+++ b/libs/midi++2/midi++/port.h
@@ -55,13 +55,6 @@ class Port {
virtual XMLNode& get_state () const;
virtual void set_state (const XMLNode&);
- // FIXME: make Manager a friend of port so these can be hidden?
-
- /* Only for use by MidiManager. Don't ever call this. */
- virtual void cycle_start (pframes_t) {}
- /* Only for use by MidiManager. Don't ever call this. */
- virtual void cycle_end () {}
-
/** Write a message to port.
* @param msg Raw MIDI message to send
* @param msglen Size of @a msg
diff --git a/libs/midi++2/mmc.cc b/libs/midi++2/mmc.cc
index 1b8de40358..1b9481c9bf 100644
--- a/libs/midi++2/mmc.cc
+++ b/libs/midi++2/mmc.cc
@@ -22,12 +22,12 @@
#include <map>
#include "timecode/time.h"
+
#include "pbd/error.h"
+
#include "midi++/mmc.h"
#include "midi++/port.h"
-#include "midi++/jack_midi_port.h"
#include "midi++/parser.h"
-#include "midi++/manager.h"
#ifndef __INT_MAX__ // 'ssize_t' won't be defined yet
typedef long ssize_t;
@@ -199,16 +199,21 @@ static void build_mmc_cmd_map ()
mmc_cmd_map.insert (newpair);
}
-
-MachineControl::MachineControl (Manager* m, jack_client_t* jack)
+MachineControl::MachineControl ()
{
build_mmc_cmd_map ();
_receive_device_id = 0x7f;
_send_device_id = 0x7f;
+}
+
+void
+MachineControl::set_ports (MIDI::Port* ip, MIDI::Port* op)
+{
+ port_connections.drop_connections ();
- _input_port = m->add_port (new JackMIDIPort ("MMC in", Port::IsInput, jack));
- _output_port = m->add_port (new JackMIDIPort ("MMC out", Port::IsOutput, jack));
+ _input_port = ip;
+ _output_port = op;
_input_port->parser()->mmc.connect_same_thread (port_connections, boost::bind (&MachineControl::process_mmc_message, this, _1, _2, _3));
_input_port->parser()->start.connect_same_thread (port_connections, boost::bind (&MachineControl::spp_start, this));
diff --git a/libs/midi++2/mtc.cc b/libs/midi++2/mtc.cc
index 3c58b6684f..1a477c3b8c 100644
--- a/libs/midi++2/mtc.cc
+++ b/libs/midi++2/mtc.cc
@@ -240,7 +240,7 @@ Parser::process_mtc_quarter_frame (MIDI::byte *msg)
/* time code is looking good */
#ifdef DEBUG_MTC
- // cerr << "for quarter frame " << which_quarter_frame << " byte = " << hex << (int) msg[1] << dec << endl;
+ cerr << "for quarter frame " << which_quarter_frame << " byte = " << hex << (int) msg[1] << dec << endl;
#endif
switch (which_quarter_frame) {
diff --git a/libs/midi++2/parser.cc b/libs/midi++2/parser.cc
index 94daef7871..7d31c99781 100644
--- a/libs/midi++2/parser.cc
+++ b/libs/midi++2/parser.cc
@@ -104,8 +104,7 @@ Parser::midi_event_type_name (eventType t)
}
}
-Parser::Parser (Port &p)
- : _port(p)
+Parser::Parser ()
{
trace_stream = 0;
trace_prefix = "";
diff --git a/libs/midi++2/port.cc b/libs/midi++2/port.cc
index 3e7896631a..1480202867 100644
--- a/libs/midi++2/port.cc
+++ b/libs/midi++2/port.cc
@@ -71,7 +71,7 @@ Port::init (string const & name, Flags flags)
_tagname = name;
_flags = flags;
- _parser = new Parser (*this);
+ _parser = new Parser ();
for (int i = 0; i < 16; i++) {
_channel[i] = new Channel (i, *this);
diff --git a/libs/midi++2/wscript b/libs/midi++2/wscript
index ea8988110d..0abbab7d40 100644
--- a/libs/midi++2/wscript
+++ b/libs/midi++2/wscript
@@ -26,12 +26,11 @@ out = 'build'
path_prefix = 'libs/midi++2/'
+
libmidi_sources = [
'midi.cc',
'channel.cc',
'ipmidi_port.cc',
- 'jack_midi_port.cc',
- 'manager.cc',
'parser.cc',
'port.cc',
'midnam_patch.cc',
@@ -68,7 +67,7 @@ def build(bld):
obj.cxxflags = [ '-fPIC', '-DWITH_JACK_MIDI' ]
# everybody loves JACK
obj.export_includes = ['.']
- obj.includes = ['.', '../surfaces/control_protocol']
+ obj.includes = ['.', '../surfaces/control_protocol', '../ardour' ]
obj.name = 'libmidipp'
obj.target = 'midipp'
obj.uselib = 'GLIBMM SIGCPP XML JACK OSX'
diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc
index a8799944b3..e0b06a9369 100644
--- a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc
+++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc
@@ -32,13 +32,14 @@
#include "pbd/xml++.h"
#include "midi++/port.h"
-#include "midi++/manager.h"
+#include "ardour/audioengine.h"
#include "ardour/filesystem_paths.h"
#include "ardour/session.h"
#include "ardour/route.h"
#include "ardour/midi_ui.h"
#include "ardour/rc_configuration.h"
+#include "ardour/midiport_manager.h"
#include "generic_midi_control_protocol.h"
#include "midicontrollable.h"
@@ -59,8 +60,8 @@ GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
, _threshold (10)
, gui (0)
{
- _input_port = MIDI::Manager::instance()->midi_input_port ();
- _output_port = MIDI::Manager::instance()->midi_output_port ();
+ _input_port = s.midi_input_port ();
+ _output_port = s.midi_output_port ();
do_feedback = false;
_feedback_interval = 10000; // microseconds
@@ -338,7 +339,7 @@ GenericMidiControlProtocol::start_learning (Controllable* c)
}
if (!mc) {
- mc = new MIDIControllable (this, *_input_port, *c, false);
+ mc = new MIDIControllable (this, *_input_port->parser(), *c, false);
}
{
@@ -435,7 +436,7 @@ GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos,
MIDI::byte value = control_number;
// Create a MIDIControllable
- MIDIControllable* mc = new MIDIControllable (this, *_input_port, *control, false);
+ MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), *control, false);
// Remove any old binding for this midi channel/type/value pair
// Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
@@ -559,7 +560,7 @@ GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
Controllable* c = Controllable::by_id (id);
if (c) {
- MIDIControllable* mc = new MIDIControllable (this, *_input_port, *c, false);
+ MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), *c, false);
if (mc->set_state (**niter, version) == 0) {
controllables.push_back (mc);
@@ -754,7 +755,7 @@ GenericMidiControlProtocol::create_binding (const XMLNode& node)
prop = node.property (X_("uri"));
uri = prop->value();
- MIDIControllable* mc = new MIDIControllable (this, *_input_port, momentary);
+ MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), momentary);
if (mc->init (uri)) {
delete mc;
@@ -892,7 +893,7 @@ GenericMidiControlProtocol::create_function (const XMLNode& node)
prop = node.property (X_("function"));
- MIDIFunction* mf = new MIDIFunction (*_input_port);
+ MIDIFunction* mf = new MIDIFunction (*_input_port->parser());
if (mf->setup (*this, prop->value(), argument, data, data_size)) {
delete mf;
@@ -988,7 +989,7 @@ GenericMidiControlProtocol::create_action (const XMLNode& node)
prop = node.property (X_("action"));
- MIDIAction* ma = new MIDIAction (*_input_port);
+ MIDIAction* ma = new MIDIAction (*_input_port->parser());
if (ma->init (*this, prop->value(), data, data_size)) {
delete ma;
diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.h b/libs/surfaces/generic_midi/generic_midi_control_protocol.h
index a7c420cc41..a4a51b7f99 100644
--- a/libs/surfaces/generic_midi/generic_midi_control_protocol.h
+++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.h
@@ -22,14 +22,11 @@
#include <list>
#include <glibmm/threads.h>
+
#include "ardour/types.h"
#include "control_protocol/control_protocol.h"
-namespace MIDI {
- class Port;
-}
-
namespace PBD {
class Controllable;
class ControllableDescriptor;
@@ -37,6 +34,11 @@ namespace PBD {
namespace ARDOUR {
class Session;
+ class MidiPort;
+}
+
+namespace MIDI {
+ class Port;
}
class MIDIControllable;
@@ -51,8 +53,8 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
int set_active (bool yn);
static bool probe() { return true; }
- MIDI::Port* input_port () const { return _input_port; }
- MIDI::Port* output_port () const { return _output_port; }
+ MIDI::Port* input_port () const { return _input_port; }
+ MIDI::Port* output_port () const { return _output_port; }
void set_feedback_interval (ARDOUR::microseconds_t);
int set_feedback (bool yn);
@@ -97,8 +99,8 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
}
private:
- MIDI::Port* _input_port;
- MIDI::Port* _output_port;
+ MIDI::Port* _input_port;
+ MIDI::Port* _output_port;
ARDOUR::microseconds_t _feedback_interval;
ARDOUR::microseconds_t last_feedback_time;
diff --git a/libs/surfaces/generic_midi/midiaction.cc b/libs/surfaces/generic_midi/midiaction.cc
index e1eed1a49e..e537122e9c 100644
--- a/libs/surfaces/generic_midi/midiaction.cc
+++ b/libs/surfaces/generic_midi/midiaction.cc
@@ -25,7 +25,7 @@
using namespace MIDI;
-MIDIAction::MIDIAction (MIDI::Port& p)
+MIDIAction::MIDIAction (MIDI::Parser& p)
: MIDIInvokable (p)
{
}
diff --git a/libs/surfaces/generic_midi/midiaction.h b/libs/surfaces/generic_midi/midiaction.h
index 521f59f26d..e2a29143ad 100644
--- a/libs/surfaces/generic_midi/midiaction.h
+++ b/libs/surfaces/generic_midi/midiaction.h
@@ -36,8 +36,6 @@ namespace Gtk {
}
namespace MIDI {
- class Channel;
- class Port;
class Parser;
}
@@ -46,7 +44,7 @@ class GenericMidiControlProtocol;
class MIDIAction : public MIDIInvokable
{
public:
- MIDIAction (MIDI::Port&);
+ MIDIAction (MIDI::Parser&);
virtual ~MIDIAction ();
int init (GenericMidiControlProtocol&, const std::string& action_name, MIDI::byte* sysex = 0, size_t ssize = 0);
diff --git a/libs/surfaces/generic_midi/midicontrollable.cc b/libs/surfaces/generic_midi/midicontrollable.cc
index 4bf6eb0389..a26617fd67 100644
--- a/libs/surfaces/generic_midi/midicontrollable.cc
+++ b/libs/surfaces/generic_midi/midicontrollable.cc
@@ -31,6 +31,7 @@
#include "midi++/port.h"
#include "midi++/channel.h"
+#include "ardour/async_midi_port.h"
#include "ardour/automation_control.h"
#include "ardour/midi_ui.h"
#include "ardour/utils.h"
@@ -43,11 +44,11 @@ using namespace MIDI;
using namespace PBD;
using namespace ARDOUR;
-MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, Port& p, bool m)
+MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, MIDI::Parser& p, bool m)
: _surface (s)
, controllable (0)
, _descriptor (0)
- , _port (p)
+ , _parser (p)
, _momentary (m)
{
_learned = false; /* from URI */
@@ -60,10 +61,10 @@ MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, Port& p, bool
feedback = true; // for now
}
-MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, Port& p, Controllable& c, bool m)
+MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, MIDI::Parser& p, Controllable& c, bool m)
: _surface (s)
, _descriptor (0)
- , _port (p)
+ , _parser (p)
, _momentary (m)
{
set_controllable (&c);
@@ -150,7 +151,7 @@ void
MIDIControllable::learn_about_external_control ()
{
drop_external_control ();
- _port.parser()->any.connect_same_thread (midi_learn_connection, boost::bind (&MIDIControllable::midi_receiver, this, _1, _2, _3));
+ _parser.any.connect_same_thread (midi_learn_connection, boost::bind (&MIDIControllable::midi_receiver, this, _1, _2, _3));
}
void
@@ -358,12 +359,6 @@ MIDIControllable::midi_receiver (Parser &, MIDI::byte *msg, size_t /*len*/)
return;
}
- /* if the our port doesn't do input anymore, forget it ... */
-
- if (!_port.parser()) {
- return;
- }
-
bind_midi ((channel_t) (msg[0] & 0xf), eventType (msg[0] & 0xF0), msg[1]);
if (controllable) {
@@ -382,49 +377,43 @@ MIDIControllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
control_channel = chn;
control_additional = additional;
- if (_port.parser() == 0) {
- return;
- }
-
- Parser& p = *_port.parser();
-
int chn_i = chn;
switch (ev) {
case MIDI::off:
- p.channel_note_off[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_note_off, this, _1, _2));
+ _parser.channel_note_off[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_note_off, this, _1, _2));
/* if this is a togglee, connect to noteOn as well,
and we'll toggle back and forth between the two.
*/
if (_momentary) {
- p.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[1], boost::bind (&MIDIControllable::midi_sense_note_on, this, _1, _2));
+ _parser.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[1], boost::bind (&MIDIControllable::midi_sense_note_on, this, _1, _2));
}
_control_description = "MIDI control: NoteOff";
break;
case MIDI::on:
- p.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_note_on, this, _1, _2));
+ _parser.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_note_on, this, _1, _2));
if (_momentary) {
- p.channel_note_off[chn_i].connect_same_thread (midi_sense_connection[1], boost::bind (&MIDIControllable::midi_sense_note_off, this, _1, _2));
+ _parser.channel_note_off[chn_i].connect_same_thread (midi_sense_connection[1], boost::bind (&MIDIControllable::midi_sense_note_off, this, _1, _2));
}
_control_description = "MIDI control: NoteOn";
break;
case MIDI::controller:
- p.channel_controller[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_controller, this, _1, _2));
+ _parser.channel_controller[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_controller, this, _1, _2));
snprintf (buf, sizeof (buf), "MIDI control: Controller %d", control_additional);
_control_description = buf;
break;
case MIDI::program:
- p.channel_program_change[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_program_change, this, _1, _2));
+ _parser.channel_program_change[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_program_change, this, _1, _2));
_control_description = "MIDI control: ProgramChange";
break;
case MIDI::pitchbend:
- p.channel_pitchbend[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_pitchbend, this, _1, _2));
+ _parser.channel_pitchbend[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_pitchbend, this, _1, _2));
_control_description = "MIDI control: Pitchbend";
break;
diff --git a/libs/surfaces/generic_midi/midicontrollable.h b/libs/surfaces/generic_midi/midicontrollable.h
index dd9dc988b4..aeed05db4a 100644
--- a/libs/surfaces/generic_midi/midicontrollable.h
+++ b/libs/surfaces/generic_midi/midicontrollable.h
@@ -36,17 +36,20 @@ namespace PBD {
namespace MIDI {
class Channel;
- class Port;
class Parser;
}
class GenericMidiControlProtocol;
+namespace ARDOUR {
+ class AsyncMIDIPort;
+}
+
class MIDIControllable : public PBD::Stateful
{
public:
- MIDIControllable (GenericMidiControlProtocol *, MIDI::Port&, PBD::Controllable&, bool momentary);
- MIDIControllable (GenericMidiControlProtocol *, MIDI::Port&, bool momentary = false);
+ MIDIControllable (GenericMidiControlProtocol *, MIDI::Parser&, PBD::Controllable&, bool momentary);
+ MIDIControllable (GenericMidiControlProtocol *, MIDI::Parser&, bool momentary = false);
virtual ~MIDIControllable ();
int init (const std::string&);
@@ -72,7 +75,7 @@ class MIDIControllable : public PBD::Stateful
bool learned() const { return _learned; }
- MIDI::Port& get_port() const { return _port; }
+ MIDI::Parser& get_parser() { return _parser; }
PBD::Controllable* get_controllable() const { return controllable; }
void set_controllable (PBD::Controllable*);
const std::string& current_uri() const { return _current_uri; }
@@ -98,8 +101,8 @@ class MIDIControllable : public PBD::Stateful
GenericMidiControlProtocol* _surface;
PBD::Controllable* controllable;
PBD::ControllableDescriptor* _descriptor;
- std::string _current_uri;
- MIDI::Port& _port;
+ std::string _current_uri;
+ MIDI::Parser& _parser;
bool setting;
int last_value;
float last_controllable_value;
diff --git a/libs/surfaces/generic_midi/midifunction.cc b/libs/surfaces/generic_midi/midifunction.cc
index 70e9337861..b0dc95e4fc 100644
--- a/libs/surfaces/generic_midi/midifunction.cc
+++ b/libs/surfaces/generic_midi/midifunction.cc
@@ -25,7 +25,7 @@
using namespace MIDI;
-MIDIFunction::MIDIFunction (MIDI::Port& p)
+MIDIFunction::MIDIFunction (MIDI::Parser& p)
: MIDIInvokable (p)
{
}
diff --git a/libs/surfaces/generic_midi/midifunction.h b/libs/surfaces/generic_midi/midifunction.h
index 8f0b0218d0..88aff0ab0a 100644
--- a/libs/surfaces/generic_midi/midifunction.h
+++ b/libs/surfaces/generic_midi/midifunction.h
@@ -33,7 +33,6 @@
namespace MIDI {
class Channel;
- class Port;
class Parser;
}
@@ -64,7 +63,7 @@ class MIDIFunction : public MIDIInvokable
TrackSetSoloIsolate,
};
- MIDIFunction (MIDI::Port&);
+ MIDIFunction (MIDI::Parser&);
virtual ~MIDIFunction ();
int setup (GenericMidiControlProtocol&, const std::string& function_name, const std::string& argument, MIDI::byte* sysex = 0, size_t ssize = 0);
diff --git a/libs/surfaces/generic_midi/midiinvokable.cc b/libs/surfaces/generic_midi/midiinvokable.cc
index 79835835a4..42c74553d8 100644
--- a/libs/surfaces/generic_midi/midiinvokable.cc
+++ b/libs/surfaces/generic_midi/midiinvokable.cc
@@ -25,8 +25,8 @@
using namespace MIDI;
-MIDIInvokable::MIDIInvokable (MIDI::Port& p)
- : _port (p)
+MIDIInvokable::MIDIInvokable (MIDI::Parser& p)
+ : _parser (p)
{
data_size = 0;
data = 0;
@@ -127,12 +127,6 @@ MIDIInvokable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
control_channel = chn;
control_additional = additional;
- if (_port.parser() == 0) {
- return;
- }
-
- Parser& p = *_port.parser();
-
int chn_i = chn;
/* incoming MIDI is parsed by Ardour' MidiUI event loop/thread, and we want our handlers to execute in that context, so we use
@@ -141,27 +135,27 @@ MIDIInvokable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
switch (ev) {
case MIDI::off:
- p.channel_note_off[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_note_off, this, _1, _2));
+ _parser.channel_note_off[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_note_off, this, _1, _2));
break;
case MIDI::on:
- p.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_note_on, this, _1, _2));
+ _parser.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_note_on, this, _1, _2));
break;
case MIDI::controller:
- p.channel_controller[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_controller, this, _1, _2));
+ _parser.channel_controller[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_controller, this, _1, _2));
break;
case MIDI::program:
- p.channel_program_change[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_program_change, this, _1, _2));
+ _parser.channel_program_change[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_program_change, this, _1, _2));
break;
case MIDI::sysex:
- p.sysex.connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_sysex, this, _1, _2, _3));
+ _parser.sysex.connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_sysex, this, _1, _2, _3));
break;
case MIDI::any:
- p.any.connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_any, this, _1, _2, _3));
+ _parser.any.connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_any, this, _1, _2, _3));
break;
default:
diff --git a/libs/surfaces/generic_midi/midiinvokable.h b/libs/surfaces/generic_midi/midiinvokable.h
index 62cbab238c..f374a126a5 100644
--- a/libs/surfaces/generic_midi/midiinvokable.h
+++ b/libs/surfaces/generic_midi/midiinvokable.h
@@ -31,7 +31,6 @@
namespace MIDI {
class Channel;
- class Port;
class Parser;
}
@@ -40,12 +39,12 @@ class GenericMidiControlProtocol;
class MIDIInvokable : public PBD::Stateful
{
public:
- MIDIInvokable (MIDI::Port&);
+ MIDIInvokable (MIDI::Parser&);
virtual ~MIDIInvokable ();
virtual int init (GenericMidiControlProtocol&, const std::string&, MIDI::byte* data = 0, size_t dsize = 0);
- MIDI::Port& get_port() const { return _port; }
+ MIDI::Parser& get_parser() { return _parser; }
void bind_midi (MIDI::channel_t, MIDI::eventType, MIDI::byte);
MIDI::channel_t get_control_channel () { return control_channel; }
@@ -55,7 +54,7 @@ class MIDIInvokable : public PBD::Stateful
protected:
GenericMidiControlProtocol* _ui;
std::string _invokable_name;
- MIDI::Port& _port;
+ MIDI::Parser& _parser;
PBD::ScopedConnection midi_sense_connection[2];
MIDI::eventType control_type;
MIDI::byte control_additional;
diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc
index 95ac0c3025..194e35a26f 100644
--- a/libs/surfaces/mackie/mackie_control_protocol.cc
+++ b/libs/surfaces/mackie/mackie_control_protocol.cc
@@ -392,7 +392,9 @@ MackieControlProtocol::set_active (bool yn)
BaseUI::run ();
- create_surfaces ();
+ if (create_surfaces ()) {
+ return -1;
+ }
connect_session_signals ();
_active = true;
update_surfaces ();
@@ -605,7 +607,7 @@ MackieControlProtocol::set_profile (const string& profile_name)
_device_profile = d->second;
}
-void
+int
MackieControlProtocol::set_device (const string& device_name, bool allow_activation)
{
map<string,DeviceInfo>::iterator d = DeviceInfo::device_info.find (device_name);
@@ -614,7 +616,7 @@ MackieControlProtocol::set_device (const string& device_name, bool allow_activat
device_name, allow_activation));
if (d == DeviceInfo::device_info.end()) {
- return;
+ return -1;
}
if (_active) {
@@ -628,13 +630,16 @@ MackieControlProtocol::set_device (const string& device_name, bool allow_activat
set_active (true);
} else {
if (_active) {
- create_surfaces ();
+ if (create_surfaces ()) {
+ return -1;
+ }
switch_banks (0, true);
}
}
+ return 0;
}
-void
+int
MackieControlProtocol::create_surfaces ()
{
string device_name;
@@ -651,7 +656,13 @@ MackieControlProtocol::create_surfaces ()
for (uint32_t n = 0; n < 1 + _device_info.extenders(); ++n) {
- boost::shared_ptr<Surface> surface (new Surface (*this, device_name, n, stype));
+ boost::shared_ptr<Surface> surface;
+
+ try {
+ surface.reset (new Surface (*this, device_name, n, stype));
+ } catch (...) {
+ return -1;
+ }
{
Glib::Threads::Mutex::Lock lm (surfaces_lock);
@@ -697,6 +708,8 @@ MackieControlProtocol::create_surfaces ()
g_source_ref (psrc->gobj());
}
}
+
+ return 0;
}
void
@@ -1237,7 +1250,7 @@ MackieControlProtocol::midi_input_handler (IOCondition ioc, MIDI::Port* port)
}
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("data available on %1\n", port->name()));
- framepos_t now = session->engine().frame_time();
+ framepos_t now = session->engine().sample_time();
port->parse (now);
}
@@ -1574,14 +1587,17 @@ MackieControlProtocol::set_ipmidi_base (int16_t portnum)
}
}
-void
+int
MackieControlProtocol::ipmidi_restart ()
{
clear_ports ();
clear_surfaces ();
- create_surfaces ();
+ if (create_surfaces ()) {
+ return -1;
+ }
switch_banks (_current_initial_bank, true);
needs_ipmidi_restart = false;
+ return 0;
}
void
diff --git a/libs/surfaces/mackie/mackie_control_protocol.h b/libs/surfaces/mackie/mackie_control_protocol.h
index ffc06a32eb..4669247726 100644
--- a/libs/surfaces/mackie/mackie_control_protocol.h
+++ b/libs/surfaces/mackie/mackie_control_protocol.h
@@ -120,8 +120,8 @@ class MackieControlProtocol
Mackie::DeviceProfile& device_profile() { return _device_profile; }
int set_active (bool yn);
- void set_device (const std::string&, bool allow_activation = true);
- void set_profile (const std::string&);
+ int set_device (const std::string&, bool allow_activation = true);
+ void set_profile (const std::string&);
FlipMode flip_mode () const { return _flip_mode; }
ViewMode view_mode () const { return _view_mode; }
@@ -291,7 +291,7 @@ class MackieControlProtocol
ARDOUR::RouteNotificationList _last_selected_routes;
- void create_surfaces ();
+ int create_surfaces ();
bool periodic();
void build_gui ();
bool midi_input_handler (Glib::IOCondition ioc, MIDI::Port* port);
@@ -301,8 +301,8 @@ class MackieControlProtocol
void build_button_map ();
void gui_track_selection_changed (ARDOUR::RouteNotificationListPtr, bool save_list);
void _gui_track_selection_changed (ARDOUR::RouteNotificationList*, bool save_list);
- void ipmidi_restart ();
- void initialize ();
+ int ipmidi_restart ();
+ void initialize ();
/* BUTTON HANDLING */
diff --git a/libs/surfaces/mackie/surface.cc b/libs/surfaces/mackie/surface.cc
index 0c45a29378..507491d84c 100644
--- a/libs/surfaces/mackie/surface.cc
+++ b/libs/surfaces/mackie/surface.cc
@@ -24,7 +24,6 @@
#include <cmath>
#include "midi++/port.h"
-#include "midi++/manager.h"
#include "ardour/automation_control.h"
#include "ardour/debug.h"
@@ -87,7 +86,11 @@ Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, ui
{
DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n");
- _port = new SurfacePort (*this);
+ try {
+ _port = new SurfacePort (*this);
+ } catch (...) {
+ throw failed_constructor ();
+ }
/* only the first Surface object has global controls */
diff --git a/libs/surfaces/mackie/surface_port.cc b/libs/surfaces/mackie/surface_port.cc
index 9f52f0dccd..508f501f59 100644
--- a/libs/surfaces/mackie/surface_port.cc
+++ b/libs/surfaces/mackie/surface_port.cc
@@ -23,60 +23,48 @@
#include <sigc++/sigc++.h>
#include <boost/shared_array.hpp>
+#include "pbd/failed_constructor.h"
+
#include "midi++/types.h"
-#include "midi++/port.h"
-#include "midi++/jack_midi_port.h"
#include "midi++/ipmidi_port.h"
-#include "midi++/manager.h"
+#include "ardour/async_midi_port.h"
#include "ardour/debug.h"
#include "ardour/rc_configuration.h"
#include "ardour/session.h"
#include "ardour/audioengine.h"
+#include "ardour/async_midi_port.h"
+#include "ardour/midiport_manager.h"
#include "controls.h"
#include "mackie_control_protocol.h"
#include "surface.h"
#include "surface_port.h"
-
#include "i18n.h"
using namespace std;
using namespace Mackie;
using namespace PBD;
+using namespace ARDOUR;
-/** @param input_port Input MIDI::Port; this object takes responsibility for
- * adding & removing it from the MIDI::Manager and destroying it. @param
- * output_port Output MIDI::Port; responsibility similarly taken.
- */
SurfacePort::SurfacePort (Surface& s)
: _surface (&s)
- , _input_port (0)
- , _output_port (0)
{
+
if (_surface->mcp().device_info().uses_ipmidi()) {
_input_port = new MIDI::IPMIDIPort (_surface->mcp().ipmidi_base() +_surface->number());
_output_port = _input_port;
} else {
- jack_client_t* jack = MackieControlProtocol::instance()->get_session().engine().jack();
-
- _input_port = new MIDI::JackMIDIPort (string_compose (_("%1 in"), _surface->name()), MIDI::Port::IsInput, jack);
- _output_port =new MIDI::JackMIDIPort (string_compose (_("%1 out"), _surface->name()), MIDI::Port::IsOutput, jack);
-
- /* MackieControl has its own thread for handling input from the input
- * port, and we don't want anything handling output from the output
- * port. This stops the Generic MIDI UI event loop in ardour from
- * attempting to handle these ports.
- */
-
- _input_port->set_centrally_parsed (false);
- _output_port->set_centrally_parsed (false);
-
- MIDI::Manager * mm = MIDI::Manager::instance();
-
- mm->add_port (_input_port);
- mm->add_port (_output_port);
+ _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, string_compose (_("%1 in"), _surface->name()), true);
+ _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, string_compose (_("%1 out"), _surface->name()), true);
+
+ if (_async_in == 0 || _async_out == 0) {
+ throw failed_constructor();
+ }
+
+ _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
+ _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
}
}
@@ -86,17 +74,15 @@ SurfacePort::~SurfacePort()
delete _input_port;
} else {
- MIDI::Manager* mm = MIDI::Manager::instance ();
-
- if (_input_port) {
- mm->remove_port (_input_port);
- delete _input_port;
+ if (_async_in) {
+ AudioEngine::instance()->unregister_port (_async_in);
+ _async_in.reset ();
}
- if (_output_port) {
+ if (_async_out) {
_output_port->drain (10000);
- mm->remove_port (_output_port);
- delete _output_port;
+ AudioEngine::instance()->unregister_port (_async_out);
+ _async_out.reset ();
}
}
}
diff --git a/libs/surfaces/mackie/surface_port.h b/libs/surfaces/mackie/surface_port.h
index 7dc20a06f2..751ee848d7 100644
--- a/libs/surfaces/mackie/surface_port.h
+++ b/libs/surfaces/mackie/surface_port.h
@@ -21,16 +21,23 @@
#include <midi++/types.h>
#include "pbd/signals.h"
+
+
#include "midi_byte_array.h"
#include "types.h"
namespace MIDI {
- class Port;
class Parser;
+ class Port;
}
class MackieControlProtocol;
+namespace ARDOUR {
+ class AsyncMIDIPort;
+ class Port;
+}
+
namespace Mackie
{
@@ -49,17 +56,17 @@ public:
/// an easier way to output bytes via midi
int write (const MidiByteArray&);
- MIDI::Port& input_port() { return *_input_port; }
- const MIDI::Port& input_port() const { return *_input_port; }
- MIDI::Port& output_port() { return *_output_port; }
- const MIDI::Port& output_port() const { return *_output_port; }
+ MIDI::Port& input_port() const { return *_input_port; }
+ MIDI::Port& output_port() const { return *_output_port; }
protected:
private:
- Mackie::Surface* _surface;
- MIDI::Port* _input_port;
- MIDI::Port* _output_port;
+ Mackie::Surface* _surface;
+ MIDI::Port* _input_port;
+ MIDI::Port* _output_port;
+ boost::shared_ptr<ARDOUR::Port> _async_in;
+ boost::shared_ptr<ARDOUR::Port> _async_out;
};
std::ostream& operator << (std::ostream& , const SurfacePort& port);
diff --git a/midi_maps/Novation_Impulse61.map b/midi_maps/Novation_Impulse61.map
new file mode 100644
index 0000000000..516167d071
--- /dev/null
+++ b/midi_maps/Novation_Impulse61.map
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ArdourMIDIBindings version="1.0.0" name="Novation Impulse 61">
+
+<!-- 2013-09-04 Alexandre Prokoudine: Map file creation -->
+<!-- This mapping relies on the Basic MIDI preset in the Impulse 61. -->
+
+<!-- Transport controls -->
+
+ <Binding channel="1" ctl="112" function="transport-start"/>
+ <Binding channel="1" ctl="113" function="transport-end"/>
+ <Binding channel="1" ctl="114" function="transport-stop"/>
+ <Binding channel="1" ctl="115" function="transport-roll"/>
+ <Binding channel="1" ctl="116" function="loop-toggle"/>
+ <Binding channel="1" ctl="117" function="rec-enable"/>
+ <Binding channel="1" ctl="117" function="rec-disable"/>
+
+<!-- Gain controls. Faders are mapped to faders. -->
+
+ <Binding channel="1" ctl="41" uri="/route/gain 1"/>
+ <Binding channel="1" ctl="42" uri="/route/gain 2"/>
+ <Binding channel="1" ctl="43" uri="/route/gain 3"/>
+ <Binding channel="1" ctl="44" uri="/route/gain 4"/>
+ <Binding channel="1" ctl="45" uri="/route/gain 5"/>
+ <Binding channel="1" ctl="46" uri="/route/gain 6"/>
+ <Binding channel="1" ctl="47" uri="/route/gain 7"/>
+ <Binding channel="1" ctl="48" uri="/route/gain 8"/>
+ <Binding channel="1" ctl="49" uri="/bus/gain master"/>
+
+<!-- Pan controls. Encoders are mapped to pan direction. -->
+<!-- There doesn't seem to be a way to map encoders to pan width. -->
+
+ <Binding channel="1" ctl="21" uri="/route/pandirection 1"/>
+ <Binding channel="1" ctl="22" uri="/route/pandirection 2"/>
+ <Binding channel="1" ctl="23" uri="/route/pandirection 3"/>
+ <Binding channel="1" ctl="24" uri="/route/pandirection 4"/>
+ <Binding channel="1" ctl="25" uri="/route/pandirection 5"/>
+ <Binding channel="1" ctl="26" uri="/route/pandirection 6"/>
+ <Binding channel="1" ctl="27" uri="/route/pandirection 7"/>
+ <Binding channel="1" ctl="28" uri="/route/pandirection 8"/>
+
+<!-- Status controls -->
+<!-- Status buttons are mapped to mute toggles. -->
+<!-- Apparently Solo toggle only works when Automap is used. Sad but true. -->
+
+ <Binding channel="1" ctl="51" uri="/route/mute 1"/>
+ <Binding channel="1" ctl="52" uri="/route/mute 2"/>
+ <Binding channel="1" ctl="53" uri="/route/mute 3"/>
+ <Binding channel="1" ctl="54" uri="/route/mute 4"/>
+ <Binding channel="1" ctl="55" uri="/route/mute 5"/>
+ <Binding channel="1" ctl="56" uri="/route/mute 6"/>
+ <Binding channel="1" ctl="57" uri="/route/mute 7"/>
+ <Binding channel="1" ctl="58" uri="/route/mute 8"/>
+ <Binding channel="1" ctl="59" uri="/bus/mute master"/>"
+
+
+</ArdourMIDIBindings>
diff --git a/wscript b/wscript
index 17707ee41d..559f99d9e3 100644
--- a/wscript
+++ b/wscript
@@ -29,6 +29,7 @@ children = [
'libs/rubberband',
'libs/surfaces',
'libs/panners',
+ 'libs/backends',
'libs/timecode',
'libs/ardour',
'libs/gtkmm2ext',