Make smf_track_get_next_event gracefully handle empty tracks.
[ardour.git] / gtk2_ardour / midi_region_view.cc
index 9e8a7a8e2943ff2a93f8aea509ffb9d8b946c9bd..a090c1e566f27f5cb7eb73feeac819c099e7b781 100644 (file)
@@ -147,8 +147,9 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<M
 void
 MidiRegionView::init (Gdk::Color& basic_color, bool wfd)
 {
-       if (wfd)
+       if (wfd) {
                midi_region()->midi_source(0)->load_model();
+       }
 
        const Meter& m = trackview.session().tempo_map().meter_at(_region->position());
        const Tempo& t = trackview.session().tempo_map().tempo_at(_region->position());
@@ -205,7 +206,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
 
        static ArdourCanvas::SimpleRect* drag_rect = NULL;
 
-       if (trackview.editor.current_mouse_mode() != MouseNote)
+       if (trackview.editor().current_mouse_mode() != MouseNote)
                return false;
 
        // Mmmm, spaghetti
@@ -214,8 +215,8 @@ MidiRegionView::canvas_event(GdkEvent* ev)
        case GDK_KEY_PRESS:
                if (ev->key.keyval == GDK_Delete && !delete_mod) {
                        delete_mod = true;
-                       original_mode = trackview.editor.current_midi_edit_mode();
-                       trackview.editor.set_midi_edit_mode(MidiEditErase);
+                       original_mode = trackview.editor().current_midi_edit_mode();
+                       trackview.editor().set_midi_edit_mode(MidiEditErase);
                        start_delta_command(_("erase notes"));
                        _mouse_state = EraseTouchDragging;
                        return true;
@@ -235,7 +236,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
                                apply_command();
                        }
                        if (delete_mod) {
-                               trackview.editor.set_midi_edit_mode(original_mode);
+                               trackview.editor().set_midi_edit_mode(original_mode);
                                _mouse_state = None;
                                delete_mod = false;
                        }
@@ -272,8 +273,8 @@ MidiRegionView::canvas_event(GdkEvent* ev)
                group->w2i(event_x, event_y);
 
                // convert event_x to global frame
-               event_frame = trackview.editor.pixel_to_frame(event_x) + _region->position();
-               trackview.editor.snap_to(event_frame);
+               event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
+               trackview.editor().snap_to(event_frame);
                // convert event_frame back to local coordinates relative to position
                event_frame -= _region->position();
 
@@ -281,7 +282,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
                case Pressed: // Drag start
 
                        // Select drag start
-                       if (_pressed_button == 1 && trackview.editor.current_midi_edit_mode() == MidiEditSelect) {
+                       if (_pressed_button == 1 && trackview.editor().current_midi_edit_mode() == MidiEditSelect) {
                                group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
                                                Gdk::Cursor(Gdk::FLEUR), ev->motion.time);
                                last_x = event_x;
@@ -304,7 +305,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
                                return true;
 
                        // Add note drag start
-                       } else if (trackview.editor.current_midi_edit_mode() == MidiEditPencil) {
+                       } else if (trackview.editor().current_midi_edit_mode() == MidiEditPencil) {
                                group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
                                                Gdk::Cursor(Gdk::FLEUR), ev->motion.time);
                                last_x = event_x;
@@ -313,7 +314,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
                                drag_start_y = event_y;
 
                                drag_rect = new ArdourCanvas::SimpleRect(*group);
-                               drag_rect->property_x1() = trackview.editor.frame_to_pixel(event_frame);
+                               drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
 
                                drag_rect->property_y1() = midi_stream_view()->note_to_y(midi_stream_view()->y_to_note(event_y));
                                drag_rect->property_x2() = event_x;
@@ -341,7 +342,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
                        }
 
                        if (_mouse_state == AddDragging)
