use new syntax for connecting to backend signals that enforces explicit connection...
[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_sequence.h"
34 #include "ardour/types.h"
35 #include "evoral/Note.hpp"
36 #include "evoral/Sequence.hpp"
37
38 namespace ARDOUR {
39
40 class Session;
41 class MidiSource;
42
43 /** This is a higher level (than MidiBuffer) model of MIDI data, with separate
44  * representations for notes (instead of just unassociated note on/off events)
45  * and controller data.  Controller data is represented as part of the
46  * Automatable base (i.e. in a map of AutomationList, keyed by Parameter).
47  * Because of this MIDI controllers and automatable controllers/widgets/etc
48  * are easily interchangeable.
49  */
50 class MidiModel : public AutomatableSequence<Evoral::MusicalTime> {
51 public:
52         typedef double TimeType;
53
54         MidiModel(MidiSource* s, size_t size=0);
55
56         NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); }
57         void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); };
58
59         /** Add/Remove notes.
60          * Technically all note operations can be implemented as one of these, but
61          * a custom command can be more efficient.
62          */
63         class DeltaCommand : public Command {
64         public:
65                 DeltaCommand (boost::shared_ptr<MidiModel> m, const std::string& name);
66                 DeltaCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node);
67
68                 const std::string& name() const { return _name; }
69
70                 void operator()();
71                 void undo();
72
73                 int set_state (const XMLNode&, int version);
74                 XMLNode& get_state ();
75
76                 void add(const boost::shared_ptr< Evoral::Note<TimeType> > note);
77                 void remove(const boost::shared_ptr< Evoral::Note<TimeType> > note);
78
79         private:
80                 XMLNode &marshal_note(const boost::shared_ptr< Evoral::Note<TimeType> > note);
81                 boost::shared_ptr< Evoral::Note<TimeType> > unmarshal_note(XMLNode *xml_note);
82
83                 boost::shared_ptr<MidiModel> _model;
84                 const std::string            _name;
85
86                 typedef std::list< boost::shared_ptr< Evoral::Note<TimeType> > > NoteList;
87
88                 NoteList _added_notes;
89                 NoteList _removed_notes;
90         };
91
92
93         /** Change note properties.
94          * More efficient than DeltaCommand and has the important property that
95          * it leaves the objects in the MidiModel (Notes) the same, thus
96          * enabling selection and other state to persist across command
97          * do/undo/redo.
98          */
99         class DiffCommand : public Command {
100         public:
101                 enum Property {
102                         NoteNumber,
103                         Velocity,
104                         StartTime,
105                         Length,
106                         Channel
107                 };
108
109                 DiffCommand (boost::shared_ptr<MidiModel> m, const std::string& name);
110                 DiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node);
111
112                 const std::string& name() const { return _name; }
113
114                 void operator()();
115                 void undo();
116
117                 int set_state (const XMLNode&, int version);
118                 XMLNode& get_state ();
119
120                 void change (const boost::shared_ptr<Evoral::Note<TimeType> > note,
121                                 Property prop, uint8_t new_value);
122                 void change (const boost::shared_ptr<Evoral::Note<TimeType> > note,
123                                 Property prop, TimeType new_time);
124
125         private:
126                 boost::shared_ptr<MidiModel> _model;
127                 const std::string            _name;
128
129                 struct NotePropertyChange {
130                         DiffCommand::Property property;
131                         boost::shared_ptr< Evoral::Note<TimeType> > note;
132                         union {
133                                 uint8_t  old_value;
134                                 TimeType old_time;
135                         };
136                         union {
137                                 uint8_t  new_value;
138                                 TimeType new_time;
139                         };
140                 };
141
142                 typedef std::list<NotePropertyChange> ChangeList;
143                 ChangeList _changes;
144
145                 XMLNode &marshal_change(const NotePropertyChange&);
146                 NotePropertyChange unmarshal_change(XMLNode *xml_note);
147         };
148
149         MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit");
150         MidiModel::DiffCommand*  new_diff_command(const std::string name="midi edit");
151         void                     apply_command(Session& session, Command* cmd);
152         void                     apply_command_as_subcommand(Session& session, Command* cmd);
153
154         bool write_to(boost::shared_ptr<MidiSource> source);
155
156         // MidiModel doesn't use the normal AutomationList serialisation code
157         // since controller data is stored in the .mid
158         XMLNode& get_state();
159         int set_state(const XMLNode&) { return 0; }
160
161         PBD::Signal0<void> ContentsChanged;
162
163         const MidiSource* midi_source() const { return _midi_source; }
164         void set_midi_source(MidiSource* source) { _midi_source = source; }
165
166         boost::shared_ptr<Evoral::Note<TimeType> > find_note (boost::shared_ptr<Evoral::Note<TimeType> >);
167
168 private:
169         struct WriteLockImpl : public AutomatableSequence<Evoral::MusicalTime>::WriteLockImpl {
170                 WriteLockImpl(Glib::Mutex::Lock* source_lock, Glib::RWLock& s, Glib::Mutex& c)
171                         : AutomatableSequence<Evoral::MusicalTime>::WriteLockImpl(s, c)
172                         , source_lock(source_lock)
173                 {}
174                 ~WriteLockImpl() {
175                         delete source_lock;
176                 }
177                 Glib::Mutex::Lock* source_lock;
178         };
179
180 public:
181         virtual WriteLock edit_lock();
182         virtual WriteLock write_lock();
183
184 private:
185         friend class DeltaCommand;
186
187         // We cannot use a boost::shared_ptr here to avoid a retain cycle
188         MidiSource* _midi_source;
189 };
190
191 } /* namespace ARDOUR */
192
193 #endif /* __ardour_midi_model_h__ */
194