* implemented editing velocities (http://tracker.ardour.org/view.php?id=2148)
authorHans Baier <hansfbaier@googlemail.com>
Thu, 3 Apr 2008 21:47:47 +0000 (21:47 +0000)
committerHans Baier <hansfbaier@googlemail.com>
Thu, 3 Apr 2008 21:47:47 +0000 (21:47 +0000)
* 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

22 files changed:
SConstruct
gtk2_ardour/ardour_ui.cc
gtk2_ardour/ardour_ui.h
gtk2_ardour/ardour_ui2.cc
gtk2_ardour/canvas-midi-event.cc
gtk2_ardour/canvas-midi-event.h
gtk2_ardour/editor_canvas_events.cc
gtk2_ardour/midi_region_view.cc
gtk2_ardour/midi_region_view.h
gtk2_ardour/midi_util.h
libs/ardour/ardour/midi_model.h
libs/ardour/ardour/midi_ring_buffer.h
libs/ardour/ardour/midi_track.h
libs/ardour/ardour/note.h
libs/ardour/ardour/session.h
libs/ardour/audioengine.cc
libs/ardour/midi_buffer.cc
libs/ardour/midi_model.cc
libs/ardour/midi_source.cc
libs/ardour/midi_track.cc
libs/ardour/session_midi.cc
libs/ardour/smf_source.cc

index 746809a3c0eb5931b2cd018322a1f34a70e4ff69..c5ab5f78b61d593fcb8d64e7c0ca9bbf9f0935d6 100644 (file)
@@ -534,7 +534,7 @@ if env['FFT_ANALYSIS']:
 if env['LV2']:
        conf = env.Configure(custom_tests = { 'CheckPKGExists' : CheckPKGExists })
        
-       if conf.CheckPKGExists ('\"slv2 >= 0.6.0\"'):
+       if conf.CheckPKGExists ('slv2'):
                libraries['slv2'] = LibraryInfo()
                libraries['slv2'].ParseConfig('pkg-config --cflags --libs slv2')
                 env.Append (CCFLAGS="-DHAVE_LV2")
index 421906fbfc3d09ef7ad6fb9a563ccb9bc2d40d6b..94981803b4a45fc71562dafbb2168eb65bc41bca 100644 (file)
@@ -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"))
 
index 0eb02d88212f8dc50b7f32a0b249b86e706fdf8e..e3dd58a310527cb4ba56885d946f986ad3339e27 100644 (file)
@@ -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 ();
index 959d5fc128f1aee20f44bba0bd03b7a615df5517..014de41f23100f66070f58d3c0fb1e343b0a044b 100644 (file)
@@ -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);
 
@@ -518,6 +521,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)
 {
index 8b14bea9861d17a310b8f8984b56ce56737679be..49851fb3f67d054d8aa7d299f90d8f8eb6aecc67 100644 (file)
@@ -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;
 
index 3fa009ed33104d9754007fa2a17d58e8a3cd316e..488bf2d42e1dc7b78f4e33fa82b78ffc5976061c 100644 (file)
@@ -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;
index d5c1305f66af7efc2679bf9e5e2b3d70848bf9a9..f2583c690dcf9f10fb6b92abd45bc915b28fe667 100644 (file)
@@ -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;
                }
index 86e06d0a3c2002f524fd48c778b35da99362faa2..180a0a71fbcdffb64ded420021de846c63b66453 100644 (file)
@@ -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)
 {
index 767d057f571e212ffc8593c9fdba07527827f55b..9f8f8f8b3edb144b5b1f772e46c4f6fbb2fa1d6f 100644 (file)
@@ -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;
 };
 
index 451fc55ea1d689fbb5f70e888ed69d169e036426..d56f1680e092c14e397a297b20f3484292d68cbf 100644 (file)
@@ -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__ */
 
index 11ef4e0f764e418a47f7750a827dfa9cfd6bf740..08521080779cc65577ff3b30b87c0c13e68ce34d 100644 (file)
@@ -58,10 +58,10 @@ public:
        MidiModel(Session& s, size_t size=0);
        
        // This is crap.
-       void write_lock()        { _lock.writer_lock(); _automation_lock.lock(); }
-       void write_unlock()      { _lock.writer_unlock(); _automation_lock.unlock(); }
-       void read_lock()   const { _lock.reader_lock(); /*_automation_lock.lock();*/ }
-       void read_unlock() const { _lock.reader_unlock(); /*_automation_lock.unlock();*/ }
+       void write_lock();
+       void write_unlock();
+       void read_lock()   const;
+       void read_unlock() const;
 
        void clear() { _notes.clear(); }
 
