fix merge conflicts with audioengine

This commit is contained in:
Paul Davis
2013-09-06 13:39:26 -04:00
146 changed files with 8446 additions and 4943 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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=$TOP/build/libs/ardour
#
# even though we set the above variables, ardour requires that these

View File

@@ -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@"

View File

@@ -1011,3 +1011,8 @@ style "meter_strip_sep" = "default"
{
bg[NORMAL] = { 0.0, 0.0, 0.0 }
}
style "settings_notebook" = "big_text"
{
}

View File

@@ -58,9 +58,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,6 +161,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
, nsm (0)
, _was_dirty (false)
, _mixer_on_top (false)
, first_time_engine_run (true)
/* transport */
@@ -214,6 +214,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
}
ui_config = new UIConfiguration();
_audio_midi_setup = new EngineControl;
editor = 0;
mixer = 0;
@@ -365,6 +366,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*
@@ -376,42 +379,148 @@ 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()
if (engine) {
return 0;
}
loading_message (_("Starting audio engine"));
try {
engine = new ARDOUR::AudioEngine (ARDOUR_COMMAND_LINE::jack_client_name, ARDOUR_COMMAND_LINE::jack_session_uuid);
} catch (...) {
return -1;
}
engine = AudioEngine::instance();
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());
engine->Halted.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::engine_halted, this, _1, false));
engine->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);
post_engine ();
/* 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().
*/
return 0;
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 ();
}
}
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);
}
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);
Glib::RefPtr<Action> action;
const char* action_name = 0;
switch (engine->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;
}
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 ();
}
update_disk_space ();
update_cpu_load ();
update_sample_rate (engine->sample_rate());
update_timecode_format ();
}
}
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 ();
@@ -477,25 +586,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 ()
@@ -681,6 +774,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));
@@ -915,7 +1009,8 @@ If you still wish to quit, please use the\n\n\
_session = 0;
}
engine->stop (true);
halt_connection.disconnect ();
engine->stop ();
quit ();
}
@@ -1046,20 +1141,26 @@ ARDOUR_UI::update_sample_rate (framecnt_t)
if (!engine->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 = engine->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,
(engine->usecs_per_cycle() / 1000.0f));
} else {
snprintf (buf, sizeof (buf), _("Audio: <span foreground=\"green\">%" PRId64 " kHz / %4.1f ms</span>"),
rate/1000,
(engine->usecs_per_cycle() / 1000.0f));
}
}
}
@@ -1183,6 +1284,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>"));
@@ -1664,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 ());
@@ -2027,127 +2140,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 ()
{
@@ -2698,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) {
@@ -3825,9 +3813,16 @@ void
ARDOUR_UI::disconnect_from_jack ()
{
if (engine) {
if (engine->disconnect_from_jack ()) {
/* drop connection to AudioEngine::Halted so that we don't act
* as if the engine unexpectedly shut down
*/
halt_connection.disconnect ();
if (engine->stop ()) {
MessageDialog msg (*editor, _("Could not disconnect from JACK"));
msg.run ();
} else {
engine->Halted.connect_same_thread (halt_connection, boost::bind (&ARDOUR_UI::engine_halted, this, _1, false));
}
update_sample_rate (0);
@@ -3838,7 +3833,7 @@ void
ARDOUR_UI::reconnect_to_jack ()
{
if (engine) {
if (engine->reconnect_to_jack ()) {
if (engine->start ()) {
MessageDialog msg (*editor, _("Could not reconnect to JACK"));
msg.run ();
}
@@ -4151,3 +4146,19 @@ ARDOUR_UI::reset_route_peak_display (Route* route)
reset_peak_display ();
}
}
EngineControl*
ARDOUR_UI::audio_midi_setup_widget ()
{
/* remove widget from any existing parent, since it is about
to be packed somewhere else.
*/
Gtk::Container* parent = _audio_midi_setup->get_parent ();
if (parent) {
parent->remove (*_audio_midi_setup);
}
return _audio_midi_setup;
}

View File

@@ -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 *);
@@ -288,6 +289,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
const std::string& announce_string() const { return _announce_string; }
EngineControl* audio_midi_setup_widget();
protected:
friend class PublicEditor;
@@ -315,6 +318,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
NSM_Client *nsm;
bool _was_dirty;
bool _mixer_on_top;
bool first_time_engine_run;
void goto_editor_window ();
void goto_mixer_window ();
@@ -710,6 +714,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 +751,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
std::string _announce_string;
void check_announcements ();
EngineControl* _audio_midi_setup;
};
#endif /* __ardour_gui_h__ */

View File

@@ -161,10 +161,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.
*/

View File

@@ -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 */
/* AudioEngine actions */
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"));
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 (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 (engine_actions, X_("JACKReconnect"), _("Reconnect"), sigc::mem_fun (*(ARDOUR_UI::instance()), &ARDOUR_UI::reconnect_to_jack));
ActionManager::engine_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);
act = ActionManager::register_action (engine_actions, X_("JACKDisconnect"), _("Disconnect"), sigc::mem_fun (*(ARDOUR_UI::instance()), &ARDOUR_UI::disconnect_from_jack));
ActionManager::engine_sensitive_actions.push_back (act);
RadioAction::Group jack_latency_group;
RadioAction::Group 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);
act = ActionManager::register_radio_action (engine_actions, latency_group, X_("JACKLatency32"), X_("32"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 32));
ActionManager::engine_sensitive_actions.push_back (act);
act = ActionManager::register_radio_action (engine_actions, latency_group, X_("JACKLatency64"), X_("64"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 64));
ActionManager::engine_sensitive_actions.push_back (act);
act = ActionManager::register_radio_action (engine_actions, latency_group, X_("JACKLatency128"), X_("128"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 128));
ActionManager::engine_sensitive_actions.push_back (act);
act = ActionManager::register_radio_action (engine_actions, latency_group, X_("JACKLatency256"), X_("256"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 256));
ActionManager::engine_sensitive_actions.push_back (act);
act = ActionManager::register_radio_action (engine_actions, latency_group, X_("JACKLatency512"), X_("512"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 512));
ActionManager::engine_sensitive_actions.push_back (act);
act = ActionManager::register_radio_action (engine_actions, latency_group, X_("JACKLatency1024"), X_("1024"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 1024));
ActionManager::engine_sensitive_actions.push_back (act);
act = ActionManager::register_radio_action (engine_actions, latency_group, X_("JACKLatency2048"), X_("2048"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 2048));
ActionManager::engine_sensitive_actions.push_back (act);
act = ActionManager::register_radio_action (engine_actions, latency_group, X_("JACKLatency4096"), X_("4096"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 4096));
ActionManager::engine_sensitive_actions.push_back (act);
act = ActionManager::register_radio_action (engine_actions, latency_group, X_("JACKLatency8192"), X_("8192"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_jack_buffer_size), (pframes_t) 8192));
ActionManager::engine_sensitive_actions.push_back (act);
/* these actions are intended to be shared across all windows */
@@ -421,7 +421,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);
@@ -647,11 +647,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 ();
}

