Add support for recording to .flac
[ardour.git] / libs / backends / portaudio / portaudio_backend.cc
index b235a98b899ac526835266742d4739d5422f5be3..b7afb4c48d70623b403b6eb2765b94702e0fc722 100644 (file)
 #include <glibmm.h>
 
 #include "portaudio_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 "pbd/windows_timer_utils.h"
 #include "pbd/windows_mmcss.h"
 
 #include "ardour/filesystem_paths.h"
 #include "ardour/port_manager.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 #include "audio_utils.h"
 
@@ -66,6 +66,7 @@ PortAudioBackend::PortAudioBackend (AudioEngine& e, AudioBackendInfo& info)
        , _pcmio (0)
        , _run (false)
        , _active (false)
+       , _use_blocking_api(false)
        , _freewheel (false)
        , _freewheeling (false)
        , _freewheel_ack (false)
@@ -92,6 +93,8 @@ PortAudioBackend::PortAudioBackend (AudioEngine& e, AudioBackendInfo& info)
        pthread_mutex_init (&_freewheel_mutex, 0);
        pthread_cond_init (&_freewheel_signal, 0);
 
+       _port_connection_queue.reserve (128);
+
        _pcmio = new PortAudioIO ();
        _midiio = new WinMMEMidiIO ();
 }
@@ -156,9 +159,22 @@ PortAudioBackend::set_driver (const std::string& name)
 bool
 PortAudioBackend::update_devices ()
 {
+       // update midi device info?
        return _pcmio->update_devices();
 }
 
+void
+PortAudioBackend::set_use_buffered_io (bool use_buffered_io)
+{
+       DEBUG_AUDIO (string_compose ("Portaudio: use_buffered_io %1 \n", use_buffered_io));
+
+       if (running()) {
+               return;
+       }
+
+       _use_blocking_api = use_buffered_io;
+}
+
 std::string
 PortAudioBackend::driver_name () const
 {
@@ -329,6 +345,24 @@ PortAudioBackend::set_systemic_output_latency (uint32_t sl)
        return 0;
 }
 
+int
+PortAudioBackend::set_systemic_midi_input_latency (std::string const device, uint32_t sl)
+{
+       MidiDeviceInfo* nfo = midi_device_info (device);
+       if (!nfo) return -1;
+       nfo->systemic_input_latency = sl;
+       return 0;
+}
+
+int
+PortAudioBackend::set_systemic_midi_output_latency (std::string const device, uint32_t sl)
+{
+       MidiDeviceInfo* nfo = midi_device_info (device);
+       if (!nfo) return -1;
+       nfo->systemic_output_latency = sl;
+       return 0;
+}
+
 /* Retrieving parameters */
 std::string
 PortAudioBackend::device_name () const
@@ -390,6 +424,22 @@ PortAudioBackend::systemic_output_latency () const
        return _systemic_audio_output_latency;
 }
 
+uint32_t
+PortAudioBackend::systemic_midi_input_latency (std::string const device) const
+{
+       MidiDeviceInfo* nfo = midi_device_info (device);
+       if (!nfo) return 0;
+       return nfo->systemic_input_latency;
+}
+
+uint32_t
+PortAudioBackend::systemic_midi_output_latency (std::string const device) const
+{
+       MidiDeviceInfo* nfo = midi_device_info (device);
+       if (!nfo) return 0;
+       return nfo->systemic_output_latency;
+}
+
 std::string
 PortAudioBackend::control_app_name () const
 {
@@ -431,6 +481,61 @@ PortAudioBackend::midi_option () const
        return _midi_driver_option;
 }
 
