summaryrefslogtreecommitdiff
path: root/libs/ardour/midi_channel_filter.cc
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2015-03-28 23:24:41 -0400
committerDavid Robillard <d@drobilla.net>2015-03-29 00:51:56 -0400
commitc9023ae73d6d70fead3e827811b384e2b171e4d6 (patch)
tree3a6cb5a0a7d436bed457f961afb272b82f1626be /libs/ardour/midi_channel_filter.cc
parent050c9c3f7d695ea8736d050f3eac5c4f0a3158ca (diff)
Fix mute of MIDI tracks with channel forcing.
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.
Diffstat (limited to 'libs/ardour/midi_channel_filter.cc')
-rw-r--r--libs/ardour/midi_channel_filter.cc143
1 files changed, 143 insertions, 0 deletions
diff --git a/libs/ardour/midi_channel_filter.cc b/libs/ardour/midi_channel_filter.cc
new file mode 100644
index 0000000000..40658802e5
--- /dev/null
+++ b/libs/ardour/midi_channel_filter.cc
@@ -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 */