View File

@@ -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 "$@"

View File

@@ -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());

View File

@@ -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);

File diff suppressed because it is too large Load Diff

View File

@@ -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>
@@ -40,87 +39,108 @@ class EngineControl : public Gtk::VBox {
~EngineControl ();
static bool need_setup ();
int setup_engine ();
int setup_engine (bool start);
bool was_used() const { return _used; }
XMLNode& get_state ();
void set_state (const XMLNode&);
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::Notebook notebook;
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;
/* core fields used by all backends */
Gtk::Button start_button;
Gtk::Button stop_button;
Gtk::HButtonBox button_box;
Gtk::ComboBoxText backend_combo;
Gtk::ComboBoxText input_device_combo;
Gtk::ComboBoxText output_device_combo;
Gtk::ComboBoxText sample_rate_combo;
Gtk::ComboBoxText buffer_size_combo;
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::ComboBoxText sample_rate_combo;
Gtk::ComboBoxText period_size_combo;
/* 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::HBox midi_hbox;
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;
sigc::connection sr_connection;
sigc::connection bs_connection;
static bool engine_running ();
void driver_changed ();
void backend_changed ();
void sample_rate_changed ();
void buffer_size_changed ();
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;
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;
bool _used;
void audio_mode_changed ();
void device_changed ();
void list_devices ();
void reshow_buffer_sizes (bool choice_changed);
static bool engine_running ();
struct State {
std::string backend;
std::string driver;
std::string device;
std::string sample_rate;
std::string buffer_size;
std::string input_latency;
std::string output_latency;
std::string input_channels;
std::string output_channels;
bool active;
void driver_changed ();
void build_command_line (std::vector<std::string>&);
State() : active (false) {};
};
typedef std::list<State> StateList;
std::map<std::string,std::vector<std::string> > devices;
std::vector<std::string> backend_devs;
void enumerate_devices (const std::string& driver);
StateList states;
#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);
State* get_matching_state (const std::string& backend,
const std::string& driver,
const std::string& device);
State* get_current_state ();
void maybe_set_state ();
void save_state ();
};
#endif /* __gtk2_ardour_engine_dialog_h__ */

View File

@@ -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"
@@ -864,7 +863,7 @@ GainMeterBase::update_meters()
}
}
void GainMeterBase::color_handler(bool /* dpi */)
void GainMeterBase::color_handler(bool /*dpi*/)
{
setup_meters();
}

