Per-region MIDI CC "automation".
authorDavid Robillard <d@drobilla.net>
Fri, 17 Aug 2007 17:25:20 +0000 (17:25 +0000)
committerDavid Robillard <d@drobilla.net>
Fri, 17 Aug 2007 17:25:20 +0000 (17:25 +0000)
Extremely broken in several ways.
This commit brought to you by the letters D, R, and my need to switch machines.

git-svn-id: svn://localhost/ardour2/trunk@2323 d708f5d6-7413-0410-9779-e7cbd77b26cf

14 files changed:
gtk2_ardour/SConscript
gtk2_ardour/automation_line.cc
gtk2_ardour/automation_region_view.cc [new file with mode: 0644]
gtk2_ardour/automation_region_view.h [new file with mode: 0644]
gtk2_ardour/midi_region_view.cc
gtk2_ardour/midi_region_view.h
gtk2_ardour/midi_time_axis.cc
gtk2_ardour/region_view.cc
gtk2_ardour/route_time_axis.cc
gtk2_ardour/route_time_axis.h
libs/ardour/ardour/automatable.h
libs/ardour/ardour/automation_event.h
libs/ardour/ardour/midi_model.h
libs/ardour/midi_model.cc

index 06ecf2d0f281aa1b9e5672497d431b9f22583c0a..ee80002f13bf35a1516e431a691b80725b613aaa 100644 (file)
@@ -100,6 +100,7 @@ control_point.cc
 automation_line.cc
 automation_time_axis.cc
 automation_controller.cc
+automation_region_view.cc
 midi_port_dialog.cc
 midi_time_axis.cc
 midi_streamview.cc
index d9d1b827f9ccf7f08f5c67b17e9f3a243f7dc03d..d5805aa3afd12563262df437d684425ac7a6440e 100644 (file)
@@ -55,7 +55,7 @@ using namespace PBD;
 using namespace Editing;
 using namespace Gnome; // for Canvas
 
-AutomationLine::AutomationLine (const string & name, TimeAxisView& tv, ArdourCanvas::Group& parent, boost::shared_ptr<AutomationList> al)
+AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanvas::Group& parent, boost::shared_ptr<AutomationList> al)
        : trackview (tv),
          _name (name),
          alist (al),
@@ -353,7 +353,7 @@ AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr)
        mr.xval = (nframes_t) floor (cp.get_x());
        mr.yval = 1.0 - ( (cp.get_y() - _y_position) / _height);
 
