X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fbackends%2Fjack%2Fjack_audiobackend.cc;h=32c9f203fa01fa460a1d23d1590a71c866fd19f8;hb=afc1186759225f8ee6ced80578e3dc18224076ee;hp=1e7cb9d8d5f283b01dd27a81421adf477c54c36a;hpb=302b08c0592a6b7c40dec8c04f52c346e14b17af;p=ardour.git diff --git a/libs/backends/jack/jack_audiobackend.cc b/libs/backends/jack/jack_audiobackend.cc index 1e7cb9d8d5..32c9f203fa 100644 --- a/libs/backends/jack/jack_audiobackend.cc +++ b/libs/backends/jack/jack_audiobackend.cc @@ -27,9 +27,6 @@ #include "pbd/error.h" -#include "jack/jack.h" -#include "jack/thread.h" - #include "ardour/audioengine.h" #include "ardour/session.h" #include "ardour/types.h" @@ -37,6 +34,7 @@ #include "jack_audiobackend.h" #include "jack_connection.h" #include "jack_utils.h" +#include "jack_session.h" #include "i18n.h" @@ -45,17 +43,18 @@ 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; } -JACKAudioBackend::JACKAudioBackend (AudioEngine& e, boost::shared_ptr jc) - : AudioBackend (e) +JACKAudioBackend::JACKAudioBackend (AudioEngine& e, AudioBackendInfo& info, boost::shared_ptr jc) + : AudioBackend (e, info) , _jack_connection (jc) , _running (false) , _freewheeling (false) , _target_sample_rate (48000) , _target_buffer_size (1024) - , _target_sample_format (FormatFloat) + , _target_num_periods (2) , _target_interleaved (false) , _target_input_channels (0) , _target_output_channels (0) @@ -63,6 +62,7 @@ JACKAudioBackend::JACKAudioBackend (AudioEngine& e, boost::shared_ptrConnected.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)); @@ -73,7 +73,7 @@ JACKAudioBackend::~JACKAudioBackend() } string -JACKAudioBackend::name() const +JACKAudioBackend::name() const { return X_("JACK"); } @@ -127,8 +127,8 @@ JACKAudioBackend::enumerate_devices () const if (all_devices.find (_target_driver) == all_devices.end()) { all_devices.insert (make_pair (_target_driver, std::set())); } - - /* store every device we've found, by driver name. + + /* store every device we've found, by driver name. * * This is so we do not confuse ALSA, FFADO, netjack etc. devices * with each other. @@ -139,7 +139,7 @@ JACKAudioBackend::enumerate_devices () const for (vector::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)); @@ -147,21 +147,21 @@ JACKAudioBackend::enumerate_devices () const statuses.push_back (DeviceStatus (*d, false)); } } - + return statuses; } vector -JACKAudioBackend::available_sample_rates (const string& /*device*/) const +JACKAudioBackend::available_sample_rates (const string& device) const { vector f; - - if (available()) { + + if (device == _target_device && available()) { f.push_back (sample_rate()); return f; } - /* if JACK is not already running, just list a bunch of reasonable + /* if JACK is not already running, just list a bunch of reasonable values and let the future sort it all out. */ @@ -175,16 +175,16 @@ JACKAudioBackend::available_sample_rates (const string& /*device*/) const f.push_back (96000.0); f.push_back (192000.0); f.push_back (384000.0); - + return f; } vector -JACKAudioBackend::available_buffer_sizes (const string& /*device*/) const +JACKAudioBackend::available_buffer_sizes (const string& device) const { vector s; - - if (available()) { + + if (device == _target_device && available()) { s.push_back (buffer_size()); return s; } @@ -204,6 +204,17 @@ JACKAudioBackend::available_buffer_sizes (const string& /*device*/) const return s; } +std::vector +JACKAudioBackend::available_period_sizes (const std::string& driver) const +{ + vector s; + if (ARDOUR::get_jack_audio_driver_supports_setting_period_count (driver)) { + s.push_back (2); + s.push_back (3); + } + return s; +} + uint32_t JACKAudioBackend::available_input_channel_count (const string& /*device*/) const { @@ -247,6 +258,16 @@ JACKAudioBackend::set_sample_rate (float sr) return -1; } +int +JACKAudioBackend::set_peridod_size (uint32_t nperiods) +{ + if (!available()) { + _target_num_periods = nperiods; + return 0; + } + return -1; +} + int JACKAudioBackend::set_buffer_size (uint32_t nframes) { @@ -264,18 +285,6 @@ JACKAudioBackend::set_buffer_size (uint32_t nframes) 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) { @@ -299,7 +308,7 @@ JACKAudioBackend::set_input_channels (uint32_t cnt) } _target_input_channels = cnt; - + return 0; } @@ -352,7 +361,7 @@ JACKAudioBackend::device_name () const if (!_jack_connection->in_control()) { return "???"; // JACK has no way (as of fall 2013) to return // the device name - } + } return _target_device; } @@ -375,7 +384,7 @@ JACKAudioBackend::sample_rate () const if (available()) { return _current_sample_rate; } else { - return 0; + return _jack_connection->probed_sample_rate (); } } return _target_sample_rate; @@ -388,16 +397,16 @@ JACKAudioBackend::buffer_size () const if (available()) { return _current_buffer_size; } else { - return 0; + return _jack_connection->probed_buffer_size (); } } return _target_buffer_size; } -SampleFormat -JACKAudioBackend::sample_format () const +uint32_t +JACKAudioBackend::period_size () const { - return FormatFloat; + return _target_num_periods; } bool @@ -406,6 +415,12 @@ JACKAudioBackend::interleaved () const return false; } +string +JACKAudioBackend::midi_option () const +{ + return _target_midi_option; +} + uint32_t JACKAudioBackend::input_channels () const { @@ -454,7 +469,7 @@ JACKAudioBackend::systemic_output_latency () const return _target_systemic_output_latency; } -size_t +size_t JACKAudioBackend::raw_buffer_size(DataType t) { std::map::const_iterator s = _raw_buffer_sizes.find(t); @@ -462,7 +477,7 @@ JACKAudioBackend::raw_buffer_size(DataType 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. @@ -474,11 +489,16 @@ JACKAudioBackend::setup_jack_startup_command () options.driver = _target_driver; options.samplerate = _target_sample_rate; options.period_size = _target_buffer_size; - options.num_periods = 2; + options.num_periods = _target_num_periods; 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 (for_latency_measurement) { + options.input_latency = 0; + options.output_latency = 0; + } else { + options.input_latency = _target_systemic_input_latency; + options.output_latency = _target_systemic_output_latency; + } options.input_channels = _target_input_channels; options.output_channels = _target_output_channels; if (_target_sample_format == FormatInt16) { @@ -486,7 +506,9 @@ JACKAudioBackend::setup_jack_startup_command () } 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 */ @@ -498,6 +520,7 @@ JACKAudioBackend::setup_jack_startup_command () /* 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; } @@ -509,38 +532,44 @@ JACKAudioBackend::setup_jack_startup_command () /* ---- BASIC STATE CONTROL API: start/stop/pause/freewheel --- */ int -JACKAudioBackend::start () +JACKAudioBackend::_start (bool for_latency_measurement) { 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 ()) { 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 + + /* 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; } - + set_jack_callbacks (); - + if (jack_activate (_priv_jack) == 0) { _running = true; } else { @@ -555,8 +584,9 @@ JACKAudioBackend::start () int JACKAudioBackend::stop () { + _running = false; // no 'engine halted message'. GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1); - + _jack_connection->close (); _current_buffer_size = 0; @@ -567,18 +597,6 @@ JACKAudioBackend::stop () 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) { @@ -586,12 +604,12 @@ JACKAudioBackend::freewheel (bool onoff) if (onoff == _freewheeling) { /* already doing what has been asked for */ - + return 0; } if (jack_set_freewheel (_priv_jack, onoff) == 0) { - _freewheeling = true; + _freewheeling = onoff; return 0; } @@ -621,8 +639,8 @@ JACKAudioBackend::transport_locate (framepos_t where) jack_transport_locate (_priv_jack, where); } -framepos_t -JACKAudioBackend::transport_frame () const +framepos_t +JACKAudioBackend::transport_frame () const { GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0); return jack_get_current_transport_frame (_priv_jack); @@ -675,14 +693,14 @@ JACKAudioBackend::get_sync_offset (pframes_t& offset) const return false; } -pframes_t +framepos_t JACKAudioBackend::sample_time () { GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0); return jack_frame_time (_priv_jack); } -pframes_t +framepos_t JACKAudioBackend::sample_time_at_cycle_start () { GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0); @@ -744,7 +762,8 @@ JACKAudioBackend::jack_timebase_callback (jack_transport_state_t state, pframes_ 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); } } @@ -758,11 +777,9 @@ int 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; @@ -772,9 +789,18 @@ JACKAudioBackend::jack_sync_callback (jack_transport_state_t state, jack_positio 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; } @@ -789,7 +815,6 @@ JACKAudioBackend::_xrun_callback (void *arg) return 0; } -#ifdef HAVE_JACK_SESSION void JACKAudioBackend::_session_callback (jack_session_event_t *event, void *arg) { @@ -797,10 +822,10 @@ JACKAudioBackend::_session_callback (jack_session_event_t *event, void *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) @@ -822,25 +847,83 @@ JACKAudioBackend::_latency_callback (jack_latency_callback_mode_t mode, void* ar } int -JACKAudioBackend::create_process_thread (boost::function f, pthread_t* thread, size_t stacksize) +JACKAudioBackend::create_process_thread (boost::function f) { GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1); - ThreadData* td = new ThreadData (this, f, stacksize); - if (jack_client_create_thread (_priv_jack, thread, jack_client_real_time_priority (_priv_jack), + jack_native_thread_t thread_id; + ThreadData* td = new ThreadData (this, f, thread_stack_size()); + + 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::wait_for_process_thread_exit (AudioBackendNativeThread thr) +JACKAudioBackend::join_process_threads () { - void* status; - /* this doesn't actively try to stop the thread, it just waits till it exits */ - return pthread_join (thr, &status); + int ret = 0; + + for (std::vector::const_iterator i = _jack_threads.begin (); + i != _jack_threads.end(); i++) { + +#if defined(USING_JACK2_EXPANSION_OF_JACK_API) || defined __jack_systemdeps_h__ + // jack_client is not used by JACK2's implementation + // also jack_client_close() leaves threads active + if (jack_client_stop_thread (NULL, *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 () +{ +#if defined COMPILER_MINGW && (!defined PTW32_VERSION || defined __jack_systemdeps_h__) + if (_main_thread == GetCurrentThread()) { + return true; + } +#else // pthreads + if (pthread_equal (_main_thread, pthread_self()) != 0) { + return true; + } +#endif + + for (std::vector::const_iterator i = _jack_threads.begin (); + i != _jack_threads.end(); i++) { + +#if defined COMPILER_MINGW && (!defined PTW32_VERSION || defined __jack_systemdeps_h__) + 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* @@ -867,13 +950,20 @@ JACKAudioBackend::process_thread () /* JACK doesn't do this for us when we use the wait API */ +#if defined COMPILER_MINGW && (!defined PTW32_VERSION || defined __jack_systemdeps_h__) + _main_thread = GetCurrentThread(); +#else + _main_thread = pthread_self (); +#endif + + 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; } @@ -960,8 +1050,9 @@ JACKAudioBackend::disconnected (const char* why) engine.halted_callback (why); /* EMIT SIGNAL */ } } -float -JACKAudioBackend::cpu_load() const + +float +JACKAudioBackend::dsp_load() const { GET_PRIVATE_JACK_POINTER_RET(_priv_jack,0); return jack_cpu_load (_priv_jack); @@ -992,7 +1083,7 @@ JACKAudioBackend::n_physical (unsigned long flags) const } } } - + jack_free (ports); } @@ -1026,13 +1117,15 @@ JACKAudioBackend::control_app_name () const if (_target_driver.empty() || _target_device.empty()) { return appname; } - + if (_target_driver == "ALSA") { - + if (_target_device == "Hammerfall DSP") { appname = "hdspconf"; } else if (_target_device == "M Audio Delta 1010") { appname = "mudita24"; + } else if (_target_device == "M2496") { + appname = "mudita24"; } } } else { @@ -1056,3 +1149,68 @@ JACKAudioBackend::launch_control_app () args.push_back (appname); Glib::spawn_async ("", args, Glib::SPAWN_SEARCH_PATH); } + +vector +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; +} + +int +JACKAudioBackend::reset_device () +{ + /* XXX need to figure out what this means for JACK + */ + return 0; +}