View File

@@ -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"

View File

@@ -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);
/* see if it is connected to the other one ... */
char const ** p = c;
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;
}

View File

@@ -23,7 +23,6 @@
#include <gtkmm2ext/utils.h>
#include <gtkmm2ext/barcontroller.h>
#include "midi++/manager.h"
#include "pbd/fastlog.h"
#include "ardour_ui.h"

View File

@@ -372,34 +372,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 */
}
@@ -407,15 +403,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;
}
}

View File

@@ -24,7 +24,10 @@
#include <time.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"
@@ -53,7 +56,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;
@@ -126,24 +130,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);
boost::shared_ptr<const Manager::PortList> p = Manager::instance()->get_midi_ports ();
for (Manager::PortList::const_iterator i = p->begin(); i != p->end(); ++i) {
if (pl.empty()) {
_port_combo.set_active_text ("");
return;
}
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) {
p->parser()->any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3));
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) {
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));
}
}
@@ -366,9 +404,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));
}
}
@@ -376,7 +414,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());

View File

@@ -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;

View File

@@ -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:

View File

@@ -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;

View File

@@ -21,7 +21,6 @@
#include <gtkmm2ext/utils.h>
#include <gtkmm2ext/barcontroller.h>
#include "midi++/manager.h"
#include "pbd/fastlog.h"
#include "ardour/pannable.h"

View File

@@ -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"

View File

