X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fbackends%2Falsa%2Falsa_audiobackend.cc;h=8befb9219cadbe91ca4319d31ece8699e2dc21c7;hb=09dd3fc369f0b9c666e5930353ca55def84d9e4e;hp=936fc990cef50a784254084a1024f4d044a54b1a;hpb=b1da9af8de8cc648d88fbc944c68f1d9f6d1b8c9;p=ardour.git diff --git a/libs/backends/alsa/alsa_audiobackend.cc b/libs/backends/alsa/alsa_audiobackend.cc index 936fc990ce..8befb9219c 100644 --- a/libs/backends/alsa/alsa_audiobackend.cc +++ b/libs/backends/alsa/alsa_audiobackend.cc @@ -47,8 +47,11 @@ AlsaAudioBackend::AlsaAudioBackend (AudioEngine& e, AudioBackendInfo& info) , _pcmi (0) , _run (false) , _active (false) + , _freewheel (false) , _freewheeling (false) , _measure_latency (false) + , _last_process_start (0) + , _process_speed_samples_per_ms (0) , _audio_device("") , _midi_driver_option(_("None")) , _device_reservation(0) @@ -93,6 +96,7 @@ AlsaAudioBackend::enumerate_devices () const std::map devices; get_alsa_audio_device_names(devices); for (std::map::const_iterator i = devices.begin (); i != devices.end(); ++i) { + if (_audio_device == "") _audio_device = i->first; _audio_device_status.push_back (DeviceStatus (i->first, true)); } return _audio_device_status; @@ -228,7 +232,7 @@ AlsaAudioBackend::can_change_sample_rate_when_running () const bool AlsaAudioBackend::can_change_buffer_size_when_running () const { - return false; + return false; // why not? :) } int @@ -253,6 +257,9 @@ AlsaAudioBackend::set_buffer_size (uint32_t bs) if (bs <= 0 || bs >= _max_buffer_size) { return -1; } + if (_run) { + return -1; + } _samples_per_period = bs; engine.buffer_size_change (bs); return 0; @@ -407,9 +414,9 @@ std::vector AlsaAudioBackend::enumerate_midi_options () const { if (_midi_options.empty()) { - _midi_options.push_back (_("None")); _midi_options.push_back (_("ALSA raw devices")); _midi_options.push_back (_("ALSA sequencer")); + _midi_options.push_back (_("None")); } return _midi_options; } @@ -498,6 +505,13 @@ AlsaAudioBackend::_start (bool for_latency_measurement) _ports.clear(); } + /* reset internal state */ + _dsp_load = 0; + _freewheeling = false; + _freewheel = false; + _last_process_start = 0; + _process_speed_samples_per_ms = 0; + release_device(); assert(_rmidi_in.size() == 0); @@ -656,11 +670,7 @@ AlsaAudioBackend::stop () int AlsaAudioBackend::freewheel (bool onoff) { - if (onoff == _freewheeling) { - return 0; - } _freewheeling = onoff; - engine.freewheel_callback (onoff); return 0; } @@ -683,13 +693,13 @@ AlsaAudioBackend::raw_buffer_size (DataType t) } /* Process time */ -pframes_t +framepos_t AlsaAudioBackend::sample_time () { return _processed_samples; } -pframes_t +framepos_t AlsaAudioBackend::sample_time_at_cycle_start () { return _processed_samples; @@ -698,7 +708,24 @@ AlsaAudioBackend::sample_time_at_cycle_start () pframes_t AlsaAudioBackend::samples_since_cycle_start () { - return 0; + if (!_active || !_run || _freewheeling || _freewheel) { + return 0; + } + if (_last_process_start == 0 || _process_speed_samples_per_ms) { + return 0; + } + + const int64_t elapsed_time_us = _last_process_start - g_get_monotonic_time(); + + assert(elapsed_time_us >=0); + + /* linear extrapolation, using [low pass] filtered process-speed. */ + const pframes_t processed_samples = elapsed_time_us * _process_speed_samples_per_ms * 1e-3; + + if (processed_samples >= _samples_per_period) { + return _samples_per_period; + } + return processed_samples; } @@ -757,6 +784,10 @@ AlsaAudioBackend::join_process_threads () bool AlsaAudioBackend::in_process_thread () { + if (pthread_equal (_main_thread, pthread_self()) != 0) { + return true; + } + for (std::vector::const_iterator i = _threads.begin (); i != _threads.end (); ++i) { if (pthread_equal (*i, pthread_self ()) != 0) { @@ -937,7 +968,7 @@ AlsaAudioBackend::register_system_audio_ports() const int a_out = _n_outputs > 0 ? _n_outputs : 2; /* audio ports */ - lr.min = lr.max = _samples_per_period + (_measure_latency ? 0 : _systemic_audio_input_latency); + lr.min = lr.max = (_measure_latency ? 0 : _systemic_audio_input_latency); for (int i = 1; i <= a_ins; ++i) { char tmp[64]; snprintf(tmp, sizeof(tmp), "system:capture_%d", i); @@ -947,7 +978,7 @@ AlsaAudioBackend::register_system_audio_ports() _system_inputs.push_back(static_cast(p)); } - lr.min = lr.max = _samples_per_period + (_measure_latency ? 0 : _systemic_audio_output_latency); + lr.min = lr.max = (_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); @@ -1008,7 +1039,7 @@ AlsaAudioBackend::register_system_midi_ports() delete mout; } LatencyRange lr; - lr.min = lr.max = _samples_per_period + (_measure_latency ? 0 : nfo->systemic_output_latency); + lr.min = lr.max = (_measure_latency ? 0 : nfo->systemic_output_latency); set_latency_range (p, false, lr); static_cast(p)->set_n_periods(2); _system_midi_out.push_back(static_cast(p)); @@ -1046,7 +1077,7 @@ AlsaAudioBackend::register_system_midi_ports() continue; } LatencyRange lr; - lr.min = lr.max = _samples_per_period + (_measure_latency ? 0 : nfo->systemic_input_latency); + lr.min = lr.max = (_measure_latency ? 0 : nfo->systemic_input_latency); set_latency_range (p, false, lr); _system_midi_in.push_back(static_cast(p)); _rmidi_in.push_back (midin); @@ -1225,9 +1256,11 @@ AlsaAudioBackend::midi_event_put ( assert (buffer && port_buffer); AlsaMidiBuffer& dst = * static_cast(port_buffer); if (dst.size () && (pframes_t)dst.back ()->timestamp () > timestamp) { +#ifndef NDEBUG + // nevermind, ::get_buffer() sorts events fprintf (stderr, "AlsaMidiBuffer: it's too late for this event. %d > %d\n", (pframes_t)dst.back ()->timestamp (), timestamp); - return -1; +#endif } dst.push_back (boost::shared_ptr(new AlsaMidiEvent (timestamp, buffer, size))); return 0; @@ -1289,14 +1322,28 @@ AlsaAudioBackend::set_latency_range (PortEngine::PortHandle port, bool for_playb LatencyRange AlsaAudioBackend::get_latency_range (PortEngine::PortHandle port, bool for_playback) { + LatencyRange r; if (!valid_port (port)) { PBD::error << _("AlsaPort::get_latency_range (): invalid port.") << endmsg; - LatencyRange r; r.min = 0; r.max = 0; return r; } - return static_cast(port)->latency_range (for_playback); + AlsaPort *p = static_cast(port); + assert(p); + + r = p->latency_range (for_playback); + if (p->is_physical() && p->is_terminal()) { + if (p->is_input() && for_playback) { + r.min += _samples_per_period; + r.max += _samples_per_period; + } + if (p->is_output() && !for_playback) { + r.min += _samples_per_period; + r.max += _samples_per_period; + } + } + return r; } /* Discovering physical ports */ @@ -1394,7 +1441,6 @@ AlsaAudioBackend::main_process_thread () _processed_samples = 0; uint64_t clock1, clock2; - clock1 = g_get_monotonic_time(); _pcmi->pcm_start (); int no_proc_errors = 0; const int bailout = 2 * _samplerate / _samples_per_period; @@ -1406,18 +1452,33 @@ AlsaAudioBackend::main_process_thread () while (_run) { long nr; bool xrun = false; - if (!_freewheeling) { + + if (_freewheeling != _freewheel) { + _freewheel = _freewheeling; + engine.freewheel_callback (_freewheel); + } + + if (!_freewheel) { nr = _pcmi->pcm_wait (); if (_pcmi->state () > 0) { ++no_proc_errors; xrun = true; } - if (_pcmi->state () < 0 || no_proc_errors > bailout) { + if (_pcmi->state () < 0) { PBD::error << _("AlsaAudioBackend: I/O error. Audio Process Terminated.") << endmsg; break; } - while (nr >= (long)_samples_per_period) { + if (no_proc_errors > bailout) { + PBD::error + << string_compose ( + _("AlsaAudioBackend: Audio Process Terminated after %1 consecutive x-runs."), + no_proc_errors) + << endmsg; + break; + } + + while (nr >= (long)_samples_per_period && _freewheeling == _freewheel) { uint32_t i = 0; clock1 = g_get_monotonic_time(); no_proc_errors = 0; @@ -1428,7 +1489,7 @@ AlsaAudioBackend::main_process_thread () } _pcmi->capt_done (_samples_per_period); - /* de-queue midi*/ + /* de-queue incoming midi*/ i = 0; for (std::vector::const_iterator it = _system_midi_in.begin (); it != _system_midi_in.end (); ++it, ++i) { assert (_rmidi_in.size() > i); @@ -1449,25 +1510,26 @@ AlsaAudioBackend::main_process_thread () memset ((*it)->get_buffer (_samples_per_period), 0, _samples_per_period * sizeof (Sample)); } + /* call engine process callback */ + _last_process_start = g_get_monotonic_time(); if (engine.process_callback (_samples_per_period)) { _pcmi->pcm_stop (); _active = false; return 0; } - i = 0; - for (std::vector::iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it, ++i) { + for (std::vector::iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it) { static_cast(*it)->next_period(); } - /* queue midi */ + /* queue outgoing midi */ i = 0; for (std::vector::const_iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it, ++i) { assert (_rmidi_out.size() > i); - const AlsaMidiBuffer src = static_cast(*it)->const_buffer(); + const AlsaMidiBuffer * src = static_cast(*it)->const_buffer(); AlsaMidiOut *rm = _rmidi_out.at(i); rm->sync_time (clock1); - for (AlsaMidiBuffer::const_iterator mit = src.begin (); mit != src.end (); ++mit) { + for (AlsaMidiBuffer::const_iterator mit = src->begin (); mit != src->end (); ++mit) { rm->send_event ((*mit)->timestamp(), (*mit)->data(), (*mit)->size()); } } @@ -1489,28 +1551,60 @@ AlsaAudioBackend::main_process_thread () clock2 = g_get_monotonic_time(); const int64_t elapsed_time = clock2 - clock1; _dsp_load = elapsed_time / (float) nomial_time; + + const double ps = 1e3 * _samples_per_period / elapsed_time; + // low pass filter, The time-constant should really be + // 1.0 - e^(-2.0 * π * v / SR); + // with v = _samples_per_period / N, and SR = _samples_per_period; + _process_speed_samples_per_ms = _process_speed_samples_per_ms + .05 * (ps - _process_speed_samples_per_ms) + 1e-12; } if (xrun && (_pcmi->capt_xrun() > 0 || _pcmi->play_xrun() > 0)) { engine.Xrun (); #if 0 - fprintf(stderr, "ALSA x-run read: %.1f ms, write: %.1f ms\n", + fprintf(stderr, "ALSA x-run read: %.2f ms, write: %.2f ms\n", _pcmi->capt_xrun() * 1000.0, _pcmi->play_xrun() * 1000.0); #endif } } else { // Freewheelin' + + // zero audio input buffers for (std::vector::const_iterator it = _system_inputs.begin (); it != _system_inputs.end (); ++it) { memset ((*it)->get_buffer (_samples_per_period), 0, _samples_per_period * sizeof (Sample)); } - for (std::vector::const_iterator it = _system_midi_in.begin (); it != _system_midi_in.end (); ++it) { + + clock1 = g_get_monotonic_time(); + uint32_t i = 0; + for (std::vector::const_iterator it = _system_midi_in.begin (); it != _system_midi_in.end (); ++it, ++i) { static_cast((*it)->get_buffer(0))->clear (); + AlsaMidiIn *rm = _rmidi_in.at(i); + void *bptr = (*it)->get_buffer(0); + midi_clear(bptr); // zero midi buffer + + // TODO add an API call for this. + pframes_t time; + uint8_t data[64]; // match MaxAlsaEventSize in alsa_rawmidi.cc + size_t size = sizeof(data); + while (rm->recv_event (time, data, size)) { + ; // discard midi-data from HW. + } + rm->sync_time (clock1); } + _last_process_start = 0; if (engine.process_callback (_samples_per_period)) { _pcmi->pcm_stop (); + _active = false; return 0; } + + // drop all outgoing MIDI messages + for (std::vector::const_iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it) { + void *bptr = (*it)->get_buffer(0); + midi_clear(bptr); + } + _dsp_load = 1.0; Glib::usleep (100); // don't hog cpu } @@ -1562,6 +1656,7 @@ static boost::shared_ptr backend_factory (AudioEngine& e); static int instantiate (const std::string& arg1, const std::string& /* arg2 */); static int deinstantiate (); static bool already_configured (); +static bool available (); static ARDOUR::AudioBackendInfo _descriptor = { "ALSA", @@ -1569,6 +1664,7 @@ static ARDOUR::AudioBackendInfo _descriptor = { deinstantiate, backend_factory, already_configured, + available }; static boost::shared_ptr @@ -1600,6 +1696,12 @@ already_configured () return false; } +static bool +available () +{ + return true; +} + extern "C" ARDOURBACKEND_API ARDOUR::AudioBackendInfo* descriptor () { return &_descriptor; @@ -1790,8 +1892,8 @@ void* AlsaMidiPort::get_buffer (pframes_t /* nframes */) for (std::vector::const_iterator i = get_connections ().begin (); i != get_connections ().end (); ++i) { - const AlsaMidiBuffer src = static_cast(*i)->const_buffer (); - for (AlsaMidiBuffer::const_iterator it = src.begin (); it != src.end (); ++it) { + const AlsaMidiBuffer * src = static_cast(*i)->const_buffer (); + for (AlsaMidiBuffer::const_iterator it = src->begin (); it != src->end (); ++it) { (_buffer[_bufperiod]).push_back (boost::shared_ptr(new AlsaMidiEvent (**it))); } }