Fix displaying of notes in auto-created MIDI region when it's the first region in...
[ardour.git] / libs / ardour / ardour / midi_model.h
1 /*
2     Copyright (C) 2007 Paul Davis
3     Author: Dave Robillard
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #ifndef __ardour_midi_model_h__ 
22 #define __ardour_midi_model_h__
23
24 #include <queue>
25 #include <deque>
26 #include <utility>
27 #include <boost/utility.hpp>
28 #include <glibmm/thread.h>
29 #include <pbd/command.h>
30 #include <ardour/types.h>
31 #include <ardour/midi_buffer.h>
32 #include <ardour/midi_ring_buffer.h>
33 #include <ardour/automatable.h>
34 #include <ardour/note.h>
35
36 namespace ARDOUR {
37
38 class Session;
39 class MidiSource;
40         
41 //                                                                     x   ,  y
42 typedef std::pair<boost::shared_ptr<const AutomationList>, std::pair<double,double> >
43                 MidiControlIterator;
44
45
46 /** This is a slightly higher level (than MidiBuffer) model of MIDI note data.
47  * Currently it only represents note data, which is represented as complete
48  * note events (ie with a start time and a duration) rather than separate
49  * note on and off events (controller data is not here since it's represented
50  * as an AutomationList)
51  */
52 class MidiModel : public boost::noncopyable, public Automatable {
53 public:
54         MidiModel(Session& s, size_t size=0);
55         
56         // This is crap.
57         void write_lock()        { _lock.writer_lock(); _automation_lock.lock(); }
58         void write_unlock()      { _lock.writer_unlock(); _automation_lock.unlock(); }
59         void read_lock()   const { _lock.reader_lock(); /*_automation_lock.lock();*/ }
60         void read_unlock() const { _lock.reader_unlock(); /*_automation_lock.unlock();*/ }
61
62         void clear() { _notes.clear(); }
63
64         NoteMode note_mode() const            { return _note_mode; }
65         void     set_note_mode(NoteMode mode) { _note_mode = mode; }
66
67         void start_write();
68         bool writing() const { return _writing; }
69         void end_write(bool delete_stuck=false);
70
71         size_t read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset) const;
72
73         /** Resizes vector if necessary (NOT realtime safe) */
74         void append(const MidiEvent& ev);
75         
76         inline const boost::shared_ptr<const Note> note_at(unsigned i) const { return _notes[i]; }
77         inline const boost::shared_ptr<Note>       note_at(unsigned i)       { return _notes[i]; }
78
79         inline size_t n_notes() const { return _notes.size(); }
80         inline bool   empty()   const { return _notes.size() == 0 && _controls.size() == 0; }
81
82         /* FIXME: use better data structure */
83         typedef std::vector< boost::shared_ptr<Note> > Notes;
84         
85         inline static bool note_time_comparator (const boost::shared_ptr<const Note> a,
86                                                  const boost::shared_ptr<const Note> b) { 
87                 return a->time() < b->time();
88         }
89
90         struct LaterNoteEndComparator {
91                 typedef const Note* value_type;
92                 inline bool operator()(const boost::shared_ptr<const Note> a,
93                                        const boost::shared_ptr<const Note> b) const { 
94                         return a->end_time() > b->end_time();
95                 }
96         };
97
98         inline       Notes& notes()       { return _notes; }
99         inline const Notes& notes() const { return _notes; }
100         
101         /** Add/Remove notes.
102          * Technically all operations can be implemented as one of these.
103          */
104         class DeltaCommand : public Command
105         {
106         public:
107                 DeltaCommand (MidiModel& m, const std::string& name)
108                         : Command(name), _model(m), _name(name) {}
109                 //DeltaCommand (MidiModel&, const XMLNode& node);
110
111                 const std::string& name() const { return _name; }
112                 
113                 void operator()();
114                 void undo();
115                 
116                 /*int set_state (const XMLNode&);
117                 XMLNode& get_state ();*/
118
119                 void add(const boost::shared_ptr<Note> note);
120                 void remove(const boost::shared_ptr<Note> note);
121
122         private:
123                 MidiModel&                           _model;
124                 const std::string                    _name;
125                 std::list< boost::shared_ptr<Note> > _added_notes;
126                 std::list< boost::shared_ptr<Note> > _removed_notes;
127         };
128
129         MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit");
130         void                     apply_command(Command* cmd);
131
132         bool edited() const { return _edited; }
133         void set_edited(bool yn) { _edited = yn; }
134         bool write_to(boost::shared_ptr<MidiSource> source);
135                 
136         // MidiModel doesn't use the normal AutomationList serialisation code, as CC data is in the .mid
137         XMLNode& get_state();
138         int set_state(const XMLNode&) { return 0; }
139
140         sigc::signal<void> ContentsChanged;
141         
142         /** Read iterator */
143         class const_iterator {
144         public:
145                 const_iterator(const MidiModel& model, double t);
146                 ~const_iterator();
147
148                 inline bool locked() const { return _locked; }
149
150                 const MidiEvent& operator*()  const { return _event; }
151                 const MidiEvent* operator->() const { return &_event; }
152
153                 const const_iterator& operator++(); // prefix only
154                 bool operator==(const const_iterator& other) const;
155                 bool operator!=(const const_iterator& other) const { return ! operator==(other); }
156                 
157                 const_iterator& operator=(const const_iterator& other);
158
159         private:
160                 friend class MidiModel;
161
162                 const MidiModel* _model;
163                 MidiEvent        _event;
164
165                 typedef std::priority_queue<
166                                 boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
167                                 LaterNoteEndComparator>
168                         ActiveNotes;
169                 
170                 mutable ActiveNotes _active_notes;
171
172                 bool                                       _is_end;
173                 bool                                       _locked;
174                 Notes::const_iterator                      _note_iter;
175                 std::vector<MidiControlIterator>           _control_iters;
176                 std::vector<MidiControlIterator>::iterator _control_iter;
177         };
178         
179         const_iterator        begin() const { return const_iterator(*this, 0); }
180         const const_iterator& end()   const { return _end_iter; }
181         
182 private:
183         friend class DeltaCommand;
184         void add_note_unlocked(const boost::shared_ptr<Note> note);
185         void remove_note_unlocked(const boost::shared_ptr<const Note> note);
186
187         friend class const_iterator;
188         bool control_to_midi_event(MidiEvent& ev, const MidiControlIterator& iter) const;
189
190 #ifndef NDEBUG
191         bool is_sorted() const;
192 #endif
193
194         void append_note_on_unlocked(double time, uint8_t note, uint8_t velocity);
195         void append_note_off_unlocked(double time, uint8_t note);
196         void append_cc_unlocked(double time, uint8_t number, uint8_t value);
197
198         mutable Glib::RWLock _lock;
199
200         Notes    _notes;
201         NoteMode _note_mode;
202         
203         typedef std::vector<size_t> WriteNotes;
204         WriteNotes _write_notes;
205         bool       _writing;
206         bool       _edited;
207
208         const const_iterator _end_iter;
209
210         mutable nframes_t      _next_read;
211         mutable const_iterator _read_iter;
212
213         typedef std::priority_queue<
214                         boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
215                         LaterNoteEndComparator>
216                 ActiveNotes;
217 };
218
219 } /* namespace ARDOUR */
220
221 #endif /* __ardour_midi_model_h__ */
222