Fix mute of MIDI tracks with channel forcing.
authorDavid Robillard <d@drobilla.net>
Sun, 29 Mar 2015 03:24:41 +0000 (23:24 -0400)
committerDavid Robillard <d@drobilla.net>
Sun, 29 Mar 2015 04:51:56 +0000 (00:51 -0400)
This moves MIDI channel filtering into a reusable class and moves filtering to
the source, rather than modifying the buffer afterwards.  This is necessary so
that the playlist trackers reflect the emitted notes (and thus are able to stop
them in situations like mute).

As a perk, this is also faster because events are just dropped on read, rather
than pushed into a buffer then later removed (which is very slow).

Really hammering on mute or solo still seems to produce stuck notes
occasionally (perhaps related to multiple-on warnings).  I am not yet sure why,
but occasional beats always.

19 files changed:
gtk2_ardour/midi_channel_selector.cc
gtk2_ardour/midi_region_view.cc
gtk2_ardour/midi_time_axis.cc
libs/ardour/ardour/midi_channel_filter.h [new file with mode: 0644]
libs/ardour/ardour/midi_playlist.h
libs/ardour/ardour/midi_playlist_source.h
libs/ardour/ardour/midi_region.h
libs/ardour/ardour/midi_source.h
libs/ardour/ardour/midi_track.h
libs/ardour/ardour/smf_source.h
libs/ardour/midi_channel_filter.cc [new file with mode: 0644]
libs/ardour/midi_diskstream.cc
libs/ardour/midi_playlist.cc
libs/ardour/midi_playlist_source.cc
libs/ardour/midi_region.cc
libs/ardour/midi_source.cc
libs/ardour/midi_track.cc
libs/ardour/smf_source.cc
libs/ardour/wscript

index 4884d72328fa430b8e17b1c6643e67739ef2acfb..1895b5c52fe670e57bace8b2d998e7bdfaeadbbf 100644 (file)
@@ -27,6 +27,7 @@
 #include <gtkmm/table.h>
 
 #include "pbd/compose.h"
+#include "pbd/ffs.h"
 
 #include "gtkmm2ext/gtk_ui.h"
 #include "gtkmm2ext/gui_thread.h"
@@ -343,10 +344,10 @@ MidiChannelSelectorWindow::MidiChannelSelectorWindow (boost::shared_ptr<MidiTrac
        playback_mask_changed ();
        capture_mask_changed ();
 
-       track->PlaybackChannelMaskChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::playback_mask_changed, this), gui_context());
-       track->PlaybackChannelModeChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::playback_mode_changed, this), gui_context());
-       track->CaptureChannelMaskChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::capture_mask_changed, this), gui_context());
-       track->CaptureChannelModeChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::capture_mode_changed, this), gui_context());
+       track->playback_filter().ChannelMaskChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::playback_mask_changed, this), gui_context());
+       track->playback_filter().ChannelModeChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::playback_mode_changed, this), gui_context());
+       track->capture_filter().ChannelMaskChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::capture_mask_changed, this), gui_context());
+       track->capture_filter().ChannelModeChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::capture_mode_changed, this), gui_context());
 }
 
 MidiChannelSelectorWindow::~MidiChannelSelectorWindow()
index 7bed675ac3d838e949aee4ae2cdda4afbf6b293b..eed9017d0d423a98b6869f811015725f4e433f2c 100644 (file)
@@ -299,7 +299,7 @@ MidiRegionView::init (bool wfd)
 
        group->raise_to_top();
 
-       midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
+       midi_view()->midi_track()->playback_filter().ChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
                                                                       boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
                                                                       gui_context ());
 
