update ALSA backend, MIDI device config
[ardour.git] / libs / backends / alsa / alsa_audiobackend.cc
index 7c4288cedb9cd4a3bea4522cf7ac5ec0d32317c1..342a478296a83db1401f4f3d21fa94cad44ba25e 100644 (file)
@@ -45,19 +45,18 @@ AlsaAudioBackend::AlsaAudioBackend (AudioEngine& e, AudioBackendInfo& info)
        , _run (false)
        , _active (false)
        , _freewheeling (false)
+       , _measure_latency (false)
        , _audio_device("")
-       , _midi_device("")
+       , _midi_driver_option(_("None"))
        , _device_reservation(0)
        , _samplerate (48000)
        , _samples_per_period (1024)
        , _periods_per_cycle (2)
-       , _dsp_load (0)
        , _n_inputs (0)
        , _n_outputs (0)
        , _systemic_audio_input_latency (0)
        , _systemic_audio_output_latency (0)
-       , _systemic_midi_input_latency (0)
-       , _systemic_midi_output_latency (0)
+       , _dsp_load (0)
        , _processed_samples (0)
        , _port_change_flag (false)
 {
@@ -281,7 +280,6 @@ int
 AlsaAudioBackend::set_systemic_input_latency (uint32_t sl)
 {
        _systemic_audio_input_latency = sl;
-       _systemic_midi_input_latency = sl;
        return 0;
 }
 
@@ -289,7 +287,24 @@ int
 AlsaAudioBackend::set_systemic_output_latency (uint32_t sl)
 {
        _systemic_audio_output_latency = sl;
-       _systemic_midi_output_latency = sl;
+       return 0;
+}
+
+int
+AlsaAudioBackend::set_systemic_midi_input_latency (std::string const device, uint32_t sl)
+{
+       struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+       if (!nfo) return -1;
+       nfo->systemic_input_latency = sl;
+       return 0;
+}
+
+int
+AlsaAudioBackend::set_systemic_midi_output_latency (std::string const device, uint32_t sl)
+{
+       struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+       if (!nfo) return -1;
+       nfo->systemic_output_latency = sl;
        return 0;
 }
 
@@ -342,35 +357,99 @@ AlsaAudioBackend::systemic_output_latency () const
        return _systemic_audio_output_latency;
 }
 
+uint32_t
+AlsaAudioBackend::systemic_midi_input_latency (std::string const device) const
+{
+       struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+       if (!nfo) return 0;
+       return nfo->systemic_input_latency;
+}
+
+uint32_t
+AlsaAudioBackend::systemic_midi_output_latency (std::string const device) const
+{
+       struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+       if (!nfo) return 0;
+       return nfo->systemic_output_latency;
+}
+
 /* MIDI */
+struct AlsaAudioBackend::AlsaMidiDeviceInfo *
+AlsaAudioBackend::midi_device_info(std::string const name) const {
+       for (std::map<std::string, struct AlsaMidiDeviceInfo*>::const_iterator i = _midi_devices.begin (); i != _midi_devices.end(); ++i) {
+               if (i->first == name) {
+                       return (i->second);
+               }
+       }
+
+       std::map<std::string, std::string> devices;
+       get_alsa_rawmidi_device_names(devices);
+       for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+               if (i->first == name) {
+                       _midi_devices[name] = new AlsaMidiDeviceInfo();
+                       return _midi_devices[name];
+               }
+       }
+       return 0;
+}
+
 std::vector<std::string>
 AlsaAudioBackend::enumerate_midi_options () const
 {
        std::vector<std::string> m;
-       m.push_back (_("-None-"));
+       m.push_back (_("None"));
+       m.push_back (_("ALSA raw devices"));
+       return m;
+}
+
+std::vector<AudioBackend::DeviceStatus>
+AlsaAudioBackend::enumerate_midi_devices () const
+{
+       std::vector<AudioBackend::DeviceStatus> s;
+       if (_midi_driver_option == _("None")) {
+               return s;
+       }
+
        std::map<std::string, std::string> devices;
        get_alsa_rawmidi_device_names(devices);
 
        for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
-               m.push_back (i->first);
-       }
-       if (m.size() > 2) {
-               m.push_back (_("-All-"));
+               s.push_back (DeviceStatus (i->first, true));
        }
-       return m;
+       return s;
 }
 
 int
 AlsaAudioBackend::set_midi_option (const std::string& opt)
 {
-       _midi_device = opt;
+       if (opt != _("None") && opt != _("ALSA raw devices")) {
+               return -1;
+       }
+       _midi_driver_option = opt;
        return 0;
 }
 
 std::string
 AlsaAudioBackend::midi_option () const
 {
-       return _midi_device;
+       return _midi_driver_option;
+}
+
+int
+AlsaAudioBackend::set_midi_device_enabled (std::string const device, bool enable)
+{
+       struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+       if (!nfo) return -1;
+       nfo->enabled = enable;
+       return 0;
+}
+
+bool
+AlsaAudioBackend::midi_device_enabled (std::string const device) const
+{
+       struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+       if (!nfo) return false;
+       return nfo->enabled;
 }
 
 /* State Control */
