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