add new sigc++2 directory
[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 #include <ardour/types.h>
36
37 namespace ARDOUR {
38
39 class Session;
40 class MidiSource;
41         
42 /**
43  * This class keeps track of the current x and y for a control
44  */
45 class MidiControlIterator {
46 public:
47         boost::shared_ptr<const AutomationList> automation_list;
48         double x;
49         double y;
50         
51         MidiControlIterator(boost::shared_ptr<const AutomationList> a_list,
52                         double a_x,
53                         double a_y)
54                 : automation_list(a_list)
55                 , x(a_x)
56                 , y(a_y)
57         {}
58 };
59
60
61 /** This is a higher level (than MidiBuffer) model of MIDI data, with separate
62  * representations for notes (instead of just unassociated note on/off events)
63  * and controller data.  Controller data is represented as part of the
64  * Automatable base (i.e. in a map of AutomationList, keyed by Parameter).
65  */
66 class MidiModel : public boost::noncopyable, public Automatable {
67 public:
68         MidiModel(MidiSource* s, size_t size=0);
69         
70         void write_lock();
71         void write_unlock();
72
73         void read_lock()   const;
74         void read_unlock() const;
75
76         void clear();
77
78         NoteMode note_mode() const            { return _note_mode; }
79         void     set_note_mode(NoteMode mode) { _note_mode = mode; }
80
81         void start_write();
82         bool writing() const { return _writing; }
83         void end_write(bool delete_stuck=false);
84
85         size_t read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset, nframes_t negative_stamp_offset) const;
86
87         /** Resizes vector if necessary (NOT realtime safe) */
88         void append(const MIDI::Event& ev);
89         
90         inline const boost::shared_ptr<const Note> note_at(unsigned i) const { return _notes[i]; }
91         inline const boost::shared_ptr<Note>       note_at(unsigned i)       { return _notes[i]; }
92
93         inline size_t n_notes() const { return _notes.size(); }
94         inline bool   empty()   const { return _notes.size() == 0 && _controls.size() == 0; }
95
96         inline static bool note_time_comparator (const boost::shared_ptr<const Note> a,
97                                                  const boost::shared_ptr<const Note> b) { 
98                 return a->time() < b->time();
99         }
100
101         struct LaterNoteEndComparator {
102                 typedef const Note* value_type;
103                 inline bool operator()(const boost::shared_ptr<const Note> a,
104                                        const boost::shared_ptr<const Note> b) const { 
105                         return a->end_time() > b->end_time();
106                 }
107         };
108
109         typedef std::vector< boost::shared_ptr<Note> > Notes;
110         inline       Notes& notes()       { return _notes; }
111         inline const Notes& notes() const { return _notes; }
112
113         /** Add/Remove notes.
114          * Technically all operations can be implemented as one of these.
115          */
116         class DeltaCommand : public Command
117         {
118         public:
119                 DeltaCommand (boost::shared_ptr<MidiModel> m, const std::string& name);
120                 DeltaCommand (boost::shared_ptr<MidiModel>,   const XMLNode& node);
121
122                 const std::string& name() const { return _name; }
123                 
124                 void operator()();
125                 void undo();
126                 
127                 int set_state (const XMLNode&);
128                 XMLNode& get_state ();
129
130                 void add(const boost::shared_ptr<Note> note);
131                 void remove(const boost::shared_ptr<Note> note);
132
133         private:
134                 XMLNode &marshal_note(const boost::shared_ptr<Note> note);
135                 boost::shared_ptr<Note> unmarshal_note(XMLNode *xml_note);
136                 
137                 boost::shared_ptr<MidiModel>         _model;
138                 const std::string                    _name;
139                 
140                 typedef std::list< boost::shared_ptr<Note> > NoteList;
141                 
142                 NoteList _added_notes;
143                 NoteList _removed_notes;
144         };
145
146         MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit");
147         void                     apply_command(Command* cmd);
148
149         bool edited() const { return _edited; }
150         void set_edited(bool yn) { _edited = yn; }
151         bool write_to(boost::shared_ptr<MidiSource> source);
152                 
153         // MidiModel doesn't use the normal AutomationList serialisation code
154         // since controller data is stored in the .mid
155         XMLNode& get_state();
156         int set_state(const XMLNode&) { return 0; }
157
158         sigc::signal<void> ContentsChanged;
159         
160         /** Read iterator */
161         class const_iterator {
162         public:
163                 const_iterator(const MidiModel& model, double t);
164                 ~const_iterator();
165
166                 inline bool locked() const { return _locked; }
167
168                 const MIDI::Event& operator*()  const { return *_event;  }
169                 const boost::shared_ptr<MIDI::Event> operator->() const  { return _event; }
170                 const boost::shared_ptr<MIDI::Event> get_event_pointer() { return _event; }
171
172                 const const_iterator& operator++(); // prefix only
173                 bool operator==(const const_iterator& other) const;
174                 bool operator!=(const const_iterator& other) const { return ! operator==(other); }
175                 
176                 const_iterator& operator=(const const_iterator& other);
177
178         private:
179                 friend class MidiModel;
180
181                 const MidiModel*               _model;
182                 boost::shared_ptr<MIDI::Event> _event;
183
184                 typedef std::priority_queue<
185                                 boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
186                                 LaterNoteEndComparator>
187                         ActiveNotes;
188                 
189                 mutable ActiveNotes _active_notes;
190
191                 bool                                       _is_end;
192                 bool                                       _locked;
193                 Notes::const_iterator                      _note_iter;
194                 std::vector<MidiControlIterator>           _control_iters;
195                 std::vector<MidiControlIterator>::iterator _control_iter;
196         };
197         
198         const_iterator        begin() const { return const_iterator(*this, 0); }
199         const const_iterator& end()   const { return _end_iter; }
200         
201         const MidiSource* midi_source() const { return _midi_source; }
202         void set_midi_source(MidiSource* source) { _midi_source = source; } 
203         bool control_to_midi_event(boost::shared_ptr<MIDI::Event>& ev, const MidiControlIterator& iter) const;
204         
205 private:
206         friend class DeltaCommand;
207         void add_note_unlocked(const boost::shared_ptr<Note> note);
208         void remove_note_unlocked(const boost::shared_ptr<const Note> note);
209
210         friend class const_iterator;
211
212 #ifndef NDEBUG
213         bool is_sorted() const;
214 #endif
215
216         void append_note_on_unlocked(uint8_t chan, double time, uint8_t note, uint8_t velocity);
217         void append_note_off_unlocked(uint8_t chan, double time, uint8_t note);
218         void append_automation_event_unlocked(AutomationType type, uint8_t chan, double time, uint8_t first_byte, uint8_t second_byte);
219         void append_pgm_change_unlocked(uint8_t chan, double time, uint8_t number); 
220
221         mutable Glib::RWLock _lock;
222
223         Notes _notes;
224         
225         NoteMode _note_mode;
226         
227         typedef std::vector<size_t> WriteNotes;
228         WriteNotes _write_notes[16];
229         bool       _writing;
230         bool       _edited;
231         
232         typedef std::vector< boost::shared_ptr<const ARDOUR::AutomationList> > AutomationLists;
233         AutomationLists _dirty_automations;
234
235         const const_iterator _end_iter;
236
237         mutable nframes_t      _next_read;
238         mutable const_iterator _read_iter;
239
240         typedef std::priority_queue<
241                         boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
242                         LaterNoteEndComparator>
243                 ActiveNotes;
244         
245         // We cannot use a boost::shared_ptr here to avoid a retain cycle
246         MidiSource* _midi_source;
247 };
248
249 } /* namespace ARDOUR */
250
251 #endif /* __ardour_midi_model_h__ */
252