index 1b3ab50f919f9cca4318ce00fcfb6a827fe3b4c7..136d587550f2f447774229e8ae2a8acbd863d7f8 100644 (file)
@@ -280,8 +280,8 @@ MidiRingBuffer::read_contents(size_t size, Byte* buf)
 inline size_t
 MidiRingBuffer::write(double time, size_t size, const Byte* buf)
 {
-       //printf("MRB - write %#X %d %d with time %lf\n",
-       //              buf[0], buf[1], buf[2], time);
+       printf("MRB - write %#X %d %d with time %lf\n",
+                       buf[0], buf[1], buf[2], time);
 
        assert(size > 0);
 
index c9ead65d49a8a556c0c1f37bed8203a5b71a6023..c2c7bed056d85edddd615bccf97c048b3f4da2e2 100644 (file)
@@ -70,6 +70,7 @@ public:
 
        int set_state(const XMLNode& node);
 
+       void midi_panic(void);
        bool write_immediate_event(size_t size, const Byte* buf);
        
        struct MidiControl : public AutomationControl {
index 41c18358d378d0511fc29ad028ff4bccb2c5f148..5b5a38d645b9ce5580c324bec6dfad910453b7f8 100644 (file)
@@ -40,7 +40,7 @@ public:
        const Note& operator=(const Note& copy);
 
        inline bool operator==(const Note& other)
-       { return time() == other.time() && note() == other.note(); }
+       { return time() == other.time() && note() == other.note() && duration() == other.duration(); }
 
        inline double  time()     const { return _on_event.time(); }
        inline double  end_time() const { return _off_event.time(); }
index 33ac03f77e28065d28887d734fa48bb401a78769..a2380357d3730ab4df86b5ba12ac875bf18a8c6b 100644 (file)
@@ -754,6 +754,7 @@ class Session : public PBD::StatefulDestructible
 
        /* MIDI */
 
+       void midi_panic(void);
        int set_mtc_port (string port_tag);
        int set_mmc_port (string port_tag);
        int set_midi_port (string port_tag);
index 84fd06d0790039ab569b591ebd9b2614038c7a5a..0f13b05a09eba8d43d965502d01b3846af32a8d1 100644 (file)
@@ -527,7 +527,8 @@ AudioEngine::remove_session ()
                session = 0;
        }
        
-       remove_all_ports ();
+       //FIXME: Preliminary bugfix for  http://tracker.ardour.org/view.php?id=1985
+       //remove_all_ports ();
 }
 
 void