index bbf0473ded61b5494d22404b00bf7744d5830744..be475216c17e0e9c57f94dd8f16257f690f28988 100644 (file)
@@ -243,18 +243,22 @@ MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
                _view->RegionViewAdded.connect (
                        sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
 
-               midi_track()->PlaybackChannelModeChanged.connect (*this, invalidator (*this),
-                                                                 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
-                                                                 gui_context());
-               midi_track()->PlaybackChannelMaskChanged.connect (*this, invalidator (*this),
-                                                                 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
-                                                                 gui_context());
-               midi_track()->CaptureChannelModeChanged.connect (*this, invalidator (*this),
-                                                                 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
-                                                                 gui_context());
-               midi_track()->CaptureChannelMaskChanged.connect (*this, invalidator (*this),
-                                                                 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
-                                                                 gui_context());
+               midi_track()->playback_filter().ChannelModeChanged.connect (
+                       *this, invalidator (*this),
+                       boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
+                       gui_context());
+               midi_track()->playback_filter().ChannelMaskChanged.connect (
+                       *this, invalidator (*this),
+                       boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
+                       gui_context());
+               midi_track()->capture_filter().ChannelModeChanged.connect (
+                       *this, invalidator (*this),
+                       boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
+                       gui_context());
+               midi_track()->capture_filter().ChannelMaskChanged.connect (
+                       *this, invalidator (*this),
+                       boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
+                       gui_context());
 
                playback_channel_mode_changed ();
                capture_channel_mode_changed ();
diff --git a/libs/ardour/ardour/midi_channel_filter.h b/libs/ardour/ardour/midi_channel_filter.h
new file mode 100644 (file)
index 0000000..934b369
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+    Copyright (C) 2006-2015 Paul Davis
+    Author: David Robillard
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __ardour_channel_filter_h__
+#define __ardour_channel_filter_h__
+
+#include <stdint.h>
+
+#include <glib.h>
+
+#include "ardour/types.h"
+#include "pbd/signals.h"
+
+namespace ARDOUR
+{
+
+class BufferSet;
+
+/** Filter/mapper for MIDI channels.
+ *
+ * Channel mapping is configured by setting a mode and "mask", where the
+ * meaning of the mask depends on the mode.
+ *
+ * If mode is FilterChannels, each mask bit represents a midi channel (bit 0 =
+ * channel 0, bit 1 = channel 1, ...).  Only events whose channel corresponds
+ * to a 1 bit will be passed.
+ *
+ * If mode is ForceChannel, mask is simply a channel number which all events
+ * will be forced to.
+ */
+class LIBARDOUR_API MidiChannelFilter
+{
+public:
+       MidiChannelFilter();
+
+       /** Filter `bufs` in-place. */
+       void filter(BufferSet& bufs);
+
+       /** Filter/map a MIDI message by channel.
+        *
+        * May modify the channel in `buf` if necessary.
+        *
+        * @return true if this event should be filtered out.
+        */
+       bool filter(uint8_t* buf, uint32_t len);
+
+       /** Atomically set the channel mode and corresponding mask.
+        * @return true iff configuration changed.
+        */
+       bool set_channel_mode(ChannelMode mode, uint16_t mask);
+
+       /** Atomically set the channel mask for the current mode.
+        * @return true iff configuration changed.
+        */
+       bool set_channel_mask(uint16_t mask);
+
+       /** Atomically get both the channel mode and mask. */
+       void get_mode_and_mask(ChannelMode* mode, uint16_t* mask) const {
+               const uint32_t mm = g_atomic_int_get(&_mode_mask);
+               *mode = static_cast<ChannelMode>((mm & 0xFFFF0000) >> 16);
+               *mask = (mm & 0x0000FFFF);
+       }
+
+       ChannelMode get_channel_mode() const {
+               return static_cast<ChannelMode>((g_atomic_int_get(&_mode_mask) & 0xFFFF0000) >> 16);
+       }
+
+       uint16_t get_channel_mask() const {
+               return g_atomic_int_get(&_mode_mask) & 0x0000FFFF;
+       }
+
+       PBD::Signal0<void> ChannelMaskChanged;
+       PBD::Signal0<void> ChannelModeChanged;
+
+private:
+       uint32_t _mode_mask;  ///< 16 bits mode, 16 bits mask
+};
+
+} /* namespace ARDOUR */
+
+#endif /* __ardour_channel_filter_h__ */
index 614a5e1c1f56ceadb699e3601119288b9427c03a..cb07bc18205d7901a11e686a627cea980dc29861 100644 (file)
@@ -42,6 +42,7 @@ namespace ARDOUR
 {
 
 class BeatsFramesConverter;
+class MidiChannelFilter;
 class MidiRegion;
 class Session;
 class Source;
@@ -77,7 +78,8 @@ public:
        framecnt_t read (Evoral::EventSink<framepos_t>& buf,
                         framepos_t                     start,
                         framecnt_t                     cnt,
-                        uint32_t                       chan_n = 0);
+                        uint32_t                       chan_n = 0,
+                        MidiChannelFilter*             filter = NULL);
 
        int set_state (const XMLNode&, int version);
 
index 6633be3c9ea041c415533e6bae96d3f273220e74..12c184d7b50c3c921184cbc817cac5aa625b77d6 100644 (file)
@@ -65,7 +65,8 @@ protected:
                                  framepos_t                     position,
                                  framepos_t                     start,
                                  framecnt_t                     cnt,
-                                 MidiStateTracker*              tracker) const;
+                                 MidiStateTracker*              tracker,
+                                 MidiChannelFilter*             filter) const;
 
        framecnt_t write_unlocked (const Lock&                 lock,
                                   MidiRingBuffer<framepos_t>& dst,
index f7e6c97ea03e7050c310e1f80f956d8ac2ca76d6..87e89c695b5709c82925a67076a7aa59b9941f60 100644 (file)
@@ -43,13 +43,14 @@ template<typename Time> class EventSink;
 
 namespace ARDOUR {
 
-class Route;
-class Playlist;
-class Session;
+class MidiChannelFilter;
 class MidiFilter;
 class MidiModel;
 class MidiSource;
 class MidiStateTracker;
+class Playlist;
+class Route;
+class Session;
 
 template<typename T> class MidiRingBuffer;
 
@@ -74,7 +75,8 @@ class LIBARDOUR_API MidiRegion : public Region
                            framecnt_t dur,
                            uint32_t  chan_n = 0,
                            NoteMode  mode = Sustained,
-                           MidiStateTracker* tracker = 0) const;
+                           MidiStateTracker* tracker = 0,
+                           MidiChannelFilter* filter = 0) const;
 
        framecnt_t master_read_at (MidiRingBuffer<framepos_t>& dst,
                                   framepos_t position,
@@ -121,7 +123,8 @@ class LIBARDOUR_API MidiRegion : public Region
                             framecnt_t dur,
                             uint32_t chan_n = 0,
                             NoteMode mode = Sustained,
-                            MidiStateTracker* tracker = 0) const;
+                            MidiStateTracker* tracker = 0,
+                            MidiChannelFilter* filter = 0) const;
 
        void register_properties ();
        void post_set (const PBD::PropertyChange&);
