Abstract definition of rt-scheduler policy
[ardour.git] / libs / backends / coreaudio / coreaudio_backend.cc
index b22049e24754b6ab3759e58f796b5233628d4f71..c4e11ef7002aa6ca6222edd501e578706e4c81c5 100644 (file)
 #include <sys/mman.h>
 #include <sys/time.h>
 
+#include <mach/thread_policy.h>
+#include <mach/thread_act.h>
+
 #include <glibmm.h>
 
 #include "coreaudio_backend.h"
-#include "rt_thread.h"
 
 #include "pbd/compose.h"
 #include "pbd/error.h"
 #include "pbd/file_utils.h"
+#include "pbd/pthread_utils.h"
 #include "ardour/filesystem_paths.h"
 #include "ardour/port_manager.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace ARDOUR;
 
@@ -111,6 +114,8 @@ CoreAudioBackend::CoreAudioBackend (AudioEngine& e, AudioBackendInfo& info)
        pthread_mutex_init (&_freewheel_mutex, 0);
        pthread_cond_init  (&_freewheel_signal, 0);
 
+       _port_connection_queue.reserve (128);
+
        _pcmio = new CoreAudioPCM ();
        _midiio = new CoreMidiIo ();
 
@@ -203,8 +208,8 @@ CoreAudioBackend::available_sample_rates2 (const std::string& input_device, cons
        std::vector<float> sr_in;
        std::vector<float> sr_out;
 
-       const uint32_t inp = name_to_id (input_device);
-       const uint32_t out = name_to_id (output_device);
+       const uint32_t inp = name_to_id (input_device, Input);
+       const uint32_t out = name_to_id (output_device, Output);
 
        if (inp == UINT32_MAX && out == UINT32_MAX) {
                return sr;
@@ -237,8 +242,8 @@ CoreAudioBackend::available_buffer_sizes2 (const std::string& input_device, cons
        std::vector<uint32_t> bs;
        std::vector<uint32_t> bs_in;
        std::vector<uint32_t> bs_out;
-       const uint32_t inp = name_to_id (input_device);
-       const uint32_t out = name_to_id (output_device);
+       const uint32_t inp = name_to_id (input_device, Input);
+       const uint32_t out = name_to_id (output_device, Output);
        if (inp == UINT32_MAX && out == UINT32_MAX) {
                return bs;
        } else if (inp == UINT32_MAX) {
@@ -292,7 +297,7 @@ int
 CoreAudioBackend::set_input_device_name (const std::string& d)
 {
        _input_audio_device = d;
-       const float sr = _pcmio->current_sample_rate(name_to_id(_input_audio_device));
+       const float sr = _pcmio->current_sample_rate(name_to_id(_input_audio_device, Input));
        if (sr > 0) { set_sample_rate(sr); }
        return 0;
 }
@@ -302,7 +307,7 @@ CoreAudioBackend::set_output_device_name (const std::string& d)
 {
        _output_audio_device = d;
        // TODO check SR.
-       const float sr = _pcmio->current_sample_rate(name_to_id(_output_audio_device));
+       const float sr = _pcmio->current_sample_rate(name_to_id(_output_audio_device, Output));
        if (sr > 0) { set_sample_rate(sr); }
        return 0;
 }
@@ -327,7 +332,13 @@ CoreAudioBackend::set_buffer_size (uint32_t bs)
        }
        _samples_per_period = bs;
        _pcmio->set_samples_per_period(bs);
-       engine.buffer_size_change (bs);
+       if (_run) {
+               coreaudio_set_realtime_policy (_main_thread);
+       }
+       for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i) {
+               coreaudio_set_realtime_policy (*i);
+       }
+       //engine.buffer_size_change (bs);
        return 0;
 }
 
@@ -459,10 +470,10 @@ void
 CoreAudioBackend::launch_control_app ()
 {
        if (name_to_id (_input_audio_device) != UINT32_MAX) {
-               _pcmio->launch_control_app(name_to_id(_input_audio_device));
+               _pcmio->launch_control_app(name_to_id(_input_audio_device, Input));
        }
        if (name_to_id (_output_audio_device) != UINT32_MAX) {
-               _pcmio->launch_control_app(name_to_id(_output_audio_device));
+               _pcmio->launch_control_app(name_to_id(_output_audio_device, Output));
        }
 }
 
@@ -507,8 +518,8 @@ CoreAudioBackend::_start (bool for_latency_measurement)
                _portmap.clear();
        }
 