-                               event_x = trackview.editor.frame_to_pixel(event_frame);
+                               event_x = trackview.editor().frame_to_pixel(event_frame);
 
                        if (drag_rect) {
                                if (event_x > drag_start_x)
@@ -376,7 +377,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
                event_y = ev->motion.y;
                group->w2i(event_x, event_y);
                group->ungrab(ev->button.time);
-               event_frame = trackview.editor.pixel_to_frame(event_x);
+               event_frame = trackview.editor().pixel_to_frame(event_x);
 
                if (_pressed_button != 1) {
                        return false;
@@ -384,7 +385,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
                        
                switch (_mouse_state) {
                case Pressed: // Clicked
-                       switch (trackview.editor.current_midi_edit_mode()) {
+                       switch (trackview.editor().current_midi_edit_mode()) {
                        case MidiEditSelect:
                        case MidiEditResize:
                                clear_selection();
@@ -404,7 +405,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
                        _mouse_state = None;
                        if (drag_rect->property_x2() > drag_rect->property_x1() + 2) {
                                const double x      = drag_rect->property_x1();
-                               const double length = trackview.editor.pixel_to_frame(
+                               const double length = trackview.editor().pixel_to_frame(
                                                        drag_rect->property_x2() - drag_rect->property_x1());
                                        
                                create_note_at(x, drag_rect->property_y1(), length);
@@ -434,7 +435,7 @@ MidiRegionView::create_note_at(double x, double y, double length)
        assert(note >= 0.0);
        assert(note <= 127.0);
 
-       nframes64_t new_note_time = trackview.editor.pixel_to_frame (x);
+       nframes64_t new_note_time = trackview.editor().pixel_to_frame (x);
        assert(new_note_time >= 0);
        new_note_time += _region->start();
 
@@ -455,7 +456,7 @@ MidiRegionView::create_note_at(double x, double y, double length)
        new_note_length = snap_to_frame(new_note_time_position_relative + new_note_length) + _region->start() 
                            - new_note_time;
 
-       const boost::shared_ptr<Evoral::Note> new_note(new Evoral::Note(
+       const boost::shared_ptr<NoteType> new_note(new NoteType(
                        0, new_note_time, new_note_length, (uint8_t)note, 0x40));
        view->update_note_range(new_note->note());
 
@@ -503,7 +504,7 @@ MidiRegionView::start_delta_command(string name)
 }
 
 void
-MidiRegionView::command_add_note(const boost::shared_ptr<Evoral::Note> note, bool selected)
+MidiRegionView::command_add_note(const boost::shared_ptr<NoteType> note, bool selected)
 {
        if (_delta_command)
                _delta_command->add(note);
@@ -773,7 +774,7 @@ MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
                                                note->property_y1() = y1;
                                                note->property_y2() = y2;
                                        } else if (CanvasHit* hit = dynamic_cast<CanvasHit*>(event)) {
-                                               double x = trackview.editor.frame_to_pixel((nframes64_t)
+                                               double x = trackview.editor().frame_to_pixel((nframes64_t)
                                                                event->note()->time() - _region->start());
                                                const double diamond_size = midi_stream_view()->note_height() / 2.0;
                                                double y = midi_stream_view()->note_to_y(event->note()->note()) 
@@ -864,7 +865,7 @@ MidiRegionView::resolve_note(uint8_t note, double end_time)
                return;
 
        if (_active_notes && _active_notes[note]) {
-               _active_notes[note]->property_x2() = trackview.editor.frame_to_pixel((nframes64_t)end_time);
+               _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel((nframes64_t)end_time);
                _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
                _active_notes[note] = NULL;
        }
@@ -882,39 +883,38 @@ MidiRegionView::extend_active_notes()
 
        for (unsigned i=0; i < 128; ++i) {
                if (_active_notes[i]) {
-                       _active_notes[i]->property_x2() = trackview.editor.frame_to_pixel(_region->length());
+                       _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
                }
        }
 }
 
 void 
-MidiRegionView::play_midi_note(boost::shared_ptr<Evoral::Note> note)
+MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
 {
-       if (!trackview.editor.is_midi_sound_notes_active()) {
-               cerr << "not_active " << endl;
+       if (!trackview.editor().sound_notes()) {
                return;
        }
-       
+
        RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
        assert(route_ui);
        
        route_ui->midi_track()->write_immediate_event(note->on_event().size(), note->on_event().buffer());
        
-       Glib::Thread::create(bind(mem_fun(this, &MidiRegionView::play_midi_note_off), note), false);
-
+       nframes_t note_length_ms = (note->off_event().time() - note->on_event().time())
+                       * (1000 / (double)route_ui->session().nominal_frame_rate());
+       Glib::signal_timeout().connect(bind(mem_fun(this, &MidiRegionView::play_midi_note_off), note),
+                       note_length_ms, G_PRIORITY_DEFAULT);
 }
 
-void 
-MidiRegionView::play_midi_note_off(boost::shared_ptr<Evoral::Note> note)
+bool
+MidiRegionView::play_midi_note_off(boost::shared_ptr<NoteType> note)
 {
        RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
        assert(route_ui);
        
-       Glib::usleep(
-               (note->off_event().time() - note->on_event().time()) * 
-               (1000000 / route_ui->session().nominal_frame_rate())
-       );
        route_ui->midi_track()->write_immediate_event(note->off_event().size(), note->off_event().buffer());
+
+       return false;
 }
 
 
@@ -925,7 +925,7 @@ MidiRegionView::play_midi_note_off(boost::shared_ptr<Evoral::Note> note)
  * event arrives, to properly display the note.
  */
 void
-MidiRegionView::add_note(const boost::shared_ptr<Evoral::Note> note)
+MidiRegionView::add_note(const boost::shared_ptr<NoteType> note)
 {
        assert(note->time() >= 0);
        assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
@@ -942,13 +942,13 @@ MidiRegionView::add_note(const boost::shared_ptr<Evoral::Note> note)
 
        CanvasNoteEvent* event = 0;
        
-       const double x = trackview.editor.frame_to_pixel((nframes64_t)note->time() - _region->start());
+       const double x = trackview.editor().frame_to_pixel((nframes64_t)note->time() - _region->start());
        
        if (midi_view()->note_mode() == Sustained) {
 
                const double y1 = midi_stream_view()->note_to_y(note->note());
                const double note_endpixel = 
-                       trackview.editor.frame_to_pixel((nframes64_t)note->end_time() - _region->start());
+                       trackview.editor().frame_to_pixel((nframes64_t)note->end_time() - _region->start());
                
                CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
                ev_rect->property_x1() = x;
@@ -956,7 +956,7 @@ MidiRegionView::add_note(const boost::shared_ptr<Evoral::Note> note)
                if (note->length() > 0)
                        ev_rect->property_x2() = note_endpixel;
                else
-                       ev_rect->property_x2() = trackview.editor.frame_to_pixel(_region->length());
+                       ev_rect->property_x2() = trackview.editor().frame_to_pixel(_region->length());
                ev_rect->property_y2() = y1 + floor(midi_stream_view()->note_height());
 
                if (note->length() == 0) {
@@ -967,7 +967,7 @@ MidiRegionView::add_note(const boost::shared_ptr<Evoral::Note> note)
                                // finish the old note rectangle
                                if (_active_notes[note->note()]) {
                                        CanvasNote* const old_rect = _active_notes[note->note()];
-                                       boost::shared_ptr<Evoral::Note> old_note = old_rect->note();
+                                       boost::shared_ptr<NoteType> old_note = old_rect->note();
                                        cerr << "MidiModel: WARNING: Note has length 0: chan " << old_note->channel()
                                                << "note " << (int)old_note->note() << " @ " << old_note->time() << endl;
                                        /* FIXME: How large to make it?  Make it a diamond? */
@@ -1012,7 +1012,7 @@ MidiRegionView::add_note(const boost::shared_ptr<Evoral::Note> note)
                event = 0;
        }
 
-       if (event) {                    
+       if (event) {
                if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
                        note_selected(event, true);
                }
@@ -1030,7 +1030,7 @@ MidiRegionView::add_pgm_change(ControlEvent& program, string displaytext)
                return;
        
        ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
-       const double x = trackview.editor.frame_to_pixel((nframes64_t)program.time - _region->start());
+       const double x = trackview.editor().frame_to_pixel((nframes64_t)program.time - _region->start());
        
        double height = midi_stream_view()->contents_height();
        
@@ -1210,8 +1210,9 @@ MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add)
                clear_selection_except(ev);
        }
 
-       _selection.insert(ev);
-       play_midi_note(ev->note());
+       if (_selection.insert(ev).second) {
+               play_midi_note(ev->note());
+       }
 
        if ( ! ev->selected()) {
                ev->selected(true);
@@ -1260,6 +1261,7 @@ MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2
                                if (!(*i)->selected()) {
                                        (*i)->selected(true);
                                        _selection.insert(*i);
+                                       play_midi_note((*i)->note());
                                }
                        // Not inside rectangle
                        } else if ((*i)->selected()) {
@@ -1279,6 +1281,7 @@ MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2
                                if (!(*i)->selected()) {
                                        (*i)->selected(true);
                                        _selection.insert(*i);
+                                       play_midi_note((*i)->note());
                                }
                        // Not inside rectangle
                        } else if ((*i)->selected()) {
@@ -1335,7 +1338,7 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote)
                        Selection::iterator next = i;
                        ++next;
 
-                       const boost::shared_ptr<Evoral::Note> copy(new Evoral::Note(*(*i)->note().get()));
+                       const boost::shared_ptr<NoteType> copy(new NoteType(*(*i)->note().get()));
 
                        // we need to snap here again in nframes64_t in order to be sample accurate 
                        double new_note_time = (*i)->note()->time();
@@ -1387,7 +1390,7 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote)
 nframes64_t
 MidiRegionView::snap_to_frame(double x)
 {
-       PublicEditor &editor = trackview.editor;
+       PublicEditor &editor = trackview.editor();
        // x is region relative
        // convert x to global frame
        nframes64_t frame = editor.pixel_to_frame(x) + _region->position();
@@ -1400,7 +1403,7 @@ MidiRegionView::snap_to_frame(double x)
 nframes64_t
 MidiRegionView::snap_to_frame(nframes64_t x)
 {
-       PublicEditor &editor = trackview.editor;
+       PublicEditor &editor = trackview.editor();
        // x is region relative
        // convert x to global frame
        nframes64_t frame = x + _region->position();
@@ -1413,14 +1416,14 @@ MidiRegionView::snap_to_frame(nframes64_t x)
 double
 MidiRegionView::snap_to_pixel(double x)
 {
-       return (double) trackview.editor.frame_to_pixel(snap_to_frame(x));
+       return (double) trackview.editor().frame_to_pixel(snap_to_frame(x));
 }
 
 double
 MidiRegionView::get_position_pixels()
 {
        nframes64_t region_frame = get_position();
-       return trackview.editor.frame_to_pixel(region_frame);
+       return trackview.editor().frame_to_pixel(region_frame);
 }
 
 void
@@ -1527,7 +1530,7 @@ MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bo
                // transform to region start relative
                current_frame += _region->start();
                
-               const boost::shared_ptr<Evoral::Note> copy(new Evoral::Note(*(canvas_note->note().get())));
+               const boost::shared_ptr<NoteType> copy(new NoteType(*(canvas_note->note().get())));
 
                // resize beginning of note
                if (note_end == CanvasNote::NOTE_ON && current_frame < copy->end_time()) {
@@ -1553,7 +1556,7 @@ MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bo
 void
 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
 {
-       const boost::shared_ptr<Evoral::Note> copy(new Evoral::Note(*(event->note().get())));
+       const boost::shared_ptr<NoteType> copy(new NoteType(*(event->note().get())));
 
        if (relative) {
                uint8_t new_velocity = copy->velocity() + velocity;
@@ -1577,7 +1580,9 @@ MidiRegionView::change_velocity(CanvasNoteEvent* ev, int8_t velocity, bool relat
        for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
                Selection::iterator next = i;
                ++next;
-               change_note_velocity(*i, velocity, relative);
+               if ( !(*((*i)->note()) == *(ev->note())) ) {
+                       change_note_velocity(*i, velocity, relative);
+               }
                i = next;
        }
        
@@ -1593,7 +1598,7 @@ MidiRegionView::change_channel(uint8_t channel)
                ++next;
 
                CanvasNoteEvent* event = *i;
-               const boost::shared_ptr<Evoral::Note> copy(new Evoral::Note(*(event->note().get())));
+               const boost::shared_ptr<NoteType> copy(new NoteType(*(event->note().get())));
 
                copy->set_channel(channel);