-        /* if xval has not changed, set it directly from the model to avoid rounding errors */
+       /* if xval has not changed, set it directly from the model to avoid rounding errors */
 
        if (mr.xval == trackview.editor.frame_to_unit((*cp.model())->when)) {
                mr.xval = (nframes_t) (*cp.model())->when;
@@ -879,7 +879,7 @@ AutomationLine::remove_point (ControlPoint& cp)
        model_representation (cp, mr);
 
        trackview.editor.current_session()->begin_reversible_command (_("remove control point"));
-        XMLNode &before = alist->get_state();
+       XMLNode &before = alist->get_state();
 
        alist->erase (mr.start, mr.end);
 
@@ -1044,7 +1044,7 @@ AutomationLine::list_changed ()
 void
 AutomationLine::reset_callback (const AutomationList& events)
 {
-        ALPoints tmp_points;
+       ALPoints tmp_points;
        uint32_t npoints = events.size();
 
        if (npoints == 0) {
@@ -1086,7 +1086,7 @@ void
 AutomationLine::clear ()
 {
        /* parent must create command */
-        XMLNode &before = get_state();
+       XMLNode &before = get_state();
        alist->clear();
        trackview.editor.current_session()->add_command (new MementoCommand<AutomationLine>(*this, &before, &get_state()));
        trackview.editor.current_session()->commit_reversible_command ();
diff --git a/gtk2_ardour/automation_region_view.cc b/gtk2_ardour/automation_region_view.cc
new file mode 100644 (file)
index 0000000..885a42b
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+    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 "automation_region_view.h"
+
+AutomationRegionView::AutomationRegionView(ArdourCanvas::Group*                      parent,
+                                           AutomationTimeAxisView&                   time_axis,
+                                           boost::shared_ptr<ARDOUR::Region>         region,
+                                           boost::shared_ptr<ARDOUR::AutomationList> list,
+                                           double                                    spu,
+                                           Gdk::Color&                               basic_color)
+       : RegionView(parent, time_axis, region, spu, basic_color)
+       , _line(list->parameter().to_string(), time_axis, *group, list)
+{ 
+       _line.set_colors();
+       _line.show();
+       _line.show_all_control_points();
+       
+       group->raise_to_top ();
+}
+
+
+void
+AutomationRegionView::set_y_position_and_height (double y, double h)
+{
+       RegionView::set_y_position_and_height(y, h - 1);
+
+       _line.set_y_position_and_height ((uint32_t)y, (uint32_t) rint (h - NAME_HIGHLIGHT_SIZE));
+}
+
+
+void
+AutomationRegionView::entered()
+{
+       _line.track_entered();
+}
+
+
+void
+AutomationRegionView::exited()
+{
+       _line.track_exited();
+}
+
diff --git a/gtk2_ardour/automation_region_view.h b/gtk2_ardour/automation_region_view.h
new file mode 100644 (file)
index 0000000..fc152f6
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+    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_automation_region_view_h__
+#define __gtk_ardour_automation_region_view_h__
+
+#include <ardour/diskstream.h>
+#include <ardour/types.h>
+
+#include "region_view.h"
+#include "automation_time_axis.h"
+#include "automation_line.h"
+#include "enums.h"
+#include "canvas.h"
+
+namespace ARDOUR {
+       class AutomationList;
+};
+
+class AutomationTimeAxisView;
+
+class AutomationRegionView : public RegionView
+{
+public:
+       AutomationRegionView(ArdourCanvas::Group*,
+                            AutomationTimeAxisView&,
+                            boost::shared_ptr<ARDOUR::Region>,
+                            boost::shared_ptr<ARDOUR::AutomationList>,
+                            double initial_samples_per_unit,
+                            Gdk::Color& basic_color);
+
+       ~AutomationRegionView() {}
+       
+       // We are a ghost.  Meta ghosts?  Crazy talk.
+       virtual GhostRegion* add_ghost(AutomationTimeAxisView&) { return NULL; }
+
+protected:
+       void set_y_position_and_height (double y, double h);
+       void entered();
+       void exited();
+
+private:
+       AutomationLine _line;
+};
+
+#endif /* __gtk_ardour_automation_region_view_h__ */
index c51cd314a2be02dc38092ca704769570d62d523e..b12f8ef4d90b19e4525f962282f376ab10afab1c 100644 (file)
@@ -45,6 +45,8 @@
 #include "public_editor.h"
 #include "ghostregion.h"
 #include "midi_time_axis.h"
+#include "automation_time_axis.h"
+#include "automation_region_view.h"
 #include "utils.h"
 #include "midi_util.h"
 #include "gui_thread.h"
@@ -58,8 +60,7 @@ using namespace PBD;
 using namespace Editing;
 using namespace ArdourCanvas;
 
-MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr<MidiRegion> r, double spu,
-                                 Gdk::Color& basic_color)
+MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color)
        : RegionView (parent, tv, r, spu, basic_color)
        , _default_note_length(0.0)
        , _active_notes(0)
@@ -71,8 +72,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
        _note_group->raise_to_top();
 }
 
-MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr<MidiRegion> r, double spu, 
-                                 Gdk::Color& basic_color, TimeAxisViewItem::Visibility visibility)
+MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color, TimeAxisViewItem::Visibility visibility)
        : RegionView (parent, tv, r, spu, basic_color, visibility)
        , _default_note_length(0.0)
        , _active_notes(0)
@@ -399,15 +399,47 @@ MidiRegionView::redisplay_model()
                return;
        
        if (_model) {
-       
+
                clear_events();
-       
                begin_write();
+               
+               _model->read_lock();
 
                for (size_t i=0; i < _model->n_notes(); ++i)
                        add_note(_model->note_at(i));
 
                end_write();
+
+               for (Automatable::Controls::const_iterator i = _model->controls().begin();
+                               i != _model->controls().end(); ++i) {
+
+                       assert(i->second);
+
+                       boost::shared_ptr<AutomationTimeAxisView> at
+                               = midi_view()->automation_child(i->second->parameter());
+                       if (!at)
+                               continue;
+
+                       Gdk::Color col = midi_stream_view()->get_region_color();
+                               
+                       boost::shared_ptr<AutomationRegionView> arv;
+
+                       {
+                               Glib::Mutex::Lock list_lock (i->second->list()->lock());
+
+                               arv = boost::shared_ptr<AutomationRegionView>(
+                                               new AutomationRegionView(at->canvas_display,
+                                                       *at.get(), _region, i->second->list(),
+                                                       midi_stream_view()->get_samples_per_unit(), col));
+
+                               _automation_children.insert(std::make_pair(i->second->parameter(), arv));
+                       }
+
+                       arv->init(col, true);
+               }
+               
+               _model->read_unlock();
+
        } else {
                cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
        }