-       uint32_t device1 = name_to_id(_input_audio_device);
-       uint32_t device2 = name_to_id(_output_audio_device);
+       uint32_t device1 = name_to_id(_input_audio_device, Input);
+       uint32_t device2 = name_to_id(_output_audio_device, Output);
 
        assert(_active_ca == false);
        assert(_active_fw == false);
@@ -603,11 +614,6 @@ CoreAudioBackend::_start (bool for_latency_measurement)
                PBD::info << _("CoreAudioBackend: adjusted input channel count to match device.") << endmsg;
        }
 
-       if (_pcmio->samples_per_period() != _samples_per_period) {
-               _samples_per_period = _pcmio->samples_per_period();
-               PBD::warning << _("CoreAudioBackend: samples per period does not match.") << endmsg;
-       }
-
        if (_pcmio->sample_rate() != _samplerate) {
                _samplerate = _pcmio->sample_rate();
                engine.sample_rate_change (_samplerate);
@@ -778,10 +784,24 @@ CoreAudioBackend::samples_since_cycle_start ()
 }
 
 uint32_t
-CoreAudioBackend::name_to_id(std::string device_name) const {
+CoreAudioBackend::name_to_id(std::string device_name, DeviceFilter filter) const {
        uint32_t device_id = UINT32_MAX;
        std::map<size_t, std::string> devices;
-       _pcmio->device_list(devices);
+       switch (filter) {
+               case Input:
+                       _pcmio->input_device_list (devices);
+                       break;
+               case Output:
+                       _pcmio->output_device_list (devices);
+                       break;
+               case Duplex:
+                       _pcmio->duplex_device_list (devices);
+                       break;
+               case All:
+               default:
+                       _pcmio->device_list (devices);
+                       break;
+       }
 
        for (std::map<size_t, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
                if (i->second == device_name) {
@@ -802,6 +822,34 @@ CoreAudioBackend::coreaudio_process_thread (void *arg)
        return 0;
 }
 
+bool
+CoreAudioBackend::coreaudio_set_realtime_policy (pthread_t thread_id) const
+{
+       thread_time_constraint_policy_data_t policy;
+#ifndef NDEBUG
+       mach_msg_type_number_t msgt = 4;
+       boolean_t dflt = false;
+       kern_return_t rv = thread_policy_get (pthread_mach_thread_np (_main_thread),
+                       THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t) &policy,
+                       &msgt, &dflt);
+       printf ("Coreaudio Main Thread(%p) %d %d %d %d DFLT %d OK: %d\n", _main_thread, policy.period, policy.computation, policy.constraint, policy.preemptible, dflt, rv == KERN_SUCCESS);
+#endif
+
+       double period_ns = 1e9 * _samples_per_period / _samplerate;
+       policy.period = AudioConvertNanosToHostTime (period_ns);
+       policy.computation = AudioConvertNanosToHostTime (period_ns * .9);
+       policy.constraint = AudioConvertNanosToHostTime (period_ns * .95);
+       policy.preemptible = true;
+       kern_return_t res = thread_policy_set (pthread_mach_thread_np (thread_id),
+                       THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t) &policy,
+                       THREAD_TIME_CONSTRAINT_POLICY_COUNT);
+
+#ifndef NDEBUG
+       printf ("Coreaudio Proc Thread(%p) %d %d %d %d OK: %d\n", thread_id, policy.period, policy.computation, policy.constraint, policy.preemptible, res == KERN_SUCCESS);
+#endif
+       return res != KERN_SUCCESS;
+}
+
 int
 CoreAudioBackend::create_process_thread (boost::function<void()> func)
 {
@@ -811,7 +859,7 @@ CoreAudioBackend::create_process_thread (boost::function<void()> func)
 
        ThreadData* td = new ThreadData (this, func, stacksize);
 
-       if (_realtime_pthread_create (SCHED_FIFO, -21, stacksize,
+       if (pbd_realtime_pthread_create (PBD_SCHED_FIFO, -22, stacksize,
                                      &thread_id, coreaudio_process_thread, td)) {
                pthread_attr_init (&attr);
                pthread_attr_setstacksize (&attr, stacksize);
@@ -820,9 +868,14 @@ CoreAudioBackend::create_process_thread (boost::function<void()> func)
                        pthread_attr_destroy (&attr);
                        return -1;
                }
+               PBD::warning << _("AudioEngine: process thread failed to acquire realtime permissions.") << endmsg;
                pthread_attr_destroy (&attr);
        }
 
+       if (coreaudio_set_realtime_policy (thread_id)) {
+               PBD::warning << _("AudioEngine: process thread failed to set mach realtime policy.") << endmsg;
+       }
+
        _threads.push_back (thread_id);
        return 0;
 }
@@ -1073,8 +1126,8 @@ CoreAudioBackend::register_system_audio_ports()
        const uint32_t a_ins = _n_inputs;
        const uint32_t a_out = _n_outputs;
 
-       const uint32_t coreaudio_reported_input_latency = _pcmio->get_latency(name_to_id(_input_audio_device), true);
-       const uint32_t coreaudio_reported_output_latency = _pcmio->get_latency(name_to_id(_output_audio_device), false);
+       const uint32_t coreaudio_reported_input_latency = _pcmio->get_latency(name_to_id(_input_audio_device, Input), true);
+       const uint32_t coreaudio_reported_output_latency = _pcmio->get_latency(name_to_id(_output_audio_device, Output), false);
 
 #ifndef NDEBUG
        printf("COREAUDIO LATENCY: i:%d, o:%d\n",
@@ -1356,7 +1409,7 @@ CoreAudioBackend::get_connections (PortEngine::PortHandle port, std::vector<std:
 int
 CoreAudioBackend::midi_event_get (
        pframes_t& timestamp,
-       size_t& size, uint8_t** buf, void* port_buffer,
+       size_t& size, uint8_t const** buf, void* port_buffer,
        uint32_t event_index)
 {
        if (!buf || !port_buffer) return -1;
@@ -1364,11 +1417,11 @@ CoreAudioBackend::midi_event_get (
        if (event_index >= source.size ()) {
                return -1;
        }
-       CoreMidiEvent * const event = source[event_index].get ();
+       CoreMidiEvent const& event = source[event_index];
 
-       timestamp = event->timestamp ();
-       size = event->size ();
-       *buf = event->data ();
+       timestamp = event.timestamp ();
+       size = event.size ();
+       *buf = event.data ();
        return 0;
 }
 
@@ -1379,15 +1432,18 @@ CoreAudioBackend::_midi_event_put (
        const uint8_t* buffer, size_t size)
 {
        if (!buffer || !port_buffer) return -1;
+       if (size >= MaxCoreMidiEventSize) {
+               return -1;
+       }
        CoreMidiBuffer& dst = * static_cast<CoreMidiBuffer*>(port_buffer);
-       if (dst.size () && (pframes_t)dst.back ()->timestamp () > timestamp) {
 #ifndef NDEBUG
+       if (dst.size () && (pframes_t)dst.back ().timestamp () > timestamp) {
                // nevermind, ::get_buffer() sorts events
                fprintf (stderr, "CoreMidiBuffer: unordered event: %d > %d\n",
-                        (pframes_t)dst.back ()->timestamp (), timestamp);
-#endif
+                        (pframes_t)dst.back ().timestamp (), timestamp);
        }
-       dst.push_back (boost::shared_ptr<CoreMidiEvent>(new CoreMidiEvent (timestamp, buffer, size)));
+#endif
+       dst.push_back (CoreMidiEvent (timestamp, buffer, size));
        return 0;
 }
 
@@ -1554,7 +1610,9 @@ CoreAudioBackend::n_physical_inputs () const
 void*
 CoreAudioBackend::get_buffer (PortEngine::PortHandle port, pframes_t nframes)
 {
-       if (!port || !valid_port (port)) return NULL;
+       assert (port);
+       assert (valid_port (port));
+       if (!port || !valid_port (port)) return NULL; // XXX remove me
        return static_cast<CoreBackendPort*>(port)->get_buffer (nframes);
 }
 
@@ -1656,6 +1714,7 @@ CoreAudioBackend::freewheel_thread ()
                        AudioEngine::thread_init_callback (this);
                        _midiio->set_enabled(false);
                        reset_midi_parsers ();
+                       coreaudio_set_realtime_policy (_main_thread);
                }
 
                // process port updates first in every cycle.
@@ -1721,6 +1780,7 @@ CoreAudioBackend::process_callback (const uint32_t n_samples, const uint64_t hos
                _reinit_thread_callback = false;
                _main_thread = pthread_self();
                AudioEngine::thread_init_callback (this);
+               coreaudio_set_realtime_policy (_main_thread);
        }
 
        if (pthread_mutex_trylock (&_process_callback_mutex)) {
@@ -1747,7 +1807,7 @@ CoreAudioBackend::process_callback (const uint32_t n_samples, const uint64_t hos
                        continue;
                }
                uint64_t time_ns;
-               uint8_t data[128]; // matches CoreMidi's MIDIPacket
+               uint8_t data[MaxCoreMidiEventSize];
                size_t size = sizeof(data);
 
                port->clear_events ();
@@ -1790,15 +1850,10 @@ CoreAudioBackend::process_callback (const uint32_t n_samples, const uint64_t hos
        /* queue outgoing midi */
        i = 0;
        for (std::vector<CoreBackendPort*>::const_iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it, ++i) {
-#if 0 // something's still b0rked with CoreMidiIo::send_events()
-               const CoreMidiBuffer *src = static_cast<const CoreMidiPort*>(*it)->const_buffer();
-               _midiio->send_events (i, nominal_time, (void*)src);
-#else // works..
                const CoreMidiBuffer *src = static_cast<const CoreMidiPort*>(*it)->const_buffer();
                for (CoreMidiBuffer::const_iterator mit = src->begin (); mit != src->end (); ++mit) {
-                       _midiio->send_event (i, (*mit)->timestamp() / nominal_time, (*mit)->data(), (*mit)->size());
+                       _midiio->send_event (i, mit->timestamp (), mit->data (), mit->size ());
                }
-#endif
        }
 
        /* write back audio */
@@ -2104,13 +2159,16 @@ CoreMidiPort::CoreMidiPort (CoreAudioBackend &b, const std::string& name, PortFl
 {
        _buffer[0].clear ();
        _buffer[1].clear ();
+
+       _buffer[0].reserve (256);
+       _buffer[1].reserve (256);
 }
 
 CoreMidiPort::~CoreMidiPort () { }
 
 struct MidiEventSorter {
-       bool operator() (const boost::shared_ptr<CoreMidiEvent>& a, const boost::shared_ptr<CoreMidiEvent>& b) {
-               return *a < *b;
+       bool operator() (CoreMidiEvent const& a, CoreMidiEvent const& b) {
+               return a < b;
        }
 };
 
@@ -2124,10 +2182,10 @@ void* CoreMidiPort::get_buffer (pframes_t /* nframes */)
                     ++i) {
                        const CoreMidiBuffer * src = static_cast<const CoreMidiPort*>(*i)->const_buffer ();
                        for (CoreMidiBuffer::const_iterator it = src->begin (); it != src->end (); ++it) {
-                               (_buffer[_bufperiod]).push_back (boost::shared_ptr<CoreMidiEvent>(new CoreMidiEvent (**it)));
+                               (_buffer[_bufperiod]).push_back (*it);
                        }
                }
-               std::sort ((_buffer[_bufperiod]).begin (), (_buffer[_bufperiod]).end (), MidiEventSorter());
+               std::stable_sort ((_buffer[_bufperiod]).begin (), (_buffer[_bufperiod]).end (), MidiEventSorter());
        }
 
        return &(_buffer[_bufperiod]);
@@ -2293,10 +2351,8 @@ CoreMidiPort::process_byte(const uint64_t time, const uint8_t byte)
 CoreMidiEvent::CoreMidiEvent (const pframes_t timestamp, const uint8_t* data, size_t size)
        : _size (size)
        , _timestamp (timestamp)
-       , _data (0)
 {
-       if (size > 0) {
-               _data = (uint8_t*) malloc (size);
+       if (size > 0 && size < MaxCoreMidiEventSize) {
                memcpy (_data, data, size);
        }
 }
@@ -2304,14 +2360,9 @@ CoreMidiEvent::CoreMidiEvent (const pframes_t timestamp, const uint8_t* data, si
 CoreMidiEvent::CoreMidiEvent (const CoreMidiEvent& other)
        : _size (other.size ())
        , _timestamp (other.timestamp ())
-       , _data (0)
 {
-       if (other.size () && other.const_data ()) {
-               _data = (uint8_t*) malloc (other.size ());
-               memcpy (_data, other.const_data (), other.size ());
+       if (other._size > 0) {
+               assert (other._size < MaxCoreMidiEventSize);
+               memcpy (_data, other._data, other._size);
        }
 };
-
-CoreMidiEvent::~CoreMidiEvent () {
-       free (_data);
-};