b6cdac186497122db898499ab0e84213a2f727f2
[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  * FIXME: Currently this stores event time stamps in frames.  This is almost
53  * certainly wrong, or at least wrong most of the time (if we add an option).
54  * This reeeeeeally needs fixing, but frame time runs deep in Ardour...
55  */
56 class MidiModel : public boost::noncopyable, public Automatable {
57 public:
58         MidiModel(MidiSource *s,  size_t size=0);
59         
60         // This is crap.
61         void write_lock();
62         void write_unlock();
63         void read_lock()   const;
64         void read_unlock() const;
65
66         void clear() { _notes.clear(); }
67
68         NoteMode note_mode() const            { return _note_mode; }
69         void     set_note_mode(NoteMode mode) { _note_mode = mode; }
70
71         void start_write();
72         bool writing() const { return _writing; }
73         void end_write(bool delete_stuck=false);
74
75         size_t read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset, nframes_t negative_stamp_offset) const;
76
77         /** Resizes vector if necessary (NOT realtime safe) */
78         void append(const MIDI::Event& ev);
79         
80         inline const boost::shared_ptr<const Note> note_at(unsigned i) const { return _notes[i]; }
81         inline const boost::shared_ptr<Note>       note_at(unsigned i)       { return _notes[i]; }
82
83         inline size_t n_notes() const { return _notes.size(); }
84         inline bool   empty()   const { return _notes.size() == 0 && _controls.size() == 0; }
85
86         /* FIXME: use better data structure */
87         typedef std::vector< boost::shared_ptr<Note> > Notes;
88         
89         inline static bool note_time_comparator (const boost::shared_ptr<const Note> a,
90                                                  const boost::shared_ptr<const Note> b) { 
91                 return a->time() < b->time();
92         }
93
94         struct LaterNoteEndComparator {
95                 typedef const Note* value_type;
96                 inline bool operator()(const boost::shared_ptr<const Note> a,
97                                        const boost::shared_ptr<const Note> b) const { 
98                         return a->end_time() > b->end_time();
99                 }
100         };
101
102         inline       Notes& notes()       { return _notes; }
103         inline const Notes& notes() const { return _notes; }
104         
105         /** Add/Remove notes.
106          * Technically all operations can be implemented as one of these.
107          */
108         class DeltaCommand : public Command
109         {
110         public:
111                 DeltaCommand (boost::shared_ptr<MidiModel> m, const std::string& name);
112                 DeltaCommand (boost::shared_ptr<MidiModel>,   const XMLNode& node);
113
114                 const std::string& name() const { return _name; }
115                 
116                 void operator()();
117                 void undo();
118                 
119                 int set_state (const XMLNode&);
120                 XMLNode& get_state ();
121
122                 void add(const boost::shared_ptr<Note> note);
123                 void remove(const boost::shared_ptr<Note> note);
124
125         private:
126                 XMLNode &marshal_note(const boost::shared_ptr<Note> note);
127                 boost::shared_ptr<Note> unmarshal_note(XMLNode *xml_note);
128                 
129                 boost::shared_ptr<MidiModel>         _model;
130                 const std::string                    _name;
131                 
132                 typedef std::list< boost::shared_ptr<Note> > NoteList;
133                 
134                 NoteList _added_notes;
135                 NoteList _removed_notes;
136         };
137
138         MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit");
139         void                     apply_command(Command* cmd);
140
141         bool edited() const { return _edited; }
142         void set_edited(bool yn) { _edited = yn; }
143         bool write_to(boost::shared_ptr<MidiSource> source);
144                 
145         // MidiModel doesn't use the normal AutomationList serialisation code, as CC data is in the .mid
146         XMLNode& get_state();
147         int set_state(const XMLNode&) { return 0; }
148
149         sigc::signal<void> ContentsChanged;
150         
151         /** Read iterator */
152         class const_iterator {
153         public:
154                 const_iterator(const MidiModel& model, double t);
155                 ~const_iterator();
156
157                 inline bool locked() const { return _locked; }
158
159                 const MIDI::Event& operator*()  const { return _event; }
160                 const MIDI::Event* operator->() const { return &_event; }
161
162                 const const_iterator& operator++(); // prefix only
163                 bool operator==(const const_iterator& other) const;
164                 bool operator!=(const const_iterator& other) const { return ! operator==(other); }
165                 
166                 const_iterator& operator=(const const_iterator& other);
167
168         private:
169                 friend class MidiModel;
170
171                 const MidiModel* _model;
172                 MIDI::Event      _event;
173
174                 typedef std::priority_queue<
175                                 boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
176                                 LaterNoteEndComparator>
177                         ActiveNotes;
178                 
179                 mutable ActiveNotes _active_notes;
180
181                 bool                                       _is_end;
182                 bool                                       _locked;
183                 Notes::const_iterator                      _note_iter;
184                 std::vector<MidiControlIterator>           _control_iters;
185                 std::vector<MidiControlIterator>::iterator _control_iter;
186         };
187         
188         const_iterator        begin() const { return const_iterator(*this, 0); }
189         const const_iterator& end()   const { return _end_iter; }
190         
191         const MidiSource *midi_source() const { return _midi_source; }
192         void set_midi_source(MidiSource *source) { _midi_source = source; } 
193         
194 private:
195         friend class DeltaCommand;
196         void add_note_unlocked(const boost::shared_ptr<Note> note);
197         void remove_note_unlocked(const boost::shared_ptr<const Note> note);
198
199         friend class const_iterator;
200         bool control_to_midi_event(MIDI::Event& ev, const MidiControlIterator& iter) const;
201
202 #ifndef NDEBUG
203         bool is_sorted() const;
204 #endif
205
206         void append_note_on_unlocked(uint8_t chan, double time, uint8_t note, uint8_t velocity);
207         void append_note_off_unlocked(uint8_t chan, double time, uint8_t note);
208         void append_cc_unlocked(uint8_t chan, double time, uint8_t number, uint8_t value);
209
210         mutable Glib::RWLock _lock;
211
212         Notes    _notes;
213         NoteMode _note_mode;
214         
215         typedef std::vector<size_t> WriteNotes;
216         WriteNotes _write_notes[16];
217         bool       _writing;
218         bool       _edited;
219
220         const const_iterator _end_iter;
221
222         mutable nframes_t      _next_read;
223         mutable const_iterator _read_iter;
224
225         typedef std::priority_queue<
226                         boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
227                         LaterNoteEndComparator>
228                 ActiveNotes;
229         
230         // We cannot use a boost::shared_ptr here to avoid a retain cycle
231         MidiSource *_midi_source;
232 };
233
234 } /* namespace ARDOUR */
235
236 #endif /* __ardour_midi_model_h__ */
237