@@ -555,6 +556,7 @@ AudioEngine::register_port (DataType dtype, const string& portname, bool input,
 {
        Port* newport = 0;
 
+       cerr << "trying to register port with name " << portname << endl;
        try {
                if (dtype == DataType::AUDIO) {
                        newport = new AudioPort (portname, (input ? Port::IsInput : Port::IsOutput), publish, frames_per_cycle());
@@ -564,11 +566,17 @@ AudioEngine::register_port (DataType dtype, const string& portname, bool input,
                        throw unknown_type();
                }
 
+               cerr << "successfully got port " << portname << " with address " << newport << endl;
+
                RCUWriter<Ports> writer (ports);
                boost::shared_ptr<Ports> ps = writer.get_copy ();
+               cerr << "Address of ports list: " << ps << endl
+                    << "Ports set size before insert: " << ps->size() << endl;
                ps->insert (ps->begin(), newport);
+               cerr << "Ports set size after insert: " << ps->size() << endl;
                /* writer goes out of scope, forces update */
 
+
                return newport;
        }
 
@@ -608,22 +616,31 @@ AudioEngine::unregister_port (Port& port)
 {
        /* caller must hold process lock */
 
+       cerr << "about to unregister Port xx  x" << &port << "\n";
+
        if (!_running) { 
                /* probably happening when the engine has been halted by JACK,
                   in which case, there is nothing we can do here.
                   */
+               cerr << "not running\n";
                return 0;
        }
 
        {
+               cerr << "before getcopy\n";
                
                RCUWriter<Ports> writer (ports);
                boost::shared_ptr<Ports> ps = writer.get_copy ();
                
+               cerr << "Ports set size: " << ps.get()->size() << endl;
+
                for (Ports::iterator i = ps->begin(); i != ps->end(); ++i) {
+                       cerr << "before delete" << endl;
                        if ((*i) == &port) {
+                               cerr << "About to delete " << &port << endl;
                                delete *i;
                                ps->erase (i);
+                               cerr << "After erasing ports size: " << ps->size();
                                break;
                        }
                }
@@ -631,6 +648,7 @@ AudioEngine::unregister_port (Port& port)
                /* writer goes out of scope, forces update */
        }
                
+       cerr << "before remove_connections\n";
        remove_connections_for (port);
 
        return 0;
index a77a608fcdb6636d5f6921c662b9013b95bc10f0..4eae1eefc62023f8c367e61b4a644e0450d650d6 100644 (file)
@@ -117,7 +117,7 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
        for (size_t i=0; i < msrc.size(); ++i) {
                const MIDI::Event& ev = msrc[i];
                if (ev.time() >= offset && ev.time() < offset+nframes) {
-                       //cout << "MidiBuffer::read_from got event, " << ev.time() << endl;
+                       cout << "MidiBuffer::read_from got event, " << int(ev.type()) << " time: " << ev.time() << " buffer size: " << _size << endl;
                        push_back(ev);
                } else {
                        cerr << "MidiBuffer event out of range, " << ev.time() << endl;
@@ -148,7 +148,7 @@ MidiBuffer::push_back(const MIDI::Event& ev)
        _events[_size].set_buffer(write_loc, false);
        ++_size;
 
-       //cerr << "MidiBuffer: pushed, size = " << _size << endl;
+       cerr << "MidiBuffer: pushed, size = " << _size << endl;
 
        _silent = false;
 
@@ -177,7 +177,7 @@ MidiBuffer::push_back(const jack_midi_event_t& ev)
        _events[_size].set_buffer(write_loc, false);
        ++_size;
 
-       //cerr << "MidiBuffer: pushed, size = " << _size << endl;
+       cerr << "MidiBuffer: pushed, size = " << _size << endl;
 
        _silent = false;
 
@@ -207,7 +207,7 @@ MidiBuffer::reserve(double time, size_t size)
        _events[_size].set_buffer(write_loc, false);
        ++_size;
 
-       //cerr << "MidiBuffer: reserved, size = " << _size << endl;
+       cerr << "MidiBuffer: reserved, size = " << _size << endl;
 
        _silent = false;
 
index a49094df91a320bdca2e4c808bb9d5b107e79fe3..4aa229b45093b2301a78445bb315227114446ffa 100644 (file)
@@ -36,6 +36,34 @@ using namespace std;
 using namespace ARDOUR;
 
 
+void 
+MidiModel::write_lock()        
+{ 
+       _lock.writer_lock(); 
+       _automation_lock.lock(); 
+}
+
+void 
+MidiModel::write_unlock()      
+{ 
+       _lock.writer_unlock(); 
+       _automation_lock.unlock(); 
+}
+
+void 
+MidiModel::read_lock()   const 
+{ 
+       _lock.reader_lock(); 
+       /*_automation_lock.lock();*/ 
+}
+
+void 
+MidiModel::read_unlock() const 
+{ 
+       _lock.reader_unlock(); 
+       /*_automation_lock.unlock();*/ 
+}
+
 // Read iterator (const_iterator)
 
 MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
@@ -51,7 +79,7 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
        model.read_lock();
        
        _note_iter = model.notes().end();
-
+       // find first note which begins after t
        for (MidiModel::Notes::const_iterator i = model.notes().begin(); i != model.notes().end(); ++i) {
                if ((*i)->time() >= t) {
                        _note_iter = i;
@@ -95,6 +123,7 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
        if (_note_iter != model.notes().end()) {
                _event = MIDI::Event((*_note_iter)->on_event(), false);
                _active_notes.push(*_note_iter);
+               cerr << " new const iterator: size active notes: " << _active_notes.size() << " is empty: " << _active_notes.empty() << endl;
                ++_note_iter;
        }
 
@@ -106,19 +135,22 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
        if (_event.size() == 0) {
                //cerr << "Created MIDI iterator @ " << t << " is at end." << endl;
                _is_end = true;
+               if(_locked) {
                _model->read_unlock();
                _locked = false;
-       } /*else {
+               }
+       } else {
                printf("MIDI Iterator = %X @ %lf\n", _event.type(), _event.time());
-       }*/
+}
 }
 
 
 MidiModel::const_iterator::~const_iterator()
 {
-       if (_locked)
+       if (_locked) {
                _model->read_unlock();
 }
+}
                
 
 const MidiModel::const_iterator&
@@ -168,6 +200,7 @@ MidiModel::const_iterator::operator++()
                t = (*_note_iter)->time();
        }
        
+       cerr << " operator++ before test: size active notes: " << _active_notes.size() << " is empty: " << _active_notes.empty() << endl;
        // Use the next earliest note off iff it's earlier than the note on
        if (_model->note_mode() == Sustained && (! _active_notes.empty())) {
                if (type == NIL || _active_notes.top()->end_time() <= (*_note_iter)->time()) {
@@ -182,22 +215,20 @@ MidiModel::const_iterator::operator++()
                        type = CC;
 
        if (type == NOTE_ON) {
-               //cerr << "********** MIDI Iterator = note on" << endl;
+               cerr << "********** MIDI Iterator = note on" << endl;
                _event = MIDI::Event((*_note_iter)->on_event(), false);
                _active_notes.push(*_note_iter);
                ++_note_iter;
        } else if (type == NOTE_OFF) {
-               //cerr << "********** MIDI Iterator = note off" << endl;
+               cerr << "********** MIDI Iterator = note off" << endl;
                _event = MIDI::Event(_active_notes.top()->off_event(), false);
                _active_notes.pop();
        } else if (type == CC) {
-               //cerr << "********** MIDI Iterator = CC" << endl;
+               cerr << "********** MIDI Iterator = CC" << endl;
                _model->control_to_midi_event(_event, *_control_iter);
        } else {
-               //cerr << "********** MIDI Iterator = END" << endl;
+               cerr << "********** MIDI Iterator = END" << endl;
                _is_end = true;
-               _model->read_unlock();
-               _locked = false;
        }
 
        assert(_is_end || _event.size() > 0);
@@ -226,6 +257,7 @@ MidiModel::const_iterator::operator=(const const_iterator& other)
 
        _model = other._model;
        _event = other._event;
+       _active_notes = other._active_notes;
        _is_end = other._is_end;
        _locked = other._locked;
        _note_iter = other._note_iter;
@@ -234,9 +266,6 @@ MidiModel::const_iterator::operator=(const const_iterator& other)
        
        assert( ! _event.owns_buffer());
        
-       if (_locked)
-               _model->read_lock();
-
        return *this;
 }
 
@@ -265,16 +294,16 @@ MidiModel::MidiModel(Session& s, size_t size)
 size_t
 MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset) const
 {
-       //cerr << this << " MM::read @ " << start << " * " << nframes << " + " << stamp_offset << endl;
-       //cerr << this << " MM # notes: " << n_notes() << endl;
+       cerr << this << " MM::read @ " << start << " * " << nframes << " + " << stamp_offset << endl;
+       cerr << this << " MM # notes: " << n_notes() << endl;
 
        size_t read_events = 0;
 
        if (start != _next_read) {
                _read_iter = const_iterator(*this, (double)start);
-               //cerr << "Repositioning iterator from " << _next_read << " to " << start << endl;
+               cerr << "Repositioning iterator from " << _next_read << " to " << start << endl;
        } else {
-               //cerr << "Using cached iterator at " << _next_read << endl;
+               cerr << "Using cached iterator at " << _next_read << endl;
        }
 
        _next_read = start + nframes;
@@ -282,7 +311,11 @@ MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes
        while (_read_iter != end() && _read_iter->time() < start + nframes) {
                assert(_read_iter->size() > 0);
                dst.write(_read_iter->time() + stamp_offset, _read_iter->size(), _read_iter->buffer());
-               //cerr << this << " MM::read event @ " << _read_iter->time() << endl;
+               cerr << this << " MM::read event @ " << _read_iter->time()  
+                    << " type: " << hex << int(_read_iter->type()) << dec 
+                    << " note: " << int(_read_iter->note()) 
+                    << " velocity: " << int(_read_iter->velocity()) 
+                    << endl;
                ++_read_iter;
                ++read_events;
        }
index 794959328a2b7769b5821e39f6170786a2272402..eab87376ebb1c6a615eaceaf0841b8761ddf5f29 100644 (file)
@@ -105,8 +105,8 @@ MidiSource::midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nfra
 {
        Glib::Mutex::Lock lm (_lock);
        if (_model) {
-               /*const size_t n_events = */_model->read(dst, start, cnt, stamp_offset);
-               //cout << "Read " << n_events << " events from model." << endl;
+               const size_t n_events = _model->read(dst, start, cnt, stamp_offset);
+               cout << "Read " << n_events << " events from model." << endl;
                return cnt;
        } else {
                return read_unlocked (dst, start, cnt, stamp_offset);
index bf97b1910690b860c63915c5efafbd80aac4fe7c..cbd9c52bf6078551fce7929029ee8656022e211b 100644 (file)
@@ -697,6 +697,19 @@ MidiTrack::set_note_mode (NoteMode m)
        midi_diskstream()->set_note_mode(m);
 }
 
+void
+MidiTrack::midi_panic() 
+{
+       for(uint8_t channel = 0; channel <= 0xF; channel++) {
+               Byte ev[3] = { MIDI_CMD_CONTROL | channel, MIDI_CTL_SUSTAIN, 0 };
+               write_immediate_event(3,  ev);
+               ev[1] = MIDI_CTL_ALL_NOTES_OFF;
+               write_immediate_event(3,  ev);
+               ev[1] = MIDI_CTL_RESET_CONTROLLERS;
+               write_immediate_event(3,  ev);
+       }
+}
+
 /** \return true on success, false on failure (no buffer space left)
  */
 bool
index eca6d25ea84244159d3ef86b1a6a38a2a19ee903..b4938636a2b97ffd1d67e7048c6b9e9280695305 100644 (file)
@@ -26,6 +26,8 @@
 #include <fcntl.h>
 #include <poll.h>
 
+#include <boost/shared_ptr.hpp>
+
 #include <midi++/mmc.h>
 #include <midi++/port.h>
 #include <midi++/manager.h>
@@ -37,6 +39,7 @@
 #include <ardour/audioengine.h>
 #include <ardour/session.h>
 #include <ardour/audio_track.h>
+#include <ardour/midi_track.h>
 #include <ardour/audio_diskstream.h>
 #include <ardour/slave.h>
 #include <ardour/cycles.h>
@@ -52,6 +55,22 @@ using namespace MIDI;
 MachineControl::CommandSignature MMC_CommandSignature;
 MachineControl::ResponseSignature MMC_ResponseSignature;
 
+
+void
+Session::midi_panic()
+{
+       {
+               boost::shared_ptr<RouteList> r = routes.reader ();
+
+               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+                       MidiTrack *track = dynamic_cast<MidiTrack*>((*i).get());
+                       if (track != 0) {
+                               track->midi_panic();
+                       }
+               }
+       }
+}
+
 int
 Session::use_config_midi_ports ()
 {
index 720626e93642e5863d840f0d8e34161274007073..6df78f3b299257dd8513a8cb5c4258115d81c58c 100644 (file)
@@ -192,7 +192,7 @@ SMFSource::flush_header ()
 
        _fd = freopen(path().c_str(), "r+", _fd);
        assert(_fd);
-       fseek(_fd, 0, 0);
+       fseek(_fd, 0, SEEK_SET);
        write_chunk("MThd", 6, data);
        write_chunk_header("MTrk", _track_size); 
 
@@ -204,9 +204,8 @@ SMFSource::flush_header ()
 int
 SMFSource::flush_footer()
 {
-       seek_to_end();
+       fseek(_fd, 0, SEEK_END);
        write_footer();
-       seek_to_end();
 
        return 0;
 }
@@ -321,11 +320,11 @@ SMFSource::read_event(uint32_t* delta_t, uint32_t* size, Byte** buf) const
        if (event_size > 1)
                fread((*buf) + 1, 1, *size - 1, _fd);
 
-       /*printf("%s read event: delta = %u, size = %u, data = ", _name.c_str(), *delta_t, *size);
+       printf("%s read event: delta = %u, size = %u, data = ", _name.c_str(), *delta_t, *size);
        for (size_t i=0; i < *size; ++i) {
                printf("%X ", (*buf)[i]);
        }
-       printf("\n");*/
+       printf("\n");
        
        return (int)*size;
 }
@@ -334,7 +333,7 @@ SMFSource::read_event(uint32_t* delta_t, uint32_t* size, Byte** buf) const
 nframes_t
 SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const
 {
-       //cerr << "SMF " << name() << " read " << start << ", count=" << cnt << ", offset=" << stamp_offset << endl;
+       cerr << "SMF read_unlocked " << name() << " read " << start << ", count=" << cnt << ", offset=" << stamp_offset << endl;
 
        // 64 bits ought to be enough for anybody
        uint64_t time = 0; // in SMF ticks, 1 tick per _ppqn
@@ -349,7 +348,7 @@ SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, n
        size_t scratch_size = 0; // keep track of scratch to minimize reallocs
 
        // FIXME: don't seek to start and search every read (brutal!)
-       fseek(_fd, _header_size, 0);
+       fseek(_fd, _header_size, SEEK_SET);
        
        // FIXME: assumes tempo never changes after start
        const double frames_per_beat = _session.tempo_map().tempo_at(_timeline_position).frames_per_beat(