#include "jack_audiobackend.h"
#include "jack_connection.h"
-#include "jack_portengine.h"
#include "jack_utils.h"
+#include "jack_session.h"
#include "i18n.h"
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)
+JACKAudioBackend::JACKAudioBackend (AudioEngine& e, AudioBackendInfo& info, boost::shared_ptr<JackConnection> jc)
+ : AudioBackend (e, info)
, _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_input_channels (0)
+ , _target_output_channels (0)
, _target_systemic_input_latency (0)
, _target_systemic_output_latency (0)
, _current_sample_rate (0)
, _current_buffer_size (0)
+ , _session (0)
{
+ _jack_connection->Connected.connect_same_thread (jack_connection_connection, boost::bind (&JACKAudioBackend::when_connected_to_jack, this));
_jack_connection->Disconnected.connect_same_thread (disconnect_connection, boost::bind (&JACKAudioBackend::disconnected, this, _1));
}
}
bool
-JACKAudioBackend::connected() const
+JACKAudioBackend::available() const
{
return (private_handle() != 0);
}
}
vector<float>
-JACKAudioBackend::available_sample_rates (const string& /*device*/) const
+JACKAudioBackend::available_sample_rates (const string& device) const
{
vector<float> f;
- if (connected()) {
+ if (device == _target_device && available()) {
f.push_back (sample_rate());
return f;
}
}
vector<uint32_t>
-JACKAudioBackend::available_buffer_sizes (const string& /*device*/) const
+JACKAudioBackend::available_buffer_sizes (const string& device) const
{
vector<uint32_t> s;
-
- if (connected()) {
+
+ if (device == _target_device && available()) {
s.push_back (buffer_size());
return s;
}
int
JACKAudioBackend::set_device_name (const string& dev)
{
- if (connected()) {
+ if (available()) {
/* need to stop and restart JACK for this to work, at present */
return -1;
}
int
JACKAudioBackend::set_sample_rate (float sr)
{
- if (!connected()) {
+ if (!available()) {
_target_sample_rate = sr;
return 0;
}
int
JACKAudioBackend::set_buffer_size (uint32_t nframes)
{
- if (!connected()) {
+ if (!available()) {
_target_buffer_size = nframes;
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)
{
int
JACKAudioBackend::set_input_channels (uint32_t cnt)
{
- if (connected()) {
- return -1;
+ if (available()) {
+ if (cnt != 0) {
+ /* can't set a real value for this while JACK runs */
+ return -1;
+ }
}
_target_input_channels = cnt;
int
JACKAudioBackend::set_output_channels (uint32_t cnt)
{
- if (connected()) {
- return -1;
+ if (available()) {
+ if (cnt != 0) {
+ /* can't set a real value for this while JACK runs */
+ return -1;
+ }
}
_target_output_channels = cnt;
int
JACKAudioBackend::set_systemic_input_latency (uint32_t l)
{
- if (connected()) {
+ if (available()) {
+ /* can't do this while JACK runs */
return -1;
}
int
JACKAudioBackend::set_systemic_output_latency (uint32_t l)
{
- if (connected()) {
+ if (available()) {
+ /* can't do this while JACK runs */
return -1;
}
std::string
JACKAudioBackend::device_name () const
{
- if (connected()) {
- return "???";
+ if (!_jack_connection->in_control()) {
+ return "???"; // JACK has no way (as of fall 2013) to return
+ // the device name
}
return _target_device;
}
+std::string
+JACKAudioBackend::driver_name() const
+{
+ if (!_jack_connection->in_control()) {
+ return "???"; // JACK has no way (as of fall 2013) to return
+ // the driver name
+ }
+
+ return _target_driver;
+}
+
float
JACKAudioBackend::sample_rate () const
{
- if (connected()) {
- return _current_sample_rate;
+ if (!_jack_connection->in_control()) {
+ if (available()) {
+ return _current_sample_rate;
+ } else {
+ return 0;
+ }
}
return _target_sample_rate;
}
uint32_t
JACKAudioBackend::buffer_size () const
{
- if (connected()) {
- return _current_buffer_size;
+ if (!_jack_connection->in_control()) {
+ if (available()) {
+ return _current_buffer_size;
+ } else {
+ return 0;
+ }
}
return _target_buffer_size;
}
-SampleFormat
-JACKAudioBackend::sample_format () const
-{
- return FormatFloat;
-}
-
bool
JACKAudioBackend::interleaved () const
{
return false;
}
+string
+JACKAudioBackend::midi_option () const
+{
+ return _target_midi_option;
+}
+
uint32_t
JACKAudioBackend::input_channels () const
{
- if (connected()) {
- return n_physical (JackPortIsInput).n_audio();
- }
- return _target_input_channels;
+ if (!_jack_connection->in_control()) {
+ if (available()) {
+ return n_physical (JackPortIsInput).n_audio();
+ } else {
+ return 0;
+ }
+ } else {
+ if (available()) {
+ return n_physical (JackPortIsInput).n_audio();
+ } else {
+ return _target_input_channels;
+ }
+ }
}
uint32_t
JACKAudioBackend::output_channels () const
{
- if (connected()) {
- return n_physical (JackPortIsOutput).n_audio();
- }
- return _target_output_channels;
+ if (!_jack_connection->in_control()) {
+ if (available()) {
+ return n_physical (JackPortIsOutput).n_audio();
+ } else {
+ return 0;
+ }
+ } else {
+ if (available()) {
+ return n_physical (JackPortIsOutput).n_audio();
+ } else {
+ return _target_output_channels;
+ }
+ }
}
uint32_t
}
void
-JACKAudioBackend::setup_jack_startup_command ()
+JACKAudioBackend::setup_jack_startup_command (bool for_latency_measurement)
{
/* first we map the parameters that have been set onto a
* JackCommandLineOptions object.
options.realtime = true;
options.ports_max = 2048;
+ ARDOUR::set_midi_option (options, _target_midi_option);
+
/* this must always be true for any server instance we start ourselves
*/
string cmdline;
- if (!get_jack_command_line_string (options, cmdline)) {
- /* error, somehow */
+ if (!get_jack_command_line_string (options, cmdline, for_latency_measurement)) {
+ /* error, somehow - we will still try to start JACK
+ * automatically but it will be without our preferred options
+ */
+ std::cerr << "get_jack_command_line_string () failed: using default settings." << std::endl;
return;
}
/* ---- BASIC STATE CONTROL API: start/stop/pause/freewheel --- */
int
-JACKAudioBackend::start ()
+JACKAudioBackend::_start (bool for_latency_measurement)
{
- if (!connected()) {
+ if (!available()) {
- if (!_jack_connection->server_running()) {
- setup_jack_startup_command ();
+ if (_jack_connection->in_control()) {
+ /* we will be starting JACK, so set up the
+ command that JACK will use when it (auto-)starts
+ */
+ setup_jack_startup_command (for_latency_measurement);
}
if (_jack_connection->open ()) {
/* Now that we have buffer size and sample rate established, the engine
can go ahead and do its stuff
*/
-
- engine.reestablish_ports ();
+
+ if (engine.reestablish_ports ()) {
+ error << _("Could not re-establish ports after connecting to JACK") << endmsg;
+ return -1;
+ }
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;
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)
{
}
if (jack_set_freewheel (_priv_jack, onoff) == 0) {
- _freewheeling = true;
+ _freewheeling = onoff;
return 0;
}
ARDOUR::Session* session = engine.session();
if (session) {
- session->jack_timebase_callback (state, nframes, pos, new_position);
+ JACKSession jsession (session);
+ jsession.timebase_callback (state, nframes, pos, new_position);
}
}
JACKAudioBackend::jack_sync_callback (jack_transport_state_t state, jack_position_t* pos)
{
TransportState tstate;
+ bool tstate_valid = true;
switch (state) {
- case JackTransportStopped:
- tstate = TransportStopped;
- break;
case JackTransportRolling:
tstate = TransportRolling;
break;
case JackTransportStarting:
tstate = TransportStarting;
break;
+ case JackTransportStopped:
+ tstate = TransportStopped;
+ break;
+ default:
+ // ignore "unofficial" states like JackTransportNetStarting (jackd2)
+ tstate_valid = false;
+ break;
}
- return engine.sync_callback (tstate, pos->frame);
+ if (tstate_valid) {
+ 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 */
+ JACKAudioBackend* jab = static_cast<JACKAudioBackend*> (arg);
+ if (jab->available()) {
+ jab->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();
+ JACKAudioBackend* jab = static_cast<JACKAudioBackend*> (arg);
+ ARDOUR::Session* session = jab->engine.session();
if (session) {
- session->jack_session_event (event);
+ JACKSession jsession (session);
+ jsession.session_event (event);
}
}
-#endif
void
JACKAudioBackend::_freewheel_callback (int onoff, void *arg)
}
int
-JACKAudioBackend::create_process_thread (boost::function<void()> f, pthread_t* thread, size_t stacksize)
+JACKAudioBackend::create_process_thread (boost::function<void()> f)
{
- GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
- ThreadData* td = new ThreadData (this, f, stacksize);
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+
+ jack_native_thread_t thread_id;
+ ThreadData* td = new ThreadData (this, f, thread_stack_size());
- if (jack_client_create_thread (_priv_jack, thread, jack_client_real_time_priority (_priv_jack),
+ if (jack_client_create_thread (_priv_jack, &thread_id, jack_client_real_time_priority (_priv_jack),
jack_is_realtime (_priv_jack), _start_process_thread, td)) {
return -1;
}
- return 0;
+ _jack_threads.push_back(thread_id);
+ return 0;
+}
+
+int
+JACKAudioBackend::join_process_threads ()
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+
+ int ret = 0;
+
+ for (std::vector<jack_native_thread_t>::const_iterator i = _jack_threads.begin ();
+ i != _jack_threads.end(); i++) {
+
+#if defined(USING_JACK2_EXPANSION_OF_JACK_API) || defined(PLATFORM_WINDOWS)
+ if (jack_client_stop_thread (_priv_jack, *i) != 0) {
+#else
+ void* status;
+ if (pthread_join (*i, &status) != 0) {
+#endif
+ error << "AudioEngine: cannot stop process thread" << endmsg;
+ ret += -1;
+ }
+ }
+
+ _jack_threads.clear();
+
+ return ret;
+}
+
+bool
+JACKAudioBackend::in_process_thread ()
+{
+ for (std::vector<jack_native_thread_t>::const_iterator i = _jack_threads.begin ();
+ i != _jack_threads.end(); i++) {
+
+#ifdef COMPILER_MINGW
+ if (*i == GetCurrentThread()) {
+ return true;
+ }
+#else // pthreads
+ if (pthread_equal (*i, pthread_self()) != 0) {
+ return true;
+ }
+#endif
+ }
+
+ return false;
+}
+
+uint32_t
+JACKAudioBackend::process_thread_count ()
+{
+ return _jack_threads.size();
}
void*
engine.halted_callback (why); /* EMIT SIGNAL */
}
}
+
float
-JACKAudioBackend::cpu_load() const
+JACKAudioBackend::dsp_load() const
{
GET_PRIVATE_JACK_POINTER_RET(_priv_jack,0);
return jack_cpu_load (_priv_jack);
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);
+ DataType t = port_data_type (jack_port_by_name (_priv_jack, ports[i]));
+ if (t != DataType::NIL) {
+ c.set (t, c.get (t) + 1);
+ }
}
}
if (_target_device == "Hammerfall DSP") {
appname = "hdspconf";
} else if (_target_device == "M Audio Delta 1010") {
- appname = "mudita";
+ appname = "mudita24";
+ } else if (_target_device == "M2496") {
+ appname = "mudita24";
}
}
} else {
args.push_back (appname);
Glib::spawn_async ("", args, Glib::SPAWN_SEARCH_PATH);
}
+
+vector<string>
+JACKAudioBackend::enumerate_midi_options () const
+{
+ return ARDOUR::enumerate_midi_options ();
+}
+
+int
+JACKAudioBackend::set_midi_option (const string& opt)
+{
+ _target_midi_option = opt;
+ return 0;
+}
+
+bool
+JACKAudioBackend::speed_and_position (double& speed, framepos_t& position)
+{
+ jack_position_t pos;
+ jack_transport_state_t state;
+ bool starting;
+
+ /* 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()
+ */
+
+ speed = 0;
+ position = 0;
+
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, true);
+
+ state = jack_transport_query (_priv_jack, &pos);
+
+ switch (state) {
+ case JackTransportStopped:
+ speed = 0;
+ starting = false;
+ break;
+ case JackTransportRolling:
+ speed = 1.0;
+ starting = false;
+ break;
+ case JackTransportLooping:
+ speed = 1.0;
+ starting = false;
+ break;
+ case JackTransportStarting:
+ starting = true;
+ // don't adjust speed here, just leave it as it was
+ break;
+ default:
+ starting = true; // jack2: JackTransportNetStarting
+ std::cerr << "WARNING: Unknown JACK transport state: " << state << std::endl;
+ }
+
+ position = pos.frame;
+ return starting;
+}