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