#include "pbd/error.h"
-#include "jack/jack.h"
-#include "jack/thread.h"
-
#include "ardour/audioengine.h"
#include "ardour/session.h"
#include "ardour/types.h"
#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_num_periods (2)
, _target_interleaved (false)
, _target_input_channels (0)
, _target_output_channels (0)
}
string
-JACKAudioBackend::name() const
+JACKAudioBackend::name() const
{
return X_("JACK");
}
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.
+
+ /* store every device we've found, by driver name.
*
* This is so we do not confuse ALSA, FFADO, netjack etc. devices
* with each other.
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));
statuses.push_back (DeviceStatus (*d, false));
}
}
-
+
return statuses;
}
JACKAudioBackend::available_sample_rates (const string& device) const
{
vector<float> f;
-
+
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.
*/
f.push_back (96000.0);
f.push_back (192000.0);
f.push_back (384000.0);
-
+
return f;
}
JACKAudioBackend::available_buffer_sizes (const string& device) const
{
vector<uint32_t> s;
-
+
if (device == _target_device && available()) {
s.push_back (buffer_size());
return s;
return s;
}
+std::vector<uint32_t>
+JACKAudioBackend::available_period_sizes (const std::string& driver) const
+{
+ vector<uint32_t> 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
{
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)
{
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)
{
}
_target_input_channels = cnt;
-
+
return 0;
}
if (!_jack_connection->in_control()) {
return "???"; // JACK has no way (as of fall 2013) to return
// the device name
- }
+ }
return _target_device;
}
if (available()) {
return _current_sample_rate;
} else {
- return 0;
+ return _jack_connection->probed_sample_rate ();
}
}
return _target_sample_rate;
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
return _target_systemic_output_latency;
}
-size_t
+size_t
JACKAudioBackend::raw_buffer_size(DataType t)
{
std::map<DataType,size_t>::const_iterator s = _raw_buffer_sizes.find(t);
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) {
}
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, for_latency_measurement)) {
+ if (!get_jack_command_line_string (options, cmdline)) {
/* error, somehow - we will still try to start JACK
* automatically but it will be without our preferred options
*/
if (!available()) {
if (_jack_connection->in_control()) {
- /* we will be starting JACK, so set up the
+ /* 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);
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
*/
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 {
int
JACKAudioBackend::stop ()
{
+ _running = false; // no 'engine halted message'.
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
-
+
_jack_connection->close ();
_current_buffer_size = 0;
if (onoff == _freewheeling) {
/* already doing what has been asked for */
-
+
return 0;
}
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);
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);
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::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) {
+#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) {
+ if (pthread_join (*i, &status) != 0)
#endif
+ {
error << "AudioEngine: cannot stop process thread" << endmsg;
ret += -1;
}
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<jack_native_thread_t>::const_iterator i = _jack_threads.begin ();
i != _jack_threads.end(); i++) {
-#ifdef COMPILER_MINGW
+#if defined COMPILER_MINGW && (!defined PTW32_VERSION || defined __jack_systemdeps_h__)
if (*i == GetCurrentThread()) {
return true;
}
/* 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;
}
}
}
-float
-JACKAudioBackend::dsp_load() const
+float
+JACKAudioBackend::dsp_load() const
{
GET_PRIVATE_JACK_POINTER_RET(_priv_jack,0);
return jack_cpu_load (_priv_jack);
}
}
}
-
+
jack_free (ports);
}
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 {
jack_transport_state_t state;
bool starting;
- /* this won't be called if the port engine in use is not JACK, so we do
+ /* 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()
*/
// 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;
+}