diff --git a/libs/backends/jack/jack_connection.cc b/libs/backends/jack/jack_connection.cc new file mode 100644 index 0000000000..fee1b299ba --- /dev/null +++ b/libs/backends/jack/jack_connection.cc @@ -0,0 +1,176 @@ +/* + 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 + +#include +#include + +#include + +#include "pbd/epa.h" + +#include "jack_connection.h" +#include "jack_utils.h" + +#define GET_PRIVATE_JACK_POINTER(j) jack_client_t* _priv_jack = (jack_client_t*) (j); if (!_priv_jack) { return; } +#define GET_PRIVATE_JACK_POINTER_RET(j,r) jack_client_t* _priv_jack = (jack_client_t*) (j); if (!_priv_jack) { return r; } + +using namespace ARDOUR; +using namespace PBD; +using std::string; +using std::vector; +using std::cerr; +using std::endl; + +bool JackConnection::_in_control = false; + +static void jack_halted_callback (void* arg) +{ + JackConnection* jc = static_cast (arg); + jc->halted_callback (); +} + +static void jack_halted_info_callback (jack_status_t code, const char* reason, void* arg) +{ + JackConnection* jc = static_cast (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) +{ + /* See if the server is already up + */ + + EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa (); + boost::scoped_ptr 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); + _in_control = false; + } else { + _in_control = true; + } +} + +JackConnection::~JackConnection () +{ + close (); +} + +int +JackConnection::open () +{ + EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa (); + boost::scoped_ptr 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 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; + + /* If we started JACK, it will be closing down */ + Glib::usleep (500000); + + Disconnected (""); /* EMIT SIGNAL */ + + return ret; + } + + return 0; +} + +void +JackConnection::halted_callback () +{ + _jack = 0; + std::cerr << "JACK HALTED\n"; + Disconnected (""); +} + +void +JackConnection::halted_info_callback (jack_status_t /*status*/, const char* reason) +{ + _jack = 0; + std::cerr << "JACK HALTED: " << reason << std::endl; + Disconnected (reason); +} + + diff --git a/libs/backends/jack/jack_connection.h b/libs/backends/jack/jack_connection.h new file mode 100644 index 0000000000..8d15be6e3a --- /dev/null +++ b/libs/backends/jack/jack_connection.h @@ -0,0 +1,41 @@ +#ifndef __libardour_jack_connection_h__ +#define __libardour_jack_connection_h__ + +#include +#include + +#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 Connected; + PBD::Signal1 Disconnected; + + void halted_callback (); + void halted_info_callback (jack_status_t, const char*); + + static bool in_control() { return _in_control; } + + private: + jack_client_t* volatile _jack; + std::string _client_name; + std::string session_uuid; + static bool _in_control; +}; + +} // namespace + +#endif /* __libardour_jack_connection_h__ */ diff --git a/libs/backends/jack/jack_portengine.cc b/libs/backends/jack/jack_portengine.cc new file mode 100644 index 0000000000..1fe77fbb70 --- /dev/null +++ b/libs/backends/jack/jack_portengine.cc @@ -0,0 +1,508 @@ +/* + 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 +#include + +#include "pbd/error.h" + +#include "jack_audiobackend.h" +#include "jack_connection.h" +#include "jack/midiport.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 ""; +} + +void +JACKAudioBackend::when_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); +} + +int +JACKAudioBackend::set_port_name (PortHandle port, const std::string& name) +{ + return jack_port_set_name ((jack_port_t*) port, name.c_str()); +} + +string +JACKAudioBackend::get_port_name (PortHandle port) const +{ + return jack_port_name ((jack_port_t*) port); +} + +PortEngine::PortHandle +JACKAudioBackend:: 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 +JACKAudioBackend::_registration_callback (jack_port_id_t /*id*/, int /*reg*/, void* arg) +{ + static_cast (arg)->manager.registration_callback (); +} + +int +JACKAudioBackend::_graph_order_callback (void *arg) +{ + return static_cast (arg)->manager.graph_order_callback (); +} + +void +JACKAudioBackend::_connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn, void* arg) +{ + static_cast (arg)->connect_callback (id_a, id_b, conn); +} + +void +JACKAudioBackend::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 +JACKAudioBackend::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 +JACKAudioBackend::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 +JACKAudioBackend::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 +JACKAudioBackend::get_connections (PortHandle port, vector& 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 +JACKAudioBackend::port_data_type (PortHandle p) const +{ + return jack_port_type_to_ardour_data_type (jack_port_type ((jack_port_t*) p)); +} + +const string& +JACKAudioBackend::my_name() const +{ + return _jack_connection->client_name(); +} + +bool +JACKAudioBackend::port_is_physical (PortHandle ph) const +{ + if (!ph) { + return false; + } + + return jack_port_flags ((jack_port_t*) ph) & JackPortIsPhysical; +} + +int +JACKAudioBackend::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector& 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 +JACKAudioBackend::n_physical_inputs () const +{ + return n_physical (JackPortIsInput); +} + +ChanCount +JACKAudioBackend::n_physical_outputs () const +{ + return n_physical (JackPortIsOutput); +} + +void +JACKAudioBackend::get_physical (DataType type, unsigned long flags, vector& 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 +JACKAudioBackend::get_physical_inputs (DataType type, vector& ins) +{ + get_physical (type, JackPortIsOutput, ins); +} + +/** Get physical ports for which JackPortIsInput is set; ie those that correspond to + * a physical output connector. + */ +void +JACKAudioBackend::get_physical_outputs (DataType type, vector& outs) +{ + get_physical (type, JackPortIsInput, outs); +} + + +bool +JACKAudioBackend::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 +JACKAudioBackend::request_input_monitoring (PortHandle port, bool yn) +{ + return jack_port_request_monitor ((jack_port_t*) port, yn); +} +int +JACKAudioBackend::ensure_input_monitoring (PortHandle port, bool yn) +{ + return jack_port_ensure_monitor ((jack_port_t*) port, yn); +} +bool +JACKAudioBackend::monitoring_input (PortHandle port) +{ + return jack_port_monitoring_input ((jack_port_t*) port); +} + +PortEngine::PortHandle +JACKAudioBackend::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 +JACKAudioBackend::unregister_port (PortHandle port) +{ + GET_PRIVATE_JACK_POINTER (_priv_jack); + (void) jack_port_unregister (_priv_jack, (jack_port_t*) port); +} + +int +JACKAudioBackend::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 +JACKAudioBackend::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 +JACKAudioBackend::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 +JACKAudioBackend::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 +JACKAudioBackend::disconnect_all (PortHandle port) +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1); + return jack_port_disconnect (_priv_jack, (jack_port_t*) port); +} + +int +JACKAudioBackend::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 +JACKAudioBackend::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 +JACKAudioBackend::get_midi_event_count (void* port_buffer) +{ + return jack_midi_get_event_count (port_buffer); +} + +void +JACKAudioBackend::midi_clear (void* port_buffer) +{ + jack_midi_clear_buffer (port_buffer); +} + +void +JACKAudioBackend::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 +JACKAudioBackend::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* +JACKAudioBackend::get_buffer (PortHandle port, pframes_t nframes) +{ + return jack_port_get_buffer ((jack_port_t*) port, nframes); +} + +uint32_t +JACKAudioBackend::port_name_size() const +{ + return jack_port_name_size (); +} diff --git a/libs/backends/jack/jack_session.cc b/libs/backends/jack/jack_session.cc new file mode 100644 index 0000000000..60d11a8f0c --- /dev/null +++ b/libs/backends/jack/jack_session.cc @@ -0,0 +1,196 @@ +/* + 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 + +#include + +#include +#include + +#include "pbd/localtime_r.h" + +#include "ardour/audioengine.h" +#include "ardour/filename_extensions.h" +#include "ardour/session.h" +#include "ardour/session_directory.h" +#include "ardour/tempo.h" + +#include "jack_session.h" + +using namespace ARDOUR; +using std::string; + +JACKSession::JACKSession (Session* s) + : SessionHandlePtr (s) +{ +} + +JACKSession::~JACKSession () +{ +} + +void +JACKSession::session_event (jack_session_event_t* event) +{ + char timebuf[128], *tmp; + time_t n; + struct tm local_time; + + time (&n); + localtime_r (&n, &local_time); + strftime (timebuf, sizeof(timebuf), "JS_%FT%T", &local_time); + + while ((tmp = strchr(timebuf, ':'))) { *tmp = '.'; } + + if (event->type == JackSessionSaveTemplate) + { + if (_session->save_template( timebuf )) { + event->flags = JackSessionSaveError; + } else { + string cmd ("ardour3 -P -U "); + cmd += event->client_uuid; + cmd += " -T "; + cmd += timebuf; + + event->command_line = strdup (cmd.c_str()); + } + } + else + { + if (_session->save_state (timebuf)) { + event->flags = JackSessionSaveError; + } else { + std::string xml_path (_session->session_directory().root_path()); + std::string legalized_filename = legalize_for_path (timebuf) + statefile_suffix; + xml_path = Glib::build_filename (xml_path, legalized_filename); + + string cmd ("ardour3 -P -U "); + cmd += event->client_uuid; + cmd += " \""; + cmd += xml_path; + cmd += '\"'; + + event->command_line = strdup (cmd.c_str()); + } + } + + /* this won't be called if the port engine in use is not JACK, so we do + not have to worry about the type of PortEngine::private_handle() + */ + + jack_client_t* jack_client = (jack_client_t*) AudioEngine::instance()->port_engine().private_handle(); + + if (jack_client) { + jack_session_reply (jack_client, event); + } + + if (event->type == JackSessionSaveAndQuit) { + _session->Quit (); /* EMIT SIGNAL */ + } + + jack_session_event_free (event); +} + +void +JACKSession::timebase_callback (jack_transport_state_t /*state*/, + pframes_t /*nframes*/, + jack_position_t* pos, + int /*new_position*/) +{ + Timecode::BBT_Time bbt; + TempoMap& tempo_map (_session->tempo_map()); + framepos_t tf = _session->transport_frame (); + + /* BBT info */ + + TempoMetric metric (tempo_map.metric_at (tf)); + + try { + tempo_map.bbt_time_rt (tf, bbt); + + pos->bar = bbt.bars; + pos->beat = bbt.beats; + pos->tick = bbt.ticks; + + // XXX still need to set bar_start_tick + + pos->beats_per_bar = metric.meter().divisions_per_bar(); + pos->beat_type = metric.meter().note_divisor(); + pos->ticks_per_beat = Timecode::BBT_Time::ticks_per_beat; + pos->beats_per_minute = metric.tempo().beats_per_minute(); + + pos->valid = jack_position_bits_t (pos->valid | JackPositionBBT); + + } catch (...) { + /* no message */ + } + +#ifdef HAVE_JACK_VIDEO_SUPPORT + //poke audio video ratio so Ardour can track Video Sync + pos->audio_frames_per_video_frame = _session->frame_rate() / _session->timecode_frames_per_second(); + pos->valid = jack_position_bits_t (pos->valid | JackAudioVideoRatio); +#endif + +#ifdef HAVE_JACK_TIMCODE_SUPPORT + /* This is not yet defined in JACK */ + + /* Timecode info */ + + pos->timecode_offset = _session->config.get_timecode_offset(); + t.timecode_frame_rate = _session->timecode_frames_per_second(); + pos->valid = jack_position_bits_t (pos->valid | JackPositionTimecode); +#endif + +#ifdef HAVE_JACK_LOOPING_SUPPORT + /* This is not yet defined in JACK */ + if (_transport_speed) { + + if (play_loop) { + + Location* location = _session->locations()->auto_loop_location(); + + if (location) { + + t.transport_state = JackTransportLooping; + t.loop_start = location->start(); + t.loop_end = location->end(); + t.valid = jack_transport_bits_t (t.valid | JackTransportLoop); + + } else { + + t.loop_start = 0; + t.loop_end = 0; + t.transport_state = JackTransportRolling; + + } + + } else { + + t.loop_start = 0; + t.loop_end = 0; + t.transport_state = JackTransportRolling; + + } + + } +#endif +} + diff --git a/libs/backends/jack/jack_session.h b/libs/backends/jack/jack_session.h new file mode 100644 index 0000000000..c912b5f170 --- /dev/null +++ b/libs/backends/jack/jack_session.h @@ -0,0 +1,47 @@ +/* + 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 __ardour_jack_audiobackend_jack_session_h__ +#define __ardour_jack_audiobackend_jack_session_h__ + +#include +#include + +#include "ardour/types.h" +#include "ardour/session_handle.h" + +namespace ARDOUR { + class Session; + +class JACKSession : public ARDOUR::SessionHandlePtr +{ + public: + JACKSession (ARDOUR::Session* s); + ~JACKSession (); + + void session_event (jack_session_event_t* event); + void timebase_callback (jack_transport_state_t /*state*/, + ARDOUR::pframes_t /*nframes*/, + jack_position_t* pos, + int /*new_position*/); +}; + +} /* namespace */ + +#endif /* __ardour_jack_audiobackend_jack_session_h__ */ diff --git a/libs/backends/jack/jack_utils.h b/libs/backends/jack/jack_utils.h new file mode 100644 index 0000000000..ee8575c5c8 --- /dev/null +++ b/libs/backends/jack/jack_utils.h @@ -0,0 +1,240 @@ +/* + 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 + +#include +#include +#include + +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& 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& 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& 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& 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 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 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& 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&); + + /** + * 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& 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& server_dir_paths, + const std::vector& server_names, + std::vector& server_paths); + + + bool get_jack_server_paths (std::vector& server_paths); + + /** + * Get absolute path to default JACK server + */ + bool get_jack_default_server_path (std::string& server_path); + + typedef std::vector > MidiOptions; + + /** + * @return The name of the jack server config file + */ + std::string get_jack_server_config_file_name (); + + std::string get_jack_server_user_config_dir_path (); + + std::string get_jack_server_user_config_file_path (); + + bool write_jack_config_file (const std::string& config_file_path, const std::string& command_line); + + struct JackCommandLineOptions { + + // see implementation for defaults + JackCommandLineOptions (); + + //operator bool + //operator ostream + + std::string server_path; + uint32_t timeout; + bool no_mlock; + uint32_t ports_max; + bool realtime; + uint32_t priority; + bool unlock_gui_libs; + bool verbose; + bool temporary; + bool playback_only; + bool capture_only; + std::string driver; + std::string input_device; + std::string output_device; + uint32_t num_periods; + uint32_t period_size; + uint32_t samplerate; + uint32_t input_channels; + uint32_t output_channels; + uint32_t input_latency; + uint32_t output_latency; + bool hardware_metering; + bool hardware_monitoring; + std::string dither_mode; + bool force16_bit; + bool soft_mode; + std::string midi_driver; + }; + + std::vector enumerate_midi_options (); + int set_midi_option (ARDOUR::JackCommandLineOptions&, const std::string& opt); + + /** + * @return true if able to build a valid command line based on options + */ + bool get_jack_command_line_string (JackCommandLineOptions& options, std::string& command_line, bool for_latency_measurement); +}