@@ -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);
}
vector<string> ports;
if (AudioEngine::instance()->get_ports ("", type, inputs ? IsInput : IsOutput, ports) > 0) {
if (ports) {
for (vector<string>::const_iterator s = ports.begin(); s != ports.end(); ) {
int n = 0;
while (ports[n]) {
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) {

View File

@@ -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"));

View File

@@ -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 ..."));

View File

@@ -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"));

View File

@@ -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());

View File

@@ -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

View File

@@ -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"
@@ -91,7 +92,7 @@ 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_audio_setup = AudioEngine::instance()->setup_required ();
need_session_info = (session_name.empty() || require_new);
_provided_session_name = session_name;
@@ -313,7 +314,7 @@ ArdourStartup::session_folder ()
void
ArdourStartup::setup_audio_page ()
{
engine_dialog = manage (new EngineControl);
engine_dialog = ARDOUR_UI::instance()->audio_midi_setup_widget ();
engine_dialog->set_border_width (12);
@@ -403,8 +404,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 ();
@@ -662,7 +661,7 @@ void
ArdourStartup::on_apply ()
{
if (engine_dialog) {
if (engine_dialog->setup_engine ()) {
if (engine_dialog->setup_engine (true)) {
set_current_page (audio_page_index);
return;
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -410,7 +410,7 @@ ProxyBase::hide ()
}
bool
ProxyBase::handle_win_event (GdkEventAny *ev)
ProxyBase::handle_win_event (GdkEventAny* /*ev*/)
{
hide();
return true;

View File

@@ -516,6 +516,7 @@ def build(bld):
'SMALLER' : '9',
'SMALL' : '10',
'NORMAL' : '11',
'BIG' : '13',
'BIGGER' : '17',
'LARGE' : '18',
'LARGER' : '28',
@@ -541,6 +542,7 @@ def build(bld):
'SMALLER' : '8',
'SMALL' : '9',
'NORMAL' : '10',
'BIG' : '14',
'BIGGER' : '17',
'LARGE' : '18',
'LARGER' : '24',

View File

@@ -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();

View File

@@ -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,68 +34,46 @@
#include "midi++/parser.h"
#include "midi++/port.h"
namespace MIDI {
#include "ardour/midi_port.h"
class Channel;
class PortRequest;
namespace ARDOUR {
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 { return xthread.selectable(); }
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;
Glib::Threads::Mutex output_fifo_lock;
CrossThreadChannel xthread;
Evoral::EventRingBuffer<MIDI::timestamp_t> input_fifo;
Glib::Threads::Mutex output_fifo_lock;
CrossThreadChannel xthread;
int create_port ();
/** Channel used to signal to the MidiControlUI that input has arrived */
std::string _connections;
PBD::ScopedConnection connect_connection;
PBD::ScopedConnection halt_connection;
void flush (void* jack_port_buffer);
void jack_halted ();
void make_connections ();
void init (std::string const &, Flags);
void flush_output_fifo (pframes_t);
static pthread_t _process_thread;
};
} // namespace MIDI
} // namespace ARDOUR
#endif // __libmidi_port_h__
#endif /* __libardour_async_midiport_h__ */

View File

@@ -0,0 +1,410 @@
/*
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;
/* 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;
/* 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__ */

View File

@@ -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 */

View File

@@ -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:

View File

@@ -33,21 +33,15 @@
#include <glibmm/threads.h>
#include "pbd/rcu.h"
#include "pbd/signals.h"
#include "pbd/stacktrace.h"
#include <jack/weakjack.h>
#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>
@@ -60,299 +54,173 @@ 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()>, pthread_t*, size_t stacksize);
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 *);
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;
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__ */

View File

@@ -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__ */

View File

@@ -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

View File

@@ -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__

View File

@@ -63,6 +63,7 @@ namespace PBD {
extern uint64_t OrderKeys;
extern uint64_t Automation;
extern uint64_t WiimoteControl;
extern uint64_t Ports;
}
}

View File

@@ -38,6 +38,7 @@ extern const char* const route_templates_dir_name;
extern const char* const surfaces_dir_name;
extern const char* const user_config_dir_name;
extern const char* const panner_dir_name;
extern const char* const backend_dir_name;
};

View File

@@ -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 ();

View File

@@ -0,0 +1,184 @@
/*
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;
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;
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__ */

View File

@@ -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__ */

View File

@@ -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__ */

View File

@@ -0,0 +1,233 @@
/*
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_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);
}

View File

@@ -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);

View File

@@ -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; }

View File

@@ -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);
protected:
friend class AudioEngine;
void set_always_parse (bool yn);
MIDI::Parser& self_parser() { return _self_parser; }
MidiPort (const std::string& name, Flags);
protected:
friend class PortManager;
MidiPort (const std::string& name, PortFlags);
private:
MidiBuffer* _buffer;
bool _has_been_mixed_down;
bool _resolve_required;
bool _input_active;
bool _always_parse;
void resolve_notes (void* jack_buffer, MidiBuffer::TimeType when);
/* 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

View File

@@ -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 ();

View File

@@ -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__

View File

@@ -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;

View File

@@ -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__ */

View File

@@ -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__ */

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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);
@@ -813,8 +812,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 +862,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);
@@ -1216,7 +1236,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 +1281,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 +1456,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 +1611,10 @@ 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;
};
} // namespace ARDOUR

View File

@@ -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;
};

View File

@@ -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;
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;
class Position;
boost::scoped_ptr<Position> _pos;
double one_ppqn_in_frames (framepos_t transport_position);
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);
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__ */

View File

@@ -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 ();

View File

@@ -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

View File

@@ -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<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 byte * msg, size_t msglen, 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 << " 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;
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);
}

View File

@@ -1754,7 +1754,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 ();
}
@@ -1775,7 +1775,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 ();
@@ -2041,12 +2041,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);
}
}
@@ -2369,13 +2369,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)

View File

@@ -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);
}

View File

@@ -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 */
}

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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 ());

View File

@@ -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;
}
}
}

View File

@@ -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 ();
}

View File

@@ -60,5 +60,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");

View File

@@ -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

View File

@@ -37,6 +37,7 @@ const char* const templates_dir_name = X_("templates");
const char* const route_templates_dir_name = X_("route_templates");
const char* const surfaces_dir_name = X_("surfaces");
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__

View File

@@ -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)

View File

@@ -54,7 +54,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 ()
@@ -505,7 +505,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);

View File

@@ -29,6 +29,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#ifdef WINDOWS_VST_SUPPORT
#include <fst.h>
@@ -66,11 +67,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"
@@ -78,6 +79,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"
@@ -330,6 +332,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;
@@ -338,9 +342,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;
@@ -536,3 +537,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);
}

View File

@@ -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;
}

View File