index 156f3dbfa0ea40795d3c73c05c062c135de0e7cd..6a55398ca9a2afbacb5c21c5a238b3cc23a682fb 100644 (file)
@@ -34,6 +34,7 @@
 
 namespace ARDOUR {
 
+class MidiChannelFilter;
 class MidiStateTracker;
 class MidiModel;
 
@@ -77,6 +78,7 @@ class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_sha
                                      framepos_t                         start,
                                      framecnt_t                         cnt,
                                      MidiStateTracker*                  tracker,
+                                     MidiChannelFilter*                 filter,
                                      const std::set<Evoral::Parameter>& filtered) const;
 
        /** Write data from a MidiRingBuffer to this source.
@@ -192,7 +194,8 @@ class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_sha
                                          framepos_t                     position,
                                          framepos_t                     start,
                                          framecnt_t                     cnt,
-                                         MidiStateTracker*              tracker) const = 0;
+                                         MidiStateTracker*              tracker,
+                                         MidiChannelFilter*             filter) const = 0;
 
        /** Write data to this source from a MidiRingBuffer.
         *  @param source Buffer to read from.
index a12a0c60870be1d5d9618aec3799794ca17d8e30..f2542f0f6b9e2d45b0353aa41e8b648db3263648 100644 (file)
 #ifndef __ardour_midi_track_h__
 #define __ardour_midi_track_h__
 
-#include "pbd/ffs.h"
-
-#include "ardour/track.h"
+#include "ardour/midi_channel_filter.h"
 #include "ardour/midi_ring_buffer.h"
+#include "ardour/track.h"
 
 namespace ARDOUR
 {
@@ -109,38 +108,22 @@ public:
 
        boost::shared_ptr<SMFSource> write_source (uint32_t n = 0);
 
-       /** Channel filtering mode.
-        * @param mask If mode is FilterChannels, each bit represents a midi channel:
-        *     bit 0 = channel 0, bit 1 = channel 1 etc. the read and write methods will only
-        *     process events whose channel bit is 1.
-        *     If mode is ForceChannel, mask is simply a channel number which all events will
-        *     be forced to while reading.
-        */
+       /* Configure capture/playback channels (see MidiChannelFilter). */
        void set_capture_channel_mode (ChannelMode mode, uint16_t mask);
        void set_playback_channel_mode (ChannelMode mode, uint16_t mask);
        void set_playback_channel_mask (uint16_t mask);
        void set_capture_channel_mask (uint16_t mask);
 
-       ChannelMode get_playback_channel_mode() const {
-               return static_cast<ChannelMode>((g_atomic_int_get(&_playback_channel_mask) & 0xffff0000) >> 16);
-       }
-       uint16_t get_playback_channel_mask() const {
-               return g_atomic_int_get(&_playback_channel_mask) & 0x0000ffff;
-       }
-       ChannelMode get_capture_channel_mode() const {
-               return static_cast<ChannelMode>((g_atomic_int_get(&_capture_channel_mask) & 0xffff0000) >> 16);
-       }
-       uint16_t get_capture_channel_mask() const {
-               return g_atomic_int_get(&_capture_channel_mask) & 0x0000ffff;
-       }
+       ChannelMode get_playback_channel_mode() const { return _playback_filter.get_channel_mode(); }
+       ChannelMode get_capture_channel_mode()  const { return _capture_filter.get_channel_mode(); }
+       uint16_t    get_playback_channel_mask() const { return _playback_filter.get_channel_mask(); }
+       uint16_t    get_capture_channel_mask()  const { return _capture_filter.get_channel_mask(); }
+
+       MidiChannelFilter& playback_filter() { return _playback_filter; }
+       MidiChannelFilter& capture_filter()  { return _capture_filter; }
 
        boost::shared_ptr<MidiPlaylist> midi_playlist ();
 
