diff --git a/gtk2_ardour/midi_channel_selector.cc b/gtk2_ardour/midi_channel_selector.cc index b0bf73c2a1..b810268633 100644 --- a/gtk2_ardour/midi_channel_selector.cc +++ b/gtk2_ardour/midi_channel_selector.cc @@ -8,7 +8,7 @@ using namespace Gtk; using namespace sigc; MidiChannelSelector::MidiChannelSelector(int no_rows, int no_columns, int start_row, int start_column) : - Table(no_rows, no_columns, true) + Table(no_rows, no_columns, true), _recursion_counter(0) { assert(no_rows >= 4); assert(no_rows >= start_row + 4); @@ -46,28 +46,37 @@ MidiChannelSelector::~MidiChannelSelector() SingleMidiChannelSelector::SingleMidiChannelSelector(uint8_t active_channel) : MidiChannelSelector() { - _active_button = 0; + _last_active_button = 0; ToggleButton *button = &_buttons[active_channel / 4][active_channel % 4]; - button->set_active(true); - _active_button = button; _active_channel = active_channel; + button->set_active(true); + _last_active_button = button; } void SingleMidiChannelSelector::button_toggled(ToggleButton *button, uint8_t channel) -{ - if(button->get_active()) { - if(_active_button) { - _active_button->set_active(false); +{ + ++_recursion_counter; + if(_recursion_counter == 1) { + // if the current button is active it must + // be different from the first one + if(button->get_active()) { + if(_last_active_button) { + _last_active_button->set_active(false); + _active_channel = channel; + _last_active_button = button; + } + } else { + // if not, the user pressed the already active button + button->set_active(true); + _active_channel = channel; } - _active_button = button; - _active_channel = channel; - channel_selected.emit(channel); - } + } + --_recursion_counter; } MidiMultipleChannelSelector::MidiMultipleChannelSelector(uint16_t initial_selection) - : MidiChannelSelector(4, 6, 0, 0) + : MidiChannelSelector(4, 6, 0, 0), _mode(FILTERING_MULTIPLE_CHANNELS) { _select_all.add(*manage(new Label(_("All")))); _select_all.signal_clicked().connect( @@ -82,6 +91,8 @@ MidiMultipleChannelSelector::MidiMultipleChannelSelector(uint16_t initial_select mem_fun(this, &MidiMultipleChannelSelector::invert_selection)); _force_channel.add(*manage(new Label(_("Force")))); + _force_channel.signal_toggled().connect( + mem_fun(this, &MidiMultipleChannelSelector::force_channels_button_toggled)); set_homogeneous(false); attach(*manage(new VSeparator()), 4, 5, 0, 4, SHRINK, FILL, 0, 0); @@ -91,10 +102,53 @@ MidiMultipleChannelSelector::MidiMultipleChannelSelector(uint16_t initial_select attach(_invert_selection, 5, 6, 2, 3); attach(_force_channel, 5, 6, 3, 4); - _selected_channels = 0; + set_selected_channels(initial_selection); +} + +MidiMultipleChannelSelector::~MidiMultipleChannelSelector() +{ + selection_changed.clear(); + force_channel_changed.clear(); +} + +const int8_t +MidiMultipleChannelSelector::get_force_channel() const +{ + if(_mode == FORCING_SINGLE_CHANNEL) { + for(int8_t i = 0; i < 16; i++) { + const ToggleButton *button = &_buttons[i / 4][i % 4]; + if(button->get_active()) { + return i; + } + } + + // this point should not be reached. + assert(false); + } + + return -1; +} + +const uint16_t +MidiMultipleChannelSelector::get_selected_channels() const +{ + uint16_t selected_channels = 0; + for(uint16_t i = 0; i < 16; i++) { + const ToggleButton *button = &_buttons[i / 4][i % 4]; + if(button->get_active()) { + selected_channels |= (1L << i); + } + } + + return selected_channels; +} + +void +MidiMultipleChannelSelector::set_selected_channels(uint16_t selected_channels) +{ for(uint16_t i = 0; i < 16; i++) { ToggleButton *button = &_buttons[i / 4][i % 4]; - if(initial_selection & (1L << i)) { + if(selected_channels & (1L << i)) { button->set_active(true); } else { button->set_active(false); @@ -105,23 +159,73 @@ MidiMultipleChannelSelector::MidiMultipleChannelSelector(uint16_t initial_select void MidiMultipleChannelSelector::button_toggled(ToggleButton *button, uint8_t channel) { - _selected_channels = _selected_channels ^ (1L << channel); - selection_changed.emit(_selected_channels); + ++_recursion_counter; + if(_recursion_counter == 1) { + if(_mode == FORCING_SINGLE_CHANNEL) { + set_selected_channels(1 << channel); + } + + force_channel_changed.emit(get_force_channel()); + selection_changed.emit(get_selected_channels()); + } + --_recursion_counter; +} + +void +MidiMultipleChannelSelector::force_channels_button_toggled() +{ + if(_force_channel.get_active()) { + _mode = FORCING_SINGLE_CHANNEL; + bool found_first_active = false; + // leave only the first button enabled + for(int i = 0; i <= 15; i++) { + ToggleButton *button = &_buttons[i / 4][i % 4]; + if(button->get_active()) { + if(found_first_active) { + ++_recursion_counter; + button->set_active(false); + --_recursion_counter; + } else { + found_first_active = true; + } + } + } + + if(!found_first_active) { + _buttons[0][0].set_active(true); + } + + _select_all.set_sensitive(false); + _select_none.set_sensitive(false); + _invert_selection.set_sensitive(false); + force_channel_changed.emit(get_force_channel()); + selection_changed.emit(get_selected_channels()); + } else { + _mode = FILTERING_MULTIPLE_CHANNELS; + _select_all.set_sensitive(true); + _select_none.set_sensitive(true); + _invert_selection.set_sensitive(true); + force_channel_changed.emit(get_force_channel()); + selection_changed.emit(get_selected_channels()); + } } void MidiMultipleChannelSelector::select_all(bool on) { + ++_recursion_counter; for(uint16_t i = 0; i < 16; i++) { ToggleButton *button = &_buttons[i / 4][i % 4]; button->set_active(on); } - selection_changed.emit(_selected_channels); + --_recursion_counter; + selection_changed.emit(get_selected_channels()); } void MidiMultipleChannelSelector::invert_selection(void) { + ++_recursion_counter; for(uint16_t i = 0; i < 16; i++) { ToggleButton *button = &_buttons[i / 4][i % 4]; if(button->get_active()) { @@ -130,6 +234,7 @@ MidiMultipleChannelSelector::invert_selection(void) button->set_active(true); } } - selection_changed.emit(_selected_channels); + --_recursion_counter; + selection_changed.emit(get_selected_channels()); } diff --git a/gtk2_ardour/midi_channel_selector.h b/gtk2_ardour/midi_channel_selector.h index 33eb3f6b75..901296903c 100644 --- a/gtk2_ardour/midi_channel_selector.h +++ b/gtk2_ardour/midi_channel_selector.h @@ -19,6 +19,7 @@ protected: virtual void button_toggled(Gtk::ToggleButton *button, uint8_t button_nr) = 0; Gtk::Label _button_labels[4][4]; Gtk::ToggleButton _buttons[4][4]; + int _recursion_counter; }; class SingleMidiChannelSelector : public MidiChannelSelector @@ -33,7 +34,7 @@ public: protected: virtual void button_toggled(Gtk::ToggleButton *button, uint8_t button_nr); - Gtk::ToggleButton *_active_button; + Gtk::ToggleButton *_last_active_button; uint8_t _active_channel; }; @@ -41,13 +42,30 @@ class MidiMultipleChannelSelector : public MidiChannelSelector { public: MidiMultipleChannelSelector(uint16_t initial_selection = 1); + virtual ~MidiMultipleChannelSelector(); - const uint16_t get_selected_channels() const { return _selected_channels; } + /** + * @return each bit in the returned word represents a midi channel, eg. + * bit 0 represents channel 0 and bit 15 represents channel 15 + * + */ + const uint16_t get_selected_channels() const; + void set_selected_channels(uint16_t selected_channels); sigc::signal selection_changed; - + sigc::signal force_channel_changed; + + const int8_t get_force_channel() const; protected: + enum Mode { + FILTERING_MULTIPLE_CHANNELS, + FORCING_SINGLE_CHANNEL + }; + + Mode _mode; + virtual void button_toggled(Gtk::ToggleButton *button, uint8_t button_nr); + void force_channels_button_toggled(); void select_all(bool on); void invert_selection(void); @@ -56,7 +74,6 @@ protected: Gtk::Button _select_none; Gtk::Button _invert_selection; Gtk::ToggleButton _force_channel; - uint16_t _selected_channels; }; #endif /*__ardour_ui_midi_channel_selector_h__*/ diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 6fa98e0b96..36defd7a57 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -60,6 +60,7 @@ using namespace ArdourCanvas; MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr r, double spu, Gdk::Color& basic_color) : RegionView (parent, tv, r, spu, basic_color) + , force_channel(-1) , last_channel_selection(0xFFFF) , _default_note_length(0.0) , _active_notes(0) @@ -76,6 +77,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr r, double spu, Gdk::Color& basic_color, TimeAxisViewItem::Visibility visibility) : RegionView (parent, tv, r, spu, basic_color, visibility) + , force_channel(-1) , last_channel_selection(0xFFFF) , _default_note_length(0.0) , _active_notes(0) @@ -131,6 +133,8 @@ MidiRegionView::init (Gdk::Color& basic_color, bool wfd) group->signal_event().connect (mem_fun (this, &MidiRegionView::canvas_event), false); + midi_view()->signal_force_channel_changed().connect( + mem_fun(this, &MidiRegionView::midi_force_channel_changed)); midi_view()->signal_channel_selection_changed().connect( mem_fun(this, &MidiRegionView::midi_channel_selection_changed)); } @@ -1233,9 +1237,19 @@ MidiRegionView::set_frame_color() } } +void +MidiRegionView::midi_force_channel_changed(int8_t channel) +{ + force_channel = channel; +} + void MidiRegionView::midi_channel_selection_changed(uint16_t selection) { + if(force_channel >= 0) { + selection = 0xFFFF; + } + for(std::vector::iterator i = _events.begin(); i != _events.end(); ++i) { diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index fdc37380f6..bc5639476f 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -238,6 +238,8 @@ class MidiRegionView : public RegionView bool canvas_event(GdkEvent* ev); bool note_canvas_event(GdkEvent* ev); + int8_t force_channel; + void midi_force_channel_changed(int8_t channel); uint16_t last_channel_selection; void midi_channel_selection_changed(uint16_t selection); diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index a199d2e676..16edd88f72 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -146,7 +146,9 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session& sess, boost::shar controls_vbox.pack_end(_midi_expander, SHRINK, 0); _channel_selector.selection_changed.connect( mem_fun(*midi_track()->midi_diskstream(), &MidiDiskstream::set_channel_mask)); - + _channel_selector.force_channel_changed.connect( + mem_fun(*midi_track()->midi_diskstream(), &MidiDiskstream::set_force_channel)); + } MidiTimeAxisView::~MidiTimeAxisView () diff --git a/gtk2_ardour/midi_time_axis.h b/gtk2_ardour/midi_time_axis.h index 0702fb65f9..98cc5bb0b7 100644 --- a/gtk2_ardour/midi_time_axis.h +++ b/gtk2_ardour/midi_time_axis.h @@ -74,8 +74,12 @@ class MidiTimeAxisView : public RouteTimeAxisView void update_range(); - sigc::signal& signal_channel_selection_changed() { return _channel_selector.selection_changed; } - + sigc::signal& signal_channel_selection_changed() + { return _channel_selector.selection_changed; } + + sigc::signal& signal_force_channel_changed() + { return _channel_selector.force_channel_changed; } + private: void append_extra_display_menu_items (); diff --git a/libs/ardour/ardour/midi_diskstream.h b/libs/ardour/ardour/midi_diskstream.h index 40df9cfc26..f52f66dc6d 100644 --- a/libs/ardour/ardour/midi_diskstream.h +++ b/libs/ardour/ardour/midi_diskstream.h @@ -102,6 +102,18 @@ class MidiDiskstream : public Diskstream return playback_mask; } + void set_force_channel(int8_t force_channel) { + _playback_buf->set_force_channel(force_channel); + _capture_buf->set_force_channel(force_channel); + } + + int8_t get_force_channel() { + int8_t playback_force_channel = _playback_buf->get_force_channel(); + int8_t capture_force_channel = _capture_buf->get_force_channel(); + assert(playback_force_channel == capture_force_channel); + return playback_force_channel; + } + protected: friend class Session; diff --git a/libs/ardour/ardour/midi_ring_buffer.h b/libs/ardour/ardour/midi_ring_buffer.h index 7f0e9f3b37..c6cde666a3 100644 --- a/libs/ardour/ardour/midi_ring_buffer.h +++ b/libs/ardour/ardour/midi_ring_buffer.h @@ -227,7 +227,7 @@ public: /** @param size Size in bytes. */ MidiRingBuffer(size_t size) - : MidiRingBufferBase(size), _channel_mask(0xFFFF) + : MidiRingBufferBase(size), _channel_mask(0xFFFF), _force_channel(-1) {} size_t write(double time, size_t size, const Byte* buf); @@ -238,8 +238,21 @@ public: size_t read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset=0); - void set_channel_mask(uint16_t channel_mask) { _channel_mask = channel_mask; } - uint16_t get_channel_mask() { return _channel_mask; } + /** + * @param channel_mask each bit in channel_mask represents a midi channel: bit 0 = channel 0, + * bit 1 = channel 1 etc. the read and write methods will only allow + * events to pass, whose channel bit is 1. + */ + void set_channel_mask(uint16_t channel_mask) { g_atomic_int_set(&channel_mask, channel_mask); } + uint16_t get_channel_mask() { return g_atomic_int_get(&_channel_mask); } + + /** + * @param channel if negative, forcing channels is deactivated and filtering channels + * is activated, if positive, the LSB of channel is the channel number + * of the channel all events are forced into and filtering is deactivated + */ + void set_force_channel(int8_t channel) { g_atomic_int_set(&_force_channel, channel); } + int8_t get_force_channel() { return g_atomic_int_get(&_force_channel); } protected: inline bool is_channel_event(Byte event_type_byte) { @@ -250,7 +263,8 @@ protected: } private: - uint16_t _channel_mask; + volatile uint16_t _channel_mask; + volatile int8_t _force_channel; }; @@ -296,13 +310,13 @@ MidiRingBuffer::write(double time, size_t size, const Byte* buf) { printf("MRB - write %#X %d %d with time %lf\n", buf[0], buf[1], buf[2], time); - + assert(size > 0); - // filter events for channels - if(is_channel_event(buf[0])) { + if(is_channel_event(buf[0]) && (g_atomic_int_get(&_force_channel) < 0)) { + // filter events for channels Byte channel_nr = buf[0] & 0x0F; - if( !(_channel_mask & (1L << channel_nr)) ) { + if( !(g_atomic_int_get(&_channel_mask) & (1L << channel_nr)) ) { return 0; } } @@ -312,7 +326,17 @@ MidiRingBuffer::write(double time, size_t size, const Byte* buf) } else { MidiRingBufferBase::write(sizeof(double), (Byte*)&time); MidiRingBufferBase::write(sizeof(size_t), (Byte*)&size); - MidiRingBufferBase::write(size, buf); + if(is_channel_event(buf[0]) && (g_atomic_int_get(&_force_channel) >= 0)) { + assert(size == 3); + Byte tmp_buf[3]; + //force event into channel + tmp_buf[0] = (buf[0] & 0xF0) | (g_atomic_int_get(&_force_channel) & 0x0F); + tmp_buf[1] = buf[1]; + tmp_buf[2] = buf[2]; + MidiRingBufferBase::write(size, tmp_buf); + } else { + MidiRingBufferBase::write(size, buf); + } return size; } @@ -363,9 +387,10 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t } // filter events for channels - if(is_channel_event(first_event_byte)) { + // filtering is only active, if forcing channels is not active + if(is_channel_event(first_event_byte) && (g_atomic_int_get(&_force_channel) < 0)) { Byte channel_nr = first_event_byte & 0x0F; - if( !(_channel_mask & (1L << channel_nr)) ) { + if( !(g_atomic_int_get(&_channel_mask) & (1L << channel_nr)) ) { return 0; } } @@ -376,6 +401,9 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t success = MidiRingBufferBase::full_read(ev.size(), write_loc); if (success) { + if(is_channel_event(first_event_byte) && (g_atomic_int_get(&_force_channel) >= 0)) { + write_loc[0] = (write_loc[0] & 0xF0) | (g_atomic_int_get(&_force_channel) & 0x0F); + } ++count; printf("MRB - read event at time %lf\n", ev.time()); } else { diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index 51f093919c..3d9aaca93f 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -1226,6 +1226,9 @@ MidiDiskstream::get_state () snprintf (buf, sizeof(buf), "0x%x", _flags); node->add_property ("flags", buf); + snprintf (buf, sizeof(buf), "0x%x", get_channel_mask()); + node->add_property("channel_mask", buf); + node->add_property ("playlist", _playlist->name()); snprintf (buf, sizeof(buf), "%f", _visible_speed); @@ -1303,6 +1306,12 @@ MidiDiskstream::set_state (const XMLNode& node) if ((prop = node.property ("flags")) != 0) { _flags = Flag (string_2_enum (prop->value(), _flags)); } + + if ((prop = node.property ("channel_mask")) != 0) { + unsigned int channel_mask; + sscanf (prop->value().c_str(), "0x%x", &channel_mask); + set_channel_mask(channel_mask); + } if ((prop = node.property ("channels")) != 0) { nchans = atoi (prop->value().c_str());