+std::vector<AudioBackend::DeviceStatus>
+PortAudioBackend::enumerate_midi_devices () const
+{
+       std::vector<AudioBackend::DeviceStatus> midi_device_status;
+       std::vector<MidiDeviceInfo*> device_info;
+
+       if (_midi_driver_option == winmme_driver_name) {
+               _midiio->update_device_info ();
+               device_info = _midiio->get_device_info ();
+       }
+
+       for (std::vector<MidiDeviceInfo*>::const_iterator i = device_info.begin();
+            i != device_info.end();
+            ++i) {
+               midi_device_status.push_back(DeviceStatus((*i)->device_name, true));
+       }
+       return midi_device_status;
+}
+
+MidiDeviceInfo*
+PortAudioBackend::midi_device_info (const std::string& device_name) const
+{
+       std::vector<MidiDeviceInfo*> dev_info;
+
+       if (_midi_driver_option == winmme_driver_name) {
+               dev_info = _midiio->get_device_info();
+
+               for (std::vector<MidiDeviceInfo*>::const_iterator i = dev_info.begin();
+                    i != dev_info.end();
+                    ++i) {
+                       if ((*i)->device_name == device_name) {
+                               return *i;
+                       }
+               }
+       }
+       return 0;
+}
+
+int
+PortAudioBackend::set_midi_device_enabled (std::string const device, bool enable)
+{
+       MidiDeviceInfo* nfo = midi_device_info(device);
+       if (!nfo) return -1;
+       nfo->enable = enable;
+       return 0;
+}
+
+bool
+PortAudioBackend::midi_device_enabled (std::string const device) const
+{
+       MidiDeviceInfo* nfo = midi_device_info(device);
+       if (!nfo) return false;
+       return nfo->enable;
+}
+
 /* State Control */
 
 static void * blocking_thread_func (void *arg)
@@ -476,27 +581,29 @@ PortAudioBackend::_start (bool for_latency_measurement)
        }
 
        /* reset internal state */
+       assert (_run == false);
+       _run = false;
        _dsp_load = 0;
        _freewheeling = false;
        _freewheel = false;
 
        PaErrorCode err = paNoError;
 
