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