index 22196846c50cfe46971bd31bacfe64bcd86d6a91..be05556ce0d298d4a22cefad0dcef0b22c2e13e2 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2001-2006 Paul Davis 
+    Copyright (C) 2001-2007 Paul Davis 
 
     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
@@ -46,6 +46,7 @@ namespace ARDOUR {
 class MidiTimeAxisView;
 class GhostRegion;
 class AutomationTimeAxisView;
+class AutomationRegionView;
 
 class MidiRegionView : public RegionView
 {
@@ -177,6 +178,9 @@ class MidiRegionView : public RegionView
        ArdourCanvas::CanvasNote**                  _active_notes;
        ArdourCanvas::Group*                        _note_group;
        ARDOUR::MidiModel::DeltaCommand*            _delta_command;
+
+       typedef std::map<const ARDOUR::Parameter, boost::shared_ptr<AutomationRegionView> > AutomationChildren;
+       AutomationChildren _automation_children;
                
        MouseState _mouse_state;
        int _pressed_button;
index 9c2808e07c36b54118fd5164db74e883e334244c..e728a7c0f619589caf94dc7c031770168d414759 100644 (file)
@@ -81,6 +81,7 @@ using namespace Editing;
 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session& sess, boost::shared_ptr<Route> rt, Canvas& canvas)
        : AxisView(sess) // FIXME: won't compile without this, why??
        , RouteTimeAxisView(ed, sess, rt, canvas)
+       , _note_mode(Sustained)
        , _note_mode_item(NULL)
        , _percussion_mode_item(NULL)
 {
index 27ae6ca7bf7dd3f1ceefa5bd94970c9d010163ed..70e82d7d12a2f7807b24708c882c4afcceb0e429 100644 (file)
@@ -58,11 +58,11 @@ static const int32_t sync_mark_width = 9;
 
 sigc::signal<void,RegionView*> RegionView::RegionViewGoingAway;
 
-RegionView::RegionView (ArdourCanvas::Group* parent, 
-                        TimeAxisView&        tv,
+RegionView::RegionView (ArdourCanvas::Group*              parent, 
+                        TimeAxisView&                     tv,
                         boost::shared_ptr<ARDOUR::Region> r,
-                        double               spu,
-                        Gdk::Color&          basic_color)
+                        double                            spu,
+                        Gdk::Color&                       basic_color)
        : TimeAxisViewItem (r->name(), *parent, tv, spu, basic_color, r->position(), r->length(),
                            TimeAxisViewItem::Visibility (TimeAxisViewItem::ShowNameText|
                                                          TimeAxisViewItem::ShowNameHighlight|
index dbfb8b029badcb98cd88cc83549e1a96d7eb0d63..020b682f0a81502f26ff8e777d47b96971e1d7fd 100644 (file)
@@ -1998,3 +1998,15 @@ RouteTimeAxisView::set_layer_display (LayerDisplay d)
 {
        _view->set_layer_display (d);
 }
+       
+
+boost::shared_ptr<AutomationTimeAxisView>
+RouteTimeAxisView::automation_child(ARDOUR::Parameter param)
+{
+       AutomationTracks::iterator i = _automation_tracks.find(param);
+       if (i != _automation_tracks.end())
+               return i->second->track;
+       else
+               return boost::shared_ptr<AutomationTimeAxisView>();
+}
+
index cd92f473222ac8e852cec1aae399a857b6e30e78..1331831f3984b2aa93c7f348d8fdd478e2911592 100644 (file)
@@ -95,9 +95,24 @@ public:
        void clear_playlist ();
        
        void build_playlist_menu (Gtk::Menu *);
+       
+       /* This is a bit nasty to expose :/ */
+       struct RouteAutomationNode {
+               ARDOUR::Parameter                         param;
+           Gtk::CheckMenuItem*                       menu_item;
+               boost::shared_ptr<AutomationTimeAxisView> track;
+           
+               RouteAutomationNode (ARDOUR::Parameter par, Gtk::CheckMenuItem* mi, boost::shared_ptr<AutomationTimeAxisView> tr)
+                   : param (par), menu_item (mi), track (tr) {}
+       };
 
        virtual void create_automation_child (ARDOUR::Parameter param, bool show) = 0;
        
+       typedef map<ARDOUR::Parameter, RouteAutomationNode*> AutomationTracks;
+       AutomationTracks automation_tracks() { return _automation_tracks; }
+
+       boost::shared_ptr<AutomationTimeAxisView> automation_child(ARDOUR::Parameter param);
+       
        string              name() const;
        StreamView*         view() const { return _view; }
        ARDOUR::RouteGroup* edit_group() const;
@@ -105,18 +120,9 @@ public:
 
 protected:
        friend class StreamView;
-       
-       struct RouteAutomationNode {
-               ARDOUR::Parameter                           param;
-           Gtk::CheckMenuItem*                       menu_item;
-               boost::shared_ptr<AutomationTimeAxisView> track;
-           
-               RouteAutomationNode (ARDOUR::Parameter par, Gtk::CheckMenuItem* mi, boost::shared_ptr<AutomationTimeAxisView> tr)
-                   : param (par), menu_item (mi), track (tr) {}
-       };
 
        struct ProcessorAutomationNode {
-               ARDOUR::Parameter                           what;
+               ARDOUR::Parameter                         what;
            Gtk::CheckMenuItem*                       menu_item;
                boost::shared_ptr<AutomationTimeAxisView> view;
            RouteTimeAxisView&                        parent;
@@ -268,7 +274,6 @@ protected:
        // Set from XML so context menu automation buttons can be correctly initialized
        set<ARDOUR::Parameter> _show_automation;
 
-       typedef map<ARDOUR::Parameter, RouteAutomationNode*> AutomationTracks;
        AutomationTracks _automation_tracks;
 
        sigc::connection modified_connection;
index 0af996f6c084f3ef3dc27499db33ff7f3faa1167..574d7af12990addbe680e19fc43c985bea3836aa 100644 (file)
@@ -52,7 +52,8 @@ public:
        boost::shared_ptr<AutomationControl> control_factory(boost::shared_ptr<AutomationList> list);
        
        typedef std::map<Parameter,boost::shared_ptr<AutomationControl> > Controls;
-       Controls controls() { return _controls; }
+       Controls& controls() { return _controls; }
+       const Controls& controls() const { return _controls; }
 
        virtual void add_control(boost::shared_ptr<AutomationControl>);
 
@@ -79,6 +80,8 @@ public:
        const std::set<Parameter>& what_can_be_automated() const { return _can_automate_list; }
 
        void mark_automation_visible(Parameter, bool);
+       
+       Glib::Mutex& automation_lock() const { return _automation_lock; }
 
 protected:
 
index 7532ede60398cba016b24f384fb5093d76a437f2..1675dbc822271ec39a9466b61f1ebb49099d7035 100644 (file)
@@ -170,7 +170,7 @@ class AutomationList : public PBD::StatefulDestructible
                Glib::Mutex::Lock lm (_lock);
                (obj.*method)(*this);
        }
-
+       
        sigc::signal<void> StateChanged;
 
        XMLNode& get_state(void); 
index f688c0fe0938c2f0fb71b913824cafd22998843c..27a11fa9fccc746a3929180c90e0b3359bc38b6d 100644 (file)
@@ -77,6 +77,12 @@ 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()    { _lock.reader_lock(); _automation_lock.lock(); }
+       void read_unlock()  { _lock.reader_unlock(); _automation_lock.unlock(); }
 
        void clear() { _notes.clear(); }
 
@@ -167,9 +173,9 @@ private:
        bool is_sorted() const;
 #endif
 
-       void append_note_on(double time, uint8_t note, uint8_t velocity);
-       void append_note_off(double time, uint8_t note);
-       void append_cc(double time, uint8_t number, uint8_t value);
+       void append_note_on_unlocked(double time, uint8_t note, uint8_t velocity);
+       void append_note_off_unlocked(double time, uint8_t note);
+       void append_cc_unlocked(double time, uint8_t number, uint8_t value);
 
        Glib::RWLock _lock;
 
index 7c67d40d759a4fb28d1a62868a9747753eca88bb..7c80ed9a3b56548fee316a957e0162513f77b7f1 100644 (file)
@@ -193,9 +193,10 @@ void
 MidiModel::start_write()
 {
        //cerr << "MM " << this << " START WRITE, MODE = " << enum_2_string(_note_mode) << endl;
-       _lock.writer_lock();
+       write_lock();
        _writing = true;
        _write_notes.clear();
+       write_unlock();
 }
 
 
@@ -209,6 +210,7 @@ MidiModel::start_write()
 void
 MidiModel::end_write(bool delete_stuck)
 {
+       write_lock();
        assert(_writing);
        
        //cerr << "MM " << this << " END WRITE: " << _notes.size() << " NOTES\n";
@@ -226,7 +228,7 @@ MidiModel::end_write(bool delete_stuck)
 
        _write_notes.clear();
        _writing = false;
-       _lock.writer_unlock();
+       write_unlock();
 }
 
 
@@ -241,12 +243,16 @@ MidiModel::end_write(bool delete_stuck)
 void
 MidiModel::append(const MidiBuffer& buf)
 { 
+       write_lock();
+
        assert(_writing);
 
        for (MidiBuffer::const_iterator i = buf.begin(); i != buf.end(); ++i) {
                assert(_notes.empty() || (*i).time() >= _notes.back().time());
                append(*i);
        }
+       
+       write_unlock();
 }
 
 
@@ -259,22 +265,26 @@ MidiModel::append(const MidiBuffer& buf)
 void
 MidiModel::append(const MidiEvent& ev)
 {
+       write_lock();
+
        assert(_notes.empty() || ev.time() >= _notes.back().time());
        assert(_writing);
 
        if (ev.is_note_on())
-               append_note_on(ev.time(), ev.note(), ev.velocity());
+               append_note_on_unlocked(ev.time(), ev.note(), ev.velocity());
        else if (ev.is_note_off())
-               append_note_off(ev.time(), ev.note());
+               append_note_off_unlocked(ev.time(), ev.note());
        else if (ev.is_cc())
-               append_cc(ev.time(), ev.cc_number(), ev.cc_value());
+               append_cc_unlocked(ev.time(), ev.cc_number(), ev.cc_value());
        else
                printf("MM Unknown event type %X\n", ev.type());
+       
+       write_unlock();
 }
 
 
 void
-MidiModel::append_note_on(double time, uint8_t note_num, uint8_t velocity)
+MidiModel::append_note_on_unlocked(double time, uint8_t note_num, uint8_t velocity)
 {
        //cerr << "MidiModel " << this << " note " << (int)note_num << " on @ " << time << endl;
 
@@ -290,7 +300,7 @@ MidiModel::append_note_on(double time, uint8_t note_num, uint8_t velocity)
 
 
 void
-MidiModel::append_note_off(double time, uint8_t note_num)
+MidiModel::append_note_off_unlocked(double time, uint8_t note_num)
 {
        //cerr << "MidiModel " << this << " note " << (int)note_num << " off @ " << time << endl;
 
@@ -322,7 +332,7 @@ MidiModel::append_note_off(double time, uint8_t note_num)
 
 
 void
-MidiModel::append_cc(double time, uint8_t number, uint8_t value)
+MidiModel::append_cc_unlocked(double time, uint8_t number, uint8_t value)
 {
        Parameter param(MidiCCAutomation, number);
 
@@ -427,7 +437,7 @@ MidiModel::DeltaCommand::operator()()
        // This could be made much faster by using a priority_queue for added and
        // removed notes (or sort here), and doing a single iteration over _model
        
-       _model._lock.writer_lock();
+       _model.write_lock();
        
        for (std::list<Note>::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
                _model.add_note_unlocked(*i);
@@ -435,7 +445,7 @@ MidiModel::DeltaCommand::operator()()
        for (std::list<Note>::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
                _model.remove_note_unlocked(*i);
        
-       _model._lock.writer_unlock();
+       _model.write_unlock();
        
        _model.ContentsChanged(); /* EMIT SIGNAL */
 }
@@ -447,7 +457,7 @@ MidiModel::DeltaCommand::undo()
        // This could be made much faster by using a priority_queue for added and
        // removed notes (or sort here), and doing a single iteration over _model
        
-       _model._lock.writer_lock();
+       _model.write_lock();
 
        for (std::list<Note>::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
                _model.remove_note_unlocked(*i);
@@ -455,7 +465,7 @@ MidiModel::DeltaCommand::undo()
        for (std::list<Note>::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
                _model.add_note_unlocked(*i);
        
-       _model._lock.writer_unlock();
+       _model.write_unlock();
        
        _model.ContentsChanged(); /* EMIT SIGNAL */
 }
@@ -484,7 +494,7 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
                source->append_event_unlocked(ev);
        }*/
 
-       _lock.reader_lock();
+       read_lock();
 
        LaterNoteEndComparator cmp;
        ActiveNotes active_notes(cmp);
@@ -518,7 +528,7 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
 
        _edited = false;
        
-       _lock.reader_unlock();
+       read_unlock();
 
        return true;
 }