-       PBD::Signal0<void> PlaybackChannelMaskChanged;
-       PBD::Signal0<void> PlaybackChannelModeChanged;
-       PBD::Signal0<void> CaptureChannelMaskChanged;
-       PBD::Signal0<void> CaptureChannelModeChanged;
-    
        PBD::Signal1<void, boost::weak_ptr<MidiSource> > DataRecorded;
        boost::shared_ptr<MidiBuffer> get_gui_feed_buffer () const;
 
@@ -162,8 +145,8 @@ private:
        NoteMode                   _note_mode;
        bool                       _step_editing;
        bool                       _input_active;
-       uint32_t                   _playback_channel_mask; // 16 bits mode, 16 bits mask
-       uint32_t                   _capture_channel_mask; // 16 bits mode, 16 bits mask
+       MidiChannelFilter          _playback_filter;
+       MidiChannelFilter          _capture_filter;
 
        virtual boost::shared_ptr<Diskstream> diskstream_factory (XMLNode const &);
        
@@ -186,32 +169,6 @@ private:
 
        /** Update automation controls to reflect any changes in buffers. */
        void update_controls (const BufferSet& bufs);
-
-       void filter_channels (BufferSet& bufs, ChannelMode mode, uint32_t mask); 
-
-/* if mode is ForceChannel, force mask to the lowest set channel or 1 if no
- * channels are set.
- */
-#define force_mask(mode,mask) (((mode) == ForceChannel) ? (((mask) ? (1<<(PBD::ffs((mask))-1)) : 1)) : mask)
-
-       void _set_playback_channel_mode(ChannelMode mode, uint16_t mask) {
-               mask = force_mask (mode, mask);
-               g_atomic_int_set(&_playback_channel_mask, (uint32_t(mode) << 16) | uint32_t(mask));
-       }
-       void _set_playback_channel_mask (uint16_t mask) {
-               mask = force_mask (get_playback_channel_mode(), mask);
-               g_atomic_int_set(&_playback_channel_mask, (uint32_t(get_playback_channel_mode()) << 16) | uint32_t(mask));
-       }
-       void _set_capture_channel_mode(ChannelMode mode, uint16_t mask) {
-               mask = force_mask (mode, mask);
-               g_atomic_int_set(&_capture_channel_mask, (uint32_t(mode) << 16) | uint32_t(mask));
-       }
-       void _set_capture_channel_mask (uint16_t mask) {
-               mask = force_mask (get_capture_channel_mode(), mask);
-               g_atomic_int_set(&_capture_channel_mask, (uint32_t(get_capture_channel_mode()) << 16) | uint32_t(mask));
-       }
-
-#undef force_mask
 };
 
 } /* namespace ARDOUR*/
index 29c640369446c74cdbf5f434e3697b5ac9215d1f..d088f2d867a3e8d6aa8606ada90d803b23d24629 100644 (file)
@@ -93,7 +93,8 @@ public:
                                  framepos_t                     position,
                                  framepos_t                     start,
                                  framecnt_t                     cnt,
