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