fix merge conflicts with audioengine
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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@"
|
||||
|
||||
@@ -1011,3 +1011,8 @@ style "meter_strip_sep" = "default"
|
||||
{
|
||||
bg[NORMAL] = { 0.0, 0.0, 0.0 }
|
||||
}
|
||||
|
||||
style "settings_notebook" = "big_text"
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
|
||||
@@ -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 ();
|
||||
}
|
||||
|
||||
@@ -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 "$@"
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
@@ -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__ */
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#include <gtkmm2ext/utils.h>
|
||||
#include <gtkmm2ext/barcontroller.h>
|
||||
#include "midi++/manager.h"
|
||||
#include "pbd/fastlog.h"
|
||||
|
||||
#include "ardour_ui.h"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <gtkmm2ext/utils.h>
|
||||
#include <gtkmm2ext/barcontroller.h>
|
||||
|
||||
#include "midi++/manager.h"
|
||||
#include "pbd/fastlog.h"
|
||||
|
||||
#include "ardour/pannable.h"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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 ..."));
|
||||
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -410,7 +410,7 @@ ProxyBase::hide ()
|
||||
}
|
||||
|
||||
bool
|
||||
ProxyBase::handle_win_event (GdkEventAny *ev)
|
||||
ProxyBase::handle_win_event (GdkEventAny* /*ev*/)
|
||||
{
|
||||
hide();
|
||||
return true;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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__ */
|
||||
410
libs/ardour/ardour/audio_backend.h
Normal file
410
libs/ardour/ardour/audio_backend.h
Normal 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__ */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
39
libs/ardour/ardour/backend_search_path.h
Normal file
39
libs/ardour/ardour/backend_search_path.h
Normal 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__ */
|
||||
@@ -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
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -63,6 +63,7 @@ namespace PBD {
|
||||
extern uint64_t OrderKeys;
|
||||
extern uint64_t Automation;
|
||||
extern uint64_t WiimoteControl;
|
||||
extern uint64_t Ports;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
184
libs/ardour/ardour/jack_audiobackend.h
Normal file
184
libs/ardour/ardour/jack_audiobackend.h
Normal 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__ */
|
||||
|
||||
40
libs/ardour/ardour/jack_connection.h
Normal file
40
libs/ardour/ardour/jack_connection.h
Normal 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__ */
|
||||
132
libs/ardour/ardour/jack_portengine.h
Normal file
132
libs/ardour/ardour/jack_portengine.h
Normal 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__ */
|
||||
233
libs/ardour/ardour/jack_utils.h
Normal file
233
libs/ardour/ardour/jack_utils.h
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
|
||||
99
libs/ardour/ardour/midiport_manager.h
Normal file
99
libs/ardour/ardour/midiport_manager.h
Normal 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__
|
||||
@@ -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;
|
||||
|
||||
|
||||
345
libs/ardour/ardour/port_engine.h
Normal file
345
libs/ardour/ardour/port_engine.h
Normal 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__ */
|
||||
169
libs/ardour/ardour/port_manager.h
Normal file
169
libs/ardour/ardour/port_manager.h
Normal 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__ */
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
303
libs/ardour/async_midi_port.cc
Normal file
303
libs/ardour/async_midi_port.cc
Normal 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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
45
libs/ardour/backend_search_path.cc
Normal file
45
libs/ardour/backend_search_path.cc
Normal 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
|
||||
@@ -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 ());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ();
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
102
libs/ardour/jack_api.cc
Normal 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,
|
||||
};
|
||||
}
|
||||
|
||||
935
libs/ardour/jack_audiobackend.cc
Normal file
935
libs/ardour/jack_audiobackend.cc
Normal 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;
|
||||
}
|
||||
164
libs/ardour/jack_connection.cc
Normal file
164
libs/ardour/jack_connection.cc
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
568
libs/ardour/jack_portengine.cc
Normal file
568
libs/ardour/jack_portengine.cc
Normal 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 ();
|
||||
}
|
||||
@@ -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
891
libs/ardour/jack_utils.cc
Normal 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;
|
||||
}
|
||||
@@ -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))) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" */
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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*
|
||||
|
||||
170
libs/ardour/midiport_manager.cc
Normal file
170
libs/ardour/midiport_manager.cc
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user