summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Baier <hansfbaier@googlemail.com>2008-04-14 06:23:11 +0000
committerHans Baier <hansfbaier@googlemail.com>2008-04-14 06:23:11 +0000
commit8b3d298f6b16fbe819a9b9911e018c811b4914e3 (patch)
tree0070828d11b1dc2c5df0f896d5197fdd9137a0a3
parent2656e0a43bb34aaa615f9cfd6f7ecb3ac4b03262 (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/SConscript1
-rw-r--r--gtk2_ardour/canvas-midi-event.cc60
-rw-r--r--gtk2_ardour/canvas-midi-event.h16
-rw-r--r--gtk2_ardour/canvas-note.cc4
-rw-r--r--gtk2_ardour/crossfade_edit.cc4
-rw-r--r--gtk2_ardour/midi_channel_selector.cc65
-rw-r--r--gtk2_ardour/midi_channel_selector.h49
-rw-r--r--gtk2_ardour/midi_region_view.cc45
-rw-r--r--gtk2_ardour/midi_region_view.h8
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; }