-                                 MidiStateTracker*              tracker) const;
+                                 MidiStateTracker*              tracker,
+                                 MidiChannelFilter*             filter) const;
 
        framecnt_t write_unlocked (const Lock&                 lock,
                                   MidiRingBuffer<framepos_t>& src,
diff --git a/libs/ardour/midi_channel_filter.cc b/libs/ardour/midi_channel_filter.cc
new file mode 100644 (file)
index 0000000..4065880
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+    Copyright (C) 2006-2015 Paul Davis
+    Author: David Robillard
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "ardour/buffer_set.h"
+#include "ardour/midi_buffer.h"
+#include "ardour/midi_channel_filter.h"
+#include "pbd/ffs.h"
+
+namespace ARDOUR {
+
+MidiChannelFilter::MidiChannelFilter()
+       : _mode_mask(0x0000FFFF)
+{}
+
+void
+MidiChannelFilter::filter(BufferSet& bufs)
+{
+       ChannelMode mode;
+       uint16_t    mask;
+       get_mode_and_mask(&mode, &mask);
+
+       if (mode == AllChannels) {
+               return;
+       }
+
+       MidiBuffer& buf = bufs.get_midi(0);
+
+       for (MidiBuffer::iterator e = buf.begin(); e != buf.end(); ) {
+               Evoral::MIDIEvent<framepos_t> ev(*e, false);
+
+               if (ev.is_channel_event()) {
+                       switch (mode) {
+                       case FilterChannels:
+                               if (0 == ((1 << ev.channel()) & mask)) {
+                                       e = buf.erase (e);
+                               } else {
+                                       ++e;
+                               }
+                               break;
+                       case ForceChannel:
+                               ev.set_channel(PBD::ffs(mask) - 1);
+                               ++e;
+                               break;
+                       case AllChannels:
+                               /* handled by the opening if() */
+                               ++e;
+                               break;
+                       }
+               } else {
+                       ++e;
+               }
+       }
+}
+
+bool
+MidiChannelFilter::filter(uint8_t* buf, uint32_t len)
+{
+       ChannelMode mode;
+       uint16_t    mask;
+       get_mode_and_mask(&mode, &mask);
+
+       const uint8_t type             = buf[0] & 0xF0;
+       const bool    is_channel_event = (0x80 <= type) && (type <= 0xE0);
+       if (!is_channel_event) {
+               return false;
+       }
+
+       const uint8_t channel = buf[0] & 0x0F;
+       switch (mode) {
+       case AllChannels:
+               return false;
+       case FilterChannels:
+               return !((1 << channel) & mask);
+       case ForceChannel:
+               buf[0] = (0xF0 & buf[0]) | (0x0F & (PBD::ffs(mask) - 1));
+               return false;
+       }
+
+       return false;
+}
+
+/** If mode is ForceChannel, force mask to the lowest set channel or 1 if no
+ *  channels are set.
+ */
+static inline uint16_t
+force_mask(const ChannelMode mode, const uint16_t mask)
+{
+       return ((mode == ForceChannel)
+               ? (mask ? (1 << (PBD::ffs(mask) - 1)) : 1)
+               : mask);
+}
+
+bool
+MidiChannelFilter::set_channel_mode(ChannelMode mode, uint16_t mask)
+{
+       ChannelMode old_mode;
+       uint16_t    old_mask;
+       get_mode_and_mask(&old_mode, &old_mask);
+
+       if (old_mode != mode || old_mask != mask) {
+               mask = force_mask(mode, mask);
+               g_atomic_int_set(&_mode_mask, (uint32_t(mode) << 16) | uint32_t(mask));
+               ChannelModeChanged();
+               return true;
+       }
+
+       return false;
+}
+
+bool
+MidiChannelFilter::set_channel_mask(uint16_t mask)
+{
+       ChannelMode mode;
+       uint16_t    old_mask;
+       get_mode_and_mask(&mode, &old_mask);
+
+       if (old_mask != mask) {
+               mask = force_mask(mode, mask);
+               g_atomic_int_set(&_mode_mask, (uint32_t(mode) << 16) | uint32_t(mask));
+               ChannelMaskChanged();
+               return true;
+       }
+
+       return false;
+}
+
+} /* namespace ARDOUR */
index f6ba10342ac8c08d48154ba45ff39eec8d276d02..e8b23b3431a512da4aa9ef27712f69e12855dd2b 100644 (file)
@@ -403,17 +403,10 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
        }
 
        if (nominally_recording || rec_nframes) {
-
-               // Pump entire port buffer into the ring buffer (FIXME: split cycles?)
-               MidiBuffer& buf = sp->get_midi_buffer(nframes);
-               ChannelMode mode = AllChannels;
-               uint32_t mask = 0xffff;
-
-               MidiTrack * mt = dynamic_cast<MidiTrack*> (_track);
-               if (mt) {
-                       mode = mt->get_capture_channel_mode ();
-                       mask = mt->get_capture_channel_mask ();
-               }
+               // Pump entire port buffer into the ring buffer (TODO: split cycles?)
+               MidiBuffer&        buf    = sp->get_midi_buffer(nframes);
+               MidiTrack*         mt     = dynamic_cast<MidiTrack*>(_track);
+               MidiChannelFilter* filter = mt ? &mt->capture_filter() : NULL;
 
                for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
                        Evoral::MIDIEvent<MidiBuffer::TimeType> ev(*i, false);
@@ -447,31 +440,12 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
                        const framecnt_t loop_offset = _num_captured_loops * loop_length;
                        const framepos_t event_time = transport_frame + loop_offset - _accumulated_capture_offset + ev.time();
                        if (event_time < 0 || event_time < first_recordable_frame) {
+                               /* Event out of range, skip */
                                continue;
                        }
-                       switch (mode) {
-                       case AllChannels:
-                               _capture_buf->write(event_time,
-                                                   ev.type(), ev.size(), ev.buffer());
-                               break;
-                       case FilterChannels:
-                               if (ev.is_channel_event()) {
-                                       if ((1<<ev.channel()) & mask) {
-                                               _capture_buf->write(event_time,
-                                                                   ev.type(), ev.size(), ev.buffer());
-                                       }
-                               } else {
-                                       _capture_buf->write(event_time,
-                                                           ev.type(), ev.size(), ev.buffer());
-                               }
-                               break;
-                       case ForceChannel:
-                               if (ev.is_channel_event()) {
-                                       ev.set_channel (PBD::ffs(mask) - 1);
-                               }
-                               _capture_buf->write(event_time,
-                                                   ev.type(), ev.size(), ev.buffer());
-                               break;
+
+                       if (!filter || !filter->filter(ev.buffer(), ev.size())) {
+                               _capture_buf->write(event_time, ev.type(), ev.size(), ev.buffer());
                        }
                }
                g_atomic_int_add(const_cast<gint*>(&_frames_pending_write), nframes);
@@ -736,6 +710,9 @@ MidiDiskstream::read (framepos_t& start, framecnt_t dur, bool reversed)
        framecnt_t loop_length = 0;
        Location*  loc         = 0;
 
+       MidiTrack*         mt     = dynamic_cast<MidiTrack*>(_track);
+       MidiChannelFilter* filter = mt ? &mt->playback_filter() : NULL;
+
        if (!reversed) {
 
                loc = loop_location;
@@ -772,7 +749,7 @@ MidiDiskstream::read (framepos_t& start, framecnt_t dur, bool reversed)
 
                this_read = min(dur,this_read);
 
-               if (midi_playlist()->read (*_playback_buf, start, this_read) != this_read) {
+               if (midi_playlist()->read (*_playback_buf, start, this_read, 0, filter) != this_read) {
                        error << string_compose(
                                        _("MidiDiskstream %1: cannot read %2 from playlist at frame %3"),
                                        id(), this_read, start) << endmsg;
index 398234e2babce6fac513fcfcee177f5e26c6a4fe..c0badf7a0ddc839cf345fbacab51214cc87e0293 100644 (file)
@@ -106,7 +106,11 @@ struct EventsSortByTimeAndType {
 };
 
 framecnt_t
-MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framecnt_t dur, unsigned chan_n)
+MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst,
+                    framepos_t                     start,
+                    framecnt_t                     dur,
+                    unsigned                       chan_n,
+                    MidiChannelFilter*             filter)
 {
        typedef pair<MidiStateTracker*,framepos_t> TrackerInfo;
 
@@ -185,7 +189,7 @@ MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framec
                }
 
                /* Read from region into target. */
-               mr->read_at (tgt, start, dur, chan_n, _note_mode, &tracker->tracker);
+               mr->read_at (tgt, start, dur, chan_n, _note_mode, &tracker->tracker, filter);
                DEBUG_TRACE (DEBUG::MidiPlaylistIO,
                             string_compose ("\tPost-read: %1 active notes\n", tracker->tracker.on()));
 
index 4f474f711936c1547a6ca3948e3d5c57df1e600e..3d9c5b1e9f58a37bb0e492427dc546f8a5d112c7 100644 (file)
@@ -126,7 +126,8 @@ MidiPlaylistSource::read_unlocked (const Lock& lock,
                                   Evoral::EventSink<framepos_t>& dst,
                                   framepos_t /*position*/,
                                   framepos_t start, framecnt_t cnt,
-                                  MidiStateTracker*) const
+                                  MidiStateTracker*,
+                                  MidiChannelFilter*) const
 {
        boost::shared_ptr<MidiPlaylist> mp = boost::dynamic_pointer_cast<MidiPlaylist> (_playlist);
 
index 5c197761acd044126d234a0d03faab4d3eac609e..3e4a656ebe2006454672452470fe052a44b7c690 100644 (file)
@@ -230,9 +230,15 @@ MidiRegion::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
 }
 
 framecnt_t
-MidiRegion::read_at (Evoral::EventSink<framepos_t>& out, framepos_t position, framecnt_t dur, uint32_t chan_n, NoteMode mode, MidiStateTracker* tracker) const
+MidiRegion::read_at (Evoral::EventSink<framepos_t>& out,
+                     framepos_t                     position,
+                     framecnt_t                     dur,
+                     uint32_t                       chan_n,
+                     NoteMode                       mode,
+                     MidiStateTracker*              tracker,
+                     MidiChannelFilter*             filter) const
 {
-       return _read_at (_sources, out, position, dur, chan_n, mode, tracker);
+       return _read_at (_sources, out, position, dur, chan_n, mode, tracker, filter);
 }
 
 framecnt_t
@@ -248,7 +254,8 @@ MidiRegion::_read_at (const SourceList&              /*srcs*/,
                       framecnt_t                     dur,
                       uint32_t                       chan_n,
                       NoteMode                       mode,
-                      MidiStateTracker*              tracker) const
+                      MidiStateTracker*              tracker,
+                      MidiChannelFilter*             filter) const
 {
        frameoffset_t internal_offset = 0;
        framecnt_t    to_read         = 0;
@@ -301,6 +308,7 @@ MidiRegion::_read_at (const SourceList&              /*srcs*/,
                        _start + internal_offset, // where to start reading in the source
                        to_read, // read duration in frames
                        tracker,
+                       filter,
                        _filtered_parameters
                    ) != to_read) {
                return 0; /* "read nothing" */
index 47cfb1b0a030c7e556cfcb5bc4452c2fb97c1050..ab972fabd872222b329ae762aa92044b1dc168b1 100644 (file)
 #include "evoral/EventSink.hpp"
 
 #include "ardour/debug.h"
+#include "ardour/file_source.h"
+#include "ardour/midi_channel_filter.h"
 #include "ardour/midi_model.h"
-#include "ardour/midi_state_tracker.h"
 #include "ardour/midi_source.h"
-#include "ardour/file_source.h"
+#include "ardour/midi_state_tracker.h"
 #include "ardour/session.h"
 #include "ardour/session_directory.h"
 #include "ardour/source_factory.h"
@@ -190,6 +191,7 @@ MidiSource::midi_read (const Lock&                        lm,
                        framepos_t                         start,
                        framecnt_t                         cnt,
                        MidiStateTracker*                  tracker,
+                       MidiChannelFilter*                 filter,
                        const std::set<Evoral::Parameter>& filtered) const
 {
        BeatsFramesConverter converter(_session.tempo_map(), source_start);
@@ -218,6 +220,13 @@ MidiSource::midi_read (const Lock&                        lm,
                for (; i != _model->end(); ++i) {
                        const framecnt_t time_frames = converter.to(i->time());
                        if (time_frames < start + cnt) {
+                               if (filter && filter->filter(i->buffer(), i->size())) {
+                                       DEBUG_TRACE (DEBUG::MidiSourceIO,
+                                                    string_compose ("%1: filter event @ %2 type %3 size %4\n",
+                                                                    _name, time_frames + source_start, i->event_type(), i->size()));
+                                       continue;
+                               }
+
                                // Offset by source start to convert event time to session time
                                dst.write (time_frames + source_start, i->event_type(), i->size(), i->buffer());
 
@@ -237,7 +246,7 @@ MidiSource::midi_read (const Lock&                        lm,
                }
                return cnt;
        } else {
-               return read_unlocked (lm, dst, source_start, start, cnt, tracker);
+               return read_unlocked (lm, dst, source_start, start, cnt, tracker, filter);
        }
 }
 
index b8f53a87d08ca0c36314ff9b12a90d5ddeb155c5..3f154644812345e4bf56d641c1f3d07621c805b9 100644 (file)
@@ -29,7 +29,6 @@
 #define isnan_local std::isnan
 #endif
 
-#include "pbd/ffs.h"
 #include "pbd/enumwriter.h"
 #include "pbd/convert.h"
 #include "evoral/midi_util.h"
@@ -72,8 +71,6 @@ MidiTrack::MidiTrack (Session& sess, string name, Route::Flag flag, TrackMode mo
        , _note_mode(Sustained)
        , _step_editing (false)
        , _input_active (true)
-       , _playback_channel_mask(0x0000ffff)
-       , _capture_channel_mask(0x0000ffff)
 {
 }
 
@@ -387,7 +384,7 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame
        fill_buffers_with_input (bufs, _input, nframes);
 
        /* filter captured data before meter sees it */
-       filter_channels (bufs, get_capture_channel_mode(), get_capture_channel_mask());
+       _capture_filter.filter (bufs);
 
        if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) {
                _meter->run (bufs, start_frame, end_frame, nframes, true);
@@ -402,9 +399,7 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame
                return dret;
        }
 
-       /* filter playback data before we do anything else */
-       
-       filter_channels (bufs, get_playback_channel_mode(), get_playback_channel_mask ());
+       /* note diskstream uses our filter to filter/map playback channels appropriately. */
 
        if (monitoring_state() == MonitoringInput) {
 
@@ -549,43 +544,6 @@ MidiTrack::push_midi_input_to_step_edit_ringbuffer (framecnt_t nframes)
        }
 }
 
-void 
-MidiTrack::filter_channels (BufferSet& bufs, ChannelMode mode, uint32_t mask)
-{
-       if (mode == AllChannels) {
-               return;
-       }
-
-       MidiBuffer& buf (bufs.get_midi (0));
-       
-       for (MidiBuffer::iterator e = buf.begin(); e != buf.end(); ) {
-               
-               Evoral::MIDIEvent<framepos_t> ev(*e, false);
-
-               if (ev.is_channel_event()) {
-                       switch (mode) {
-                       case FilterChannels:
-                               if (0 == ((1<<ev.channel()) & mask)) {
-                                       e = buf.erase (e);
-                               } else {
-                                       ++e;
-                               }
-                               break;
-                       case ForceChannel:
-                               ev.set_channel (PBD::ffs (mask) - 1);
-                               ++e;
-                               break;
-                       case AllChannels:
-                               /* handled by the opening if() */
-                               ++e;
-                               break;
-                       }
-               } else {
-                       ++e;
-               }
-       }
-}
-
 void
 MidiTrack::write_out_of_band_data (BufferSet& bufs, framepos_t /*start*/, framepos_t /*end*/, framecnt_t nframes)
 {
@@ -815,52 +773,34 @@ MidiTrack::write_source (uint32_t)
 }
 
 void
-MidiTrack::set_playback_channel_mode(ChannelMode mode, uint16_t mask) 
+MidiTrack::set_playback_channel_mode(ChannelMode mode, uint16_t mask)
 {
-       ChannelMode old = get_playback_channel_mode ();
-       uint16_t old_mask = get_playback_channel_mask ();
-
-       if (old != mode || mask != old_mask) {
-               _set_playback_channel_mode (mode, mask);
-               PlaybackChannelModeChanged ();
-               _session.set_dirty ();
+       if (_playback_filter.set_channel_mode(mode, mask)) {
+               _session.set_dirty();
        }
 }
 
 void
-MidiTrack::set_capture_channel_mode(ChannelMode mode, uint16_t mask) 
+MidiTrack::set_capture_channel_mode(ChannelMode mode, uint16_t mask)
 {
-       ChannelMode old = get_capture_channel_mode ();
-       uint16_t old_mask = get_capture_channel_mask ();
-
-       if (old != mode || mask != old_mask) {
-               _set_capture_channel_mode (mode, mask);
-               CaptureChannelModeChanged ();
-               _session.set_dirty ();
+       if (_capture_filter.set_channel_mode(mode, mask)) {
+               _session.set_dirty();
        }
 }
 
 void
 MidiTrack::set_playback_channel_mask (uint16_t mask)
 {
-       uint16_t old = get_playback_channel_mask();
-
-       if (old != mask) {
-               _set_playback_channel_mask (mask);
-               PlaybackChannelMaskChanged ();
-               _session.set_dirty ();
+       if (_playback_filter.set_channel_mask(mask)) {
+               _session.set_dirty();
        }
 }
 
 void
 MidiTrack::set_capture_channel_mask (uint16_t mask)
 {
-       uint16_t old = get_capture_channel_mask();
-
-       if (old != mask) {
-               _set_capture_channel_mask (mask);
-               CaptureChannelMaskChanged ();
-               _session.set_dirty ();
+       if (_capture_filter.set_channel_mask(mask)) {
+               _session.set_dirty();
        }
 }
 
@@ -949,7 +889,7 @@ MidiTrack::act_on_mute ()
        if (muted() || _mute_master->muted_by_others_at(MuteMaster::AllPoints)) {
                /* only send messages for channels we are using */
 
-               uint16_t mask = get_playback_channel_mask();
+               uint16_t mask = _playback_filter.get_channel_mask();
 
                for (uint8_t channel = 0; channel <= 0xF; channel++) {
 
index 7f5e8059c936c624b5d9a91bc29421bb740216ea..d5c89b5ee97bd0bbfe9fb3535ad389530e755699 100644 (file)
 #include "evoral/Control.hpp"
 #include "evoral/SMF.hpp"
 
+#include "ardour/debug.h"
+#include "ardour/midi_channel_filter.h"
 #include "ardour/midi_model.h"
 #include "ardour/midi_ring_buffer.h"
 #include "ardour/midi_state_tracker.h"
 #include "ardour/parameter_types.h"
 #include "ardour/session.h"
 #include "ardour/smf_source.h"
-#include "ardour/debug.h"
 
 #include "i18n.h"
 
@@ -208,7 +209,8 @@ SMFSource::read_unlocked (const Lock&                    lock,
                           framepos_t const               source_start,
                           framepos_t                     start,
                           framecnt_t                     duration,
-                          MidiStateTracker*              tracker) const
+                          MidiStateTracker*              tracker,
+                          MidiChannelFilter*             filter) const
 {
        int      ret  = 0;
        uint64_t time = 0; // in SMF ticks, 1 tick per _ppqn
@@ -281,9 +283,11 @@ SMFSource::read_unlocked (const Lock&                    lock,
                const framepos_t ev_frame_time = converter.to(Evoral::Beats::ticks_at_rate(time, ppqn())) + source_start;
 
                if (ev_frame_time < start + duration) {
-                       destination.write (ev_frame_time, ev_type, ev_size, ev_buffer);
-                       if (tracker) {
-                               tracker->track(ev_buffer);
+                       if (!filter || !filter->filter(ev_buffer, ev_size)) {
+                               destination.write (ev_frame_time, ev_type, ev_size, ev_buffer);
+                               if (tracker) {
+                                       tracker->track(ev_buffer);
+                               }
                        }
                } else {
                        break;
index 82aa2085f83cbbbfccb3d7819c17d1d1bc14b8a8..41d0edc3dd11a7f0b435411273a8416ffda46384 100644 (file)
@@ -109,6 +109,7 @@ libardour_sources = [
         'meter.cc',
         'midi_automation_list_binder.cc',
         'midi_buffer.cc',
+        'midi_channel_filter.cc',
         'midi_clock_slave.cc',
         'midi_diskstream.cc',
         'midi_model.cc',