Support multiple readers for MIDI source/model
[ardour.git] / libs / ardour / midi_region.cc
1 /*
2     Copyright (C) 2000-2006 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id: midiregion.cc 746 2006-08-02 02:44:23Z drobilla $
19 */
20
21 #include <cmath>
22 #include <climits>
23 #include <cfloat>
24
25 #include <set>
26
27 #include <glibmm/threads.h>
28 #include <glibmm/fileutils.h>
29 #include <glibmm/miscutils.h>
30
31 #include "evoral/Beats.hpp"
32
33 #include "pbd/xml++.h"
34 #include "pbd/basename.h"
35
36 #include "ardour/automation_control.h"
37 #include "ardour/midi_model.h"
38 #include "ardour/midi_region.h"
39 #include "ardour/midi_ring_buffer.h"
40 #include "ardour/midi_source.h"
41 #include "ardour/region_factory.h"
42 #include "ardour/session.h"
43 #include "ardour/source_factory.h"
44 #include "ardour/tempo.h"
45 #include "ardour/types.h"
46
47 #include "pbd/i18n.h"
48 #include <locale.h>
49
50 using namespace std;
51 using namespace ARDOUR;
52 using namespace PBD;
53
54 namespace ARDOUR {
55         namespace Properties {
56                 PBD::PropertyDescriptor<double> start_beats;
57                 PBD::PropertyDescriptor<double> length_beats;
58         }
59 }
60
61 void
62 MidiRegion::make_property_quarks ()
63 {
64         Properties::start_beats.property_id = g_quark_from_static_string (X_("start-beats"));
65         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start-beats = %1\n", Properties::start_beats.property_id));
66         Properties::length_beats.property_id = g_quark_from_static_string (X_("length-beats"));
67         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length-beats = %1\n", Properties::length_beats.property_id));
68 }
69
70 void
71 MidiRegion::register_properties ()
72 {
73         add_property (_start_beats);
74         add_property (_length_beats);
75 }
76
77 /* Basic MidiRegion constructor (many channels) */
78 MidiRegion::MidiRegion (const SourceList& srcs)
79         : Region (srcs)
80         , _start_beats (Properties::start_beats, 0.0)
81         , _length_beats (Properties::length_beats, midi_source(0)->length_beats().to_double())
82 {
83         register_properties ();
84         midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this));
85         model_changed ();
86         assert(_name.val().find("/") == string::npos);
87         assert(_type == DataType::MIDI);
88 }
89
90 MidiRegion::MidiRegion (boost::shared_ptr<const MidiRegion> other)
91         : Region (other)
92         , _start_beats (Properties::start_beats, other->_start_beats)
93         , _length_beats (Properties::length_beats, other->_length_beats)
94 {
95         //update_length_beats ();
96         register_properties ();
97
98         assert(_name.val().find("/") == string::npos);
99         midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this));
100         model_changed ();
101 }
102
103 /** Create a new MidiRegion that is part of an existing one */
104 MidiRegion::MidiRegion (boost::shared_ptr<const MidiRegion> other, frameoffset_t offset, const int32_t sub_num)
105         : Region (other, offset, sub_num)
106         , _start_beats (Properties::start_beats, 0.0)
107         , _length_beats (Properties::length_beats, other->_length_beats)
108 {
109         _start_beats = (_session.tempo_map().exact_qn_at_frame (other->_position + offset, sub_num) - (other->pulse() * 4.0)) + other->_start_beats;
110
111         update_length_beats (sub_num);
112         register_properties ();
113
114         assert(_name.val().find("/") == string::npos);
115         midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this));
116         model_changed ();
117 }
118
119 MidiRegion::~MidiRegion ()
120 {
121 }
122
123 /** Export the MIDI data of the MidiRegion to a new MIDI file (SMF).
124  */
125 bool
126 MidiRegion::do_export (string path) const
127 {
128         boost::shared_ptr<MidiSource> newsrc;
129
130         /* caller must check for pre-existing file */
131         assert (!path.empty());
132         assert (!Glib::file_test (path, Glib::FILE_TEST_EXISTS));
133         newsrc = boost::dynamic_pointer_cast<MidiSource>(
134                 SourceFactory::createWritable(DataType::MIDI, _session,
135                                               path, false, _session.frame_rate()));
136
137         BeatsFramesConverter bfc (_session.tempo_map(), _position);
138         Evoral::Beats const bbegin = bfc.from (_start);
139         Evoral::Beats const bend = bfc.from (_start + _length);
140
141         {
142                 /* Lock our source since we'll be reading from it.  write_to() will
143                    take a lock on newsrc. */
144                 Source::Lock lm (midi_source(0)->mutex());
145                 if (midi_source(0)->export_write_to (lm, newsrc, bbegin, bend)) {
146                         return false;
147                 }
148         }
149
150         return true;
151 }
152
153
154 /** Create a new MidiRegion that has its own version of some/all of the Source used by another.
155  */
156 boost::shared_ptr<MidiRegion>
157 MidiRegion::clone (string path) const
158 {
159         boost::shared_ptr<MidiSource> newsrc;
160
161         /* caller must check for pre-existing file */
162         assert (!path.empty());
163         assert (!Glib::file_test (path, Glib::FILE_TEST_EXISTS));
164         newsrc = boost::dynamic_pointer_cast<MidiSource>(
165                 SourceFactory::createWritable(DataType::MIDI, _session,
166                                               path, false, _session.frame_rate()));
167         return clone (newsrc);
168 }
169
170 boost::shared_ptr<MidiRegion>
171 MidiRegion::clone (boost::shared_ptr<MidiSource> newsrc) const
172 {
173         BeatsFramesConverter bfc (_session.tempo_map(), _position);
174         Evoral::Beats const bbegin = bfc.from (_start);
175         Evoral::Beats const bend = bfc.from (_start + _length);
176
177         {
178                 /* Lock our source since we'll be reading from it.  write_to() will
179                    take a lock on newsrc. */
180                 Source::Lock lm (midi_source(0)->mutex());
181                 if (midi_source(0)->write_to (lm, newsrc, bbegin, bend)) {
182                         return boost::shared_ptr<MidiRegion> ();
183                 }
184         }
185
186         PropertyList plist;
187
188         plist.add (Properties::name, PBD::basename_nosuffix (newsrc->name()));
189         plist.add (Properties::whole_file, true);
190         plist.add (Properties::start, _start);
191         plist.add (Properties::start_beats, _start_beats);
192         plist.add (Properties::length, _length);
193         plist.add (Properties::beat, _beat);
194         plist.add (Properties::length_beats, _length_beats);
195         plist.add (Properties::layer, 0);
196
197         boost::shared_ptr<MidiRegion> ret (boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (newsrc, plist, true)));
198         ret->set_pulse (pulse());
199
200         return ret;
201 }
202
203 void
204 MidiRegion::post_set (const PropertyChange& pc)
205 {
206         Region::post_set (pc);
207
208         if (pc.contains (Properties::length) && !pc.contains (Properties::length_beats)) {
209                 /* we're called by Stateful::set_values() which sends a change
210                    only if the value is different from _current.
211                    session load means we can clobber length_beats here in error (not all properties differ from current),
212                    so disallow (this has been set from XML state anyway).
213                 */
214                 if (!_session.loading()) {
215                         /* update non-musically */
216                         update_length_beats (0);
217                 }
218         } else if (pc.contains (Properties::start) && !pc.contains (Properties::start_beats)) {
219                 set_start_beats_from_start_frames ();
220         }
221 }
222
223 void
224 MidiRegion::set_start_beats_from_start_frames ()
225 {
226         _start_beats = (pulse() * 4.0) - _session.tempo_map().quarter_note_at_frame (_position - _start);
227 }
228
229 void
230 MidiRegion::set_length_internal (framecnt_t len, const int32_t sub_num)
231 {
232         Region::set_length_internal (len, sub_num);
233         update_length_beats (sub_num);
234 }
235
236 void
237 MidiRegion::update_after_tempo_map_change (bool /* send */)
238 {
239         boost::shared_ptr<Playlist> pl (playlist());
240
241         if (!pl) {
242                 return;
243         }
244
245         const framepos_t old_pos = _position;
246         const framepos_t old_length = _length;
247         const framepos_t old_start = _start;
248
249         PropertyChange s_and_l;
250
251         if (position_lock_style() == AudioTime) {
252                 recompute_position_from_lock_style (0);
253
254                 /*
255                   set _start to new position in tempo map.
256
257                   The user probably expects the region contents to maintain audio position as the
258                   tempo changes, but AFAICT this requires modifying the src file to use
259                   SMPTE timestamps with the current disk read model (?).
260
261                   We could arguably use _start to set _start_beats here,
262                   resulting in viewport-like behaviour (the contents maintain
263                   their musical position while the region is stationary).
264
265                   For now, the musical position at the region start is retained, but subsequent events
266                   will maintain their beat distance according to the map.
267                 */
268                 _start = _session.tempo_map().frame_at_quarter_note (pulse() * 4.0)
269                         - _session.tempo_map().frame_at_quarter_note ((pulse() * 4.0) - start_beats());
270
271                 /* _length doesn't change for audio-locked regions. update length_beats to match. */
272                 _length_beats = _session.tempo_map().quarter_note_at_frame (_position + _length) - _session.tempo_map().quarter_note_at_frame (_position);
273
274                 s_and_l.add (Properties::start);
275                 s_and_l.add (Properties::length_beats);
276
277                 send_change  (s_and_l);
278                 return;
279         }
280
281         Region::update_after_tempo_map_change (false);
282
283         /* _start has now been updated. */
284         _length = _session.tempo_map().frame_at_pulse (pulse() + (_length_beats / 4.0)) - _position;
285
286         if (old_start != _start) {
287                 s_and_l.add (Properties::start);
288         }
289         if (old_length != _length) {
290                 s_and_l.add (Properties::length);
291         }
292         if (old_pos != _position) {
293                 s_and_l.add (Properties::position);
294         }
295
296         send_change (s_and_l);
297 }
298
299 void
300 MidiRegion::update_length_beats (const int32_t sub_num)
301 {
302         _length_beats = _session.tempo_map().exact_qn_at_frame (_position + _length, sub_num) - (pulse() * 4.0);
303 }
304
305 void
306 MidiRegion::set_position_internal (framepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
307 {
308         Region::set_position_internal (pos, allow_bbt_recompute, sub_num);
309
310         /* don't clobber _start _length and _length_beats if session loading.*/
311         if (_session.loading()) {
312                 return;
313         }
314
315         /* set _start to new position in tempo map */
316         _start = _session.tempo_map().frame_at_quarter_note (pulse() * 4.0)
317                 - _session.tempo_map().frame_at_quarter_note ((pulse() * 4.0) - start_beats());
318
319         /* in construction from src */
320         if (_length_beats == 0.0) {
321                 update_length_beats (sub_num);
322         }
323
324         if (position_lock_style() == AudioTime) {
325                 _length_beats = _session.tempo_map().quarter_note_at_frame (_position + _length) - _session.tempo_map().quarter_note_at_frame (_position);
326         } else {
327                 /* leave _length_beats alone, and change _length to reflect the state of things
328                    at the new position (tempo map may dictate a different number of frames).
329                 */
330                 Region::set_length_internal (_session.tempo_map().frame_at_quarter_note ((pulse() * 4.0) + length_beats()) - _position, sub_num);
331         }
332 }
333
334 framecnt_t
335 MidiRegion::read_at (Evoral::EventSink<framepos_t>& out,
336                      framepos_t                     position,
337                      framecnt_t                     dur,
338                      Evoral::Range<framepos_t>*     loop_range,
339                      MidiCursor&                    cursor,
340                      uint32_t                       chan_n,
341                      NoteMode                       mode,
342                      MidiStateTracker*              tracker,
343                      MidiChannelFilter*             filter) const
344 {
345         return _read_at (_sources, out, position, dur, loop_range, cursor, chan_n, mode, tracker, filter);
346 }
347
348 framecnt_t
349 MidiRegion::master_read_at (MidiRingBuffer<framepos_t>& out,
350                             framepos_t                  position,
351                             framecnt_t                  dur,
352                             Evoral::Range<framepos_t>*  loop_range,
353                             MidiCursor&                 cursor,
354                             uint32_t                    chan_n,
355                             NoteMode                    mode) const
356 {
357         return _read_at (_master_sources, out, position, dur, loop_range, cursor, chan_n, mode); /* no tracker */
358 }
359
360 framecnt_t
361 MidiRegion::_read_at (const SourceList&              /*srcs*/,
362                       Evoral::EventSink<framepos_t>& dst,
363                       framepos_t                     position,
364                       framecnt_t                     dur,
365                       Evoral::Range<framepos_t>*     loop_range,
366                       MidiCursor&                    cursor,
367                       uint32_t                       chan_n,
368                       NoteMode                       mode,
369                       MidiStateTracker*              tracker,
370                       MidiChannelFilter*             filter) const
371 {
372         frameoffset_t internal_offset = 0;
373         framecnt_t    to_read         = 0;
374
375         /* precondition: caller has verified that we cover the desired section */
376
377         assert(chan_n == 0);
378
379         if (muted()) {
380                 return 0; /* read nothing */
381         }
382
383         if (position < _position) {
384                 /* we are starting the read from before the start of the region */
385                 internal_offset = 0;
386                 dur -= _position - position;
387         } else {
388                 /* we are starting the read from after the start of the region */
389                 internal_offset = position - _position;
390         }
391
392         if (internal_offset >= _length) {
393                 return 0; /* read nothing */
394         }
395
396         if ((to_read = min (dur, _length - internal_offset)) == 0) {
397                 return 0; /* read nothing */
398         }
399
400         boost::shared_ptr<MidiSource> src = midi_source(chan_n);
401
402         Glib::Threads::Mutex::Lock lm(src->mutex());
403
404         src->set_note_mode(lm, mode);
405
406 #if 0
407         cerr << "MR " << name () << " read @ " << position << " + " << to_read
408              << " dur was " << dur
409              << " len " << _length
410              << " l-io " << (_length - internal_offset)
411              << " _position = " << _position
412              << " _start = " << _start
413              << " intoffset = " << internal_offset
414              << " pulse = " << pulse()
415              << " start_pulse = " << start_pulse()
416              << " start_beat = " << _start_beats
417              << endl;
418 #endif
419
420         /* This call reads events from a source and writes them to `dst' timed in session frames */
421
422         if (src->midi_read (
423                     lm, // source lock
424                     dst, // destination buffer
425                     _position - _start, // start position of the source in session frames
426                     _start + internal_offset, // where to start reading in the source
427                     to_read, // read duration in frames
428                     loop_range,
429                     cursor,
430                     tracker,
431                     filter,
432                     _filtered_parameters,
433                     pulse(),
434                     _start_beats
435                     ) != to_read) {
436                 return 0; /* "read nothing" */
437         }
438
439         return to_read;
440 }
441
442 XMLNode&
443 MidiRegion::state ()
444 {
445         return Region::state ();
446 }
447
448 int
449 MidiRegion::set_state (const XMLNode& node, int version)
450 {
451         int ret = Region::set_state (node, version);
452
453         if (ret == 0) {
454                 /* set length beats to the frame (non-musical) */
455                 if (position_lock_style() == AudioTime) {
456                         update_length_beats (0);
457                 }
458         }
459
460         return ret;
461 }
462
463 void
464 MidiRegion::recompute_at_end ()
465 {
466         /* our length has changed
467          * so what? stuck notes are dealt with via a note state tracker
468          */
469 }
470
471 void
472 MidiRegion::recompute_at_start ()
473 {
474         /* as above, but the shift was from the front
475          * maybe bump currently active note's note-ons up so they sound here?
476          * that could be undesireable in certain situations though.. maybe
477          * remove the note entirely, including it's note off?  something needs to
478          * be done to keep the played MIDI sane to avoid messing up voices of
479          * polyhonic things etc........
480          */
481 }
482
483 int
484 MidiRegion::separate_by_channel (ARDOUR::Session&, vector< boost::shared_ptr<Region> >&) const
485 {
486         // TODO
487         return -1;
488 }
489
490 boost::shared_ptr<Evoral::Control>
491 MidiRegion::control (const Evoral::Parameter& id, bool create)
492 {
493         return model()->control(id, create);
494 }
495
496 boost::shared_ptr<const Evoral::Control>
497 MidiRegion::control (const Evoral::Parameter& id) const
498 {
499         return model()->control(id);
500 }
501
502 boost::shared_ptr<MidiModel>
503 MidiRegion::model()
504 {
505         return midi_source()->model();
506 }
507
508 boost::shared_ptr<const MidiModel>
509 MidiRegion::model() const
510 {
511         return midi_source()->model();
512 }
513
514 boost::shared_ptr<MidiSource>
515 MidiRegion::midi_source (uint32_t n) const
516 {
517         // Guaranteed to succeed (use a static cast?)
518         return boost::dynamic_pointer_cast<MidiSource>(source(n));
519 }
520
521 /* don't use this. hopefully it will go away.
522    currently used by headless-chicken session utility.
523 */
524 void
525 MidiRegion::clobber_sources (boost::shared_ptr<MidiSource> s)
526 {
527        drop_sources();
528
529        _sources.push_back (s);
530        s->inc_use_count ();
531        _master_sources.push_back (s);
532        s->inc_use_count ();
533
534        s->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(s)));
535
536 }
537
538 void
539 MidiRegion::model_changed ()
540 {
541         if (!model()) {
542                 return;
543         }
544
545         /* build list of filtered Parameters, being those whose automation state is not `Play' */
546
547         _filtered_parameters.clear ();
548
549         Automatable::Controls const & c = model()->controls();
550
551         for (Automatable::Controls::const_iterator i = c.begin(); i != c.end(); ++i) {
552                 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
553                 assert (ac);
554                 if (ac->alist()->automation_state() != Play) {
555                         _filtered_parameters.insert (ac->parameter ());
556                 }
557         }
558
559         /* watch for changes to controls' AutoState */
560         midi_source()->AutomationStateChanged.connect_same_thread (
561                 _model_connection, boost::bind (&MidiRegion::model_automation_state_changed, this, _1)
562                 );
563 }
564
565 void
566 MidiRegion::model_automation_state_changed (Evoral::Parameter const & p)
567 {
568         /* Update our filtered parameters list after a change to a parameter's AutoState */
569
570         boost::shared_ptr<AutomationControl> ac = model()->automation_control (p);
571         if (!ac || ac->alist()->automation_state() == Play) {
572                 /* It should be "impossible" for ac to be NULL, but if it is, don't
573                    filter the parameter so events aren't lost. */
574                 _filtered_parameters.erase (p);
575         } else {
576                 _filtered_parameters.insert (p);
577         }
578
579         /* the source will have an iterator into the model, and that iterator will have been set up
580            for a given set of filtered_parameters, so now that we've changed that list we must invalidate
581            the iterator.
582         */
583         Glib::Threads::Mutex::Lock lm (midi_source(0)->mutex(), Glib::Threads::TRY_LOCK);
584         if (lm.locked()) {
585                 /* TODO: This is too aggressive, we need more fine-grained invalidation. */
586                 midi_source(0)->invalidate (lm);
587         }
588 }
589
590 /** This is called when a trim drag has resulted in a -ve _start time for this region.
591  *  Fix it up by adding some empty space to the source.
592  */
593 void
594 MidiRegion::fix_negative_start ()
595 {
596         BeatsFramesConverter c (_session.tempo_map(), _position);
597
598         model()->insert_silence_at_start (c.from (-_start));
599         _start = 0;
600         _start_beats = 0.0;
601 }
602
603 void
604 MidiRegion::set_start_internal (framecnt_t s, const int32_t sub_num)
605 {
606         Region::set_start_internal (s, sub_num);
607         if (position_lock_style() == AudioTime) {
608                 set_start_beats_from_start_frames ();
609         }
610 }
611
612 void
613 MidiRegion::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
614 {
615         if (locked()) {
616                 return;
617         }
618
619         PropertyChange what_changed;
620
621
622         /* Set position before length, otherwise for MIDI regions this bad thing happens:
623          * 1. we call set_length_internal; length in beats is computed using the region's current
624          *    (soon-to-be old) position
625          * 2. we call set_position_internal; position is set and length in frames re-computed using
626          *    length in beats from (1) but at the new position, which is wrong if the region
627          *    straddles a tempo/meter change.
628          */
629
630         if (_position != position) {
631
632                 const double pos_qn = _session.tempo_map().exact_qn_at_frame (position, sub_num);
633                 const double old_pos_qn = pulse() * 4.0;
634
635                 /* sets _pulse to new position.*/
636                 set_position_internal (position, true, sub_num);
637                 what_changed.add (Properties::position);
638
639                 double new_start_qn = start_beats() + (pos_qn - old_pos_qn);
640                 const framepos_t new_start = _session.tempo_map().frame_at_quarter_note (pos_qn)
641                         - _session.tempo_map().frame_at_quarter_note (pos_qn - new_start_qn);
642
643                 if (!verify_start_and_length (new_start, length)) {
644                         return;
645                 }
646
647                 _start_beats = new_start_qn;
648                 what_changed.add (Properties::start_beats);
649
650                 set_start_internal (new_start, sub_num);
651                 what_changed.add (Properties::start);
652         }
653
654         if (_length != length) {
655
656                 if (!verify_start_and_length (_start, length)) {
657                         return;
658                 }
659
660                 set_length_internal (length, sub_num);
661                 what_changed.add (Properties::length);
662                 what_changed.add (Properties::length_beats);
663         }
664
665         set_whole_file (false);
666
667         PropertyChange start_and_length;
668
669         start_and_length.add (Properties::start);
670         start_and_length.add (Properties::length);
671
672         if (what_changed.contains (start_and_length)) {
673                 first_edit ();
674         }
675
676         if (!what_changed.empty()) {
677                 send_change (what_changed);
678         }
679 }