diff options
author | Hans Baier <hansfbaier@googlemail.com> | 2008-04-14 06:23:11 +0000 |
---|---|---|
committer | Hans Baier <hansfbaier@googlemail.com> | 2008-04-14 06:23:11 +0000 |
commit | 8b3d298f6b16fbe819a9b9911e018c811b4914e3 (patch) | |
tree | 0070828d11b1dc2c5df0f896d5197fdd9137a0a3 | |
parent | 2656e0a43bb34aaa615f9cfd6f7ecb3ac4b03262 (diff) |
* first working version of editing MIDI channels of individual notes, see: http://www.flickr.com/photos/24012642@N02/2412142661/
git-svn-id: svn://localhost/ardour2/branches/3.0@3252 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r-- | gtk2_ardour/SConscript | 1 | ||||
-rw-r--r-- | gtk2_ardour/canvas-midi-event.cc | 60 | ||||
-rw-r--r-- | gtk2_ardour/canvas-midi-event.h | 16 | ||||
-rw-r--r-- | gtk2_ardour/canvas-note.cc | 4 | ||||
-rw-r--r-- | gtk2_ardour/crossfade_edit.cc | 4 | ||||
-rw-r--r-- | gtk2_ardour/midi_channel_selector.cc | 65 | ||||
-rw-r--r-- | gtk2_ardour/midi_channel_selector.h | 49 | ||||
-rw-r--r-- | gtk2_ardour/midi_region_view.cc | 45 | ||||
-rw-r--r-- | gtk2_ardour/midi_region_view.h | 8 |
9 files changed, 239 insertions, 13 deletions
diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript index 4f6f1a5646..5b4fa141f9 100644 --- a/gtk2_ardour/SConscript +++ b/gtk2_ardour/SConscript @@ -192,6 +192,7 @@ midi_region_view.cc midi_scroomer.cc midi_streamview.cc midi_time_axis.cc +midi_channel_selector.cc mixer_strip.cc mixer_ui.cc new_session_dialog.cc diff --git a/gtk2_ardour/canvas-midi-event.cc b/gtk2_ardour/canvas-midi-event.cc index 0709024075..56ec1ec160 100644 --- a/gtk2_ardour/canvas-midi-event.cc +++ b/gtk2_ardour/canvas-midi-event.cc @@ -36,6 +36,7 @@ CanvasMidiEvent::CanvasMidiEvent(MidiRegionView& region, Item* item, : _region(region) , _item(item) , _text(0) + , _channel_selector_widget() , _state(None) , _note(note) , _selected(false) @@ -43,6 +44,12 @@ CanvasMidiEvent::CanvasMidiEvent(MidiRegionView& region, Item* item, _text = new Text(*(item->property_parent())); } +CanvasMidiEvent::~CanvasMidiEvent() +{ + if(_text) delete _text; + if(_channel_selector_widget) delete _channel_selector_widget; +} + void CanvasMidiEvent::move_event(double dx, double dy) { @@ -71,6 +78,50 @@ CanvasMidiEvent::hide_velocity(void) _text->hide(); } +void +CanvasMidiEvent::on_channel_change(uint8_t channel) +{ + _region.note_selected(this, true); + hide_channel_selector(); + _region.change_channel(channel); +} + +void +CanvasMidiEvent::show_channel_selector(void) +{ + if(_channel_selector_widget == 0) { + cerr << "Note has channel: " << int(_note->channel()) << endl; + SingleMidiChannelSelector* _channel_selector = new SingleMidiChannelSelector(_note->channel()); + _channel_selector->show_all(); + _channel_selector->channel_selected.connect( + sigc::mem_fun(this, &CanvasMidiEvent::on_channel_change)); + + _channel_selector_widget = + new Widget(*(_item->property_parent()), + x1(), + y2() + 2, + (Gtk::Widget &) *_channel_selector); + + _channel_selector_widget->hide(); + _channel_selector_widget->property_height() = 100; + _channel_selector_widget->property_width() = 100; + _channel_selector_widget->raise_to_top(); + _channel_selector_widget->show(); + } else { + hide_channel_selector(); + } +} + +void +CanvasMidiEvent::hide_channel_selector(void) +{ + if(_channel_selector_widget) { + _channel_selector_widget->hide(); + delete _channel_selector_widget; + _channel_selector_widget = 0; + } +} + void CanvasMidiEvent::selected(bool yn) { @@ -120,7 +171,6 @@ CanvasMidiEvent::on_event(GdkEvent* ev) } return true; } else if(ev->scroll.direction == GDK_SCROLL_DOWN) { - _region.note_selected(this, true); if (_region.mouse_state() == MidiRegionView::SelectTouchDragging) { // TODO: absolute velocity @@ -164,6 +214,8 @@ CanvasMidiEvent::on_event(GdkEvent* ev) case GDK_BUTTON_PRESS: if (ev->button.button == 1) { _state = Pressed; + } else if (ev->button.button == 3) { + show_channel_selector(); } return true; @@ -236,7 +288,11 @@ CanvasMidiEvent::on_event(GdkEvent* ev) event_x = ev->button.x; event_y = ev->button.y; _item->property_parent().get_value()->w2i(event_x, event_y); - + + if(ev->button.button == 3) { + return true; + } + switch (_state) { case Pressed: // Clicked if (_region.midi_view()->editor.current_midi_edit_mode() == Editing::MidiEditSelect) { diff --git a/gtk2_ardour/canvas-midi-event.h b/gtk2_ardour/canvas-midi-event.h index 488bf2d42e..5c5f3ef520 100644 --- a/gtk2_ardour/canvas-midi-event.h +++ b/gtk2_ardour/canvas-midi-event.h @@ -21,7 +21,9 @@ #define __gtk_ardour_canvas_midi_event_h__ #include "simplerect.h" +#include "midi_channel_selector.h" #include <libgnomecanvasmm/text.h> +#include <libgnomecanvasmm/widget.h> #include <ardour/midi_model.h> class Editor; @@ -49,7 +51,7 @@ public: Item* item, const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>()); - virtual ~CanvasMidiEvent() { if(_text) delete _text; } + virtual ~CanvasMidiEvent(); bool on_event(GdkEvent* ev); @@ -60,6 +62,15 @@ public: void show_velocity(); void hide_velocity(); + + /** + * This slot is called, when a new channel is selected for the event + * */ + void on_channel_change(uint8_t channel); + + void show_channel_selector(); + + void hide_channel_selector(); virtual void set_outline_color(uint32_t c) = 0; virtual void set_fill_color(uint32_t c) = 0; @@ -76,7 +87,8 @@ protected: MidiRegionView& _region; Item* const _item; - Text* _text; + Text* _text; + Widget* _channel_selector_widget; State _state; const boost::shared_ptr<ARDOUR::Note> _note; bool _own_note; diff --git a/gtk2_ardour/canvas-note.cc b/gtk2_ardour/canvas-note.cc index b39deb0fed..bc15c3468d 100644 --- a/gtk2_ardour/canvas-note.cc +++ b/gtk2_ardour/canvas-note.cc @@ -47,8 +47,8 @@ CanvasNote::on_event(GdkEvent* ev) last_x = event_x; return true; - } - + } + case GDK_MOTION_NOTIFY: event_x = ev->motion.x; diff --git a/gtk2_ardour/crossfade_edit.cc b/gtk2_ardour/crossfade_edit.cc index 5f6f0793b7..82869af396 100644 --- a/gtk2_ardour/crossfade_edit.cc +++ b/gtk2_ardour/crossfade_edit.cc @@ -433,12 +433,12 @@ CrossfadeEditor::canvas_event (GdkEvent* event) case GDK_BUTTON_PRESS: add_control_point ((event->button.x - canvas_border)/effective_width(), 1.0 - ((event->button.y - canvas_border)/effective_height())); - return TRUE; + return true; break; default: break; } - return FALSE; + return false; } CrossfadeEditor::Point::~Point() diff --git a/gtk2_ardour/midi_channel_selector.cc b/gtk2_ardour/midi_channel_selector.cc new file mode 100644 index 0000000000..327b9d74ae --- /dev/null +++ b/gtk2_ardour/midi_channel_selector.cc @@ -0,0 +1,65 @@ +#include "midi_channel_selector.h" +#include <sstream> + +using namespace std; + +MidiChannelSelector::MidiChannelSelector() : + Gtk::Table(4,4,true) +{ + property_column_spacing() = 0; + property_row_spacing() = 0; + + uint8_t channel_nr = 0; + for(int row = 0; row < 4; ++row) { + for(int column = 0; column < 4; ++column) { + ostringstream channel; + channel << int(++channel_nr); + _button_labels[row][column].set_text(channel.str()); + _button_labels[row][column].set_justify(Gtk::JUSTIFY_RIGHT); + _buttons[row][column].add(_button_labels[row][column]); + _buttons[row][column].signal_toggled().connect( + sigc::bind( + sigc::mem_fun(this, &MidiChannelSelector::button_toggled), + &_buttons[row][column], + channel_nr - 1)); + attach(_buttons[row][column], column, column + 1, row, row + 1); + } + } +} + +MidiChannelSelector::~MidiChannelSelector() +{ +} + +SingleMidiChannelSelector::SingleMidiChannelSelector(uint8_t active_channel) + : MidiChannelSelector() +{ + _active_button = 0; + Gtk::ToggleButton *button = &_buttons[active_channel / 4][active_channel % 4]; + button->set_active(true); + _active_button = button; + _active_channel = active_channel; +} + +void +SingleMidiChannelSelector::button_toggled(Gtk::ToggleButton *button, uint8_t channel) +{ + if(button->get_active()) { + if(_active_button) { + _active_button->set_active(false); + } + _active_button = button; + _active_channel = channel; + channel_selected.emit(channel); + } +} + +void +MidiMultipleChannelSelector::button_toggled(Gtk::ToggleButton *button, uint8_t channel) +{ + if(button->get_active()) { + _selected_channels.insert(channel); + } else { + _selected_channels.erase(channel); + } +} diff --git a/gtk2_ardour/midi_channel_selector.h b/gtk2_ardour/midi_channel_selector.h new file mode 100644 index 0000000000..5060087ea6 --- /dev/null +++ b/gtk2_ardour/midi_channel_selector.h @@ -0,0 +1,49 @@ +#ifndef __ardour_ui_midi_channel_selector_h__ +#define __ardour_ui_midi_channel_selector_h__ + +#include "gtkmm/table.h" +#include "sigc++/trackable.h" +#include "gtkmm/togglebutton.h" +#include "gtkmm/label.h" +#include <set> + +class MidiChannelSelector : public Gtk::Table +{ +public: + MidiChannelSelector(); + virtual ~MidiChannelSelector() = 0; + +protected: + virtual void button_toggled(Gtk::ToggleButton *button, uint8_t button_nr) = 0; + Gtk::Label _button_labels[4][4]; + Gtk::ToggleButton _buttons[4][4]; +}; + +class SingleMidiChannelSelector : public MidiChannelSelector +{ +public: + SingleMidiChannelSelector(uint8_t active_channel = 0); + + const uint8_t get_active_channel() const { return _active_channel; } + + sigc::signal<void, uint8_t> channel_selected; + +protected: + virtual void button_toggled(Gtk::ToggleButton *button, uint8_t button_nr); + + Gtk::ToggleButton *_active_button; + uint8_t _active_channel; +}; + +class MidiMultipleChannelSelector : public MidiChannelSelector +{ +public: + const std::set<uint8_t>& get_selected_channels() const { return _selected_channels; } + +protected: + virtual void button_toggled(Gtk::ToggleButton *button, uint8_t button_nr); + + std::set<uint8_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 21d2e4dc71..100c387580 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -184,10 +184,15 @@ MidiRegionView::canvas_event(GdkEvent* ev) return false; case GDK_BUTTON_PRESS: - if (_mouse_state != SelectTouchDragging && _mouse_state != EraseTouchDragging) + if (_mouse_state != SelectTouchDragging && + _mouse_state != EraseTouchDragging && + ev->button.button == 1 ) { + _pressed_button = ev->button.button; _mouse_state = Pressed; + return true; + } _pressed_button = ev->button.button; - return true; + return false; case GDK_ENTER_NOTIFY: /* FIXME: do this on switch to note tool, too, if the pointer is already in */ @@ -306,6 +311,10 @@ MidiRegionView::canvas_event(GdkEvent* ev) group->ungrab(ev->button.time); event_frame = trackview.editor.pixel_to_frame(event_x); + if(_pressed_button != 1) { + return false; + } + switch (_mouse_state) { case Pressed: // Clicked switch (trackview.editor.current_midi_edit_mode()) { @@ -328,8 +337,7 @@ MidiRegionView::canvas_event(GdkEvent* ev) case AddDragging: // Add drag done _mouse_state = None; if (drag_rect->property_x2() > drag_rect->property_x1() + 2) { - const double x = drag_rect->property_x1() - + trackview.editor.frame_to_pixel(_region->start()); + const double x = drag_rect->property_x1(); const double length = trackview.editor.pixel_to_frame( drag_rect->property_x2() - drag_rect->property_x1()); @@ -365,6 +373,7 @@ MidiRegionView::create_note_at(double x, double y, double duration) nframes_t new_note_time = trackview.editor.pixel_to_frame (x); assert(new_note_time >= 0); + new_note_time += _region->start(); /* const Meter& m = trackview.session().tempo_map().meter_at(new_note_time); @@ -1161,6 +1170,34 @@ MidiRegionView::change_velocity(uint8_t velocity, bool relative) apply_command(); } +void +MidiRegionView::change_channel(uint8_t channel) +{ + start_delta_command(_("change channel")); + for (Selection::iterator i = _selection.begin(); i != _selection.end();) { + Selection::iterator next = i; + ++next; + + CanvasMidiEvent *event = *i; + const boost::shared_ptr<Note> copy(new Note(*(event->note().get()))); + + copy->set_channel(channel); + + command_remove_note(event); + command_add_note(copy); + + _marked_for_selection.insert(copy); + i = next; + } + + // dont keep notes selected if tweaking a single note + if(_marked_for_selection.size() == 1) { + _marked_for_selection.clear(); + } + + apply_command(); +} + void MidiRegionView::note_entered(ArdourCanvas::CanvasMidiEvent* ev) diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index c6b28c0f53..6c390db0b9 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -165,11 +165,17 @@ class MidiRegionView : public RegionView /** * This function is called while the user adjusts the velocity on a selection of notes - * @param velocity the relative or absolute velocity, dependin on the value of relative + * @param velocity the relative or absolute velocity, depending on the value of relative * @param relative true if the given velocity represents a delta to be applied to all notes, false * if the absolute value of the note shoud be set */ void change_velocity(uint8_t velocity, bool relative=false); + + /** + * This function is called when the user adjusts the midi channel of a selection of notes + * @param channel - the channel number of the new channel, zero-based + */ + void change_channel(uint8_t channel); enum MouseState { None, Pressed, SelectTouchDragging, SelectRectDragging, AddDragging, EraseTouchDragging }; MouseState mouse_state() const { return _mouse_state; } |