-#ifdef USE_BLOCKING_API
-       err = _pcmio->open_blocking_stream(name_to_id(_input_audio_device),
-                                          name_to_id(_output_audio_device),
-                                          _samplerate,
-                                          _samples_per_period);
-
-#else
-       err = _pcmio->open_callback_stream(name_to_id(_input_audio_device),
-                                          name_to_id(_output_audio_device),
-                                          _samplerate,
-                                          _samples_per_period,
-                                          portaudio_callback,
-                                          this);
-
-#endif
+       if (_use_blocking_api) {
+               DEBUG_AUDIO("Opening blocking audio stream\n");
+               err = _pcmio->open_blocking_stream(name_to_id(_input_audio_device),
+                                                  name_to_id(_output_audio_device),
+                                                  _samplerate,
+                                                  _samples_per_period);
+       } else {
+               DEBUG_AUDIO("Opening callback audio stream\n");
+               err = _pcmio->open_callback_stream(name_to_id(_input_audio_device),
+                                                  name_to_id(_output_audio_device),
+                                                  _samplerate,
+                                                  _samples_per_period,
+                                                  portaudio_callback,
+                                                  this);
+       }
 
        // reintepret Portaudio error messages
        switch (err) {
@@ -536,7 +643,6 @@ PortAudioBackend::_start (bool for_latency_measurement)
 
        _measure_latency = for_latency_measurement;
 
-       _run = true;
        _port_change_flag = false;
 
        if (_midi_driver_option == winmme_driver_name) {
@@ -554,7 +660,6 @@ PortAudioBackend::_start (bool for_latency_measurement)
 
        if (register_system_midi_ports () != 0) {
                DEBUG_PORTS("Failed to register system midi ports.\n")
-               _run = false;
                return PortRegistrationError;
        }
 
@@ -562,7 +667,6 @@ PortAudioBackend::_start (bool for_latency_measurement)
 
        if (register_system_audio_ports()) {
                DEBUG_PORTS("Failed to register system audio ports.\n");
-               _run = false;
                return PortRegistrationError;
        }
 
@@ -571,30 +675,40 @@ PortAudioBackend::_start (bool for_latency_measurement)
 
        if (engine.reestablish_ports ()) {
                DEBUG_PORTS("Could not re-establish ports.\n");
-               _run = false;
                return PortReconnectError;
        }
 
-       engine.reconnect_ports ();
        _run = true;
+
+       engine.reconnect_ports ();
        _port_change_flag = false;
 
-#ifdef USE_BLOCKING_API
-       if (!start_blocking_process_thread()) {
-               return ProcessThreadStartError;
-       }
-#else
-       if (_pcmio->start_stream() != paNoError) {
-               DEBUG_AUDIO("Unable to start stream\n");
-               return AudioDeviceOpenError;
-       }
+       if (_use_blocking_api) {
+               if (!start_blocking_process_thread()) {
+                       return ProcessThreadStartError;
+               }
+       } else {
+               if (_pcmio->start_stream() != paNoError) {
+                       DEBUG_AUDIO("Unable to start stream\n");
+                       return AudioDeviceOpenError;
+               }
 
-       if (!start_freewheel_process_thread()) {
-               DEBUG_AUDIO("Unable to start freewheel thread\n");
-               stop();
-               return ProcessThreadStartError;
+               if (!start_freewheel_process_thread()) {
+                       DEBUG_AUDIO("Unable to start freewheel thread\n");
+                       stop();
+                       return ProcessThreadStartError;
+               }
+
+               /* wait for backend to become active */
+               int timeout = 5000;
+               while (!_active && --timeout > 0) { Glib::usleep (1000); }
+
+               if (timeout == 0 || !_active) {
+                       PBD::error << _("PortAudio:: failed to start device.") << endmsg;
+                       stop ();
+                       return ProcessThreadStartError;
+               }
        }
-#endif
 
        return NoError;
 }
@@ -602,7 +716,7 @@ PortAudioBackend::_start (bool for_latency_measurement)
 int
 PortAudioBackend::portaudio_callback(const void* input,
                                      void* output,
-                                     unsigned long frame_count,
+                                     unsigned long sample_count,
                                      const PaStreamCallbackTimeInfo* time_info,
                                      PaStreamCallbackFlags status_flags,
                                      void* user_data)
@@ -611,21 +725,19 @@ PortAudioBackend::portaudio_callback(const void* input,
 
        if (!pa_backend->process_callback((const float*)input,
                                          (float*)output,
-                                         frame_count,
+                                         sample_count,
                                          time_info,
                                          status_flags)) {
                return paAbort;
        }
 
-       process_port_connection_changes();
-
        return paContinue;
 }
 
 bool
 PortAudioBackend::process_callback(const float* input,
                                    float* output,
-                                   uint32_t frame_count,
+                                   uint32_t sample_count,
                                    const PaStreamCallbackTimeInfo* timeInfo,
                                    PaStreamCallbackFlags statusFlags)
 {
@@ -655,7 +767,7 @@ PortAudioBackend::process_callback(const float* input,
        }
 
        if (!_run || _freewheel) {
-               memset(output, 0, frame_count * sizeof(float) * _system_outputs.size());
+               memset(output, 0, sample_count * sizeof(float) * _system_outputs.size());
                return true;
        }
 
@@ -667,13 +779,15 @@ PortAudioBackend::process_callback(const float* input,
                AudioEngine::thread_init_callback (this);
        }
 
+       process_port_connection_changes();
+
        return blocking_process_main (input, output);
 }
 
 bool
 PortAudioBackend::start_blocking_process_thread ()
 {
-       if (_realtime_pthread_create (SCHED_FIFO, -20, 100000,
+       if (pbd_realtime_pthread_create (PBD_SCHED_FIFO, -20, 100000,
                                &_main_blocking_thread, blocking_thread_func, this))
        {
                if (pthread_create (&_main_blocking_thread, NULL, blocking_thread_func, this))
@@ -724,20 +838,19 @@ PortAudioBackend::stop ()
 
        _run = false;
 
-#ifdef USE_BLOCKING_API
-       if (!stop_blocking_process_thread ()) {
-               return -1;
-       }
-#else
-       _pcmio->close_stream ();
-       _active = false;
+       if (_use_blocking_api) {
+               if (!stop_blocking_process_thread()) {
+                       return -1;
+               }
+       } else {
+               _pcmio->close_stream();
+               _active = false;
 
-       if (!stop_freewheel_process_thread ()) {
-               return -1;
+               if (!stop_freewheel_process_thread()) {
+                       return -1;
+               }
        }
 
-#endif
-
        unregister_ports();
 
        return (_active == false) ? 0 : -1;
@@ -810,6 +923,7 @@ PortAudioBackend::freewheel_process_thread()
                                _reinit_thread_callback = true; // hand over _main_thread
                                _freewheel_ack = false; // prepare next handshake
                                _midiio->set_enabled(true);
+                               engine.freewheel_callback (_freewheeling);
                        } else {
                                first_run = true;
                                _freewheel = true;
@@ -890,13 +1004,13 @@ PortAudioBackend::raw_buffer_size (DataType t)
 }
 
 /* Process time */
-framepos_t
+samplepos_t
 PortAudioBackend::sample_time ()
 {
        return _processed_samples;
 }
 
-framepos_t
+samplepos_t
 PortAudioBackend::sample_time_at_cycle_start ()
 {
        return _processed_samples;
@@ -1001,7 +1115,7 @@ PortAudioBackend::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, portaudio_process_thread, td)) {
                pthread_attr_init (&attr);
                pthread_attr_setstacksize (&attr, stacksize);
@@ -1037,16 +1151,15 @@ PortAudioBackend::join_process_threads ()
 bool
 PortAudioBackend::in_process_thread ()
 {
-#ifdef USE_BLOCKING_API
-       if (pthread_equal (_main_blocking_thread, pthread_self()) != 0) {
-               return true;
-       }
-#else
-       if (pthread_equal (_main_thread, pthread_self()) != 0) {
-               return true;
+       if (_use_blocking_api) {
+               if (pthread_equal(_main_blocking_thread, pthread_self()) != 0) {
+                       return true;
+               }
+       } else {
+               if (pthread_equal(_main_thread, pthread_self()) != 0) {
+                       return true;
+               }
        }
-#endif
-
        for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i)
        {
                if (pthread_equal (*i, pthread_self ()) != 0) {
@@ -1115,6 +1228,16 @@ PortAudioBackend::get_port_name (PortEngine::PortHandle port) const
        return static_cast<PamPort*>(port)->name ();
 }
 
+PortFlags
+PortAudioBackend::get_port_flags (PortEngine::PortHandle port) const
+{
+       if (!valid_port (port)) {
+               DEBUG_PORTS("get_port_flags: Invalid Port(s)\n");
+               return PortFlags (0);
+       }
+       return static_cast<PamPort*>(port)->flags ();
+}
+
 int
 PortAudioBackend::get_port_property (PortHandle port,
                                      const std::string& key,
@@ -1136,6 +1259,24 @@ PortAudioBackend::get_port_property (PortHandle port,
        return -1;
 }
 
+int
+PortAudioBackend::set_port_property (PortHandle port,
+                                     const std::string& key,
+                                     const std::string& value,
+                                     const std::string& type)
+{
+       if (!valid_port (port)) {
+               DEBUG_PORTS("get_port_name: Invalid Port(s)\n");
+               return -1;
+       }
+
+       if (key == "http://jackaudio.org/metadata/pretty-name" && type.empty ()) {
+               static_cast<PamPort*>(port)->set_pretty_name (value);
+               return 0;
+       }
+       return -1;
+}
+
 PortEngine::PortHandle
 PortAudioBackend::get_port_by_name (const std::string& name) const
 {
@@ -1303,13 +1444,19 @@ PortAudioBackend::register_system_midi_ports()
        for (std::vector<WinMMEMidiInputDevice*>::const_iterator i = inputs.begin ();
             i != inputs.end ();
             ++i) {
-               std::string port_name = "system_midi:" + (*i)->name() + " capture";
+               std::string port_name = "system:midi_capture_" + (*i)->name();
                PortHandle p =
                    add_port (port_name,
                              DataType::MIDI,
                              static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal));
                if (!p) return -1;
+
+               MidiDeviceInfo* info = _midiio->get_device_info((*i)->name());
+               if (info) { // assert?
+                       lr.min = lr.max = _samples_per_period + info->systemic_input_latency;
+               }
                set_latency_range (p, false, lr);
+
                PortMidiPort* midi_port = static_cast<PortMidiPort*>(p);
                midi_port->set_pretty_name ((*i)->name());
                _system_midi_in.push_back (midi_port);
@@ -1321,13 +1468,19 @@ PortAudioBackend::register_system_midi_ports()
        for (std::vector<WinMMEMidiOutputDevice*>::const_iterator i = outputs.begin ();
             i != outputs.end ();
             ++i) {
-               std::string port_name = "system_midi:" + (*i)->name() + " playback";
+               std::string port_name = "system:midi_playback_" + (*i)->name();
                PortHandle p =
                    add_port (port_name,
                              DataType::MIDI,
                              static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
                if (!p) return -1;
+
+               MidiDeviceInfo* info = _midiio->get_device_info((*i)->name());
+               if (info) { // assert?
+                       lr.min = lr.max = _samples_per_period + info->systemic_output_latency;
+               }
                set_latency_range (p, false, lr);
+
                PortMidiPort* midi_port = static_cast<PortMidiPort*>(p);
                midi_port->set_n_periods(2);
                midi_port->set_pretty_name ((*i)->name());
@@ -1357,6 +1510,24 @@ PortAudioBackend::unregister_ports (bool system_only)
        }
 }
 
+void
+PortAudioBackend::update_system_port_latecies ()
+{
+       for (std::vector<PamPort*>::const_iterator it = _system_inputs.begin (); it != _system_inputs.end (); ++it) {
+               (*it)->update_connected_latency (true);
+       }
+       for (std::vector<PamPort*>::const_iterator it = _system_outputs.begin (); it != _system_outputs.end (); ++it) {
+               (*it)->update_connected_latency (false);
+       }
+
+       for (std::vector<PamPort*>::const_iterator it = _system_midi_in.begin (); it != _system_midi_in.end (); ++it) {
+               (*it)->update_connected_latency (true);
+       }
+       for (std::vector<PamPort*>::const_iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it) {
+               (*it)->update_connected_latency (false);
+       }
+}
+
 int
 PortAudioBackend::connect (const std::string& src, const std::string& dst)
 {
@@ -1478,7 +1649,7 @@ PortAudioBackend::get_connections (PortEngine::PortHandle port, std::vector<std:
 int
 PortAudioBackend::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;
@@ -1486,11 +1657,11 @@ PortAudioBackend::midi_event_get (
        if (event_index >= source.size ()) {
                return -1;
        }
-       PortMidiEvent * const event = source[event_index].get ();
+       PortMidiEvent 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;
 }
 
@@ -1502,13 +1673,15 @@ 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
+       if (dst.size () && (pframes_t)dst.back ().timestamp () > timestamp) {
                // nevermind, ::get_buffer() sorts events
                DEBUG_MIDI (string_compose ("PortMidiBuffer: unordered event: %1 > %2\n",
-                                           (pframes_t)dst.back ()->timestamp (),
+                                           (pframes_t)dst.back ().timestamp (),
                                            timestamp));
        }
-       dst.push_back (boost::shared_ptr<PortMidiEvent>(new PortMidiEvent (timestamp, buffer, size)));
+#endif
+       dst.push_back (PortMidiEvent (timestamp, buffer, size));
        return 0;
 }
 
@@ -1684,7 +1857,9 @@ PortAudioBackend::n_physical_inputs () const
 void*
 PortAudioBackend::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<PamPort*>(port)->get_buffer (nframes);
 }
 
@@ -1904,7 +2079,7 @@ PortAudioBackend::process_incoming_midi ()
                mbuf->clear();
                uint64_t timestamp;
                pframes_t sample_offset;
-               uint8_t data[256];
+               uint8_t data[MaxWinMidiEventSize];
                size_t size = sizeof(data);
                while (_midiio->dequeue_input_event(i,
                                                    _cycle_timer.get_start(),
@@ -1945,14 +2120,14 @@ PortAudioBackend::process_outgoing_midi ()
                for (PortMidiBuffer::const_iterator mit = src->begin(); mit != src->end();
                     ++mit) {
                        uint64_t timestamp =
-                           _cycle_timer.timestamp_from_sample_offset((*mit)->timestamp());
+                           _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(),
+                                                 mit->timestamp(),
                                                  timestamp,
-                                                 (*mit)->size()));
-                       _midiio->enqueue_output_event(i, timestamp, (*mit)->data(), (*mit)->size());
+                                                 mit->size()));
+                       _midiio->enqueue_output_event(i, timestamp, mit->data(), mit->size());
                }
        }
 }
@@ -1985,6 +2160,7 @@ PortAudioBackend::process_port_connection_changes ()
                manager.graph_order_callback();
        }
        if (connections_changed || ports_changed) {
+               update_system_port_latecies ();
                engine.latency_callback(false);
                engine.latency_callback(true);
        }
@@ -2001,7 +2177,7 @@ static bool already_configured ();
 static bool available ();
 
 static ARDOUR::AudioBackendInfo _descriptor = {
-       "PortAudio",
+       BACKEND_NAME,
        instantiate,
        deinstantiate,
        backend_factory,
@@ -2175,6 +2351,36 @@ bool PamPort::is_physically_connected () const
        return false;
 }
 
+void
+PamPort::set_latency_range (const LatencyRange &latency_range, bool for_playback)
+{
+       if (for_playback) {
+               _playback_latency_range = latency_range;
+       } else {
+               _capture_latency_range = latency_range;
+       }
+
+       for (std::vector<PamPort*>::const_iterator it = _connections.begin (); it != _connections.end (); ++it) {
+               if ((*it)->is_physical ()) {
+                       (*it)->update_connected_latency (is_input ());
+               }
+       }
+}
+
+void
+PamPort::update_connected_latency (bool for_playback)
+{
+       LatencyRange lr;
+       lr.min = lr.max = 0;
+       for (std::vector<PamPort*>::const_iterator it = _connections.begin (); it != _connections.end (); ++it) {
+               LatencyRange l;
+               l = (*it)->latency_range (for_playback);
+               lr.min = std::max (lr.min, l.min);
+               lr.max = std::max (lr.max, l.max);
+       }
+       set_latency_range (lr, for_playback);
+}
+
 /******************************************************************************/
 
 PortAudioPort::PortAudioPort (PortAudioBackend &b, const std::string& name, PortFlags flags)
@@ -2220,13 +2426,16 @@ PortMidiPort::PortMidiPort (PortAudioBackend &b, const std::string& name, PortFl
 {
        _buffer[0].clear ();
        _buffer[1].clear ();
+
+       _buffer[0].reserve (256);
+       _buffer[1].reserve (256);
 }
 
 PortMidiPort::~PortMidiPort () { }
 
 struct MidiEventSorter {
-       bool operator() (const boost::shared_ptr<PortMidiEvent>& a, const boost::shared_ptr<PortMidiEvent>& b) {
-               return *a < *b;
+       bool operator() (PortMidiEvent const& a, PortMidiEvent const& b) {
+               return a < b;
        }
 };
 
@@ -2239,10 +2448,10 @@ void* PortMidiPort::get_buffer (pframes_t /* nframes */)
                                ++i) {
                        const PortMidiBuffer * src = static_cast<const PortMidiPort*>(*i)->const_buffer ();
                        for (PortMidiBuffer::const_iterator it = src->begin (); it != src->end (); ++it) {
-                               (_buffer[_bufperiod]).push_back (boost::shared_ptr<PortMidiEvent>(new PortMidiEvent (**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]);
 }
@@ -2250,10 +2459,8 @@ void* PortMidiPort::get_buffer (pframes_t /* nframes */)
 PortMidiEvent::PortMidiEvent (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 < MaxWinMidiEventSize) {
                memcpy (_data, data, size);
        }
 }
@@ -2261,14 +2468,9 @@ PortMidiEvent::PortMidiEvent (const pframes_t timestamp, const uint8_t* data, si
 PortMidiEvent::PortMidiEvent (const PortMidiEvent& 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 < MaxWinMidiEventSize);
+               memcpy (_data, other._data, other._size);
        }
 };
-
-PortMidiEvent::~PortMidiEvent () {
-       free (_data);
-};