Immediate-event/out-of-band injection update & tweak clearing buffers
authorRobin Gareus <robin@gareus.org>
Sat, 30 Sep 2017 21:16:04 +0000 (23:16 +0200)
committerRobin Gareus <robin@gareus.org>
Sat, 30 Sep 2017 21:16:04 +0000 (23:16 +0200)
Immediate events are used for MIDI-Panic and to inject GUI generated
events e.g. patch-changes, note-events from the track-header
(scroomer-keyboard) and patch-change audition.

Current behavior:

- snapshot copy immediate events from ringbuffer into a buffer at
  the beginning of each the cycle.
- Inject immediate events into input-buffer directly after reading the input
- process "normally"
- pass immediate event-buffer to disk-writer, so it can skip them
  (don't write immediate events to disk)
- if the Route is not monitoring input: clear buffer before disk-reader
  and re-inject (original) immediate events after the disk-reader
- immediate events process normally and are also sent to outputs.

libs/ardour/ardour/midi_track.h
libs/ardour/ardour/route.h
libs/ardour/midi_track.cc
libs/ardour/route.cc

index dfd5a42d13aea71f800409219369cb0fc7c9e90a..93856282c4aa1cfdface62362049240e34831ff4 100644 (file)
@@ -126,6 +126,8 @@ public:
 
        MonitorState monitoring_state () const;
 
+       MidiBuffer const& immediate_event_buffer () const { return _immediate_event_buffer; }
+
        void set_input_active (bool);
        bool input_active () const;
        PBD::Signal0<void> InputActiveChanged;
@@ -136,16 +138,19 @@ protected:
        void act_on_mute ();
        void monitoring_changed (bool, PBD::Controllable::GroupControlDisposition);
 
+       void snapshot_out_of_band_data (samplecnt_t nframes);
+       void write_out_of_band_data (BufferSet& bufs, samplecnt_t /* nframes */) const;
+
+
 private:
        MidiRingBuffer<samplepos_t> _immediate_events;
+       MidiBuffer                  _immediate_event_buffer;
        MidiRingBuffer<samplepos_t> _step_edit_ring_buffer;
-       NoteMode                   _note_mode;
-       bool                       _step_editing;
-       bool                       _input_active;
-       MidiChannelFilter          _playback_filter;
-       MidiChannelFilter          _capture_filter;
-
-       void write_out_of_band_data (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, samplecnt_t nframes);
+       NoteMode                    _note_mode;
+       bool                        _step_editing;
+       bool                        _input_active;
+       MidiChannelFilter           _playback_filter;
+       MidiChannelFilter           _capture_filter;
 
        void set_state_part_two ();
        void set_state_part_three ();
@@ -157,7 +162,7 @@ private:
        void map_input_active (bool);
 
        /** Update automation controls to reflect any changes in buffers. */
-       void update_controls (const BufferSet& bufs);
+       void update_controls (BufferSet const& bufs);
        void restore_controls ();
 };
 
index 1437fd79edf01ab9309d8f0d0283bb9cbd6f0216..830ef4bab37ea82b677276fe165bd0fa7c46f9ff 100644 (file)
@@ -597,7 +597,9 @@ protected:
 
        virtual int no_roll_unlocked (pframes_t nframes, samplepos_t start_sample, samplepos_t end_sample, bool session_state_changing);
 
-       virtual void write_out_of_band_data (BufferSet& /* bufs */, samplepos_t /* start_sample */, samplepos_t /* end_sample */, samplecnt_t /* nframes */) {}
+       virtual void snapshot_out_of_band_data (samplecnt_t /* nframes */) {}
+       virtual void write_out_of_band_data (BufferSet&, samplecnt_t /* nframes */) const {}
+       virtual void update_controls (BufferSet const&) {}
 
        void process_output_buffers (BufferSet& bufs,
                                     samplepos_t start_sample, samplepos_t end_sample,
index 2e5fd2a0b4c7a18139a0afac2696ec93db0dc85f..20faa80bc80f9a4e5985a59969b04428e87170bb 100644 (file)
@@ -73,6 +73,7 @@ using namespace PBD;
 MidiTrack::MidiTrack (Session& sess, string name, TrackMode mode)
        : Track (sess, name, PresentationInfo::MidiTrack, mode, DataType::MIDI)
        , _immediate_events(6096) // FIXME: size?
+       , _immediate_event_buffer(6096)
        , _step_edit_ring_buffer(64) // FIXME: size?
        , _note_mode (Sustained)
        , _step_editing (false)
@@ -303,16 +304,19 @@ MidiTrack::restore_controls ()
 }
 
 void
-MidiTrack::update_controls(const BufferSet& bufs)
+MidiTrack::update_controls (BufferSet const& bufs)
 {
        const MidiBuffer& buf = bufs.get_midi(0);
        for (MidiBuffer::const_iterator e = buf.begin(); e != buf.end(); ++e) {
-               const Evoral::Event<samplepos_t>&         ev      = *e;
+               const Evoral::Event<samplepos_t>&         ev     = *e;
                const Evoral::Parameter                  param   = midi_parameter(ev.buffer(), ev.size());
                const boost::shared_ptr<AutomationControl> control = automation_control (param);
                if (control) {
-                       control->set_double(ev.value(), _session.transport_sample(), false);
-                       control->Changed (false, Controllable::NoGroup);
+                       double old = control->get_double (false, 0);
+                       control->set_double (ev.value(), 0, false);
+                       if (old != ev.value()) {
+                               control->Changed (false, Controllable::NoGroup);
+                       }
                }
        }
 }
@@ -416,30 +420,35 @@ MidiTrack::push_midi_input_to_step_edit_ringbuffer (samplecnt_t nframes)
 }
 
 void
