7c67d40d759a4fb28d1a62868a9747753eca88bb
[ardour.git] / libs / ardour / midi_model.cc
1 /*
2     Copyright (C) 2007 Paul Davis 
3         Written by Dave Robillard, 2007
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 #include <iostream>
22 #include <algorithm>
23 #include <pbd/enumwriter.h>
24 #include <ardour/midi_model.h>
25 #include <ardour/midi_events.h>
26 #include <ardour/midi_source.h>
27 #include <ardour/types.h>
28 #include <ardour/session.h>
29
30 using namespace std;
31 using namespace ARDOUR;
32
33 // Note
34
35 MidiModel::Note::Note(double t, double d, uint8_t n, uint8_t v)
36         : _on_event(t, 3, NULL, true)
37         , _off_event(t + d, 3, NULL, true)
38 {
39         _on_event.buffer()[0] = MIDI_CMD_NOTE_ON;
40         _on_event.buffer()[1] = n;
41         _on_event.buffer()[2] = v;
42         
43         _off_event.buffer()[0] = MIDI_CMD_NOTE_OFF;
44         _off_event.buffer()[1] = n;
45         _off_event.buffer()[2] = 0x40;
46         
47         assert(time() == t);
48         assert(duration() == d);
49         assert(note() == n);
50         assert(velocity() == v);
51 }
52
53
54 MidiModel::Note::Note(const Note& copy)
55         : _on_event(copy._on_event, true)
56         , _off_event(copy._off_event, true)
57 {
58         /*
59         assert(copy._on_event.size == 3);
60         _on_event.buffer = _on_event_buffer;
61         memcpy(_on_event_buffer, copy._on_event_buffer, 3);
62         
63         assert(copy._off_event.size == 3);
64         _off_event.buffer = _off_event_buffer;
65         memcpy(_off_event_buffer, copy._off_event_buffer, 3);
66         */
67
68         assert(time() == copy.time());
69         assert(end_time() == copy.end_time());
70         assert(note() == copy.note());
71         assert(velocity() == copy.velocity());
72         assert(duration() == copy.duration());
73 }
74
75
76 const MidiModel::Note&
77 MidiModel::Note::operator=(const Note& copy)
78 {
79         _on_event = copy._on_event;
80         _off_event = copy._off_event;
81         /*_on_event.time = copy._on_event.time;
82         assert(copy._on_event.size == 3);
83         memcpy(_on_event_buffer, copy._on_event_buffer, 3);
84         
85         _off_event.time = copy._off_event.time;
86         assert(copy._off_event.size == 3);
87         memcpy(_off_event_buffer, copy._off_event_buffer, 3);
88         */
89         
90         assert(time() == copy.time());
91         assert(end_time() == copy.end_time());
92         assert(note() == copy.note());
93         assert(velocity() == copy.velocity());
94         assert(duration() == copy.duration());
95
96         return *this;
97 }
98
99 // MidiModel
100
101 MidiModel::MidiModel(Session& s, size_t size)
102         : Automatable(s, "midi model")
103         , _notes(size)
104         , _note_mode(Sustained)
105         , _writing(false)
106         , _edited(false)
107         , _active_notes(LaterNoteEndComparator())
108 {
109 }
110
111
112 /** Read events in frame range \a start .. \a start+cnt into \a dst,
113  * adding \a stamp_offset to each event's timestamp.
114  * \return number of events written to \a dst
115  */
116 size_t
117 MidiModel::read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset) const
118 {
119         size_t read_events = 0;
120
121         /* FIXME: cache last lookup value to avoid O(n) search every time */
122
123         if (_note_mode == Sustained) {
124
125                 for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) {
126
127                         while ( ! _active_notes.empty() ) {
128                                 const Note* const earliest_off = _active_notes.top();
129                                 const MidiEvent&  off_ev       = earliest_off->off_event();
130                                 if (off_ev.time() < start + nframes && off_ev.time() <= n->time()) {
131                                         dst.write(off_ev.time() + stamp_offset, off_ev.size(), off_ev.buffer());
132                                         _active_notes.pop();
133                                         ++read_events;
134                                 } else {
135                                         break;
136                                 }
137                         }
138
139                         if (n->time() >= start + nframes)
140                                 break;
141
142                         // Note on
143                         if (n->time() >= start) {
144                                 const MidiEvent& on_ev = n->on_event();
145                                 dst.write(on_ev.time() + stamp_offset, on_ev.size(), on_ev.buffer());
146                                 _active_notes.push(&(*n));
147                                 ++read_events;
148                         }
149
150                 }
151                         
152                 // Write any trailing note offs
153                 while ( ! _active_notes.empty() ) {
154                         const Note* const earliest_off = _active_notes.top();
155                         const MidiEvent&  off_ev       = earliest_off->off_event();
156                         if (off_ev.time() < start + nframes) {
157                                 dst.write(off_ev.time() + stamp_offset, off_ev.size(), off_ev.buffer());
158                                 _active_notes.pop();
159                                 ++read_events;
160                         } else {
161                                 break;
162                         }
163                 }
164
165         // Percussive
166         } else {
167                 for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) {
168                         // Note on
169                         if (n->time() >= start) {
170                                 if (n->time() < start + nframes) {
171                                         const MidiEvent& ev = n->on_event();
172                                         dst.write(ev.time() + stamp_offset, ev.size(), ev.buffer());
173                                         ++read_events;
174                                 } else {
175                                         break;
176                                 }
177                         }
178                 }
179         }
180
181         return read_events;
182 }
183
184
185 /** Begin a write of events to the model.
186  *
187  * If \a mode is Sustained, complete notes with duration are constructed as note
188  * on/off events are received.  Otherwise (Percussive), only note on events are
189  * stored; note off events are discarded entirely and all contained notes will
190  * have duration 0.
191  */
192 void
193 MidiModel::start_write()
194 {
195         //cerr << "MM " << this << " START WRITE, MODE = " << enum_2_string(_note_mode) << endl;
196         _lock.writer_lock();
197         _writing = true;
198         _write_notes.clear();
199 }
200
201
202
203 /** Finish a write of events to the model.
204  *
205  * If \a delete_stuck is true and the current mode is Sustained, note on events
206  * that were never resolved with a corresonding note off will be deleted.
207  * Otherwise they will remain as notes with duration 0.
208  */
209 void
210 MidiModel::end_write(bool delete_stuck)
211 {
212         assert(_writing);
213         
214         //cerr << "MM " << this << " END WRITE: " << _notes.size() << " NOTES\n";
215
216         if (_note_mode == Sustained && delete_stuck) {
217                 for (Notes::iterator n = _notes.begin(); n != _notes.end() ; ) {
218                         if (n->duration() == 0) {
219                                 cerr << "WARNING: Stuck note lost: " << n->note() << endl;
220                                 n = _notes.erase(n);
221                         } else {
222                                 ++n;
223                         }
224                 }
225         }
226
227         _write_notes.clear();
228         _writing = false;
229         _lock.writer_unlock();
230 }
231
232
233 /** Append contents of \a buf to model.  NOT realtime safe.
234  *
235  * Timestamps of events in \a buf are expected to be relative to
236  * the start of this model (t=0) and MUST be monotonically increasing
237  * and MUST be >= the latest event currently in the model.
238  *
239  * Events in buf are deep copied.
240  */
241 void
242 MidiModel::append(const MidiBuffer& buf)
243
244         assert(_writing);
245
246         for (MidiBuffer::const_iterator i = buf.begin(); i != buf.end(); ++i) {
247                 assert(_notes.empty() || (*i).time() >= _notes.back().time());
248                 append(*i);
249         }
250 }
251
252
253 /** Append \a in_event to model.  NOT realtime safe.
254  *
255  * Timestamps of events in \a buf are expected to be relative to
256  * the start of this model (t=0) and MUST be monotonically increasing
257  * and MUST be >= the latest event currently in the model.
258  */
259 void
260 MidiModel::append(const MidiEvent& ev)
261 {
262         assert(_notes.empty() || ev.time() >= _notes.back().time());
263         assert(_writing);
264
265         if (ev.is_note_on())
266                 append_note_on(ev.time(), ev.note(), ev.velocity());
267         else if (ev.is_note_off())
268                 append_note_off(ev.time(), ev.note());
269         else if (ev.is_cc())
270                 append_cc(ev.time(), ev.cc_number(), ev.cc_value());
271         else
272                 printf("MM Unknown event type %X\n", ev.type());
273 }
274
275
276 void
277 MidiModel::append_note_on(double time, uint8_t note_num, uint8_t velocity)
278 {
279         //cerr << "MidiModel " << this << " note " << (int)note_num << " on @ " << time << endl;
280
281         assert(_writing);
282         _notes.push_back(Note(time, 0, note_num, velocity));
283         if (_note_mode == Sustained) {
284                 //cerr << "MM Sustained: Appending active note on " << (unsigned)(uint8_t)note_num << endl;
285                 _write_notes.push_back(_notes.size() - 1);
286         } else {
287                 //cerr << "MM Percussive: NOT appending active note on" << endl;
288         }
289 }
290
291
292 void
293 MidiModel::append_note_off(double time, uint8_t note_num)
294 {
295         //cerr << "MidiModel " << this << " note " << (int)note_num << " off @ " << time << endl;
296
297         assert(_writing);
298         if (_note_mode == Percussive) {
299                 //cerr << "MM Ignoring note off (percussive mode)" << endl;
300                 return;
301         } else {
302                 //cerr << "MM Attempting to resolve note off " << (unsigned)(uint8_t)note_num << endl;
303         }
304
305         /* FIXME: make _write_notes fixed size (127 noted) for speed */
306         
307         /* FIXME: note off velocity for that one guy out there who actually has
308          * keys that send it */
309
310         for (WriteNotes::iterator n = _write_notes.begin(); n != _write_notes.end(); ++n) {
311                 Note& note = _notes[*n];
312                 //cerr << (unsigned)(uint8_t)note.note() << " ? " << (unsigned)note_num << endl;
313                 if (note.note() == note_num) {
314                         assert(time > note.time());
315                         note.set_duration(time - note.time());
316                         _write_notes.erase(n);
317                         //cerr << "MM resolved note, duration: " << note.duration() << endl;
318                         break;
319                 }
320         }
321 }
322
323
324 void
325 MidiModel::append_cc(double time, uint8_t number, uint8_t value)
326 {
327         Parameter param(MidiCCAutomation, number);
328
329         //cerr << "MidiModel " << this << " add CC " << (int)number << " = " << (int)value
330         //      << " @ " << time << endl;
331         
332         boost::shared_ptr<AutomationControl> control = Automatable::control(param, true);
333         control->list()->fast_simple_add(time, (double)value);
334 }
335
336
337 void
338 MidiModel::add_note_unlocked(const Note& note)
339 {
340         //cerr << "MidiModel " << this << " add note " << (int)note.note() << " @ " << note.time() << endl;
341         Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note, note_time_comparator);
342         _notes.insert(i, note);
343 }
344
345
346 void
347 MidiModel::remove_note_unlocked(const Note& note)
348 {
349         //cerr << "MidiModel " << this << " remove note " << (int)note.note() << " @ " << note.time() << endl;
350         Notes::iterator n = find(_notes.begin(), _notes.end(), note);
351         if (n != _notes.end())
352                 _notes.erase(n);
353 }
354
355 /** Slow!  for debugging only. */
356 #ifndef NDEBUG
357 bool
358 MidiModel::is_sorted() const
359 {
360         bool t = 0;
361         for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n)
362                 if (n->time() < t)
363                         return false;
364                 else
365                         t = n->time();
366
367         return true;
368 }
369 #endif
370
371 /** Start a new command.
372  *
373  * This has no side-effects on the model or Session, the returned command
374  * can be held on to for as long as the caller wishes, or discarded without
375  * formality, until apply_command is called and ownership is taken.
376  */
377 MidiModel::DeltaCommand*
378 MidiModel::new_delta_command(const string name)
379 {
380         DeltaCommand* cmd =  new DeltaCommand(*this, name);
381         return cmd;
382 }
383
384
385 /** Apply a command.
386  *
387  * Ownership of cmd is taken, it must not be deleted by the caller.
388  * The command will constitute one item on the undo stack.
389  */
390 void
391 MidiModel::apply_command(Command* cmd)
392 {
393         _session.begin_reversible_command(cmd->name());
394         (*cmd)();
395         assert(is_sorted());
396         _session.commit_reversible_command(cmd);
397         _edited = true;
398 }
399
400
401 // MidiEditCommand
402
403
404 void
405 MidiModel::DeltaCommand::add(const Note& note)
406 {
407         //cerr << "MEC: apply" << endl;
408
409         _removed_notes.remove(note);
410         _added_notes.push_back(note);
411 }
412
413
414 void
415 MidiModel::DeltaCommand::remove(const Note& note)
416 {
417         //cerr << "MEC: remove" << endl;
418
419         _added_notes.remove(note);
420         _removed_notes.push_back(note);
421 }
422
423                 
424 void 
425 MidiModel::DeltaCommand::operator()()
426 {
427         // This could be made much faster by using a priority_queue for added and
428         // removed notes (or sort here), and doing a single iteration over _model
429         
430         _model._lock.writer_lock();
431         
432         for (std::list<Note>::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
433                 _model.add_note_unlocked(*i);
434         
435         for (std::list<Note>::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
436                 _model.remove_note_unlocked(*i);
437         
438         _model._lock.writer_unlock();
439         
440         _model.ContentsChanged(); /* EMIT SIGNAL */
441 }
442
443
444 void
445 MidiModel::DeltaCommand::undo()
446 {
447         // This could be made much faster by using a priority_queue for added and
448         // removed notes (or sort here), and doing a single iteration over _model
449         
450         _model._lock.writer_lock();
451
452         for (std::list<Note>::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
453                 _model.remove_note_unlocked(*i);
454         
455         for (std::list<Note>::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
456                 _model.add_note_unlocked(*i);
457         
458         _model._lock.writer_unlock();
459         
460         _model.ContentsChanged(); /* EMIT SIGNAL */
461 }
462
463
464 bool
465 MidiModel::write_to(boost::shared_ptr<MidiSource> source)
466 {
467         //cerr << "Writing model to " << source->name() << endl;
468
469         /* This could be done using a temporary MidiRingBuffer and using
470          * MidiModel::read and MidiSource::write, but this is more efficient
471          * and doesn't require any buffer size assumptions (ie it's worth
472          * the code duplication).
473          *
474          * This is also different from read in that note off events are written
475          * regardless of the track mode.  This is so the user can switch a
476          * recorded track (with note durations from some instrument) to percussive,
477          * save, reload, then switch it back to sustained preserving the original
478          * note durations.
479          */
480
481         /* Percussive 
482         for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) {
483                 const MidiEvent& ev = n->on_event();
484                 source->append_event_unlocked(ev);
485         }*/
486
487         _lock.reader_lock();
488
489         LaterNoteEndComparator cmp;
490         ActiveNotes active_notes(cmp);
491                 
492         // Foreach note
493         for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) {
494
495                 // Write any pending note offs earlier than this note on
496                 while ( ! active_notes.empty() ) {
497                         const Note* const earliest_off = active_notes.top();
498                         const MidiEvent&  off_ev       = earliest_off->off_event();
499                         if (off_ev.time() <= n->time()) {
500                                 source->append_event_unlocked(off_ev);
501                                 active_notes.pop();
502                         } else {
503                                 break;
504                         }
505                 }
506
507                 // Write this note on
508                 source->append_event_unlocked(n->on_event());
509                 if (n->duration() > 0)
510                         active_notes.push(&(*n));
511         }
512                 
513         // Write any trailing note offs
514         while ( ! active_notes.empty() ) {
515                 source->append_event_unlocked(active_notes.top()->off_event());
516                 active_notes.pop();
517         }
518
519         _edited = false;
520         
521         _lock.reader_unlock();
522
523         return true;
524 }
525
526 XMLNode&
527 MidiModel::get_state()
528 {
529         XMLNode *node = new XMLNode("MidiModel");
530         return *node;
531 }
532