add a new enum to identify how SMF data should be used when naming imported MIDI...
[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                 /* Lock our source since we'll be reading from it.  write_to() will
182                    take a lock on newsrc. */
183                 Source::Lock lm (midi_source(0)->mutex());
184                 if (midi_source(0)->write_to (lm, newsrc, bbegin, bend)) {
185                         return boost::shared_ptr<MidiRegion> ();
186                 }
187         }
188
189         PropertyList plist;
190
191         plist.add (Properties::name, PBD::basename_nosuffix (newsrc->name()));
192         plist.add (Properties::whole_file, true);
193         plist.add (Properties::start, _start);
194         plist.add (Properties::start_beats, _start_beats);
195         plist.add (Properties::length, _length);
196         plist.add (Properties::beat, _beat);
197         plist.add (Properties::length_beats, _length_beats);
198         plist.add (Properties::layer, 0);
199
200         boost::shared_ptr<MidiRegion> ret (boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (newsrc, plist, true)));
201         ret->set_quarter_note (quarter_note());
202
203         return ret;
204 }
205
206 void
207 MidiRegion::post_set (const PropertyChange& pc)
208 {
209         Region::post_set (pc);
210
211         if (pc.contains (Properties::length) && !pc.contains (Properties::length_beats)) {
212                 /* we're called by Stateful::set_values() which sends a change
213                    only if the value is different from _current.
214                    session load means we can clobber length_beats here in error (not all properties differ from current),
215                    so disallow (this has been set from XML state anyway).
216                 */
217                 if (!_session.loading()) {
218                         update_length_beats (0);
219                 }
220         }
221
222         if (pc.contains (Properties::start) && !pc.contains (Properties::start_beats)) {
223                 set_start_beats_from_start_frames ();
224         }
225 }
226
227 void
228 MidiRegion::set_start_beats_from_start_frames ()
229 {
230         if (position_lock_style() == AudioTime) {
231                 _start_beats = quarter_note() - _session.tempo_map().quarter_note_at_frame (_position - _start);
232         }
233 }
234
235 void
236 MidiRegion::set_length_internal (framecnt_t len, const int32_t sub_num)
237 {
238         Region::set_length_internal (len, sub_num);
239         update_length_beats (sub_num);
240 }
241
242 void
243 MidiRegion::update_after_tempo_map_change (bool /* send */)
244 {
245         boost::shared_ptr<Playlist> pl (playlist());
246
247         if (!pl) {
248                 return;
249         }
250
251         const framepos_t old_pos = _position;
252         const framepos_t old_length = _length;
253         const framepos_t old_start = _start;
254
255         PropertyChange s_and_l;
256
257         if (position_lock_style() == AudioTime) {
258                 recompute_position_from_lock_style (0);
259
260                 /*
261                   set _start to new position in tempo map.
262
263                   The user probably expects the region contents to maintain audio position as the
264                   tempo changes, but AFAICT this requires modifying the src file to use
265                   SMPTE timestamps with the current disk read model (?).
266
267                   We could arguably use _start to set _start_beats here,
268                   resulting in viewport-like behaviour (the contents maintain
269                   their musical position while the region is stationary).
270
271                   For now, the musical position at the region start is retained, but subsequent events
272                   will maintain their beat distance according to the map.
273                 */
274                 _start = _session.tempo_map().frames_between_quarter_notes (quarter_note() - start_beats(), quarter_note());
275
276                 /* _length doesn't change for audio-locked regions. update length_beats to match. */
277                 _length_beats = _session.tempo_map().quarter_note_at_frame (_position + _length) - quarter_note();
278
279                 s_and_l.add (Properties::start);
280                 s_and_l.add (Properties::length_beats);
281
282                 send_change  (s_and_l);
283                 return;
284         }
285
286         Region::update_after_tempo_map_change (false);
287
288         /* _start has now been updated. */
289         _length = max ((framecnt_t) 1, _session.tempo_map().frames_between_quarter_notes (quarter_note(), quarter_note() + _length_beats));
290
291         if (old_start != _start) {
292                 s_and_l.add (Properties::start);
293         }
294         if (old_length != _length) {
295                 s_and_l.add (Properties::length);
296         }
297         if (old_pos != _position) {
298                 s_and_l.add (Properties::position);
299         }
300
301         send_change (s_and_l);
302 }
303
304 void
305 MidiRegion::update_length_beats (const int32_t sub_num)
306 {
307         _length_beats = _session.tempo_map().exact_qn_at_frame (_position + _length, sub_num) - quarter_note();
308 }
309
310 void
311 MidiRegion::set_position_internal (framepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
312 {
313         Region::set_position_internal (pos, allow_bbt_recompute, sub_num);
314
315         /* don't clobber _start _length and _length_beats if session loading.*/
316         if (_session.loading()) {
317                 return;
318         }
319
320         /* set _start to new position in tempo map */
321         _start = _session.tempo_map().frames_between_quarter_notes (quarter_note() - start_beats(), quarter_note());
322
323         /* in construction from src */
324         if (_length_beats == 0.0) {
325                 update_length_beats (sub_num);
326         }
327
328         if (position_lock_style() == AudioTime) {
329                 _length_beats = _session.tempo_map().quarter_note_at_frame (_position + _length) - quarter_note();
330         } else {
331                 /* leave _length_beats alone, and change _length to reflect the state of things
332                    at the new position (tempo map may dictate a different number of frames).
333                 */
334                 Region::set_length_internal (_session.tempo_map().frames_between_quarter_notes (quarter_note(), quarter_note() + length_beats()), sub_num);
335         }
336 }
337
338 framecnt_t
339 MidiRegion::read_at (Evoral::EventSink<framepos_t>& out,
340                      framepos_t                     position,
341                      framecnt_t                     dur,
342                      Evoral::Range<framepos_t>*     loop_range,
343                      MidiCursor&                    cursor,
344                      uint32_t                       chan_n,
345                      NoteMode                       mode,
346                      MidiStateTracker*              tracker,
347                      MidiChannelFilter*             filter) const
348 {
349         return _read_at (_sources, out, position, dur, loop_range, cursor, chan_n, mode, tracker, filter);
350 }
351
352 framecnt_t
353 MidiRegion::master_read_at (MidiRingBuffer<framepos_t>& out,
354                             framepos_t                  position,
355                             framecnt_t                  dur,
356                             Evoral::Range<framepos_t>*  loop_range,
357                             MidiCursor&                 cursor,
358                             uint32_t                    chan_n,
359                             NoteMode                    mode) const
360 {
361         return _read_at (_master_sources, out, position, dur, loop_range, cursor, chan_n, mode); /* no tracker */
362 }
363
364 framecnt_t
365 MidiRegion::_read_at (const SourceList&              /*srcs*/,
366                       Evoral::EventSink<framepos_t>& dst,
367                       framepos_t                     position,
368                       framecnt_t                     dur,
369                       Evoral::Range<framepos_t>*     loop_range,
370                       MidiCursor&                    cursor,
371                       uint32_t                       chan_n,
372                       NoteMode                       mode,
373                       MidiStateTracker*              tracker,
374                       MidiChannelFilter*             filter) const
375 {
376         frameoffset_t internal_offset = 0;
377         framecnt_t    to_read         = 0;
378
379         /* precondition: caller has verified that we cover the desired section */
380
381         assert(chan_n == 0);
382
383         if (muted()) {
384                 return 0; /* read nothing */
385         }
386
387         if (position < _position) {
388                 /* we are starting the read from before the start of the region */
389                 internal_offset = 0;
390                 dur -= _position - position;
391         } else {
392                 /* we are starting the read from after the start of the region */
393                 internal_offset = position - _position;
394         }
395
396         if (internal_offset >= _length) {
397                 return 0; /* read nothing */
398         }
399
400         if ((to_read = min (dur, _length - internal_offset)) == 0) {
401                 return 0; /* read nothing */
402         }
403
404         boost::shared_ptr<MidiSource> src = midi_source(chan_n);
405
406         Glib::Threads::Mutex::Lock lm(src->mutex());
407
408         src->set_note_mode(lm, mode);
409
410 #if 0
411         cerr << "MR " << name () << " read @ " << position << " + " << to_read
412              << " dur was " << dur
413              << " len " << _length
414              << " l-io " << (_length - internal_offset)
415              << " _position = " << _position
416              << " _start = " << _start
417              << " intoffset = " << internal_offset
418              << " quarter_note = " << quarter_note()
419              << " start_beat = " << _start_beats
420              << endl;
421 #endif
422
423         /* This call reads events from a source and writes them to `dst' timed in session frames */
424
425         if (src->midi_read (
426                     lm, // source lock
427                     dst, // destination buffer
428                     _position - _start, // start position of the source in session frames
429                     _start + internal_offset, // where to start reading in the source
430                     to_read, // read duration in frames
431                     loop_range,
432                     cursor,
433                     tracker,
434                     filter,
435                     _filtered_parameters,
436                     quarter_note(),
437                     _start_beats
438                     ) != to_read) {
439                 return 0; /* "read nothing" */
440         }
441
442         return to_read;
443 }
444
445 XMLNode&
446 MidiRegion::state ()
447 {
448         return Region::state ();
449 }
450
451 int
452 MidiRegion::set_state (const XMLNode& node, int version)
453 {
454         int ret = Region::set_state (node, version);
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         set_start_beats_from_start_frames ();
604 }
605
606 void
607 MidiRegion::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
608 {
609         if (locked()) {
610                 return;
611         }
612
613         PropertyChange what_changed;
614
615
616         /* Set position before length, otherwise for MIDI regions this bad thing happens:
617          * 1. we call set_length_internal; length in beats is computed using the region's current
618          *    (soon-to-be old) position
619          * 2. we call set_position_internal; position is set and length in frames re-computed using
620          *    length in beats from (1) but at the new position, which is wrong if the region
621          *    straddles a tempo/meter change.
622          */
623
624         if (_position != position) {
625
626                 const double pos_qn = _session.tempo_map().exact_qn_at_frame (position, sub_num);
627                 const double old_pos_qn = quarter_note();
628
629                 /* sets _pulse to new position.*/
630                 set_position_internal (position, true, sub_num);
631                 what_changed.add (Properties::position);
632
633                 double new_start_qn = start_beats() + (pos_qn - old_pos_qn);
634                 framepos_t new_start = _session.tempo_map().frames_between_quarter_notes (pos_qn - new_start_qn, pos_qn);
635
636                 if (!verify_start_and_length (new_start, length)) {
637                         return;
638                 }
639
640                 _start_beats = new_start_qn;
641                 what_changed.add (Properties::start_beats);
642
643                 set_start_internal (new_start, sub_num);
644                 what_changed.add (Properties::start);
645         }
646
647         if (_length != length) {
648
649                 if (!verify_start_and_length (_start, length)) {
650                         return;
651                 }
652
653                 set_length_internal (length, sub_num);
654                 what_changed.add (Properties::length);
655                 what_changed.add (Properties::length_beats);
656         }
657
658         set_whole_file (false);
659
660         PropertyChange start_and_length;
661
662         start_and_length.add (Properties::start);
663         start_and_length.add (Properties::length);
664
665         if (what_changed.contains (start_and_length)) {
666                 first_edit ();
667         }
668
669         if (!what_changed.empty()) {
670                 send_change (what_changed);
671         }
672 }