-MidiTrack::write_out_of_band_data (BufferSet& bufs, samplepos_t /*start*/, samplepos_t /*end*/, samplecnt_t nframes)
+MidiTrack::snapshot_out_of_band_data (samplecnt_t nframes)
 {
-       MidiBuffer& buf (bufs.get_midi (0));
-
-       update_controls (bufs);
+       _immediate_event_buffer.clear ();
+       if (0 == _immediate_events.read_space()) {
+               return;
+       }
 
-       // Append immediate events
+       assert (nframes > 0);
 
-       if (_immediate_events.read_space()) {
+       DEBUG_TRACE (DEBUG::MidiIO, string_compose ("%1 has %2 of immediate events to deliver\n",
+                               name(), _immediate_events.read_space()));
 
-               DEBUG_TRACE (DEBUG::MidiIO, string_compose ("%1 has %2 of immediate events to deliver\n",
-                                                           name(), _immediate_events.read_space()));
+       /* write as many of the immediate events as we can, but give "true" as
+        * the last argument ("stop on overflow in destination") so that we'll
+        * ship the rest out next time.
+        *
+        * the Port::port_offset() + (nframes-1) argument puts all these events at the last
+        * possible position of the output buffer, so that we do not
+        * violate monotonicity when writing. Port::port_offset() will
+        * be non-zero if we're in a split process cycle.
+        */
+       _immediate_events.read (_immediate_event_buffer, 0, 1, Port::port_offset() + nframes - 1, true);
+}
 
-               /* write as many of the immediate events as we can, but give "true" as
-                * the last argument ("stop on overflow in destination") so that we'll
-                * ship the rest out next time.
-                *
-                * the Port::port_offset() + (nframes-1) argument puts all these events at the last
-                * possible position of the output buffer, so that we do not
-                * violate monotonicity when writing. Port::port_offset() will
-                * be non-zero if we're in a split process cycle.
-                */
-               _immediate_events.read (buf, 0, 1, Port::port_offset() + nframes - 1, true);
-       }
+void
+MidiTrack::write_out_of_band_data (BufferSet& bufs, samplecnt_t nframes) const
+{
+       MidiBuffer& buf (bufs.get_midi (0));
+       buf.merge_from (_immediate_event_buffer, nframes);
 }
 
 int
index 0f8c892b2498b56dafe0a45de23a062db297a3f9..236f2d7c0cb580a551c689028f7f865920ba4688 100644 (file)
@@ -402,7 +402,8 @@ Route::process_output_buffers (BufferSet& bufs,
         * Also during remaining_latency_preroll, transport_rolling () is false, but
         * we may need to monitor disk instead.
         */
-       bool silence = _have_internal_generator ? false : (monitoring_state () == MonitoringSilence);
+       MonitorState ms = monitoring_state ();
+       bool silence = _have_internal_generator ? false : (ms == MonitoringSilence);
 
        _main_outs->no_outs_cuz_we_no_monitor (silence);
 
@@ -524,6 +525,24 @@ Route::process_output_buffers (BufferSet& bufs,
                                        /* input->latency() + */ latency, /* output->latency() + */ playback_latency);
                }
 
+               bool re_inject_oob_data = false;
+               if ((*i) == _disk_reader) {
+                       /* Well now, we've made it past the disk-writer and to the disk-reader.
+                        * Time to decide what to do about monitoring.
+                        *
+                        * Even when not doing MonitoringDisk, we need to run the processors,
+                        * so that it advances its internal buffers (IFF run_disk_reader is true).
+                        *
+                        */
+                       if (ms == MonitoringDisk || ms == MonitoringSilence) {
+                               /* this will clear out-of-band data, too (e.g. MIDI-PC, Panic etc.
+                                * OOB data is written at the end of the cycle (nframes - 1),
+                                * and jack does not re-order events, so we push them back later */
+                               re_inject_oob_data = true;
+                               bufs.silence (nframes, 0);
+                       }
+               }
+
                double pspeed = speed;
                if ((!run_disk_reader && (*i) == _disk_reader) || (!run_disk_writer && (*i) == _disk_writer)) {
                        /* run with speed 0, no-roll */
@@ -531,6 +550,7 @@ Route::process_output_buffers (BufferSet& bufs,
                }
 
                (*i)->run (bufs, start_sample - latency, end_sample - latency, pspeed, nframes, *i != _processors.back());
+
                bufs.set_count ((*i)->output_streams());
 
                /* Note: plugin latency may change. While the plugin does inform the session via
@@ -542,6 +562,11 @@ Route::process_output_buffers (BufferSet& bufs,
                if ((*i)->active ()) {
                        latency += (*i)->signal_latency ();
                }
+
+               if (re_inject_oob_data) {
+                       write_out_of_band_data (bufs, nframes);
+               }
+
 #if 0
                if ((*i) == _delayline) {
                        latency += _delayline->get_delay ();
@@ -701,14 +726,18 @@ Route::run_route (samplepos_t start_sample, samplepos_t end_sample, pframes_t nf
                bufs.silence (nframes, 0);
        }
 
+       snapshot_out_of_band_data (nframes);
        /* append immediate messages to the first MIDI buffer (thus sending it to the first output port) */
 
-       write_out_of_band_data (bufs, start_sample, end_sample, nframes);
+       write_out_of_band_data (bufs, nframes);
 
        /* run processor chain */
 
        process_output_buffers (bufs, start_sample, end_sample, nframes, declick, gain_automation_ok, run_disk_reader);
 
+       /* map events (e.g. MIDI-CC) back to control-parameters */
+       update_controls (bufs);
+
        flush_processor_buffers_locked (nframes);
 }