Saving of edited MIDI data to disk (on session save).
[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 <boost/utility.hpp>
26 #include <glibmm/thread.h>
27 #include <pbd/command.h>
28 #include <ardour/types.h>
29 #include <ardour/midi_buffer.h>
30 #include <ardour/midi_ring_buffer.h>
31
32 namespace ARDOUR {
33
34 class Session;
35 class MidiSource;
36
37
38 /** This is a slightly higher level (than MidiBuffer) model of MIDI note data.
39  * Currently it only represents note data, which is represented as complete
40  * note events (ie with a start time and a duration) rather than separate
41  * note on and off events (controller data is not here since it's represented
42  * as an AutomationList)
43  */
44 class MidiModel : public boost::noncopyable {
45 public:
46         struct Note {
47                 Note(double time=0, double dur=0, uint8_t note=0, uint8_t vel=0x40);
48                 Note(const Note& copy);
49                 
50                 const MidiModel::Note& operator=(const MidiModel::Note& copy);
51
52                 inline bool operator==(const Note& other)
53                         { return time() == other.time() && note() == other.note(); }
54
55                 inline double  time()     const { return _on_event.time(); }
56                 inline double  end_time() const { return _off_event.time(); }
57                 inline uint8_t note()     const { return _on_event.note(); }
58                 inline uint8_t velocity() const { return _on_event.velocity(); }
59                 inline double  duration() const { return _off_event.time() - _on_event.time(); }
60
61                 inline void set_time(double t)      { _off_event.time() = t + duration(); _on_event.time() = t; }
62                 inline void set_note(uint8_t n)     { _on_event.buffer()[1] = n; _off_event.buffer()[1] = n; }
63                 inline void set_velocity(uint8_t n) { _on_event.buffer()[2] = n; }
64                 inline void set_duration(double d)  { _off_event.time() = _on_event.time() + d; }
65
66                 inline MidiEvent& on_event()  { return _on_event; }
67                 inline MidiEvent& off_event() { return _off_event; }
68         
69                 inline const MidiEvent& on_event()  const { return _on_event; }
70                 inline const MidiEvent& off_event() const { return _off_event; }
71
72         private:
73                 // Event buffers are self-contained
74                 MidiEvent _on_event;
75                 MidiEvent _off_event;
76         };
77
78         MidiModel(Session& s, size_t size=0);
79
80         void clear() { _notes.clear(); }
81
82         NoteMode note_mode() const            { return _note_mode; }
83         void     set_note_mode(NoteMode mode) { _note_mode = mode; }
84
85         void start_write();
86         bool currently_writing() const { return _writing; }
87         void end_write(bool delete_stuck=false);
88
89         size_t read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset) const;
90
91         /** Resizes vector if necessary (NOT realtime safe) */
92         void append(const MidiBuffer& data);
93         
94         /** Resizes vector if necessary (NOT realtime safe) */
95         void append(double time, size_t size, const Byte* in_buffer);
96         
97         inline const Note& note_at(unsigned i) const { return _notes[i]; }
98
99         inline size_t n_notes() const { return _notes.size(); }
100
101         typedef std::vector<Note> Notes;
102         
103         inline static bool note_time_comparator (const Note& a, const Note& b) { 
104                 return a.time() < b.time();
105         }
106
107         struct LaterNoteEndComparator {
108                 typedef const Note* value_type;
109                 inline bool operator()(const Note* const a, const Note* const b) { 
110                         return a->end_time() > b->end_time();
111                 }
112         };
113
114         inline       Notes& notes()       { return _notes; }
115         inline const Notes& notes() const { return _notes; }
116         
117         /** Add/Remove notes.
118          * Technically all operations can be implemented as one of these.
119          */
120         class DeltaCommand : public Command
121         {
122         public:
123                 DeltaCommand (MidiModel& m, const std::string& name)
124                         : Command(name), _model(m), _name(name) {}
125                 //DeltaCommand (MidiModel&, const XMLNode& node);
126
127                 const std::string& name() const { return _name; }
128                 
129                 void operator()();
130                 void undo();
131                 
132                 /*int set_state (const XMLNode&);
133                 XMLNode& get_state ();*/
134
135                 void add(const Note& note);
136                 void remove(const Note& note);
137
138         private:
139                 MidiModel&      _model;
140                 std::string     _name;
141                 std::list<Note> _added_notes;
142                 std::list<Note> _removed_notes;
143         };
144
145         MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit");
146         void                     apply_command(Command* cmd);
147
148         bool edited() const { return _edited; }
149         bool write_to(boost::shared_ptr<MidiSource> source);
150
151         sigc::signal<void> ContentsChanged;
152         
153 private:
154         friend class DeltaCommand;
155         void add_note_unlocked(const Note& note);
156         void remove_note_unlocked(const Note& note);
157
158         bool is_sorted() const;
159
160         void append_note_on(double time, uint8_t note, uint8_t velocity);
161         void append_note_off(double time, uint8_t note);
162
163         Session& _session;
164
165         Glib::RWLock _lock;
166
167         Notes    _notes;
168         NoteMode _note_mode;
169         
170         typedef std::vector<size_t> WriteNotes;
171         WriteNotes _write_notes;
172         bool       _writing;
173         bool       _edited;
174         
175         // note state for read():
176         
177         typedef std::priority_queue<const Note*,std::vector<const Note*>,
178                         LaterNoteEndComparator> ActiveNotes;
179
180         mutable ActiveNotes _active_notes;
181 };
182
183 } /* namespace ARDOUR */
184
185 #endif /* __ardour_midi_model_h__ */
186