@@ -386,6 +465,11 @@ static void * pthread_process (void *arg)
 int
 AlsaAudioBackend::_start (bool for_latency_measurement)
 {
+       if (!_active && _run) {
+               // recover from 'halted', reap threads
+               stop();
+       }
+
        if (_active || _run) {
                PBD::error << _("AlsaAudioBackend: already active.") << endmsg;
                return -1;
@@ -467,12 +551,7 @@ AlsaAudioBackend::_start (bool for_latency_measurement)
                PBD::warning << _("AlsaAudioBackend: sample rate does not match.") << endmsg;
        }
 
-       if (for_latency_measurement) {
-               _systemic_audio_input_latency = 0;
-               _systemic_audio_output_latency = 0;
-               _systemic_midi_input_latency = 0;
-               _systemic_midi_output_latency = 0;
-       }
+       _measure_latency = for_latency_measurement;
 
        register_system_midi_ports();
 
@@ -682,6 +761,8 @@ AlsaAudioBackend::process_thread_count ()
 void
 AlsaAudioBackend::update_latencies ()
 {
+       // trigger latency callback in RT thread (locked graph)
+       port_connect_add_remove_callback();
 }
 
 /* PORTENGINE API */
@@ -842,7 +923,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 + _systemic_audio_input_latency;
+       lr.min = lr.max = _samples_per_period + _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);
@@ -852,7 +933,7 @@ AlsaAudioBackend::register_system_audio_ports()
                _system_inputs.push_back(static_cast<AlsaPort*>(p));
        }
 
-       lr.min = lr.max = _samples_per_period + _systemic_audio_output_latency;
+       lr.min = lr.max = _samples_per_period + _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);
@@ -867,35 +948,24 @@ AlsaAudioBackend::register_system_audio_ports()
 int
 AlsaAudioBackend::register_system_midi_ports()
 {
-       LatencyRange lr;
-       std::vector<std::string> devices;
+       std::map<std::string, std::string> devices;
+       int midi_ins = 0;
+       int midi_outs = 0;
 
-       if (_midi_device == _("-None-")) {
+       if (_midi_driver_option == _("None")) {
                return 0;
        }
-       else if (_midi_device == _("-All-")) {
-               std::map<std::string, std::string> devmap;
-               get_alsa_rawmidi_device_names(devmap);
-               for (std::map<std::string, std::string>::const_iterator i = devmap.begin (); i != devmap.end(); ++i) {
-                       devices.push_back (i->second);
-               }
-       } else {
-               std::map<std::string, std::string> devmap;
-               get_alsa_rawmidi_device_names(devmap);
-               for (std::map<std::string, std::string>::const_iterator i = devmap.begin (); i != devmap.end(); ++i) {
-                       if (i->first == _midi_device) {
-                               devices.push_back (i->second);
-                               break;
-                       }
-               }
-       }
+       get_alsa_rawmidi_device_names(devices);
 
-       for (std::vector<std::string>::const_iterator i = devices.begin (); i != devices.end (); ++i) {
+       for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+               struct AlsaMidiDeviceInfo * nfo = midi_device_info(i->first);
+               if (!nfo) continue;
+               if (!nfo->enabled) continue;
 
-               AlsaRawMidiOut *mout = new AlsaRawMidiOut (i->c_str());
+               AlsaRawMidiOut *mout = new AlsaRawMidiOut (i->second.c_str());
                if (mout->state ()) {
                        PBD::warning << string_compose (
-                                       _("AlsaRawMidiOut: failed to open midi device '%1'."), *i)
+                                       _("AlsaRawMidiOut: failed to open midi device '%1'."), i->second)
                                << endmsg;
                        delete mout;
                } else {
@@ -903,18 +973,29 @@ AlsaAudioBackend::register_system_midi_ports()
                        mout->sync_time (g_get_monotonic_time());
                        if (mout->start ()) {
                                PBD::warning << string_compose (
-                                               _("AlsaRawMidiOut: failed to start midi device '%1'."), *i)
+                                               _("AlsaRawMidiOut: failed to start midi device '%1'."), i->second)
                                        << endmsg;
                                delete mout;
                        } else {
+                               char tmp[64];
+                               snprintf(tmp, sizeof(tmp), "system:midi_playback_%d", ++midi_ins);
+                               PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
+                               if (!p) {
+                                       mout->stop();
+                                       delete mout;
+                               }
+                               LatencyRange lr;
+                               lr.min = lr.max = _samples_per_period + nfo->systemic_output_latency;
+                               set_latency_range (p, false, lr);
+                               _system_midi_out.push_back(static_cast<AlsaPort*>(p));
                                _rmidi_out.push_back (mout);
                        }
                }
 
-               AlsaRawMidiIn *midin = new AlsaRawMidiIn (i->c_str());
+               AlsaRawMidiIn *midin = new AlsaRawMidiIn (i->second.c_str());
                if (midin->state ()) {
                        PBD::warning << string_compose (
-                                       _("AlsaRawMidiIn: failed to open midi device '%1'."), *i)
+                                       _("AlsaRawMidiIn: failed to open midi device '%1'."), i->second)
                                << endmsg;
                        delete midin;
                } else {
@@ -922,38 +1003,26 @@ AlsaAudioBackend::register_system_midi_ports()
                        midin->sync_time (g_get_monotonic_time());
                        if (midin->start ()) {
                                PBD::warning << string_compose (
-                                               _("AlsaRawMidiIn: failed to start midi device '%1'."), *i)
+                                               _("AlsaRawMidiIn: failed to start midi device '%1'."), i->second)
                                        << endmsg;
                                delete midin;
                        } else {
+                               char tmp[64];
+                               snprintf(tmp, sizeof(tmp), "system:midi_capture_%d", ++midi_outs);
+                               PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal));
+                               if (!p) {
+                                       midin->stop();
+                                       delete midin;
+                                       continue;
+                               }
+                               LatencyRange lr;
+                               lr.min = lr.max = _samples_per_period + nfo->systemic_input_latency;
+                               set_latency_range (p, false, lr);
+                               _system_midi_in.push_back(static_cast<AlsaPort*>(p));
                                _rmidi_in.push_back (midin);
                        }
                }
        }
