summaryrefslogtreecommitdiff
path: root/gtk2_ardour
diff options
context:
space:
mode:
authorHans Baier <hansfbaier@googlemail.com>2008-04-03 21:47:47 +0000
committerHans Baier <hansfbaier@googlemail.com>2008-04-03 21:47:47 +0000
commitfbfe9a798313fcb98b2d25df8d23c5c90c76a7ef (patch)
tree4d99a55cb00c85b67c2936cddce46ae411f44fe8 /gtk2_ardour
parent6554200e66cc243e92818e6e74d4647d1c34ae9c (diff)
* implemented editing velocities (http://tracker.ardour.org/view.php?id=2148)
* added MIDI panic button (http://tracker.ardour.org/view.php?id=2118) * bugfix: moving notes above midi 127 or below 0 does not wrap around anymore * bugfix: deadlock on editing notes after playback (http://tracker.ardour.org/view.php?id=2140) due to unbalanced lock acquire/release * bugfix: First note off lost in playback (http://tracker.ardour.org/view.php?id=2132) * bugfix: Last note off lost in saving MIDI files (http://tracker.ardour.org/view.php?id=2132) * bandaid fix for http://tracker.ardour.org/view.php?id=1985 (Cannot reopen session because jack ports are not unregistered on session close) * bandaid fix: replaced conf.CheckPKGExists ('\"slv2 >= 0.6.0\"') by conf.CheckPKGExists ('slv2') in SConstruct, because the former would fail, even if SLV 0.6.0 was installed * added/enabled debugging output for debugging MIDI model (might be removed later) git-svn-id: svn://localhost/ardour2/branches/3.0@3211 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'gtk2_ardour')
-rw-r--r--gtk2_ardour/ardour_ui.cc1
-rw-r--r--gtk2_ardour/ardour_ui.h3
-rw-r--r--gtk2_ardour/ardour_ui2.cc13
-rw-r--r--gtk2_ardour/canvas-midi-event.cc64
-rw-r--r--gtk2_ardour/canvas-midi-event.h12
-rw-r--r--gtk2_ardour/editor_canvas_events.cc9
-rw-r--r--gtk2_ardour/midi_region_view.cc95
-rw-r--r--gtk2_ardour/midi_region_view.h17
-rw-r--r--gtk2_ardour/midi_util.h9
9 files changed, 207 insertions, 16 deletions
diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc
index 421906fbfc..94981803b4 100644
--- a/gtk2_ardour/ardour_ui.cc
+++ b/gtk2_ardour/ardour_ui.cc
@@ -159,6 +159,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
auditioning_alert_button (_("AUDITION")),
solo_alert_button (_("SOLO")),
+ midi_panic_button (_("Panic")),
shown_flag (false),
error_log_button (_("Errors"))
diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h
index 0eb02d8821..e3dd58a310 100644
--- a/gtk2_ardour/ardour_ui.h
+++ b/gtk2_ardour/ardour_ui.h
@@ -479,6 +479,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI
Gtk::ToggleButton auditioning_alert_button;
Gtk::ToggleButton solo_alert_button;
+ Gtk::ToggleButton midi_panic_button;
+
Gtk::VBox alert_box;
void solo_blink (bool);
@@ -490,6 +492,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI
void solo_alert_toggle ();
void audition_alert_toggle ();
+ void midi_panic_toggle ();
void big_clock_value_changed ();
void primary_clock_value_changed ();
diff --git a/gtk2_ardour/ardour_ui2.cc b/gtk2_ardour/ardour_ui2.cc
index 959d5fc128..014de41f23 100644
--- a/gtk2_ardour/ardour_ui2.cc
+++ b/gtk2_ardour/ardour_ui2.cc
@@ -344,12 +344,15 @@ ARDOUR_UI::setup_transport ()
solo_alert_button.signal_pressed().connect (mem_fun(*this,&ARDOUR_UI::solo_alert_toggle));
auditioning_alert_button.set_name ("TransportAuditioningAlert");
auditioning_alert_button.signal_pressed().connect (mem_fun(*this,&ARDOUR_UI::audition_alert_toggle));
+ midi_panic_button.set_name("TransportMidiPanic");
+ midi_panic_button.signal_clicked().connect (mem_fun(*this, &ARDOUR_UI::midi_panic_toggle));
tooltips().set_tip (solo_alert_button, _("When active, something is soloed.\nClick to de-solo everything"));
tooltips().set_tip (auditioning_alert_button, _("When active, auditioning is taking place\nClick to stop the audition"));
alert_box.pack_start (solo_alert_button, false, false);
alert_box.pack_start (auditioning_alert_button, false, false);
+ alert_box.pack_start (midi_panic_button, false, false);
transport_tearoff_hbox.set_border_width (3);
@@ -519,6 +522,16 @@ ARDOUR_UI::solo_alert_toggle ()
}
void
+ARDOUR_UI::midi_panic_toggle ()
+{
+ if (session) {
+ session->midi_panic();
+ midi_panic_button.set_active (false);
+ midi_panic_button.set_state (STATE_NORMAL);
+ }
+}
+
+void
ARDOUR_UI::solo_blink (bool onoff)
{
if (session == 0) {
diff --git a/gtk2_ardour/canvas-midi-event.cc b/gtk2_ardour/canvas-midi-event.cc
index 8b14bea986..49851fb3f6 100644
--- a/gtk2_ardour/canvas-midi-event.cc
+++ b/gtk2_ardour/canvas-midi-event.cc
@@ -35,12 +35,41 @@ CanvasMidiEvent::CanvasMidiEvent(MidiRegionView& region, Item* item,
const boost::shared_ptr<ARDOUR::Note> note)
: _region(region)
, _item(item)
+ , _text(0)
, _state(None)
, _note(note)
, _selected(false)
{
+ _text = new Text(*(item->property_parent()));
}
+void
+CanvasMidiEvent::move_event(double dx, double dy)
+{
+ _item->move(dx, dy);
+ _text->move(dx, dy);
+}
+
+void
+CanvasMidiEvent::show_velocity(void)
+{
+ _text->property_x() = (x1() + x2()) /2;
+ _text->property_y() = (y1() + y2()) /2;
+ ostringstream velo(ios::ate);
+ velo << int(_note->velocity());
+ _text->property_text() = velo.str();
+ _text->property_justification() = Gtk::JUSTIFY_CENTER;
+ _text->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get();
+ _text->show();
+ _text->lower_to_bottom();
+ _text->raise(2);
+}
+
+void
+CanvasMidiEvent::hide_velocity(void)
+{
+ _text->hide();
+}
void
CanvasMidiEvent::selected(bool yn)
@@ -49,11 +78,13 @@ CanvasMidiEvent::selected(bool yn)
return;
} else if (yn) {
set_fill_color(UINT_INTERPOLATE(note_fill_color(_note->velocity()),
- ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get(), 0.85));
+ ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get(), 0.1));
set_outline_color(ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get());
+ show_velocity();
} else {
set_fill_color(note_fill_color(_note->velocity()));
set_outline_color(note_outline_color(_note->velocity()));
+ hide_velocity();
}
_selected = yn;
@@ -70,11 +101,38 @@ CanvasMidiEvent::on_event(GdkEvent* ev)
double event_x, event_y, dx, dy;
nframes_t event_frame;
bool select_mod;
+ uint8_t d_velocity = 10;
if (_region.get_time_axis_view().editor.current_mouse_mode() != Editing::MouseNote)
return false;
switch (ev->type) {
+ case GDK_SCROLL:
+ if (Keyboard::modifier_state_equals (ev->scroll.state, Keyboard::Level4Modifier)) {
+ d_velocity = 1;
+ }
+
+ if(ev->scroll.direction == GDK_SCROLL_UP) {
+ _region.note_selected(this, true);
+ if (_region.mouse_state() == MidiRegionView::SelectTouchDragging) {
+ // TODO: absolute velocity
+ } else {
+ _region.change_velocity(d_velocity, true);
+ }
+ return true;
+ } else if(ev->scroll.direction == GDK_SCROLL_DOWN) {
+
+ _region.note_selected(this, true);
+ if (_region.mouse_state() == MidiRegionView::SelectTouchDragging) {
+ // TODO: absolute velocity
+ } else {
+ _region.change_velocity(-d_velocity, true);
+ }
+ return true;
+ } else {
+ return false;
+ }
+
case GDK_KEY_PRESS:
if (_note && ev->key.keyval == GDK_Delete) {
selected(true);
@@ -92,11 +150,15 @@ CanvasMidiEvent::on_event(GdkEvent* ev)
case GDK_ENTER_NOTIFY:
_region.note_entered(this);
_item->grab_focus();
+ show_velocity();
Keyboard::magic_widget_grab_focus();
break;
case GDK_LEAVE_NOTIFY:
Keyboard::magic_widget_drop_focus();
+ if(! selected()) {
+ hide_velocity();
+ }
_region.get_canvas_group()->grab_focus();
break;
diff --git a/gtk2_ardour/canvas-midi-event.h b/gtk2_ardour/canvas-midi-event.h
index 3fa009ed33..488bf2d42e 100644
--- a/gtk2_ardour/canvas-midi-event.h
+++ b/gtk2_ardour/canvas-midi-event.h
@@ -21,6 +21,7 @@
#define __gtk_ardour_canvas_midi_event_h__
#include "simplerect.h"
+#include <libgnomecanvasmm/text.h>
#include <ardour/midi_model.h>
class Editor;
@@ -48,13 +49,18 @@ public:
Item* item,
const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>());
- virtual ~CanvasMidiEvent() {}
+ virtual ~CanvasMidiEvent() { if(_text) delete _text; }
bool on_event(GdkEvent* ev);
bool selected() const { return _selected; }
void selected(bool yn);
+ void move_event(double dx, double dy);
+
+ void show_velocity();
+ void hide_velocity();
+
virtual void set_outline_color(uint32_t c) = 0;
virtual void set_fill_color(uint32_t c) = 0;
@@ -63,9 +69,6 @@ public:
virtual double x2() = 0;
virtual double y2() = 0;
- const Item* item() const { return _item; }
- Item* item() { return _item; }
-
const boost::shared_ptr<ARDOUR::Note> note() { return _note; }
protected:
@@ -73,6 +76,7 @@ protected:
MidiRegionView& _region;
Item* const _item;
+ Text* _text;
State _state;
const boost::shared_ptr<ARDOUR::Note> _note;
bool _own_note;
diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc
index d5c1305f66..f2583c690d 100644
--- a/gtk2_ardour/editor_canvas_events.cc
+++ b/gtk2_ardour/editor_canvas_events.cc
@@ -40,6 +40,7 @@
#include "control_point.h"
#include "canvas_impl.h"
#include "simplerect.h"
+#include "canvas-midi-event.h"
#include "i18n.h"
@@ -48,6 +49,7 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
using namespace Gtk;
+using namespace ArdourCanvas;
bool
Editor::track_canvas_scroll (GdkEventScroll* ev)
@@ -56,6 +58,7 @@ Editor::track_canvas_scroll (GdkEventScroll* ev)
double wx, wy;
nframes_t xdelta;
int direction = ev->direction;
+ CanvasMidiEvent *midi_event = dynamic_cast<CanvasMidiEvent *>(track_canvas->get_item_at(ev->x, ev->y));
retry:
switch (direction) {
@@ -94,6 +97,9 @@ Editor::track_canvas_scroll (GdkEventScroll* ev)
current_stepping_trackview->step_height (true);
return true;
} else {
+ if(midi_event) {
+ return midi_event->on_event(reinterpret_cast<GdkEvent *>(ev));
+ }
scroll_tracks_up_line ();
return true;
}
@@ -129,6 +135,9 @@ Editor::track_canvas_scroll (GdkEventScroll* ev)
current_stepping_trackview->step_height (false);
return true;
} else {
+ if(midi_event) {
+ return midi_event->on_event(reinterpret_cast<GdkEvent *>(ev));
+ }
scroll_tracks_down_line ();
return true;
}
diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc
index 86e06d0a3c..180a0a71fb 100644
--- a/gtk2_ardour/midi_region_view.cc
+++ b/gtk2_ardour/midi_region_view.cc
@@ -423,8 +423,22 @@ MidiRegionView::redisplay_model()
_model->read_lock();
- for (size_t i=0; i < _model->n_notes(); ++i)
+ /*
+ MidiModel::Notes notes = _model->notes();
+ cerr << endl << "Model contains " << notes.size() << " Notes:" << endl;
+ for(MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) {
+ Note note = *(*i).get();
+ cerr << "MODEL: Note time: " << note.time() << " duration: " << note.duration()
+ << " end-time: " << note.end_time()
+ << " velocity: " << int(note.velocity())
+ //<< " Note-on: " << note.on_event().
+ //<< " Note-off: " << note.off_event()
+ << endl;
+ }*/
+
+ for (size_t i=0; i < _model->n_notes(); ++i) {
add_note(_model->note_at(i));
+ }
end_write();
@@ -538,11 +552,15 @@ MidiRegionView::set_y_position_and_height (double y, double h)
note->show();
}
+ note->hide_velocity();
note->property_y1() = y1;
note->property_y2() = y2;
+ if(note->selected()) {
+ note->show_velocity();
}
}
}
+ }
_model->read_unlock();
}
@@ -609,6 +627,7 @@ MidiRegionView::end_write()
{
delete[] _active_notes;
_active_notes = NULL;
+ _marked_for_selection.clear();
}
@@ -657,6 +676,8 @@ MidiRegionView::add_note(const boost::shared_ptr<Note> note)
ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
+ CanvasMidiEvent *event = 0;
+
if (midi_view()->note_mode() == Sustained) {
//cerr << "MRV::add_note sustained " << note->note() << " @ " << note->time()
@@ -687,6 +708,7 @@ MidiRegionView::add_note(const boost::shared_ptr<Note> note)
ev_rect->show();
_events.push_back(ev_rect);
+ event = ev_rect;
MidiGhostRegion* gr;
@@ -711,9 +733,19 @@ MidiRegionView::add_note(const boost::shared_ptr<Note> note)
ev_diamond->property_fill_color_rgba() = note_fill_color(note->velocity());
ev_diamond->property_outline_color_rgba() = note_outline_color(note->velocity());
_events.push_back(ev_diamond);
+ event = ev_diamond;
+ } else {
+ event = 0;
+ }
+ if(event) {
+ Note *note = event->note().get();
+
+ if(_marked_for_selection.find(note) != _marked_for_selection.end()) {
+ note_selected(event, true);
}
}
+}
void
MidiRegionView::delete_selection()
@@ -813,7 +845,7 @@ void
MidiRegionView::move_selection(double dx, double dy)
{
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i)
- (*i)->item()->move(dx, dy);
+ (*i)->move_event(dx, dy);
}
@@ -851,7 +883,6 @@ MidiRegionView::note_dropped(CanvasMidiEvent* ev, double dt, uint8_t dnote)
// Make sure the note pitch does not exceed the MIDI standard range
if (dnote <= 127 && (highest_note_in_selection + dnote > 127)) {
highest_note_difference = highest_note_in_selection - 127;
- cerr << "Highest note difference: " << (int) highest_note_difference;
}
start_delta_command();
@@ -868,9 +899,15 @@ MidiRegionView::note_dropped(CanvasMidiEvent* ev, double dt, uint8_t dnote)
copy->set_time(0);
}
- uint8_t new_pitch = (*i)->note()->note() + dnote - highest_note_difference;
- if(new_pitch > 127) {
- new_pitch = 127;
+ uint8_t original_pitch = (*i)->note()->note();
+ uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
+
+ // keep notes in standard midi range
+ clamp_0_to_127(new_pitch);
+
+ //notes which are dragged beyond the standard midi range snap back to their original place
+ if((original_pitch != 0 && new_pitch == 0) || (original_pitch != 127 && new_pitch == 127)) {
+ new_pitch = original_pitch;
}
lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
@@ -880,19 +917,16 @@ MidiRegionView::note_dropped(CanvasMidiEvent* ev, double dt, uint8_t dnote)
command_add_note(copy);
- _selection.erase(i);
+ _marked_for_selection.insert(copy.get());
i = next;
}
apply_command();
- //cerr << "new lowest note (selection): " << int(lowest_note_in_selection) << " new highest note(selection): " << int(highest_note_in_selection) << endl;
-
// care about notes being moved beyond the upper/lower bounds on the canvas
if(lowest_note_in_selection < midi_stream_view()->lowest_note() ||
highest_note_in_selection > midi_stream_view()->highest_note()
) {
- //cerr << "resetting note range" << endl;
midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
}
}
@@ -1031,12 +1065,15 @@ MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bo
command_remove_note(canvas_note);
copy->on_event().time() = current_frame;
command_add_note(copy);
+ _marked_for_selection.insert(copy.get());
}
// resize end of note
if (note_end == CanvasNote::NOTE_OFF && current_frame > copy->time()) {
command_remove_note(canvas_note);
+ command_remove_note(canvas_note);
copy->off_event().time() = current_frame;
command_add_note(copy);
+ _marked_for_selection.insert(copy.get());
}
delete resize_rect;
@@ -1045,9 +1082,45 @@ MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bo
_resize_data.clear();
apply_command();
- clear_selection();
}
+
+void
+MidiRegionView::change_velocity(uint8_t velocity, bool relative)
+{
+ start_delta_command();
+ 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())));
+
+ if(relative) {
+ uint8_t new_velocity = copy->velocity() + velocity;
+ clamp_0_to_127(new_velocity);
+
+ copy->set_velocity(new_velocity);
+ } else { // absolute
+ copy->set_velocity(velocity);
+ }
+
+ command_remove_note(event);
+ command_add_note(copy);
+
+ _marked_for_selection.insert(copy.get());
+ 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 767d057f57..9f8f8f8b3e 100644
--- a/gtk2_ardour/midi_region_view.h
+++ b/gtk2_ardour/midi_region_view.h
@@ -103,6 +103,7 @@ class MidiRegionView : public RegionView
void command_remove_note(ArdourCanvas::CanvasMidiEvent* ev) {
if (_delta_command && ev->note()) {
+ _selection.erase(ev);
_delta_command->remove(ev->note());
ev->selected(true);
}
@@ -162,6 +163,14 @@ class MidiRegionView : public RegionView
*/
void commit_resizing(ArdourCanvas::CanvasNote::NoteEnd note_end, double event_x, bool relative);
+ /**
+ * 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 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);
+
enum MouseState { None, Pressed, SelectTouchDragging, SelectRectDragging, AddDragging, EraseTouchDragging };
MouseState mouse_state() const { return _mouse_state; }
@@ -221,9 +230,17 @@ class MidiRegionView : public RegionView
MouseState _mouse_state;
int _pressed_button;
+ /// currently selected CanvasMidiEvents
typedef std::set<ArdourCanvas::CanvasMidiEvent*> Selection;
Selection _selection;
+ /**
+ * this enables vanilla notes to be marked for selection
+ * they are added to _selection when redisplay_model is called
+ * this is necessary for selecting notes during/after model manipulations
+ */
+ std::set<ARDOUR::Note *> _marked_for_selection;
+
std::vector<NoteResizeData *> _resize_data;
};
diff --git a/gtk2_ardour/midi_util.h b/gtk2_ardour/midi_util.h
index 451fc55ea1..d56f1680e0 100644
--- a/gtk2_ardour/midi_util.h
+++ b/gtk2_ardour/midi_util.h
@@ -54,5 +54,14 @@ inline static uint32_t note_fill_color(uint8_t vel)
}
}
+inline static void clamp_0_to_127(uint8_t &val)
+{
+ if( (127 < val) && (val < 192) ) {
+ val = 127;
+ } else if( (192 <= val) && (val < 255) ) {
+ val = 0;
+ }
+}
+
#endif /* __gtk_ardour_midi_util_h__ */