@@ -1337,7 +1337,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;
@@ -1371,7 +1371,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);
char buf1[name_size+1];
char buf2[name_size+1];
@@ -1404,10 +1404,11 @@ IO::find_port_hole (const char* base)
*/
for (n = 1; n < 9999; ++n) {
char buf[jack_port_name_size()];
size_t size = AudioEngine::instance()->port_name_size() + 1;
char buf[size];
PortSet::iterator i = _ports.begin();
snprintf (buf, jack_port_name_size(), _("%s %u"), base, n);
snprintf (buf, size, _("%s %u"), base, n);
for ( ; i != _ports.end(); ++i) {
if (i->name() == buf) {
@@ -1638,7 +1639,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);
}

102
libs/ardour/jack_api.cc Normal file
View File

@@ -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 "ardour/jack_connection.h"
#include "ardour/jack_audiobackend.h"
#include "ardour/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,
};
}

View File

@@ -0,0 +1,935 @@
/*
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 <math.h>
#include <boost/scoped_ptr.hpp>
#include <glibmm/timer.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 "ardour/jack_audiobackend.h"
#include "ardour/jack_connection.h"
#include "ardour/jack_portengine.h"
#include "ardour/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
{
return string();
}
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;
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;
}

View File

@@ -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 "ardour/jack_connection.h"
#include "ardour/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);
}

View File

@@ -0,0 +1,568 @@
/*
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 "ardour/jack_portengine.h"
#include "ardour/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 ();
}

View File

@@ -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;
}

891
libs/ardour/jack_utils.cc Normal file
View File

@@ -0,0 +1,891 @@
/*
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 WAF_BUILD
#include "libardour-config.h"
#endif
#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 "ardour/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 (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;
}

View File

@@ -36,8 +36,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"
@@ -114,7 +112,9 @@ LadspaPlugin::init (void *mod, 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))) {

View File

@@ -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 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;

View File

@@ -290,7 +290,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;
@@ -1924,7 +1924,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" */

View File

@@ -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

View File

@@ -31,6 +31,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"
@@ -41,7 +43,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
{
@@ -64,19 +66,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

View File

@@ -202,8 +202,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
@@ -376,8 +376,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;
}
}
@@ -446,7 +446,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);
@@ -799,7 +799,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 ||
@@ -834,7 +834,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:
@@ -1044,7 +1044,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);
}
}
@@ -1111,7 +1111,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;
@@ -1259,12 +1259,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);
}
}

View File

@@ -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);
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 JACK MIDI port 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;
}

View File

@@ -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;
@@ -106,8 +95,8 @@ MidiControlUI::midi_input_handler (IOCondition ioc, MIDI::Port* port)
CrossThreadChannel::drain (port->selectable());
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);
}
@@ -128,22 +117,19 @@ MidiControlUI::clear_ports ()
void
MidiControlUI::reset_ports ()
{
clear_ports ();
if (port_sources.empty()) {
AsyncMIDIPort* async = dynamic_cast<AsyncMIDIPort*> (_session.midi_input_port());
boost::shared_ptr<const MIDI::Manager::PortList> plist = MIDI::Manager::instance()->get_midi_ports ();
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*

View File

@@ -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;
}

View File

@@ -25,11 +25,12 @@
#include "pbd/error.h"
#include "midi++/port.h"
#include "ardour/debug.h"
#include "ardour/slave.h"
#include "ardour/session.h"
#include "ardour/audioengine.h"
#include "ardour/debug.h"
#include "ardour/midi_buffer.h"
#include "ardour/midi_port.h"
#include "ardour/session.h"
#include "ardour/slave.h"
#include "i18n.h"
@@ -48,8 +49,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;
@@ -70,7 +72,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()
@@ -94,15 +99,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
@@ -154,7 +156,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
@@ -255,7 +258,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)
@@ -306,7 +308,7 @@ MTC_Slave::update_mtc_time (const 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_self()));
DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", ::pthread_self()));
TimecodeFormat tc_format;
bool reset_tc = true;
@@ -422,7 +424,7 @@ MTC_Slave::update_mtc_time (const 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_self()));
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);
@@ -446,7 +448,7 @@ MTC_Slave::update_mtc_time (const 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;
@@ -526,9 +528,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;
@@ -551,7 +554,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
@@ -575,11 +578,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();
@@ -589,11 +592,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;
}
@@ -625,9 +638,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) {
@@ -643,8 +654,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() ));
}
}
@@ -657,14 +668,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",

Some files were not shown because too many files have changed in this diff Show More