X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fbackends%2Falsa%2Falsa_audiobackend.cc;h=725fc80b765c63b9f64340c2afaa3229ec07d72b;hb=dac57703ccd7205ac94c14b407b802a69f4187f1;hp=1e9ebd77648fb03657da02e3d62c43bdc203d390;hpb=a226c4ce380bc61fd113521a695da9e6123b28c0;p=ardour.git diff --git a/libs/backends/alsa/alsa_audiobackend.cc b/libs/backends/alsa/alsa_audiobackend.cc index 1e9ebd7764..725fc80b76 100644 --- a/libs/backends/alsa/alsa_audiobackend.cc +++ b/libs/backends/alsa/alsa_audiobackend.cc @@ -58,7 +58,7 @@ AlsaAudioBackend::AlsaAudioBackend (AudioEngine& e, AudioBackendInfo& info) , _last_process_start (0) , _input_audio_device("") , _output_audio_device("") - , _midi_driver_option(_("None")) + , _midi_driver_option(get_standard_device_name(DeviceNone)) , _device_reservation(0) , _samplerate (48000) , _samples_per_period (1024) @@ -69,6 +69,8 @@ AlsaAudioBackend::AlsaAudioBackend (AudioEngine& e, AudioBackendInfo& info) , _systemic_audio_output_latency (0) , _dsp_load (0) , _processed_samples (0) + , _midi_ins (0) + , _midi_outs (0) , _port_change_flag (false) { _instance_name = s_instance_name; @@ -116,7 +118,7 @@ AlsaAudioBackend::enumerate_input_devices () const _input_audio_device_status.clear(); std::map devices; get_alsa_audio_device_names(devices, HalfDuplexIn); - _input_audio_device_status.push_back (DeviceStatus (_("None"), true)); + _input_audio_device_status.push_back (DeviceStatus (get_standard_device_name(DeviceNone), true)); for (std::map::const_iterator i = devices.begin (); i != devices.end(); ++i) { if (_input_audio_device == "") _input_audio_device = i->first; _input_audio_device_status.push_back (DeviceStatus (i->first, true)); @@ -130,7 +132,7 @@ AlsaAudioBackend::enumerate_output_devices () const _output_audio_device_status.clear(); std::map devices; get_alsa_audio_device_names(devices, HalfDuplexOut); - _output_audio_device_status.push_back (DeviceStatus (_("None"), true)); + _output_audio_device_status.push_back (DeviceStatus (get_standard_device_name(DeviceNone), true)); for (std::map::const_iterator i = devices.begin (); i != devices.end(); ++i) { if (_output_audio_device == "") _output_audio_device = i->first; _output_audio_device_status.push_back (DeviceStatus (i->first, true)); @@ -216,16 +218,16 @@ AlsaAudioBackend::acquire_device(const char* device_name) } std::vector -AlsaAudioBackend::available_sample_rates (const std::string& input_device, const std::string& output_device) const +AlsaAudioBackend::available_sample_rates2 (const std::string& input_device, const std::string& output_device) const { std::vector sr; - if (input_device == _("None") && output_device == _("None")) { + if (input_device == get_standard_device_name(DeviceNone) && output_device == get_standard_device_name(DeviceNone)) { return sr; } - else if (input_device == _("None")) { + else if (input_device == get_standard_device_name(DeviceNone)) { sr = available_sample_rates (output_device); } - else if (output_device == _("None")) { + else if (output_device == get_standard_device_name(DeviceNone)) { sr = available_sample_rates (input_device); } else { std::vector sr_in = available_sample_rates (input_device); @@ -240,7 +242,7 @@ AlsaAudioBackend::available_sample_rates (const std::string& device) const { ALSADeviceInfo *nfo = NULL; std::vector sr; - if (device == _("None")) { + if (device == get_standard_device_name(DeviceNone)) { return sr; } if (device == _input_audio_device && _input_audio_device_info.valid) { @@ -262,16 +264,16 @@ AlsaAudioBackend::available_sample_rates (const std::string& device) const } std::vector -AlsaAudioBackend::available_buffer_sizes (const std::string& input_device, const std::string& output_device) const +AlsaAudioBackend::available_buffer_sizes2 (const std::string& input_device, const std::string& output_device) const { std::vector bs; - if (input_device == _("None") && output_device == _("None")) { + if (input_device == get_standard_device_name(DeviceNone) && output_device == get_standard_device_name(DeviceNone)) { return bs; } - else if (input_device == _("None")) { + else if (input_device == get_standard_device_name(DeviceNone)) { bs = available_buffer_sizes (output_device); } - else if (output_device == _("None")) { + else if (output_device == get_standard_device_name(DeviceNone)) { bs = available_buffer_sizes (input_device); } else { std::vector bs_in = available_buffer_sizes (input_device); @@ -286,7 +288,7 @@ AlsaAudioBackend::available_buffer_sizes (const std::string& device) const { ALSADeviceInfo *nfo = NULL; std::vector bs; - if (device == _("None")) { + if (device == get_standard_device_name(DeviceNone)) { return bs; } if (device == _input_audio_device && _input_audio_device_info.valid) { @@ -309,7 +311,7 @@ AlsaAudioBackend::available_buffer_sizes (const std::string& device) const uint32_t AlsaAudioBackend::available_input_channel_count (const std::string& device) const { - if (device == _("None")) { + if (device == get_standard_device_name(DeviceNone)) { return 0; } if (device == _input_audio_device && _input_audio_device_info.valid) { @@ -321,7 +323,7 @@ AlsaAudioBackend::available_input_channel_count (const std::string& device) cons uint32_t AlsaAudioBackend::available_output_channel_count (const std::string& device) const { - if (device == _("None")) { + if (device == get_standard_device_name(DeviceNone)) { return 0; } if (device == _output_audio_device && _output_audio_device_info.valid) { @@ -330,6 +332,15 @@ AlsaAudioBackend::available_output_channel_count (const std::string& device) con return 128; } +std::vector +AlsaAudioBackend::available_period_sizes (const std::string& driver) const +{ + std::vector ps; + ps.push_back (2); + ps.push_back (3); + return ps; +} + bool AlsaAudioBackend::can_change_sample_rate_when_running () const { @@ -350,7 +361,7 @@ AlsaAudioBackend::set_input_device_name (const std::string& d) } _input_audio_device = d; - if (d == _("None")) { + if (d == get_standard_device_name(DeviceNone)) { _input_audio_device_info.valid = false; return 0; } @@ -382,7 +393,7 @@ AlsaAudioBackend::set_output_device_name (const std::string& d) _output_audio_device = d; - if (d == _("None")) { + if (d == get_standard_device_name(DeviceNone)) { _output_audio_device_info.valid = false; return 0; } @@ -422,6 +433,19 @@ AlsaAudioBackend::set_sample_rate (float sr) return 0; } +int +AlsaAudioBackend::set_peridod_size (uint32_t n) +{ + if (n == 0 || n > 3) { + return -1; + } + if (_run) { + return -1; + } + _periods_per_cycle = n; + return 0; +} + int AlsaAudioBackend::set_buffer_size (uint32_t bs) { @@ -461,6 +485,9 @@ int AlsaAudioBackend::set_systemic_input_latency (uint32_t sl) { _systemic_audio_input_latency = sl; + if (_run) { + update_systemic_audio_latencies(); + } return 0; } @@ -468,6 +495,9 @@ int AlsaAudioBackend::set_systemic_output_latency (uint32_t sl) { _systemic_audio_output_latency = sl; + if (_run) { + update_systemic_audio_latencies(); + } return 0; } @@ -477,6 +507,9 @@ AlsaAudioBackend::set_systemic_midi_input_latency (std::string const device, uin struct AlsaMidiDeviceInfo * nfo = midi_device_info(device); if (!nfo) return -1; nfo->systemic_input_latency = sl; + if (_run && nfo->enabled) { + update_systemic_midi_latencies (); + } return 0; } @@ -486,17 +519,65 @@ AlsaAudioBackend::set_systemic_midi_output_latency (std::string const device, ui struct AlsaMidiDeviceInfo * nfo = midi_device_info(device); if (!nfo) return -1; nfo->systemic_output_latency = sl; + if (_run && nfo->enabled) { + update_systemic_midi_latencies (); + } return 0; } +void +AlsaAudioBackend::update_systemic_audio_latencies () +{ + const uint32_t lcpp = (_periods_per_cycle - 2) * _samples_per_period; + LatencyRange lr; + + lr.min = lr.max = lcpp + (_measure_latency ? 0 : _systemic_audio_input_latency); + for (std::vector::const_iterator it = _system_outputs.begin (); it != _system_outputs.end (); ++it) { + set_latency_range (*it, true, lr); + } + + lr.min = lr.max = (_measure_latency ? 0 : _systemic_audio_output_latency); + for (std::vector::const_iterator it = _system_inputs.begin (); it != _system_inputs.end (); ++it) { + set_latency_range (*it, false, lr); + } + update_latencies (); +} + +void +AlsaAudioBackend::update_systemic_midi_latencies () +{ + uint32_t i = 0; + for (std::vector::iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it, ++i) { + assert (_rmidi_out.size() > i); + AlsaMidiOut *rm = _rmidi_out.at(i); + struct AlsaMidiDeviceInfo * nfo = midi_device_info (rm->name()); + assert (nfo); + LatencyRange lr; + lr.min = lr.max = (_measure_latency ? 0 : nfo->systemic_output_latency); + set_latency_range (*it, false, lr); + } + + i = 0; + for (std::vector::const_iterator it = _system_midi_in.begin (); it != _system_midi_in.end (); ++it, ++i) { + assert (_rmidi_in.size() > i); + AlsaMidiIO *rm = _rmidi_in.at(i); + struct AlsaMidiDeviceInfo * nfo = midi_device_info (rm->name()); + assert (nfo); + LatencyRange lr; + lr.min = lr.max = (_measure_latency ? 0 : nfo->systemic_input_latency); + set_latency_range (*it, true, lr); + } + update_latencies (); +} + /* Retrieving parameters */ std::string AlsaAudioBackend::device_name () const { - if (_input_audio_device != _("None")) { + if (_input_audio_device != get_standard_device_name(DeviceNone)) { return _input_audio_device; } - if (_output_audio_device != _("None")) { + if (_output_audio_device != get_standard_device_name(DeviceNone)) { return _output_audio_device; } return ""; @@ -526,6 +607,12 @@ AlsaAudioBackend::buffer_size () const return _samples_per_period; } +uint32_t +AlsaAudioBackend::period_size () const +{ + return _periods_per_cycle; +} + bool AlsaAudioBackend::interleaved () const { @@ -581,7 +668,7 @@ AlsaAudioBackend::midi_device_info(std::string const name) const { } } - assert(_midi_driver_option != _("None")); + assert(_midi_driver_option != get_standard_device_name(DeviceNone)); std::map devices; if (_midi_driver_option == _("ALSA raw devices")) { @@ -605,7 +692,7 @@ AlsaAudioBackend::enumerate_midi_options () const if (_midi_options.empty()) { _midi_options.push_back (_("ALSA raw devices")); _midi_options.push_back (_("ALSA sequencer")); - _midi_options.push_back (_("None")); + _midi_options.push_back (get_standard_device_name(DeviceNone)); } return _midi_options; } @@ -632,7 +719,10 @@ AlsaAudioBackend::enumerate_midi_devices () const int AlsaAudioBackend::set_midi_option (const std::string& opt) { - if (opt != _("None") && opt != _("ALSA raw devices") && opt != _("ALSA sequencer")) { + if (opt != get_standard_device_name(DeviceNone) && opt != _("ALSA raw devices") && opt != _("ALSA sequencer")) { + return -1; + } + if (_run && _midi_driver_option != opt) { return -1; } _midi_driver_option = opt; @@ -650,7 +740,41 @@ AlsaAudioBackend::set_midi_device_enabled (std::string const device, bool enable { struct AlsaMidiDeviceInfo * nfo = midi_device_info(device); if (!nfo) return -1; + const bool prev_enabled = nfo->enabled; nfo->enabled = enable; + + if (_run && prev_enabled != enable) { + if (enable) { + // add ports for the given device + register_system_midi_ports(device); + } else { + // remove all ports provided by the given device + uint32_t i = 0; + for (std::vector::iterator it = _system_midi_out.begin (); it != _system_midi_out.end ();) { + assert (_rmidi_out.size() > i); + AlsaMidiOut *rm = _rmidi_out.at(i); + if (rm->name () != device) { ++it; ++i; continue; } + it = _system_midi_out.erase (it); + unregister_port (*it); + rm->stop(); + _rmidi_out.erase (_rmidi_out.begin() + i); + delete rm; + } + + i = 0; + for (std::vector::iterator it = _system_midi_in.begin (); it != _system_midi_in.end ();) { + assert (_rmidi_in.size() > i); + AlsaMidiIn *rm = _rmidi_in.at(i); + if (rm->name () != device) { ++it; ++i; continue; } + it = _system_midi_in.erase (it); + unregister_port (*it); + rm->stop(); + _rmidi_in.erase (_rmidi_in.begin() + i); + delete rm; + } + } + update_systemic_midi_latencies (); + } return 0; } @@ -682,7 +806,7 @@ AlsaAudioBackend::_start (bool for_latency_measurement) if (_active || _run) { PBD::error << _("AlsaAudioBackend: already active.") << endmsg; - return -1; + return BackendReinitializationError; } if (_ports.size()) { @@ -711,17 +835,17 @@ AlsaAudioBackend::_start (bool for_latency_measurement) std::string alsa_device; std::map devices; - if (_input_audio_device == _("None") && _output_audio_device == _("None")) { + if (_input_audio_device == get_standard_device_name(DeviceNone) && _output_audio_device == get_standard_device_name(DeviceNone)) { PBD::error << _("AlsaAudioBackend: At least one of input or output device needs to be set."); - return -1; + return AudioDeviceInvalidError; } if (_input_audio_device != _output_audio_device) { - if (_input_audio_device != _("None") && _output_audio_device != _("None")) { + if (_input_audio_device != get_standard_device_name(DeviceNone) && _output_audio_device != get_standard_device_name(DeviceNone)) { PBD::error << _("AlsaAudioBackend: Cannot use two different devices."); - return -1; + return AudioDeviceInvalidError; } - if (_input_audio_device != _("None")) { + if (_input_audio_device != get_standard_device_name(DeviceNone)) { get_alsa_audio_device_names(devices, HalfDuplexIn); audio_device = _input_audio_device; duplex = 1; @@ -744,28 +868,59 @@ AlsaAudioBackend::_start (bool for_latency_measurement) } if (alsa_device == "") { PBD::error << _("AlsaAudioBackend: Cannot find configured device. Is it still connected?"); - return -1; + return AudioDeviceNotAvailableError; } acquire_device(alsa_device.c_str()); _pcmi = new Alsa_pcmi ( (duplex & 2) ? alsa_device.c_str() : NULL, (duplex & 1) ? alsa_device.c_str() : NULL, - 0, _samplerate, _samples_per_period, _periods_per_cycle, 0); - switch (_pcmi->state ()) { - case 0: /* OK */ break; - case -1: PBD::error << _("AlsaAudioBackend: failed to open device.") << endmsg; break; - case -2: PBD::error << _("AlsaAudioBackend: failed to allocate parameters.") << endmsg; break; - case -3: PBD::error << _("AlsaAudioBackend: cannot set requested sample rate.") << endmsg; break; - case -4: PBD::error << _("AlsaAudioBackend: cannot set requested period size.") << endmsg; break; - case -5: PBD::error << _("AlsaAudioBackend: cannot set requested number of periods.") << endmsg; break; - case -6: PBD::error << _("AlsaAudioBackend: unsupported sample format.") << endmsg; break; - default: PBD::error << _("AlsaAudioBackend: initialization failed.") << endmsg; break; + /* ctrl name */ 0, + _samplerate, _samples_per_period, + _periods_per_cycle, /* _periods_per_cycle */ 2, + /* debug */ 0); + + AudioBackend::ErrorCode error_code = NoError; + switch (_pcmi->state()) { + case 0: /* OK */ + break; + case -1: + PBD::error << _("AlsaAudioBackend: failed to open device.") << endmsg; + error_code = AudioDeviceOpenError; + break; + case -2: + PBD::error << _("AlsaAudioBackend: failed to allocate parameters.") << endmsg; + error_code = AudioDeviceOpenError; + break; + case -3: + PBD::error << _("AlsaAudioBackend: cannot set requested sample rate.") + << endmsg; + error_code = SampleRateNotSupportedError; + break; + case -4: + PBD::error << _("AlsaAudioBackend: cannot set requested period size.") + << endmsg; + error_code = PeriodSizeNotSupportedError; + break; + case -5: + PBD::error << _("AlsaAudioBackend: cannot set requested number of periods.") + << endmsg; + error_code = PeriodCountNotSupportedError; + break; + case -6: + PBD::error << _("AlsaAudioBackend: unsupported sample format.") << endmsg; + error_code = SampleFormatNotSupportedError; + break; + default: + PBD::error << _("AlsaAudioBackend: initialization failed.") << endmsg; + error_code = AudioDeviceOpenError; + break; } + if (_pcmi->state ()) { delete _pcmi; _pcmi = 0; release_device(); - return -1; + return error_code; } #ifndef NDEBUG @@ -803,13 +958,14 @@ AlsaAudioBackend::_start (bool for_latency_measurement) _measure_latency = for_latency_measurement; + _midi_ins = _midi_outs = 0; register_system_midi_ports(); if (register_system_audio_ports()) { PBD::error << _("AlsaAudioBackend: failed to register system ports.") << endmsg; delete _pcmi; _pcmi = 0; release_device(); - return -1; + return PortRegistrationError; } engine.sample_rate_change (_samplerate); @@ -819,7 +975,7 @@ AlsaAudioBackend::_start (bool for_latency_measurement) PBD::error << _("AlsaAudioBackend: Could not re-establish ports.") << endmsg; delete _pcmi; _pcmi = 0; release_device(); - return -1; + return PortReconnectError; } engine.reconnect_ports (); @@ -835,7 +991,7 @@ AlsaAudioBackend::_start (bool for_latency_measurement) delete _pcmi; _pcmi = 0; release_device(); _run = false; - return -1; + return ProcessThreadStartError; } else { PBD::warning << _("AlsaAudioBackend: cannot acquire realtime permissions.") << endmsg; } @@ -849,10 +1005,10 @@ AlsaAudioBackend::_start (bool for_latency_measurement) delete _pcmi; _pcmi = 0; release_device(); _run = false; - return -1; + return ProcessThreadStartError; } - return 0; + return NoError; } int @@ -884,6 +1040,7 @@ AlsaAudioBackend::stop () unregister_ports(); delete _pcmi; _pcmi = 0; + _midi_ins = _midi_outs = 0; release_device(); return (_active == false) ? 0 : -1; @@ -899,7 +1056,7 @@ AlsaAudioBackend::freewheel (bool onoff) float AlsaAudioBackend::dsp_load () const { - return std::min(100.f, 100.f * _dsp_load); + return 100.f * _dsp_load; } size_t @@ -1180,6 +1337,8 @@ AlsaAudioBackend::register_system_audio_ports() const int a_ins = _n_inputs; const int a_out = _n_outputs; + const uint32_t lcpp = (_periods_per_cycle - 2) * _samples_per_period; + /* audio ports */ lr.min = lr.max = (_measure_latency ? 0 : _systemic_audio_input_latency); for (int i = 1; i <= a_ins; ++i) { @@ -1191,7 +1350,7 @@ AlsaAudioBackend::register_system_audio_ports() _system_inputs.push_back(static_cast(p)); } - lr.min = lr.max = (_measure_latency ? 0 : _systemic_audio_output_latency); + lr.min = lr.max = lcpp + (_measure_latency ? 0 : _systemic_audio_output_latency); for (int i = 1; i <= a_out; ++i) { char tmp[64]; snprintf(tmp, sizeof(tmp), "system:playback_%d", i); @@ -1204,13 +1363,13 @@ AlsaAudioBackend::register_system_audio_ports() } int -AlsaAudioBackend::register_system_midi_ports() +AlsaAudioBackend::register_system_midi_ports(const std::string device) { std::map devices; - int midi_ins = 0; - int midi_outs = 0; - if (_midi_driver_option == _("None")) { + // TODO use consistent numbering when re-adding devices: _midi_ins, _midi_outs + + if (_midi_driver_option == get_standard_device_name(DeviceNone)) { return 0; } else if (_midi_driver_option == _("ALSA raw devices")) { get_alsa_rawmidi_device_names(devices); @@ -1219,15 +1378,18 @@ AlsaAudioBackend::register_system_midi_ports() } for (std::map::const_iterator i = devices.begin (); i != devices.end(); ++i) { + if (!device.empty() && device != i->first) { + continue; + } struct AlsaMidiDeviceInfo * nfo = midi_device_info(i->first); if (!nfo) continue; if (!nfo->enabled) continue; AlsaMidiOut *mout; if (_midi_driver_option == _("ALSA raw devices")) { - mout = new AlsaRawMidiOut (i->second.c_str()); + mout = new AlsaRawMidiOut (i->first, i->second.c_str()); } else { - mout = new AlsaSeqMidiOut (i->second.c_str()); + mout = new AlsaSeqMidiOut (i->first, i->second.c_str()); } if (mout->state ()) { @@ -1245,7 +1407,7 @@ AlsaAudioBackend::register_system_midi_ports() delete mout; } else { char tmp[64]; - snprintf(tmp, sizeof(tmp), "system:midi_playback_%d", ++midi_ins); + snprintf(tmp, sizeof(tmp), "system:midi_playback_%d", ++_midi_ins); PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast(IsInput | IsPhysical | IsTerminal)); if (!p) { mout->stop(); @@ -1253,8 +1415,8 @@ AlsaAudioBackend::register_system_midi_ports() } LatencyRange lr; lr.min = lr.max = (_measure_latency ? 0 : nfo->systemic_output_latency); - set_latency_range (p, false, lr); - static_cast(p)->set_n_periods(2); + set_latency_range (p, true, lr); + static_cast(p)->set_n_periods(_periods_per_cycle); // TODO check MIDI alignment _system_midi_out.push_back(static_cast(p)); _rmidi_out.push_back (mout); } @@ -1262,9 +1424,9 @@ AlsaAudioBackend::register_system_midi_ports() AlsaMidiIn *midin; if (_midi_driver_option == _("ALSA raw devices")) { - midin = new AlsaRawMidiIn (i->second.c_str()); + midin = new AlsaRawMidiIn (i->first, i->second.c_str()); } else { - midin = new AlsaSeqMidiIn (i->second.c_str()); + midin = new AlsaSeqMidiIn (i->first, i->second.c_str()); } if (midin->state ()) { @@ -1282,7 +1444,7 @@ AlsaAudioBackend::register_system_midi_ports() delete midin; } else { char tmp[64]; - snprintf(tmp, sizeof(tmp), "system:midi_capture_%d", ++midi_outs); + snprintf(tmp, sizeof(tmp), "system:midi_capture_%d", ++_midi_outs); PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast(IsOutput | IsPhysical | IsTerminal)); if (!p) { midin->stop(); @@ -1653,11 +1815,10 @@ AlsaAudioBackend::main_process_thread () _active = true; _processed_samples = 0; - uint64_t clock1, clock2; + uint64_t clock1; _pcmi->pcm_start (); int no_proc_errors = 0; const int bailout = 2 * _samplerate / _samples_per_period; - const int64_t nominal_time = 1e6 * _samples_per_period / _samplerate; manager.registration_callback(); manager.graph_order_callback(); @@ -1760,17 +1921,10 @@ AlsaAudioBackend::main_process_thread () nr -= _samples_per_period; _processed_samples += _samples_per_period; - /* calculate DSP load */ - clock2 = g_get_monotonic_time(); - const int64_t elapsed_time = clock2 - clock1; - // low pass filter - const float load = elapsed_time / (float) nominal_time; - if (load > _dsp_load) { - _dsp_load = load; - } else { - const float a = .2 * _samples_per_period / _samplerate; - _dsp_load = _dsp_load + a * (load - _dsp_load) + 1e-12; - } + _dsp_load_calc.set_max_time(_samplerate, _samples_per_period); + _dsp_load_calc.set_start_timestamp_us (clock1); + _dsp_load_calc.set_stop_timestamp_us (g_get_monotonic_time()); + _dsp_load = _dsp_load_calc.get_dsp_load (); } if (xrun && (_pcmi->capt_xrun() > 0 || _pcmi->play_xrun() > 0)) {