1 /*
2  * Copyright (C) 2006-2016 David Robillard <d@drobilla.net>
3  * Copyright (C) 2007-2017 Paul Davis <paul@linuxaudiosystems.com>
4  * Copyright (C) 2008 Hans Baier <hansfbaier@googlemail.com>
5  * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
6  * Copyright (C) 2012-2015 Tim Mayberry <mojofunk@gmail.com>
7  * Copyright (C) 2016-2017 Nick Mainsbridge <mainsbridge@gmail.com>
8  * Copyright (C) 2016-2017 Robin Gareus <robin@gareus.org>
9  * Copyright (C) 2016 Julien "_FrnchFrgg_" RIVAUD <frnchfrgg@free.fr>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License along
22  * with this program; if not, write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25 
26 #include <cmath>
27 #include <climits>
28 #include <cfloat>
29 
30 #include <set>
31 
32 #include <glibmm/threads.h>
33 #include <glibmm/fileutils.h>
34 #include <glibmm/miscutils.h>
35 
36 #include "temporal/beats.h"
37 
38 #include "pbd/xml++.h"
39 #include "pbd/basename.h"
40 
41 #include "ardour/automation_control.h"
42 #include "ardour/midi_cursor.h"
43 #include "ardour/midi_model.h"
44 #include "ardour/midi_region.h"
45 #include "ardour/midi_ring_buffer.h"
46 #include "ardour/midi_source.h"
47 #include "ardour/playlist.h"
48 #include "ardour/region_factory.h"
49 #include "ardour/session.h"
50 #include "ardour/source_factory.h"
51 #include "ardour/tempo.h"
52 #include "ardour/thawlist.h"
53 #include "ardour/types.h"
54 #include "ardour/evoral_types_convert.h"
55 
56 #include "pbd/i18n.h"
57 #include <locale.h>
58 
59 using namespace std;
60 using namespace ARDOUR;
61 using namespace PBD;
62 
63 namespace ARDOUR {
64 	namespace Properties {
65 		PBD::PropertyDescriptor<double> start_beats;
66 		PBD::PropertyDescriptor<double> length_beats;
67 	}
68 }
69 
70 void
make_property_quarks()71 MidiRegion::make_property_quarks ()
72 {
73 	Properties::start_beats.property_id = g_quark_from_static_string (X_("start-beats"));
74 	DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start-beats = %1\n", Properties::start_beats.property_id));
75 	Properties::length_beats.property_id = g_quark_from_static_string (X_("length-beats"));
76 	DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length-beats = %1\n", Properties::length_beats.property_id));
77 }
78 
79 void
register_properties()80 MidiRegion::register_properties ()
81 {
82 	add_property (_start_beats);
83 	add_property (_length_beats);
84 }
85 
86 /* Basic MidiRegion constructor (many channels) */
MidiRegion(const SourceList & srcs)87 MidiRegion::MidiRegion (const SourceList& srcs)
88 	: Region (srcs)
89 	, _start_beats (Properties::start_beats, 0.0)
90 	, _length_beats (Properties::length_beats, midi_source(0)->length_beats().to_double())
91 	, _ignore_shift (false)
92 {
93 	register_properties ();
94 	midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this));
95 	model_changed ();
96 	assert(_name.val().find("/") == string::npos);
97 	assert(_type == DataType::MIDI);
98 }
99 
MidiRegion(boost::shared_ptr<const MidiRegion> other)100 MidiRegion::MidiRegion (boost::shared_ptr<const MidiRegion> other)
101 	: Region (other)
102 	, _start_beats (Properties::start_beats, other->_start_beats)
103 	, _length_beats (Properties::length_beats, other->_length_beats)
104 	, _ignore_shift (false)
105 {
106 	//update_length_beats ();
107 	register_properties ();
108 
109 	assert(_name.val().find("/") == string::npos);
110 	midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this));
111 	model_changed ();
112 }
113 
114 /** Create a new MidiRegion that is part of an existing one */
MidiRegion(boost::shared_ptr<const MidiRegion> other,MusicSample offset)115 MidiRegion::MidiRegion (boost::shared_ptr<const MidiRegion> other, MusicSample offset)
116 	: Region (other, offset)
117 	, _start_beats (Properties::start_beats, other->_start_beats)
118 	, _length_beats (Properties::length_beats, other->_length_beats)
119 	, _ignore_shift (false)
120 {
121 
122 	register_properties ();
123 
124 	const double offset_quarter_note = _session.tempo_map().exact_qn_at_sample (other->_position + offset.sample, offset.division) - other->_quarter_note;
125 	if (offset.sample != 0) {
126 		_start_beats = other->_start_beats + offset_quarter_note;
127 		_length_beats = other->_length_beats - offset_quarter_note;
128 	}
129 
130 	assert(_name.val().find("/") == string::npos);
131 	midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this));
132 	model_changed ();
133 }
134 
~MidiRegion()135 MidiRegion::~MidiRegion ()
136 {
137 }
138 
139 /** Export the MIDI data of the MidiRegion to a new MIDI file (SMF).
140  */
141 bool
do_export(string path) const142 MidiRegion::do_export (string path) const
143 {
144 	boost::shared_ptr<MidiSource> newsrc;
145 
146 	/* caller must check for pre-existing file */
147 	assert (!path.empty());
148 	assert (!Glib::file_test (path, Glib::FILE_TEST_EXISTS));
149 	newsrc = boost::dynamic_pointer_cast<MidiSource>(SourceFactory::createWritable(DataType::MIDI, _session, path, _session.sample_rate()));
150 
151 	BeatsSamplesConverter bfc (_session.tempo_map(), _position);
152 	Temporal::Beats const bbegin = bfc.from (_start);
153 	Temporal::Beats const bend = bfc.from (_start + _length);
154 
155 	{
156 		/* Lock our source since we'll be reading from it.  write_to() will
157 		   take a lock on newsrc. */
158 		Source::Lock lm (midi_source(0)->mutex());
159 		if (midi_source(0)->export_write_to (lm, newsrc, bbegin, bend)) {
160 			return false;
161 		}
162 	}
163 
164 	return true;
165 }
166 
167 
168 /** Create a new MidiRegion that has its own version of some/all of the Source used by another.
169  */
170 boost::shared_ptr<MidiRegion>
clone(string path) const171 MidiRegion::clone (string path) const
172 {
173 	boost::shared_ptr<MidiSource> newsrc;
174 
175 	/* caller must check for pre-existing file */
176 	assert (!path.empty());
177 	assert (!Glib::file_test (path, Glib::FILE_TEST_EXISTS));
178 	newsrc = boost::dynamic_pointer_cast<MidiSource>(
179 		SourceFactory::createWritable(DataType::MIDI, _session, path, _session.sample_rate()));
180 	return clone (newsrc);
181 }
182 
183 boost::shared_ptr<MidiRegion>
clone(boost::shared_ptr<MidiSource> newsrc,ThawList * tl) const184 MidiRegion::clone (boost::shared_ptr<MidiSource> newsrc, ThawList* tl) const
185 {
186 	BeatsSamplesConverter bfc (_session.tempo_map(), _position);
187 	Temporal::Beats const bbegin = bfc.from (_start);
188 	Temporal::Beats const bend = bfc.from (_start + _length);
189 
190 	{
191 		boost::shared_ptr<MidiSource> ms = midi_source(0);
192 		Source::Lock lm (ms->mutex());
193 
194 		if (!ms->model()) {
195 			ms->load_model (lm);
196 		}
197 
198 		/* Lock our source since we'll be reading from it.  write_to() will
199 		   take a lock on newsrc.
200 		*/
201 
202 		if (ms->write_to (lm, newsrc, bbegin, bend)) {
203 			return boost::shared_ptr<MidiRegion> ();
204 		}
205 	}
206 
207 	PropertyList plist;
208 
209 	plist.add (Properties::name, PBD::basename_nosuffix (newsrc->name()));
210 	plist.add (Properties::whole_file, true);
211 	plist.add (Properties::start, _start);
212 	plist.add (Properties::start_beats, _start_beats);
213 	plist.add (Properties::length, _length);
214 	plist.add (Properties::position, _position);
215 	plist.add (Properties::beat, _beat);
216 	plist.add (Properties::length_beats, _length_beats);
217 	plist.add (Properties::layer, 0);
218 
219 	boost::shared_ptr<MidiRegion> ret (boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (newsrc, plist, true, tl)));
220 	ret->set_quarter_note (quarter_note());
221 
222 	return ret;
223 }
224 
225 void
post_set(const PropertyChange & pc)226 MidiRegion::post_set (const PropertyChange& pc)
227 {
228 	Region::post_set (pc);
229 
230 	if (pc.contains (Properties::length) && !pc.contains (Properties::length_beats)) {
231 		/* we're called by Stateful::set_values() which sends a change
232 		   only if the value is different from _current.
233 		   session load means we can clobber length_beats here in error (not all properties differ from current),
234 		   so disallow (this has been set from XML state anyway).
235 		*/
236 		if (!_session.loading()) {
237 			update_length_beats (0);
238 		}
239 	}
240 
241 	if (pc.contains (Properties::start) && !pc.contains (Properties::start_beats)) {
242 		set_start_beats_from_start_samples ();
243 	}
244 }
245 
246 void
set_start_beats_from_start_samples()247 MidiRegion::set_start_beats_from_start_samples ()
248 {
249 	if (position_lock_style() == AudioTime) {
250 		_start_beats = quarter_note() - _session.tempo_map().quarter_note_at_sample (_position - _start);
251 	}
252 }
253 
254 void
set_length_internal(samplecnt_t len,const int32_t sub_num)255 MidiRegion::set_length_internal (samplecnt_t len, const int32_t sub_num)
256 {
257 	Region::set_length_internal (len, sub_num);
258 	update_length_beats (sub_num);
259 }
260 
261 void
update_after_tempo_map_change(bool)262 MidiRegion::update_after_tempo_map_change (bool /* send */)
263 {
264 	boost::shared_ptr<Playlist> pl (playlist());
265 
266 	if (!pl) {
267 		return;
268 	}
269 
270 	const samplepos_t old_pos = _position;
271 	const samplepos_t old_length = _length;
272 	const samplepos_t old_start = _start;
273 
274 	PropertyChange s_and_l;
275 
276 	if (position_lock_style() == AudioTime) {
277 		recompute_position_from_lock_style (0);
278 
279 		/*
280 		  set _start to new position in tempo map.
281 
282 		  The user probably expects the region contents to maintain audio position as the
283 		  tempo changes, but AFAICT this requires modifying the src file to use
284 		  SMPTE timestamps with the current disk read model (?).
285 
286 		  We could arguably use _start to set _start_beats here,
287 		  resulting in viewport-like behaviour (the contents maintain
288 		  their musical position while the region is stationary).
289 
290 		  For now, the musical position at the region start is retained, but subsequent events
291 		  will maintain their beat distance according to the map.
292 		*/
293 		_start = _session.tempo_map().samples_between_quarter_notes (quarter_note() - start_beats(), quarter_note());
294 
295 		/* _length doesn't change for audio-locked regions. update length_beats to match. */
296 		_length_beats = _session.tempo_map().quarter_note_at_sample (_position + _length) - quarter_note();
297 
298 		s_and_l.add (Properties::start);
299 		s_and_l.add (Properties::length_beats);
300 
301 		send_change  (s_and_l);
302 		return;
303 	}
304 
305 	Region::update_after_tempo_map_change (false);
306 
307 	/* _start has now been updated. */
308 	_length = max ((samplecnt_t) 1, _session.tempo_map().samples_between_quarter_notes (quarter_note(), quarter_note() + _length_beats));
309 
310 	if (old_start != _start) {
311 		s_and_l.add (Properties::start);
312 	}
313 	if (old_length != _length) {
314 		s_and_l.add (Properties::length);
315 	}
316 	if (old_pos != _position) {
317 		s_and_l.add (Properties::position);
318 	}
319 
320 	send_change (s_and_l);
321 }
322 
323 void
update_length_beats(const int32_t sub_num)324 MidiRegion::update_length_beats (const int32_t sub_num)
325 {
326 	_length_beats = _session.tempo_map().exact_qn_at_sample (_position + _length, sub_num) - quarter_note();
327 }
328 
329 void
set_position_internal(samplepos_t pos,bool allow_bbt_recompute,const int32_t sub_num)330 MidiRegion::set_position_internal (samplepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
331 {
332 	Region::set_position_internal (pos, allow_bbt_recompute, sub_num);
333 
334 	/* don't clobber _start _length and _length_beats if session loading.*/
335 	if (_session.loading()) {
336 		return;
337 	}
338 
339 	/* set _start to new position in tempo map */
340 	_start = _session.tempo_map().samples_between_quarter_notes (quarter_note() - start_beats(), quarter_note());
341 
342 	/* in construction from src */
343 	if (_length_beats == 0.0) {
344 		update_length_beats (sub_num);
345 	}
346 
347 	if (position_lock_style() == AudioTime) {
348 		_length_beats = _session.tempo_map().quarter_note_at_sample (_position + _length) - quarter_note();
349 	} else {
350 		/* leave _length_beats alone, and change _length to reflect the state of things
351 		   at the new position (tempo map may dictate a different number of samples).
352 		*/
353 		Region::set_length_internal (_session.tempo_map().samples_between_quarter_notes (quarter_note(), quarter_note() + length_beats()), sub_num);
354 	}
355 }
356 
357 void
set_position_music_internal(double qn)358 MidiRegion::set_position_music_internal (double qn)
359 {
360 	Region::set_position_music_internal (qn);
361 	/* set _start to new position in tempo map */
362 	_start = _session.tempo_map().samples_between_quarter_notes (quarter_note() - start_beats(), quarter_note());
363 
364 	if (position_lock_style() == AudioTime) {
365 		_length_beats = _session.tempo_map().quarter_note_at_sample (_position + _length) - quarter_note();
366 
367 	} else {
368 		/* leave _length_beats alone, and change _length to reflect the state of things
369 		   at the new position (tempo map may dictate a different number of samples).
370 		*/
371 		_length = _session.tempo_map().samples_between_quarter_notes (quarter_note(), quarter_note() + length_beats());
372 	}
373 }
374 
375 samplecnt_t
read_at(Evoral::EventSink<samplepos_t> & out,samplepos_t position,samplecnt_t dur,Evoral::Range<samplepos_t> * loop_range,MidiCursor & cursor,uint32_t chan_n,NoteMode mode,MidiStateTracker * tracker,MidiChannelFilter * filter) const376 MidiRegion::read_at (Evoral::EventSink<samplepos_t>& out,
377                      samplepos_t                     position,
378                      samplecnt_t                     dur,
379                      Evoral::Range<samplepos_t>*     loop_range,
380                      MidiCursor&                    cursor,
381                      uint32_t                       chan_n,
382                      NoteMode                       mode,
383                      MidiStateTracker*              tracker,
384                      MidiChannelFilter*             filter) const
385 {
386 	return _read_at (_sources, out, position, dur, loop_range, cursor, chan_n, mode, tracker, filter);
387 }
388 
389 samplecnt_t
master_read_at(MidiRingBuffer<samplepos_t> & out,samplepos_t position,samplecnt_t dur,Evoral::Range<samplepos_t> * loop_range,MidiCursor & cursor,uint32_t chan_n,NoteMode mode) const390 MidiRegion::master_read_at (MidiRingBuffer<samplepos_t>& out,
391                             samplepos_t                  position,
392                             samplecnt_t                  dur,
393                             Evoral::Range<samplepos_t>*  loop_range,
394                             MidiCursor&                 cursor,
395                             uint32_t                    chan_n,
396                             NoteMode                    mode) const
397 {
398 	return _read_at (_master_sources, out, position, dur, loop_range, cursor, chan_n, mode); /* no tracker */
399 }
400 
401 samplecnt_t
_read_at(const SourceList &,Evoral::EventSink<samplepos_t> & dst,samplepos_t position,samplecnt_t dur,Evoral::Range<samplepos_t> * loop_range,MidiCursor & cursor,uint32_t chan_n,NoteMode mode,MidiStateTracker * tracker,MidiChannelFilter * filter) const402 MidiRegion::_read_at (const SourceList&              /*srcs*/,
403                       Evoral::EventSink<samplepos_t>& dst,
404                       samplepos_t                     position,
405                       samplecnt_t                     dur,
406                       Evoral::Range<samplepos_t>*     loop_range,
407                       MidiCursor&                    cursor,
408                       uint32_t                       chan_n,
409                       NoteMode                       mode,
410                       MidiStateTracker*              tracker,
411                       MidiChannelFilter*             filter) const
412 {
413 	sampleoffset_t internal_offset = 0;
414 	samplecnt_t    to_read         = 0;
415 
416 	/* precondition: caller has verified that we cover the desired section */
417 
418 	assert(chan_n == 0);
419 
420 	if (muted()) {
421 		return 0; /* read nothing */
422 	}
423 
424 	if (position < _position) {
425 		/* we are starting the read from before the start of the region */
426 		internal_offset = 0;
427 		dur -= _position - position;
428 	} else {
429 		/* we are starting the read from after the start of the region */
430 		internal_offset = position - _position;
431 	}
432 
433 	if (internal_offset >= _length) {
434 		return 0; /* read nothing */
435 	}
436 
437 	if ((to_read = min (dur, _length - internal_offset)) == 0) {
438 		return 0; /* read nothing */
439 	}
440 
441 	boost::shared_ptr<MidiSource> src = midi_source(chan_n);
442 
443 	Glib::Threads::Mutex::Lock lm(src->mutex());
444 
445 	src->set_note_mode(lm, mode);
446 
447 #if 0
448 	cerr << "MR " << name () << " read @ " << position << " + " << to_read
449 	     << " dur was " << dur
450 	     << " len " << _length
451 	     << " l-io " << (_length - internal_offset)
452 	     << " _position = " << _position
453 	     << " _start = " << _start
454 	     << " intoffset = " << internal_offset
455 	     << " quarter_note = " << quarter_note()
456 	     << " start_beat = " << _start_beats
457 	     << endl;
458 #endif
459 
460 	/* This call reads events from a source and writes them to `dst' timed in session samples */
461 
462 	if (src->midi_read (
463 		    lm, // source lock
464 		    dst, // destination buffer
465 		    _position - _start, // start position of the source in session samples
466 		    _start + internal_offset, // where to start reading in the source
467 		    to_read, // read duration in samples
468 		    loop_range,
469 		    cursor,
470 		    tracker,
471 		    filter,
472 		    _filtered_parameters,
473 		    quarter_note(),
474 		    _start_beats
475 		    ) != to_read) {
476 		return 0; /* "read nothing" */
477 	}
478 
479 	return to_read;
480 }
481 
482 
483 int
render(Evoral::EventSink<samplepos_t> & dst,uint32_t chan_n,NoteMode mode,MidiChannelFilter * filter) const484 MidiRegion::render (Evoral::EventSink<samplepos_t>& dst,
485                     uint32_t                        chan_n,
486                     NoteMode                        mode,
487                     MidiChannelFilter*              filter) const
488 {
489 	sampleoffset_t internal_offset = 0;
490 
491 	/* precondition: caller has verified that we cover the desired section */
492 
493 	assert(chan_n == 0);
494 
495 	if (muted()) {
496 		return 0; /* read nothing */
497 	}
498 
499 
500 	/* dump pulls from zero to infinity ... */
501 
502 	if (_position) {
503 		/* we are starting the read from before the start of the region */
504 		internal_offset = 0;
505 	} else {
506 		/* we are starting the read from after the start of the region */
507 		internal_offset = -_position;
508 	}
509 
510 	if (internal_offset >= _length) {
511 		return 0; /* read nothing */
512 	}
513 
514 	boost::shared_ptr<MidiSource> src = midi_source(chan_n);
515 
516 	Glib::Threads::Mutex::Lock lm(src->mutex());
517 
518 	src->set_note_mode(lm, mode);
519 
520 #if 0
521 	cerr << "MR " << name () << " render "
522 	     << " _position = " << _position
523 	     << " _start = " << _start
524 	     << " intoffset = " << internal_offset
525 	     << " quarter_note = " << quarter_note()
526 	     << " start_beat = " << _start_beats
527 	     << " a1 " << _position - _start
528 	     << " a2 " << _start + internal_offset
529 	     << " a3 " << _length
530 	     << endl;
531 #endif
532 
533 	MidiCursor cursor;
534 	MidiStateTracker tracker;
535 
536 	/* This call reads events from a source and writes them to `dst' timed in session samples */
537 
538 	src->midi_read (
539 		lm, // source lock
540 		dst, // destination buffer
541 		_position - _start, // start position of the source in session samples
542 		_start + internal_offset, // where to start reading in the source
543 		_length, // length to read
544 		0,
545 		cursor,
546 		&tracker,
547 		filter,
548 		_filtered_parameters,
549 		quarter_note(),
550 		_start_beats);
551 
552 	/* resolve any notes that were "cut off" by the end of the region. The
553 	 * Note-Off's get inserted at the end of the region
554 	 */
555 
556 	tracker.resolve_notes (dst, (_position - _start) + (_start + internal_offset + _length));
557 
558 	return 0;
559 }
560 
561 
562 XMLNode&
state()563 MidiRegion::state ()
564 {
565 	return Region::state ();
566 }
567 
568 int
set_state(const XMLNode & node,int version)569 MidiRegion::set_state (const XMLNode& node, int version)
570 {
571 	int ret = Region::set_state (node, version);
572 
573 	return ret;
574 }
575 
576 void
recompute_at_end()577 MidiRegion::recompute_at_end ()
578 {
579 	/* our length has changed
580 	 * so what? stuck notes are dealt with via a note state tracker
581 	 */
582 }
583 
584 void
recompute_at_start()585 MidiRegion::recompute_at_start ()
586 {
587 	/* as above, but the shift was from the front
588 	 * maybe bump currently active note's note-ons up so they sound here?
589 	 * that could be undesireable in certain situations though.. maybe
590 	 * remove the note entirely, including it's note off?  something needs to
591 	 * be done to keep the played MIDI sane to avoid messing up voices of
592 	 * polyhonic things etc........
593 	 */
594 }
595 
596 int
separate_by_channel(vector<boost::shared_ptr<Region>> &) const597 MidiRegion::separate_by_channel (vector< boost::shared_ptr<Region> >&) const
598 {
599 	// TODO
600 	return -1;
601 }
602 
603 boost::shared_ptr<Evoral::Control>
control(const Evoral::Parameter & id,bool create)604 MidiRegion::control (const Evoral::Parameter& id, bool create)
605 {
606 	return model()->control(id, create);
607 }
608 
609 boost::shared_ptr<const Evoral::Control>
control(const Evoral::Parameter & id) const610 MidiRegion::control (const Evoral::Parameter& id) const
611 {
612 	return model()->control(id);
613 }
614 
615 boost::shared_ptr<MidiModel>
model()616 MidiRegion::model()
617 {
618 	return midi_source()->model();
619 }
620 
621 boost::shared_ptr<const MidiModel>
model() const622 MidiRegion::model() const
623 {
624 	return midi_source()->model();
625 }
626 
627 boost::shared_ptr<MidiSource>
midi_source(uint32_t n) const628 MidiRegion::midi_source (uint32_t n) const
629 {
630 	// Guaranteed to succeed (use a static cast?)
631 	return boost::dynamic_pointer_cast<MidiSource>(source(n));
632 }
633 
634 /* don't use this. hopefully it will go away.
635    currently used by headless-chicken session utility.
636 */
637 void
clobber_sources(boost::shared_ptr<MidiSource> s)638 MidiRegion::clobber_sources (boost::shared_ptr<MidiSource> s)
639 {
640        drop_sources();
641 
642        _sources.push_back (s);
643        s->inc_use_count ();
644        _master_sources.push_back (s);
645        s->inc_use_count ();
646 
647        s->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(s)));
648 
649 }
650 
651 void
model_changed()652 MidiRegion::model_changed ()
653 {
654 	if (!model()) {
655 		return;
656 	}
657 
658 	/* build list of filtered Parameters, being those whose automation state is not `Play' */
659 
660 	_filtered_parameters.clear ();
661 
662 	Automatable::Controls const & c = model()->controls();
663 
664 	for (Automatable::Controls::const_iterator i = c.begin(); i != c.end(); ++i) {
665 		boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
666 		assert (ac);
667 		if (ac->alist()->automation_state() != Play) {
668 			_filtered_parameters.insert (ac->parameter ());
669 		}
670 	}
671 
672 	/* watch for changes to controls' AutoState */
673 	midi_source()->AutomationStateChanged.connect_same_thread (
674 		_model_connection, boost::bind (&MidiRegion::model_automation_state_changed, this, _1)
675 		);
676 
677 	model()->ContentsShifted.connect_same_thread (_model_shift_connection, boost::bind (&MidiRegion::model_shifted, this, _1));
678 	model()->ContentsChanged.connect_same_thread (_model_changed_connection, boost::bind (&MidiRegion::model_contents_changed, this));
679 }
680 
681 void
model_contents_changed()682 MidiRegion::model_contents_changed ()
683 {
684 	send_change (Properties::contents);
685 }
686 
687 void
model_shifted(double qn_distance)688 MidiRegion::model_shifted (double qn_distance)
689 {
690 	if (!model()) {
691 		return;
692 	}
693 
694 	if (!_ignore_shift) {
695 		PropertyChange what_changed;
696 		_start_beats += qn_distance;
697 		samplepos_t const new_start = _session.tempo_map().samples_between_quarter_notes (_quarter_note - _start_beats, _quarter_note);
698 		_start = new_start;
699 		what_changed.add (Properties::start);
700 		what_changed.add (Properties::start_beats);
701 		what_changed.add (Properties::contents);
702 		send_change (what_changed);
703 	} else {
704 		_ignore_shift = false;
705 	}
706 }
707 
708 void
model_automation_state_changed(Evoral::Parameter const & p)709 MidiRegion::model_automation_state_changed (Evoral::Parameter const & p)
710 {
711 	/* Update our filtered parameters list after a change to a parameter's AutoState */
712 
713 	boost::shared_ptr<AutomationControl> ac = model()->automation_control (p);
714 	if (!ac || ac->alist()->automation_state() == Play) {
715 		/* It should be "impossible" for ac to be NULL, but if it is, don't
716 		   filter the parameter so events aren't lost. */
717 		_filtered_parameters.erase (p);
718 	} else {
719 		_filtered_parameters.insert (p);
720 	}
721 
722 	/* the source will have an iterator into the model, and that iterator will have been set up
723 	   for a given set of filtered_parameters, so now that we've changed that list we must invalidate
724 	   the iterator.
725 	*/
726 	Glib::Threads::Mutex::Lock lm (midi_source(0)->mutex(), Glib::Threads::TRY_LOCK);
727 	if (lm.locked()) {
728 		/* TODO: This is too aggressive, we need more fine-grained invalidation. */
729 		midi_source(0)->invalidate (lm);
730 	}
731 }
732 
733 /** This is called when a trim drag has resulted in a -ve _start time for this region.
734  *  Fix it up by adding some empty space to the source.
735  */
736 void
fix_negative_start()737 MidiRegion::fix_negative_start ()
738 {
739 	BeatsSamplesConverter c (_session.tempo_map(), _position);
740 
741 	_ignore_shift = true;
742 
743 	model()->insert_silence_at_start (Temporal::Beats (- _start_beats));
744 
745 	_start = 0;
746 	_start_beats = 0.0;
747 }
748 
749 void
set_start_internal(samplecnt_t s,const int32_t sub_num)750 MidiRegion::set_start_internal (samplecnt_t s, const int32_t sub_num)
751 {
752 	Region::set_start_internal (s, sub_num);
753 
754 	set_start_beats_from_start_samples ();
755 }
756 
757 void
trim_to_internal(samplepos_t position,samplecnt_t length,const int32_t sub_num)758 MidiRegion::trim_to_internal (samplepos_t position, samplecnt_t length, const int32_t sub_num)
759 {
760 	if (locked()) {
761 		return;
762 	}
763 
764 	PropertyChange what_changed;
765 
766 
767 	/* Set position before length, otherwise for MIDI regions this bad thing happens:
768 	 * 1. we call set_length_internal; length in beats is computed using the region's current
769 	 *    (soon-to-be old) position
770 	 * 2. we call set_position_internal; position is set and length in samples re-computed using
771 	 *    length in beats from (1) but at the new position, which is wrong if the region
772 	 *    straddles a tempo/meter change.
773 	 */
774 
775 	if (_position != position) {
776 
777 		const double pos_qn = _session.tempo_map().exact_qn_at_sample (position, sub_num);
778 		const double old_pos_qn = quarter_note();
779 
780 		/* sets _pulse to new position.*/
781 		set_position_internal (position, true, sub_num);
782 		what_changed.add (Properties::position);
783 
784 		double new_start_qn = start_beats() + (pos_qn - old_pos_qn);
785 		samplepos_t new_start = _session.tempo_map().samples_between_quarter_notes (pos_qn - new_start_qn, pos_qn);
786 
787 		if (!verify_start_and_length (new_start, length)) {
788 			return;
789 		}
790 
791 		_start_beats = new_start_qn;
792 		what_changed.add (Properties::start_beats);
793 
794 		set_start_internal (new_start, sub_num);
795 		what_changed.add (Properties::start);
796 	}
797 
798 	if (_length != length) {
799 
800 		if (!verify_start_and_length (_start, length)) {
801 			return;
802 		}
803 
804 		set_length_internal (length, sub_num);
805 		what_changed.add (Properties::length);
806 		what_changed.add (Properties::length_beats);
807 	}
808 
809 	set_whole_file (false);
810 
811 	PropertyChange start_and_length;
812 
813 	start_and_length.add (Properties::start);
814 	start_and_length.add (Properties::length);
815 
816 	if (what_changed.contains (start_and_length)) {
817 		first_edit ();
818 	}
819 
820 	if (!what_changed.empty()) {
821 		send_change (what_changed);
822 	}
823 }
824 
825 bool
set_name(const std::string & str)826 MidiRegion::set_name (const std::string& str)
827 {
828 	if (_name == str) {
829 		return true;
830 	}
831 
832 	if (!Session::session_name_is_legal (str).empty ()) {
833 		return false;
834 	}
835 
836 	return Region::set_name (str);
837 }
838