-
-       const int m_ins = _rmidi_in.size();
-       const int m_out = _rmidi_out.size();
-
-       lr.min = lr.max = _samples_per_period + _systemic_midi_input_latency;
-       for (int i = 1; i <= m_ins; ++i) {
-               char tmp[64];
-               snprintf(tmp, sizeof(tmp), "system:midi_capture_%d", i);
-               PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal));
-               if (!p) return -1;
-               set_latency_range (p, false, lr);
-               _system_midi_in.push_back(static_cast<AlsaPort*>(p));
-       }
-
-       lr.min = lr.max = _samples_per_period + _systemic_midi_output_latency;
-       for (int i = 1; i <= m_out; ++i) {
-               char tmp[64];
-               snprintf(tmp, sizeof(tmp), "system:midi_playback_%d", i);
-               PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
-               if (!p) return -1;
-               set_latency_range (p, true, lr);
-               _system_midi_out.push_back(static_cast<AlsaPort*>(p));
-       }
-
        return 0;
 }
 
@@ -1297,6 +1366,8 @@ AlsaAudioBackend::main_process_thread ()
        clock1 = g_get_monotonic_time();
        _pcmi->pcm_start ();
        int no_proc_errors = 0;
+       const int bailout = 2 * _samplerate / _samples_per_period;
+       const int64_t nomial_time = 1e6 * _samples_per_period / _samplerate;
 
        manager.registration_callback();
        manager.graph_order_callback();
@@ -1311,7 +1382,7 @@ AlsaAudioBackend::main_process_thread ()
                                ++no_proc_errors;
                                xrun = true;
                        }
-                       if (_pcmi->state () < 0 || no_proc_errors > 50) {
+                       if (_pcmi->state () < 0 || no_proc_errors > bailout) {
                                PBD::error << _("AlsaAudioBackend: I/O error. Audio Process Terminated.") << endmsg;
                                break;
                        }
@@ -1386,7 +1457,6 @@ AlsaAudioBackend::main_process_thread ()
                                /* calculate DSP load */
                                clock2 = g_get_monotonic_time();
                                const int64_t elapsed_time = clock2 - clock1;
-                               const int64_t nomial_time = 1e6 * _samples_per_period / _samplerate;
                                _dsp_load = elapsed_time / (float) nomial_time;
                        }
 
@@ -1438,6 +1508,10 @@ AlsaAudioBackend::main_process_thread ()
                if (connections_changed) {
                        manager.graph_order_callback();
                }
+               if (connections_changed || ports_changed) {
+                       engine.latency_callback(false);
+                       engine.latency_callback(true);
+               }
 
        }
        _pcmi->pcm_stop ();
@@ -1459,7 +1533,7 @@ static int deinstantiate ();
 static bool already_configured ();
 
 static ARDOUR::AudioBackendInfo _descriptor = {
-       "Alsa",
+       "ALSA",
        instantiate,
        deinstantiate,
        backend_factory,