diff options
author | David Robillard <d@drobilla.net> | 2007-07-30 16:33:10 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2007-07-30 16:33:10 +0000 |
commit | 2cbaa2751c0e346f2a6549f832a0ef69b8dd352c (patch) | |
tree | b5e5d8ad4989a9c3a4bc06beba02b52470e94e11 | |
parent | 633d9131af0a04bed812ce75e9f1da07fe7dfcd4 (diff) |
Basic canvas note event handling framework.
Note dragging (non-functional).
git-svn-id: svn://localhost/ardour2/trunk@2187 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r-- | gtk2_ardour/SConscript | 1 | ||||
-rw-r--r-- | gtk2_ardour/canvas-hit.h | 41 | ||||
-rw-r--r-- | gtk2_ardour/canvas-midi-event.cc | 122 | ||||
-rw-r--r-- | gtk2_ardour/canvas-midi-event.h | 59 | ||||
-rw-r--r-- | gtk2_ardour/canvas-note.h | 43 | ||||
-rw-r--r-- | gtk2_ardour/diamond.h | 5 | ||||
-rw-r--r-- | gtk2_ardour/midi_region_view.cc | 162 | ||||
-rw-r--r-- | gtk2_ardour/midi_region_view.h | 5 | ||||
-rw-r--r-- | libs/ardour/midi_diskstream.cc | 36 | ||||
-rw-r--r-- | libs/ardour/midi_model.cc | 5 |
10 files changed, 394 insertions, 85 deletions
diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript index 605e7d132b..5470fe68cc 100644 --- a/gtk2_ardour/SConscript +++ b/gtk2_ardour/SConscript @@ -109,6 +109,7 @@ canvas-simplerect.c simplerect.cc canvas-waveview.c diamond.cc +canvas-midi-event.cc crossfade_edit.cc crossfade_view.cc curvetest.cc diff --git a/gtk2_ardour/canvas-hit.h b/gtk2_ardour/canvas-hit.h new file mode 100644 index 0000000000..2760e62c79 --- /dev/null +++ b/gtk2_ardour/canvas-hit.h @@ -0,0 +1,41 @@ +/* + Copyright (C) 2007 Paul Davis + Author: Dave 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. +*/ + +#ifndef __gtk_ardour_canvas_hit_h__ +#define __gtk_ardour_canvas_hit_h__ + +#include <iostream> +#include "simplerect.h" +#include "diamond.h" + +namespace Gnome { +namespace Canvas { + +class CanvasHit : public Diamond, public CanvasMidiEvent { +public: + CanvasHit(MidiRegionView& region, Group& group, double size) + : Diamond(group, size), CanvasMidiEvent(region, this) {} + + bool on_event(GdkEvent* ev) { return CanvasMidiEvent::on_event(ev); } +}; + +} // namespace Gnome +} // namespace Canvas + +#endif /* __gtk_ardour_canvas_hit_h__ */ diff --git a/gtk2_ardour/canvas-midi-event.cc b/gtk2_ardour/canvas-midi-event.cc new file mode 100644 index 0000000000..73c42cc098 --- /dev/null +++ b/gtk2_ardour/canvas-midi-event.cc @@ -0,0 +1,122 @@ +/* + Copyright (C) 2007 Paul Davis + Author: Dave 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 <iostream> +#include "canvas-midi-event.h" +#include "midi_region_view.h" +#include "public_editor.h" +#include "editing_syms.h" + + +using namespace std; + +namespace Gnome { +namespace Canvas { + + +CanvasMidiEvent::CanvasMidiEvent(MidiRegionView& region, Item* item) + : _region(region) + , _item(item) + , _state(None) +{ +} + + +bool +CanvasMidiEvent::on_event(GdkEvent* ev) +{ + static double last_x, last_y; + double event_x, event_y; + + if (_region.get_time_axis_view().editor.current_mouse_mode() != Editing::MouseNote) + return false; + + switch (ev->type) { + /*case GDK_ENTER_NOTIFY: + cerr << "ENTERED: " << ev->crossing.state << endl; + if ( (ev->crossing.state & GDK_BUTTON2_MASK) ) { + + } + break; + */ + case GDK_KEY_PRESS: + cerr << "EVENT KEY PRESS\n"; // doesn't work :/ + break; + + case GDK_BUTTON_PRESS: + _state = Pressed; + return true; + + case GDK_MOTION_NOTIFY: + event_x = ev->motion.x; + event_y = ev->motion.y; + _item->property_parent().get_value()->w2i(event_x, event_y); + + switch (_state) { + case Pressed: + _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, + Gdk::Cursor(Gdk::FLEUR), ev->motion.time); + _state = Dragging; + last_x = event_x; + last_y = event_y; + return true; + case Dragging: + if (ev->motion.is_hint) { + int t_x; + int t_y; + GdkModifierType state; + gdk_window_get_pointer(ev->motion.window, &t_x, &t_y, &state); + event_x = t_x; + event_y = t_y; + } + + _item->move(event_x - last_x, event_y - last_y); + + last_x = event_x; + last_y = event_y; + + return true; + default: + break; + } + break; + + case GDK_BUTTON_RELEASE: + switch (_state) { + case Pressed: // Clicked + _state = None; + return true; + case Dragging: // Dropped + _item->ungrab(ev->button.time); + _state = None; + return true; + default: + break; + } + + default: + break; + } + + return false; +} + +} // namespace Canvas +} // namespace Gnome + diff --git a/gtk2_ardour/canvas-midi-event.h b/gtk2_ardour/canvas-midi-event.h new file mode 100644 index 0000000000..bd98e82efd --- /dev/null +++ b/gtk2_ardour/canvas-midi-event.h @@ -0,0 +1,59 @@ +/* + Copyright (C) 2007 Paul Davis + Author: Dave 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. +*/ + +#ifndef __gtk_ardour_canvas_midi_event_h__ +#define __gtk_ardour_canvas_midi_event_h__ + +#include "simplerect.h" + +class Editor; +class MidiRegionView; + +namespace Gnome { +namespace Canvas { + + +/** This manages all the event handling for any MIDI event on the canvas. + * + * This is not actually a canvas item itself to avoid the dreaded diamond, + * since various types of canvas items (Note (rect), Hit (diamond), etc) + * need to share this functionality but can't share an ancestor. + * + * Note: Because of this, derived classes need to manually bounce events to + * on_event, it won't happen automatically. + */ +class CanvasMidiEvent { +public: + CanvasMidiEvent(MidiRegionView& region, Item* item); + virtual ~CanvasMidiEvent() {} + + virtual bool on_event(GdkEvent* ev); + +private: + enum State { None, Pressed, Dragging }; + + MidiRegionView& _region; + Item* const _item; + State _state; +}; + +} // namespace Gnome +} // namespace Canvas + +#endif /* __gtk_ardour_canvas_midi_event_h__ */ diff --git a/gtk2_ardour/canvas-note.h b/gtk2_ardour/canvas-note.h new file mode 100644 index 0000000000..2d64095187 --- /dev/null +++ b/gtk2_ardour/canvas-note.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2007 Paul Davis + Author: Dave 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. +*/ + +#ifndef __gtk_ardour_canvas_note_h__ +#define __gtk_ardour_canvas_note_h__ + +#include <iostream> +#include "simplerect.h" +#include "canvas-midi-event.h" + +namespace Gnome { +namespace Canvas { + +class CanvasNote : public SimpleRect, public CanvasMidiEvent { +public: + CanvasNote(MidiRegionView& region, Group& group) + : SimpleRect(group), CanvasMidiEvent(region, this) + { + } + + bool on_event(GdkEvent* ev) { return CanvasMidiEvent::on_event(ev); } +}; + +} // namespace Gnome +} // namespace Canvas + +#endif /* __gtk_ardour_canvas_note_h__ */ diff --git a/gtk2_ardour/diamond.h b/gtk2_ardour/diamond.h index a7f8c684ff..14cec449f7 100644 --- a/gtk2_ardour/diamond.h +++ b/gtk2_ardour/diamond.h @@ -21,14 +21,15 @@ #define __ardour_diamond_h__ #include <libgnomecanvasmm/polygon.h> +#include "canvas-midi-event.h" namespace Gnome { namespace Canvas { -class Diamond : public Gnome::Canvas::Polygon { +class Diamond : public Polygon { public: - Diamond(Gnome::Canvas::Group& group, double height); + Diamond(Group& group, double height); }; diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 77c297f422..78e0b6e539 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -41,7 +41,7 @@ #include "midi_time_axis.h" #include "simplerect.h" #include "simpleline.h" -#include "diamond.h" +#include "canvas-hit.h" #include "public_editor.h" #include "ghostregion.h" #include "midi_time_axis.h" @@ -108,56 +108,124 @@ MidiRegionView::init (Gdk::Color& basic_color, bool wfd) bool MidiRegionView::canvas_event(GdkEvent* ev) { - if (trackview.editor.current_mouse_mode() == MouseNote) { - if (ev->type == GDK_BUTTON_PRESS) { - MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview); - MidiStreamView* const view = mtv->midi_view(); - - const uint8_t note_range = view->highest_note() - view->lowest_note() + 1; - const double footer_height = name_highlight->property_y2() - name_highlight->property_y1(); - const double roll_height = trackview.height - footer_height; - - double x = ev->button.x; - double y = ev->button.y; - get_canvas_group()->w2i(x, y); + enum State { None, Pressed, Dragging }; + static State _state; - double note = floor((roll_height - y) / roll_height * (double)note_range) + view->lowest_note(); - assert(note >= 0.0); - assert(note <= 127.0); + static double last_x, last_y; + double event_x, event_y; + + if (trackview.editor.current_mouse_mode() != MouseNote) + return false; + + switch (ev->type) { + case GDK_BUTTON_PRESS: + //group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, ev->button.time); + // This should happen on release... + if (ev->button.button == 1) + create_note_at(ev->button.x, ev->button.y); + + _state = Pressed; + return true; + + case GDK_MOTION_NOTIFY: + cerr << "MOTION\n"; + event_x = ev->motion.x; + event_y = ev->motion.y; + group->w2i(event_x, event_y); + + switch (_state) { + case Pressed: + cerr << "SELECT DRAG START\n"; + //group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, + // Gdk::Cursor(Gdk::FLEUR), ev->motion.time); + _state = Dragging; + last_x = event_x; + last_y = event_y; + return true; + case Dragging: + if (ev->motion.is_hint) { + int t_x; + int t_y; + GdkModifierType state; + gdk_window_get_pointer(ev->motion.window, &t_x, &t_y, &state); + event_x = t_x; + event_y = t_y; + } - const nframes_t stamp = trackview.editor.pixel_to_frame (x); - assert(stamp >= 0); - //assert(stamp <= _region->length()); - - const Meter& m = trackview.session().tempo_map().meter_at(stamp); - const Tempo& t = trackview.session().tempo_map().tempo_at(stamp); - double dur = m.frames_per_bar(t, trackview.session().frame_rate()) / m.beats_per_bar(); - - MidiModel* model = midi_region()->midi_source(0)->model(); - - // Add a 1 beat long note (for now) - const MidiModel::Note new_note(stamp, dur, (uint8_t)note, 0x40); - - model->begin_command(); - model->add_note(new_note); - model->finish_command(); + cerr << "SELECT DRAG" << endl; + //move(event_x - last_x, event_y - last_y); - view->update_bounds(new_note.note()); + last_x = event_x; + last_y = event_y; - add_note(new_note); + return true; + default: + break; + } + break; + + case GDK_BUTTON_RELEASE: + cerr << "RELEASE\n"; + //group->ungrab(ev->button.time); + switch (_state) { + case Pressed: + cerr << "CLICK\n"; + //create_note_at(ev->button.x, ev->button.y); + _state = None; + return true; + case Dragging: + cerr << "SELECT RECT DONE\n"; + _state = None; + return true; + default: + break; } - } + default: + break; + } + return false; } -bool -MidiRegionView::note_canvas_event(GdkEvent* ev) +/** Add a note to the model, and the view, at a canvas (click) coordinate */ +void +MidiRegionView::create_note_at(double x, double y) { - cerr << "NOTE CANVAS EVENT" << endl; + MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview); + MidiStreamView* const view = mtv->midi_view(); + + const uint8_t note_range = view->highest_note() - view->lowest_note() + 1; + const double footer_height = name_highlight->property_y2() - name_highlight->property_y1(); + const double roll_height = trackview.height - footer_height; + + get_canvas_group()->w2i(x, y); + + double note = floor((roll_height - y) / roll_height * (double)note_range) + view->lowest_note(); + assert(note >= 0.0); + assert(note <= 127.0); - return true; + const nframes_t stamp = trackview.editor.pixel_to_frame (x); + assert(stamp >= 0); + //assert(stamp <= _region->length()); + + const Meter& m = trackview.session().tempo_map().meter_at(stamp); + const Tempo& t = trackview.session().tempo_map().tempo_at(stamp); + double dur = m.frames_per_bar(t, trackview.session().frame_rate()) / m.beats_per_bar(); + + MidiModel* model = midi_region()->midi_source(0)->model(); + + // Add a 1 beat long note (for now) + const MidiModel::Note new_note(stamp, dur, (uint8_t)note, 0x40); + + model->begin_command(); + model->add_note(new_note); + model->finish_command(); + + view->update_bounds(new_note.note()); + + add_note(new_note); } @@ -279,7 +347,7 @@ MidiRegionView::add_ghost (AutomationTimeAxisView& atv) void MidiRegionView::begin_write() { - _active_notes = new ArdourCanvas::SimpleRect*[128]; + _active_notes = new CanvasNote*[128]; for (unsigned i=0; i < 128; ++i) _active_notes[i] = NULL; } @@ -323,7 +391,7 @@ MidiRegionView::add_event (const MidiEvent& ev) const double y1 = trackview.height - (pixel_range * (note - view->lowest_note() + 1)) - footer_height - 3.0; - ArdourCanvas::SimpleRect * ev_rect = new Gnome::Canvas::SimpleRect(*group); + CanvasNote* ev_rect = new CanvasNote(*this, *group); ev_rect->property_x1() = trackview.editor.frame_to_pixel ( (nframes_t)ev.time); ev_rect->property_y1() = y1; @@ -335,8 +403,6 @@ MidiRegionView::add_event (const MidiEvent& ev) ev_rect->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8); ev_rect->property_fill_color_rgba() = 0xFFFFFF66; - ev_rect->signal_event().connect(sigc::mem_fun(this, &MidiRegionView::note_canvas_event)); - ev_rect->raise_to_top(); _events.push_back(ev_rect); @@ -358,14 +424,12 @@ MidiRegionView::add_event (const MidiEvent& ev) const double y = trackview.height - (pixel_range * (note - view->lowest_note() + 1)) - footer_height - 3.0; - Diamond* ev_diamond = new Diamond(*group, std::min(pixel_range, 5.0)); + CanvasHit* ev_diamond = new CanvasHit(*this, *group, std::min(pixel_range, 5.0)); ev_diamond->move(x, y); ev_diamond->show(); ev_diamond->property_outline_color_rgba() = 0xFFFFFFDD; ev_diamond->property_fill_color_rgba() = 0xFFFFFF66; - ev_diamond->signal_event().connect(sigc::mem_fun(this, &MidiRegionView::note_canvas_event)); - _events.push_back(ev_diamond); } } @@ -385,7 +449,7 @@ MidiRegionView::extend_active_notes() } -/** Add a MIDI note (with duration). +/** Add a MIDI note to the view (with duration). * * This does no 'realtime' note resolution, notes from a MidiModel have a * duration so they can be drawn in full immediately. @@ -419,7 +483,7 @@ MidiRegionView::add_note (const MidiModel::Note& note) const double y1 = trackview.height - (pixel_range * (note.note() - view->lowest_note() + 1)) - footer_height - 3.0; - ArdourCanvas::SimpleRect * ev_rect = new Gnome::Canvas::SimpleRect(*group); + ArdourCanvas::SimpleRect * ev_rect = new CanvasNote(*this, *group); ev_rect->property_x1() = trackview.editor.frame_to_pixel((nframes_t)note.time()); ev_rect->property_y1() = y1; ev_rect->property_x2() = trackview.editor.frame_to_pixel((nframes_t)(note.end_time())); @@ -437,7 +501,7 @@ MidiRegionView::add_note (const MidiModel::Note& note) const double y = trackview.height - (pixel_range * (note.note() - view->lowest_note() + 1)) - footer_height - 3.0; - Diamond* ev_diamond = new Diamond(*group, std::min(pixel_range, 5.0)); + CanvasHit* ev_diamond = new CanvasHit(*this, *group, std::min(pixel_range, 5.0)); ev_diamond->move(x, y); ev_diamond->show(); ev_diamond->property_fill_color_rgba() = fill; diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index 17f190a3cd..bfac56209d 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -33,6 +33,7 @@ #include "automation_line.h" #include "enums.h" #include "canvas.h" +#include "canvas-note.h" namespace ARDOUR { class MidiRegion; @@ -70,6 +71,8 @@ class MidiRegionView : public RegionView void end_write(); void extend_active_notes(); + void create_note_at(double x, double y); + protected: /* this constructor allows derived types @@ -101,7 +104,7 @@ class MidiRegionView : public RegionView bool note_canvas_event(GdkEvent* ev); std::vector<ArdourCanvas::Item*> _events; - ArdourCanvas::SimpleRect** _active_notes; + ArdourCanvas::CanvasNote** _active_notes; }; #endif /* __gtk_ardour_midi_region_view_h__ */ diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index 74acae522a..3fed0cf68c 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -827,18 +827,6 @@ MidiDiskstream::do_refill () return 0; } - /* if there are 2+ chunks of disk i/o possible for - this track, let the caller know so that it can arrange - for us to be called again, ASAP. - */ - - // FIXME: using disk_io_chunk_frames as an event count, not good - // count vs duration semantic differences are nonexistant for audio, - // which makes translating for MIDI code confusing... - if (_playback_buf->write_space() >= (_slaved?3:2) * disk_io_chunk_frames) { - ret = 1; - } - /* if we're running close to normal speed and there isn't enough space to do disk_io_chunk_frames of I/O, then don't bother. @@ -847,7 +835,7 @@ MidiDiskstream::do_refill () */ if ((write_space < disk_io_chunk_frames) && fabs (_actual_speed) < 2.0f) { - cerr << "No refill 1\n"; + //cerr << "No refill 1\n"; return 0; } @@ -857,12 +845,12 @@ MidiDiskstream::do_refill () */ if (_slaved && write_space < (_playback_buf->capacity() / 2)) { - cerr << "No refill 2\n"; + //cerr << "No refill 2\n"; return 0; } if (reversed) { - cerr << "No refill 3 (reverse)\n"; + //cerr << "No refill 3 (reverse)\n"; return 0; } @@ -874,26 +862,10 @@ MidiDiskstream::do_refill () return 0; } -#if 0 - // or this - if (file_frame > max_frames - total_space) { - - /* to close to the end: read what we can, and zero fill the rest */ - - zero_fill = total_space - (max_frames - file_frame); - total_space = max_frames - file_frame; - - } else { - zero_fill = 0; - } -#endif - - // At this point we: + // At this point we... assert(_playback_buf->write_space() > 0); // ... have something to write to, and assert(file_frame <= max_frames); // ... something to write - // So (read it, then) write it: - nframes_t file_frame_tmp = file_frame; nframes_t to_read = min(disk_io_chunk_frames, (max_frames - file_frame)); diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index b1a87551b1..7374916c14 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -86,7 +86,7 @@ MidiModel::read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframe { size_t read_events = 0; - //cerr << "MM READ " << start << " .. " << nframes << endl; + cerr << "MM READ @ " << start << " + " << nframes << endl; /* FIXME: cache last lookup value to avoid the search */ @@ -140,6 +140,9 @@ MidiModel::read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframe } } + if (read_events > 0) + cerr << "MM READ " << read_events << " EVENTS" << endl; + return read_events; } |