don't mix framepos with quarter notes when calculating _start frames.
[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                      uint32_t                       chan_n,
340                      NoteMode                       mode,
341                      MidiStateTracker*              tracker,
342                      MidiChannelFilter*             filter) const
343 {
344         return _read_at (_sources, out, position, dur, loop_range, chan_n, mode, tracker, filter);
345 }
346
347 framecnt_t
348 MidiRegion::master_read_at (MidiRingBuffer<framepos_t>& out,
349                             framepos_t                  position,
350                             framecnt_t                  dur,
351                             Evoral::Range<framepos_t>*  loop_range,
352                             uint32_t                    chan_n,
353                             NoteMode                    mode) const
354 {
355         return _read_at (_master_sources, out, position, dur, loop_range, chan_n, mode); /* no tracker */
356 }
357
358 framecnt_t
359 MidiRegion::_read_at (const SourceList&              /*srcs*/,
360                       Evoral::EventSink<framepos_t>& dst,
361                       framepos_t                     position,
362                       framecnt_t                     dur,
363                       Evoral::Range<framepos_t>*     loop_range,
364                       uint32_t                       chan_n,
365                       NoteMode                       mode,
366                       MidiStateTracker*              tracker,
367                       MidiChannelFilter*             filter) const
368 {
369         frameoffset_t internal_offset = 0;
370         framecnt_t    to_read         = 0;
371
372         /* precondition: caller has verified that we cover the desired section */
373
374         assert(chan_n == 0);
375
376         if (muted()) {
377                 return 0; /* read nothing */
378         }
379
380         if (position < _position) {
381                 /* we are starting the read from before the start of the region */
382                 internal_offset = 0;
383                 dur -= _position - position;
384         } else {
385                 /* we are starting the read from after the start of the region */
386                 internal_offset = position - _position;
387         }
388
389         if (internal_offset >= _length) {
390                 return 0; /* read nothing */
391         }
392
393         if ((to_read = min (dur, _length - internal_offset)) == 0) {
394                 return 0; /* read nothing */
395         }
396
397         boost::shared_ptr<MidiSource> src = midi_source(chan_n);
398
399         Glib::Threads::Mutex::Lock lm(src->mutex());
400
401         src->set_note_mode(lm, mode);
402
403 #if 0
404         cerr << "MR " << name () << " read @ " << position << " + " << to_read
405              << " dur was " << dur
406              << " len " << _length
407              << " l-io " << (_length - internal_offset)
408              << " _position = " << _position
409              << " _start = " << _start
410              << " intoffset = " << internal_offset
411              << " pulse = " << pulse()
412              << " start_pulse = " << start_pulse()
413              << " start_beat = " << _start_beats
414              << endl;
415 #endif
416
417         /* This call reads events from a source and writes them to `dst' timed in session frames */
418
419         if (src->midi_read (
420                     lm, // source lock
421                     dst, // destination buffer
422                     _position - _start, // start position of the source in session frames
423                     _start + internal_offset, // where to start reading in the source
424                     to_read, // read duration in frames
425                     loop_range,
426                     tracker,
427                     filter,
428                     _filtered_parameters,
429                     pulse(),
430                     _start_beats
431                     ) != to_read) {
432                 return 0; /* "read nothing" */
433         }
434
435         return to_read;
436 }
437
438 XMLNode&
439 MidiRegion::state ()
440 {
441         return Region::state ();
442 }
443
444 int
445 MidiRegion::set_state (const XMLNode& node, int version)
446 {
447         int ret = Region::set_state (node, version);
448
449         if (ret == 0) {
450                 /* set length beats to the frame (non-musical) */
451                 if (position_lock_style() == AudioTime) {
452                         update_length_beats (0);
453                 }
454         }
455
456         return ret;
457 }
458
459 void
460 MidiRegion::recompute_at_end ()
461 {
462         /* our length has changed
463          * so what? stuck notes are dealt with via a note state tracker
464          */
465 }
466
467 void
468 MidiRegion::recompute_at_start ()
469 {
470         /* as above, but the shift was from the front
471          * maybe bump currently active note's note-ons up so they sound here?
472          * that could be undesireable in certain situations though.. maybe
473          * remove the note entirely, including it's note off?  something needs to
474          * be done to keep the played MIDI sane to avoid messing up voices of
475          * polyhonic things etc........
476          */
477 }
478
479 int
480 MidiRegion::separate_by_channel (ARDOUR::Session&, vector< boost::shared_ptr<Region> >&) const
481 {
482         // TODO
483         return -1;
484 }
485
486 boost::shared_ptr<Evoral::Control>
487 MidiRegion::control (const Evoral::Parameter& id, bool create)
488 {
489         return model()->control(id, create);
490 }
491
492 boost::shared_ptr<const Evoral::Control>
493 MidiRegion::control (const Evoral::Parameter& id) const
494 {
495         return model()->control(id);
496 }
497
498 boost::shared_ptr<MidiModel>
499 MidiRegion::model()
500 {
501         return midi_source()->model();
502 }
503
504 boost::shared_ptr<const MidiModel>
505 MidiRegion::model() const
506 {
507         return midi_source()->model();
508 }
509
510 boost::shared_ptr<MidiSource>
511 MidiRegion::midi_source (uint32_t n) const
512 {
513         // Guaranteed to succeed (use a static cast?)
514         return boost::dynamic_pointer_cast<MidiSource>(source(n));
515 }
516
517 /* don't use this. hopefully it will go away.
518    currently used by headless-chicken session utility.
519 */
520 void
521 MidiRegion::clobber_sources (boost::shared_ptr<MidiSource> s)
522 {
523        drop_sources();
524
525        _sources.push_back (s);
526        s->inc_use_count ();
527        _master_sources.push_back (s);
528        s->inc_use_count ();
529
530        s->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(s)));
531
532 }
533
534 void
535 MidiRegion::model_changed ()
536 {
537         if (!model()) {
538                 return;
539         }
540
541         /* build list of filtered Parameters, being those whose automation state is not `Play' */
542
543         _filtered_parameters.clear ();
544
545         Automatable::Controls const & c = model()->controls();
546
547         for (Automatable::Controls::const_iterator i = c.begin(); i != c.end(); ++i) {
548                 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
549                 assert (ac);
550                 if (ac->alist()->automation_state() != Play) {
551                         _filtered_parameters.insert (ac->parameter ());
552                 }
553         }
554
555         /* watch for changes to controls' AutoState */
556         midi_source()->AutomationStateChanged.connect_same_thread (
557                 _model_connection, boost::bind (&MidiRegion::model_automation_state_changed, this, _1)
558                 );
559 }
560
561 void
562 MidiRegion::model_automation_state_changed (Evoral::Parameter const & p)
563 {
564         /* Update our filtered parameters list after a change to a parameter's AutoState */
565
566         boost::shared_ptr<AutomationControl> ac = model()->automation_control (p);
567         if (!ac || ac->alist()->automation_state() == Play) {
568                 /* It should be "impossible" for ac to be NULL, but if it is, don't
569                    filter the parameter so events aren't lost. */
570                 _filtered_parameters.erase (p);
571         } else {
572                 _filtered_parameters.insert (p);
573         }
574
575         /* the source will have an iterator into the model, and that iterator will have been set up
576            for a given set of filtered_parameters, so now that we've changed that list we must invalidate
577            the iterator.
578         */
579         Glib::Threads::Mutex::Lock lm (midi_source(0)->mutex(), Glib::Threads::TRY_LOCK);
580         if (lm.locked()) {
581                 /* TODO: This is too aggressive, we need more fine-grained invalidation. */
582                 midi_source(0)->invalidate (lm);
583         }
584 }
585
586 /** This is called when a trim drag has resulted in a -ve _start time for this region.
587  *  Fix it up by adding some empty space to the source.
588  */
589 void
590 MidiRegion::fix_negative_start ()
591 {
592         BeatsFramesConverter c (_session.tempo_map(), _position);
593
594         model()->insert_silence_at_start (c.from (-_start));
595         _start = 0;
596         _start_beats = 0.0;
597 }
598
599 void
600 MidiRegion::set_start_internal (framecnt_t s, const int32_t sub_num)
601 {
602         Region::set_start_internal (s, sub_num);
603         if (position_lock_style() == AudioTime) {
604                 set_start_beats_from_start_frames ();
605         }
606 }
607
608 void
609 MidiRegion::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
610 {
611         if (locked()) {
612                 return;
613         }
614
615         PropertyChange what_changed;
616
617
618         /* Set position before length, otherwise for MIDI regions this bad thing happens:
619          * 1. we call set_length_internal; length in beats is computed using the region's current
620          *    (soon-to-be old) position
621          * 2. we call set_position_internal; position is set and length in frames re-computed using
622          *    length in beats from (1) but at the new position, which is wrong if the region
623          *    straddles a tempo/meter change.
624          */
625
626         if (_position != position) {
627
628                 const double pos_qn = _session.tempo_map().exact_qn_at_frame (position, sub_num);
629                 const double old_pos_qn = pulse() * 4.0;
630
631                 /* sets _pulse to new position.*/
632                 set_position_internal (position, true, sub_num);
633                 what_changed.add (Properties::position);
634
635                 double new_start_qn = start_beats() + (pos_qn - old_pos_qn);
636                 const framepos_t new_start = _session.tempo_map().frame_at_quarter_note (pos_qn)
637                         - _session.tempo_map().frame_at_quarter_note (pos_qn - new_start_qn);
638
639                 if (!verify_start_and_length (new_start, length)) {
640                         return;
641                 }
642
643                 _start_beats = new_start_qn;
644                 what_changed.add (Properties::start_beats);
645
646                 set_start_internal (new_start, sub_num);
647                 what_changed.add (Properties::start);
648         }
649
650         if (_length != length) {
651
652                 if (!verify_start_and_length (_start, length)) {
653                         return;
654                 }
655
656                 set_length_internal (length, sub_num);
657                 what_changed.add (Properties::length);
658                 what_changed.add (Properties::length_beats);
659         }
660
661         set_whole_file (false);
662
663         PropertyChange start_and_length;
664
665         start_and_length.add (Properties::start);
666         start_and_length.add (Properties::length);
667
668         if (what_changed.contains (start_and_length)) {
669                 first_edit ();
670         }
671
672         if (!what_changed.empty()) {
673                 send_change (what_changed);
674         }
675 }