diff options
Diffstat (limited to 'libs/backends/portaudio/portaudio_backend.cc')
-rw-r--r-- | libs/backends/portaudio/portaudio_backend.cc | 192 |
1 files changed, 166 insertions, 26 deletions
diff --git a/libs/backends/portaudio/portaudio_backend.cc b/libs/backends/portaudio/portaudio_backend.cc index 71986cd376..a1fa2c96d1 100644 --- a/libs/backends/portaudio/portaudio_backend.cc +++ b/libs/backends/portaudio/portaudio_backend.cc @@ -36,8 +36,17 @@ #include "ardour/port_manager.h" #include "i18n.h" +#include "win_utils.h" +#include "debug.h" + using namespace ARDOUR; +namespace { + +const char * const winmme_driver_name = X_("WinMME"); + +} + static std::string s_instance_name; size_t PortAudioBackend::_max_buffer_size = 8192; std::vector<std::string> PortAudioBackend::_midi_options; @@ -51,7 +60,9 @@ PortAudioBackend::PortAudioBackend (AudioEngine& e, AudioBackendInfo& info) , _active (false) , _freewheel (false) , _measure_latency (false) - , _last_process_start (0) + , m_cycle_count(0) + , m_total_deviation_us(0) + , m_max_deviation_us(0) , _input_audio_device("") , _output_audio_device("") , _midi_driver_option(_("None")) @@ -69,11 +80,13 @@ PortAudioBackend::PortAudioBackend (AudioEngine& e, AudioBackendInfo& info) pthread_mutex_init (&_port_callback_mutex, 0); _pcmio = new PortAudioIO (); + _midiio = new WinMMEMidiIO (); } PortAudioBackend::~PortAudioBackend () { delete _pcmio; _pcmio = 0; + delete _midiio; _midiio = 0; pthread_mutex_destroy (&_port_callback_mutex); } @@ -343,7 +356,7 @@ std::vector<std::string> PortAudioBackend::enumerate_midi_options () const { if (_midi_options.empty()) { - //_midi_options.push_back (_("PortMidi")); + _midi_options.push_back (winmme_driver_name); _midi_options.push_back (_("None")); } return _midi_options; @@ -352,9 +365,10 @@ PortAudioBackend::enumerate_midi_options () const int PortAudioBackend::set_midi_option (const std::string& opt) { - if (opt != _("None") /* && opt != _("PortMidi")*/) { + if (opt != _("None") && opt != winmme_driver_name) { return -1; } + DEBUG_MIDI (string_compose ("Setting midi option to %1\n", opt)); _midi_driver_option = opt; return 0; } @@ -401,7 +415,6 @@ PortAudioBackend::_start (bool for_latency_measurement) _dsp_load = 0; _freewheeling = false; _freewheel = false; - _last_process_start = 0; _pcmio->pcm_setup (name_to_id(_input_audio_device), name_to_id(_output_audio_device), _samplerate, _samples_per_period); @@ -441,10 +454,28 @@ PortAudioBackend::_start (bool for_latency_measurement) _run = true; _port_change_flag = false; - // TODO MIDI + if (_midi_driver_option == winmme_driver_name) { + _midiio->set_enabled(true); + //_midiio->set_port_changed_callback(midi_port_change, this); + _midiio->start(); // triggers port discovery, callback coremidi_rediscover() + } + + m_cycle_timer.set_samplerate(_samplerate); + m_cycle_timer.set_samples_per_cycle(_samples_per_period); + + DEBUG_MIDI ("Registering MIDI ports\n"); + + if (register_system_midi_ports () != 0) { + PBD::error << _ ("PortAudioBackend: failed to register system midi ports.") + << endmsg; + _run = false; + return -1; + } + + DEBUG_AUDIO ("Registering Audio ports\n"); if (register_system_audio_ports()) { - PBD::error << _("PortAudioBackend: failed to register system ports.") << endmsg; + PBD::error << _("PortAudioBackend: failed to register system audio ports.") << endmsg; _run = false; return -1; } @@ -557,12 +588,11 @@ PortAudioBackend::samples_since_cycle_start () if (!_active || !_run || _freewheeling || _freewheel) { return 0; } - if (_last_process_start == 0) { + if (!m_cycle_timer.valid()) { return 0; } - const int64_t elapsed_time_us = g_get_monotonic_time() - _last_process_start; - return std::max((pframes_t)0, (pframes_t)rint(1e-6 * elapsed_time_us * _samplerate)); + return m_cycle_timer.samples_since_cycle_start (utils::get_microseconds()); } int @@ -846,6 +876,52 @@ PortAudioBackend::register_system_audio_ports() return 0; } +int +PortAudioBackend::register_system_midi_ports() +{ + if (_midi_driver_option == _("None")) { + DEBUG_MIDI ("No MIDI backend selected, not system midi ports available\n"); + return 0; + } + + LatencyRange lr; + lr.min = lr.max = _samples_per_period; + + const std::vector<WinMMEMidiInputDevice*> inputs = _midiio->get_inputs(); + + for (std::vector<WinMMEMidiInputDevice*>::const_iterator i = inputs.begin (); + i != inputs.end (); + ++i) { + std::string port_name = "system_midi:" + (*i)->name() + " capture"; + PortHandle p = + add_port (port_name, + 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<PortMidiPort*>(p)); + DEBUG_MIDI (string_compose ("Registered MIDI input port: %1\n", port_name)); + } + + const std::vector<WinMMEMidiOutputDevice*> outputs = _midiio->get_outputs(); + + for (std::vector<WinMMEMidiOutputDevice*>::const_iterator i = outputs.begin (); + i != outputs.end (); + ++i) { + std::string port_name = "system_midi:" + (*i)->name() + " playback"; + PortHandle p = + add_port (port_name, + DataType::MIDI, + static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal)); + if (!p) return -1; + set_latency_range (p, false, lr); + static_cast<PortMidiPort*>(p)->set_n_periods(2); + _system_midi_out.push_back (static_cast<PortMidiPort*>(p)); + DEBUG_MIDI (string_compose ("Registered MIDI output port: %1\n", port_name)); + } + return 0; +} + void PortAudioBackend::unregister_ports (bool system_only) { @@ -1015,11 +1091,10 @@ PortAudioBackend::midi_event_put ( if (!buffer || !port_buffer) return -1; PortMidiBuffer& dst = * static_cast<PortMidiBuffer*>(port_buffer); if (dst.size () && (pframes_t)dst.back ()->timestamp () > timestamp) { -#ifndef NDEBUG // nevermind, ::get_buffer() sorts events - fprintf (stderr, "PortMidiBuffer: unordered event: %d > %d\n", - (pframes_t)dst.back ()->timestamp (), timestamp); -#endif + DEBUG_MIDI (string_compose ("PortMidiBuffer: unordered event: %1 > %2\n", + (pframes_t)dst.back ()->timestamp (), + timestamp)); } dst.push_back (boost::shared_ptr<PortMidiEvent>(new PortMidiEvent (timestamp, buffer, size))); return 0; @@ -1200,7 +1275,10 @@ PortAudioBackend::main_process_thread () _processed_samples = 0; uint64_t clock1, clock2; + int64_t min_elapsed_us = 1000000; + int64_t max_elapsed_us = 0; const int64_t nomial_time = 1e6 * _samples_per_period / _samplerate; + // const int64_t nomial_time = m_cycle_timer.get_length_us(); manager.registration_callback(); manager.graph_order_callback(); @@ -1224,9 +1302,7 @@ PortAudioBackend::main_process_thread () case 0: // OK break; case 1: -#ifndef NDEBUG - printf("PortAudio: Xrun\n"); -#endif + DEBUG_AUDIO ("PortAudio: Xrun\n"); engine.Xrun (); break; default: @@ -1235,7 +1311,7 @@ PortAudioBackend::main_process_thread () } uint32_t i = 0; - clock1 = g_get_monotonic_time(); + clock1 = utils::get_microseconds (); /* get audio */ i = 0; @@ -1248,6 +1324,26 @@ PortAudioBackend::main_process_thread () for (std::vector<PamPort*>::const_iterator it = _system_midi_in.begin (); it != _system_midi_in.end (); ++it, ++i) { PortMidiBuffer* mbuf = static_cast<PortMidiBuffer*>((*it)->get_buffer(0)); mbuf->clear(); + uint64_t timestamp; + pframes_t sample_offset; + uint8_t data[256]; + size_t size = sizeof(data); + while (_midiio->dequeue_input_event (i, + m_cycle_timer.get_start (), + m_cycle_timer.get_next_start (), + timestamp, + data, + size)) { + sample_offset = m_cycle_timer.samples_since_cycle_start (timestamp); + midi_event_put (mbuf, sample_offset, data, size); + DEBUG_MIDI (string_compose ("Dequeuing incoming MIDI data for device: %1 " + "sample_offset: %2 timestamp: %3, size: %4\n", + _midiio->get_inputs ()[i]->name (), + sample_offset, + timestamp, + size)); + size = sizeof(data); + } } /* clear output buffers */ @@ -1255,25 +1351,61 @@ PortAudioBackend::main_process_thread () memset ((*it)->get_buffer (_samples_per_period), 0, _samples_per_period * sizeof (Sample)); } + m_last_cycle_start = m_cycle_timer.get_start (); + m_cycle_timer.reset_start(utils::get_microseconds()); + m_cycle_count++; + + uint64_t cycle_diff_us = (m_cycle_timer.get_start () - m_last_cycle_start); + int64_t deviation_us = (cycle_diff_us - m_cycle_timer.get_length_us()); + m_total_deviation_us += std::abs(deviation_us); + m_max_deviation_us = + std::max (m_max_deviation_us, (uint64_t)std::abs (deviation_us)); + + if ((m_cycle_count % 1000) == 0) { + uint64_t mean_deviation_us = m_total_deviation_us / m_cycle_count; + DEBUG_TIMING ( + string_compose ("Mean avg cycle deviation: %1(ms), max %2(ms)\n", + mean_deviation_us * 1e-3, + m_max_deviation_us * 1e-3)); + } + + if (std::abs(deviation_us) > m_cycle_timer.get_length_us()) { + DEBUG_TIMING (string_compose ( + "time between process(ms): %1, Est(ms): %2, Dev(ms): %3\n", + cycle_diff_us * 1e-3, + m_cycle_timer.get_length_us () * 1e-3, + deviation_us * 1e-3)); + } + /* call engine process callback */ - _last_process_start = g_get_monotonic_time(); if (engine.process_callback (_samples_per_period)) { _pcmio->pcm_stop (); _active = false; return 0; } -#if 0 /* mixdown midi */ for (std::vector<PamPort*>::iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it) { - static_cast<PortBackendMidiPort*>(*it)->next_period(); + static_cast<PortMidiPort*>(*it)->next_period(); } - /* queue outgoing midi */ i = 0; for (std::vector<PamPort*>::const_iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it, ++i) { - // TODO + const PortMidiBuffer* src = static_cast<const PortMidiPort*>(*it)->const_buffer(); + + for (PortMidiBuffer::const_iterator mit = src->begin (); mit != src->end (); ++mit) { + uint64_t timestamp = + m_cycle_timer.timestamp_from_sample_offset ((*mit)->timestamp ()); + DEBUG_MIDI ( + string_compose ("Queuing outgoing MIDI data for device: " + "%1 sample_offset: %2 timestamp: %3, size: %4\n", + _midiio->get_outputs ()[i]->name (), + (*mit)->timestamp (), + timestamp, + (*mit)->size ())); + _midiio->enqueue_output_event ( + i, timestamp, (*mit)->data (), (*mit)->size ()); + } } -#endif /* write back audio */ i = 0; @@ -1284,10 +1416,19 @@ PortAudioBackend::main_process_thread () _processed_samples += _samples_per_period; /* calculate DSP load */ - clock2 = g_get_monotonic_time(); + clock2 = utils::get_microseconds (); const int64_t elapsed_time = clock2 - clock1; _dsp_load = elapsed_time / (float) nomial_time; + max_elapsed_us = std::max (elapsed_time, max_elapsed_us); + min_elapsed_us = std::min (elapsed_time, min_elapsed_us); + if ((m_cycle_count % 1000) == 0) { + DEBUG_TIMING ( + string_compose ("Elapsed process time(usecs) max: %1, min: %2\n", + max_elapsed_us, + min_elapsed_us)); + } + } else { // Freewheelin' @@ -1296,11 +1437,10 @@ PortAudioBackend::main_process_thread () memset ((*it)->get_buffer (_samples_per_period), 0, _samples_per_period * sizeof (Sample)); } - clock1 = g_get_monotonic_time(); + clock1 = utils::get_microseconds (); // TODO clear midi or stop midi recv when entering fwheelin' - _last_process_start = 0; if (engine.process_callback (_samples_per_period)) { _pcmio->pcm_stop (); _active = false; |