1 /*
2  * Copyright (C) 2006-2016 David Robillard <d@drobilla.net>
3  * Copyright (C) 2007-2018 Paul Davis <paul@linuxaudiosystems.com>
4  * Copyright (C) 2008-2009 Hans Baier <hansfbaier@googlemail.com>
5  * Copyright (C) 2009-2011 Carl Hetherington <carl@carlh.net>
6  * Copyright (C) 2012-2016 Tim Mayberry <mojofunk@gmail.com>
7  * Copyright (C) 2014-2015 Robin Gareus <robin@gareus.org>
8  * Copyright (C) 2014-2016 John Emmas <john@creativepost.co.uk>
9  * Copyright (C) 2016 Nick Mainsbridge <mainsbridge@gmail.com>
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 <vector>
27 
28 #include <sys/time.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <regex.h>
33 
34 #include "pbd/file_utils.h"
35 #include "pbd/stl_delete.h"
36 #include "pbd/strsplit.h"
37 #include "pbd/timing.h"
38 
39 #include "pbd/gstdio_compat.h"
40 #include <glibmm/miscutils.h>
41 #include <glibmm/fileutils.h>
42 
43 #include "evoral/Control.h"
44 #include "evoral/SMF.h"
45 
46 #include "ardour/debug.h"
47 #include "ardour/midi_channel_filter.h"
48 #include "ardour/midi_model.h"
49 #include "ardour/midi_ring_buffer.h"
50 #include "ardour/midi_state_tracker.h"
51 #include "ardour/parameter_types.h"
52 #include "ardour/session.h"
53 #include "ardour/smf_source.h"
54 
55 #include "pbd/i18n.h"
56 
57 using namespace ARDOUR;
58 using namespace Glib;
59 using namespace PBD;
60 using namespace Evoral;
61 using namespace std;
62 
63 /** Constructor used for new internal-to-session files.  File cannot exist. */
SMFSource(Session & s,const string & path,Source::Flag flags)64 SMFSource::SMFSource (Session& s, const string& path, Source::Flag flags)
65 	: Source(s, DataType::MIDI, path, flags)
66 	, MidiSource(s, path, flags)
67 	, FileSource(s, DataType::MIDI, path, string(), flags)
68 	, Evoral::SMF()
69 	, _open (false)
70 	, _last_ev_time_beats(0.0)
71 	, _last_ev_time_samples(0)
72 	, _smf_last_read_end (0)
73 	, _smf_last_read_time (0)
74 {
75 	/* note that origin remains empty */
76 
77 	if (init (_path, false)) {
78 		throw failed_constructor ();
79 	}
80 
81         assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
82 	existence_check ();
83 
84 	_flags = Source::Flag (_flags | Empty);
85 
86 	/* file is not opened until write */
87 
88 	if (flags & Writable) {
89 		return;
90 	}
91 
92 	if (open (_path)) {
93 		throw failed_constructor ();
94 	}
95 
96 	_open = true;
97 }
98 
99 /** Constructor used for external-to-session files.  File must exist. */
SMFSource(Session & s,const string & path)100 SMFSource::SMFSource (Session& s, const string& path)
101 	: Source(s, DataType::MIDI, path, Source::Flag (0))
102 	, MidiSource(s, path, Source::Flag (0))
103 	, FileSource(s, DataType::MIDI, path, string(), Source::Flag (0))
104 	, Evoral::SMF()
105 	, _open (false)
106 	, _last_ev_time_beats(0.0)
107 	, _last_ev_time_samples(0)
108 	, _smf_last_read_end (0)
109 	, _smf_last_read_time (0)
110 {
111 	/* note that origin remains empty */
112 
113 	if (init (_path, true)) {
114 		throw failed_constructor ();
115 	}
116 
117         assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
118 	existence_check ();
119 
120 	if (_flags & Writable) {
121 		/* file is not opened until write */
122 		return;
123 	}
124 
125 	if (open (_path)) {
126 		throw failed_constructor ();
127 	}
128 
129 	_open = true;
130 }
131 
132 /** Constructor used for existing internal-to-session files. */
SMFSource(Session & s,const XMLNode & node,bool must_exist)133 SMFSource::SMFSource (Session& s, const XMLNode& node, bool must_exist)
134 	: Source(s, node)
135 	, MidiSource(s, node)
136 	, FileSource(s, node, must_exist)
137 	, _open (false)
138 	, _last_ev_time_beats(0.0)
139 	, _last_ev_time_samples(0)
140 	, _smf_last_read_end (0)
141 	, _smf_last_read_time (0)
142 {
143 	if (set_state(node, Stateful::loading_state_version)) {
144 		throw failed_constructor ();
145 	}
146 
147 	/* we expect the file to exist, but if no MIDI data was ever added
148 	   it will have been removed at last session close. so, we don't
149 	   require it to exist if it was marked Empty.
150 	*/
151 
152 	try {
153 
154 		if (init (_path, true)) {
155 			throw failed_constructor ();
156 		}
157 
158 	} catch (MissingSource& err) {
159 		if (0 == (_flags & Source::Empty)) {
160 			/* Don't throw, create the source.
161 			 * Since MIDI is writable, we cannot use a SilentFileSource.
162 			 */
163 			_flags = Source::Flag (_flags | Source::Empty | Source::Missing);
164 		}
165 
166 		/* we don't care that the file was not found, because
167 			 it was empty. But FileSource::init() will have
168 			 failed to set our _path correctly, so we have to do
169 			 this ourselves. Use the first entry in the search
170 			 path for MIDI files, which is assumed to be the
171 			 correct "main" location.
172 		*/
173 		std::vector<string> sdirs = s.source_search_path (DataType::MIDI);
174 		_path = Glib::build_filename (sdirs.front(), _path);
175 		/* This might be important, too */
176 		_file_is_new = true;
177 	}
178 
179 	if (!(_flags & Source::Empty)) {
180 		assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
181 		existence_check ();
182 	} else {
183 		assert (_flags & Source::Writable);
184 		/* file will be opened on write */
185 		return;
186 	}
187 
188 	if (open (_path)) {
189 		throw failed_constructor ();
190 	}
191 
192 	_open = true;
193 }
194 
~SMFSource()195 SMFSource::~SMFSource ()
196 {
197 	if (removable()) {
198 		::g_unlink (_path.c_str());
199 	}
200 }
201 
202 int
open_for_write()203 SMFSource::open_for_write ()
204 {
205 	if (create (_path)) {
206 		return -1;
207 	}
208 	_open = true;
209 	return 0;
210 }
211 
212 void
close()213 SMFSource::close ()
214 {
215 	/* nothing to do: file descriptor is never kept open.
216 	 * Note, keep `_open = true` regardless.
217 	 */
218 }
219 
220 extern PBD::Timing minsert;
221 
222 /** All stamps in audio samples */
223 samplecnt_t
read_unlocked(const Lock & lock,Evoral::EventSink<samplepos_t> & destination,samplepos_t const source_start,samplepos_t start,samplecnt_t duration,Evoral::Range<samplepos_t> * loop_range,MidiStateTracker * tracker,MidiChannelFilter * filter) const224 SMFSource::read_unlocked (const Lock&                    lock,
225                           Evoral::EventSink<samplepos_t>& destination,
226                           samplepos_t const               source_start,
227                           samplepos_t                     start,
228                           samplecnt_t                     duration,
229                           Evoral::Range<samplepos_t>*     loop_range,
230                           MidiStateTracker*              tracker,
231                           MidiChannelFilter*             filter) const
232 {
233 	int      ret  = 0;
234 	uint64_t time = 0; // in SMF ticks, 1 tick per _ppqn
235 
236 	if (writable() && !_open) {
237 		/* nothing to read since nothing has ben written */
238 		return duration;
239 	}
240 
241 	DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: start %1 duration %2\n", start, duration));
242 
243 	// Output parameters for read_event (which will allocate scratch in buffer as needed)
244 	uint32_t ev_delta_t = 0;
245 	uint32_t ev_size    = 0;
246 	uint8_t* ev_buffer  = 0;
247 
248 	size_t scratch_size = 0; // keep track of scratch to minimize reallocs
249 
250 	BeatsSamplesConverter converter(_session.tempo_map(), source_start);
251 
252 	const uint64_t start_ticks = converter.from(start).to_ticks();
253 	DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: start in ticks %1\n", start_ticks));
254 
255 	if (_smf_last_read_end == 0 || start != _smf_last_read_end) {
256 		DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: seek to %1\n", start));
257 		Evoral::SMF::seek_to_start();
258 		while (time < start_ticks) {
259 			Evoral::event_id_t ignored;
260 
261 			ret = read_event(&ev_delta_t, &ev_size, &ev_buffer, &ignored);
262 			if (ret == -1) { // EOF
263 				_smf_last_read_end = start + duration;
264 				return duration;
265 			}
266 			time += ev_delta_t; // accumulate delta time
267 		}
268 	} else {
269 		DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: set time to %1\n", _smf_last_read_time));
270 		time = _smf_last_read_time;
271 	}
272 
273 	_smf_last_read_end = start + duration;
274 
275 	while (true) {
276 		Evoral::event_id_t ignored; /* XXX don't ignore note id's ??*/
277 
278 		ret = read_event(&ev_delta_t, &ev_size, &ev_buffer, &ignored);
279 		if (ret == -1) { // EOF
280 			break;
281 		}
282 
283 		time += ev_delta_t; // accumulate delta time
284 		_smf_last_read_time = time;
285 
286 		if (ret == 0) { // meta-event (skipped, just accumulate time)
287 			continue;
288 		}
289 
290 		DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked delta %1, time %2, buf[0] %3\n",
291 								  ev_delta_t, time, ev_buffer[0]));
292 
293 		assert(time >= start_ticks);
294 
295 		/* Note that we add on the source start time (in session samples) here so that ev_sample_time
296 		   is in session samples.
297 		*/
298 		const samplepos_t ev_sample_time = converter.to(Temporal::Beats::ticks_at_rate(time, ppqn())) + source_start;
299 
300 		if (loop_range) {
301 			loop_range->squish (ev_sample_time);
302 		}
303 
304 		if (ev_sample_time < start + duration) {
305 			if (!filter || !filter->filter(ev_buffer, ev_size)) {
306 				destination.write (ev_sample_time, Evoral::MIDI_EVENT, ev_size, ev_buffer);
307 				if (tracker) {
308 					tracker->track(ev_buffer);
309 				}
310 			}
311 		} else {
312 			break;
313 		}
314 
315 		if (ev_size > scratch_size) {
316 			scratch_size = ev_size;
317 		}
318 		ev_size = scratch_size; // ensure read_event only allocates if necessary
319 	}
320 
321 	return duration;
322 }
323 
324 samplecnt_t
write_unlocked(const Lock & lock,MidiRingBuffer<samplepos_t> & source,samplepos_t position,samplecnt_t cnt)325 SMFSource::write_unlocked (const Lock&                 lock,
326                            MidiRingBuffer<samplepos_t>& source,
327                            samplepos_t                  position,
328                            samplecnt_t                  cnt)
329 {
330 	if (!_writing) {
331 		mark_streaming_write_started (lock);
332 	}
333 
334 	samplepos_t        time;
335 	Evoral::EventType type;
336 	uint32_t          size;
337 
338 	size_t   buf_capacity = 4;
339 	uint8_t* buf          = (uint8_t*)malloc(buf_capacity);
340 
341 	if (_model && !_model->writing()) {
342 		_model->start_write();
343 	}
344 
345 	Evoral::Event<samplepos_t> ev;
346 	while (true) {
347 		/* Get the event time, in samples since session start but ignoring looping. */
348 		bool ret;
349 		if (!(ret = source.peek ((uint8_t*)&time, sizeof (time)))) {
350 			/* Ring is empty, no more events. */
351 			break;
352 		}
353 
354 		if ((cnt != max_samplecnt) &&
355 		    (time > position + _capture_length + cnt)) {
356 			/* The diskstream doesn't want us to write everything, and this
357 			   event is past the end of this block, so we're done for now. */
358 			break;
359 		}
360 
361 		/* Read the time, type, and size of the event. */
362 		if (!(ret = source.read_prefix (&time, &type, &size))) {
363 			error << _("Unable to read event prefix, corrupt MIDI ring") << endmsg;
364 			break;
365 		}
366 
367 		/* Enlarge body buffer if necessary now that we know the size. */
368 		if (size > buf_capacity) {
369 			buf_capacity = size;
370 			buf = (uint8_t*)realloc(buf, size);
371 		}
372 
373 		/* Read the event body into buffer. */
374 		ret = source.read_contents(size, buf);
375 		if (!ret) {
376 			error << _("Event has time and size but no body, corrupt MIDI ring") << endmsg;
377 			break;
378 		}
379 
380 		/* Convert event time from absolute to source relative. */
381 		if (time < position) {
382 			error << _("Event time is before MIDI source position") << endmsg;
383 			break;
384 		}
385 		time -= position;
386 
387 		ev.set(buf, size, time);
388 		ev.set_event_type(Evoral::MIDI_EVENT);
389 		ev.set_id(Evoral::next_event_id());
390 
391 		if (!(ev.is_channel_event() || ev.is_smf_meta_event() || ev.is_sysex())) {
392 			continue;
393 		}
394 
395 		append_event_samples(lock, ev, position);
396 	}
397 
398 	Evoral::SMF::flush ();
399 	free (buf);
400 
401 	return cnt;
402 }
403 
404 /** Append an event with a timestamp in beats */
405 void
append_event_beats(const Glib::Threads::Mutex::Lock & lock,const Evoral::Event<Temporal::Beats> & ev)406 SMFSource::append_event_beats (const Glib::Threads::Mutex::Lock&   lock,
407                                const Evoral::Event<Temporal::Beats>& ev)
408 {
409 	if (!_writing || ev.size() == 0)  {
410 		return;
411 	}
412 
413 #if 0
414 	printf("SMFSource: %s - append_event_beats ID = %d time = %lf, size = %u, data = ",
415                name().c_str(), ev.id(), ev.time(), ev.size());
416 	       for (size_t i = 0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");
417 #endif
418 
419 	Temporal::Beats time = ev.time();
420 	if (time < _last_ev_time_beats) {
421 		const Temporal::Beats difference = _last_ev_time_beats - time;
422 		if (difference.to_double() / (double)ppqn() < 1.0) {
423 			/* Close enough.  This problem occurs because Sequence is not
424 			   actually ordered due to fuzzy time comparison.  I'm pretty sure
425 			   this is inherently a bad idea which causes problems all over the
426 			   place, but tolerate it here for now anyway. */
427 			time = _last_ev_time_beats;
428 		} else {
429 			/* Out of order by more than a tick. */
430 			warning << string_compose(_("Skipping event with unordered beat time %1 < %2 (off by %3 beats, %4 ticks)"),
431 			                          ev.time(), _last_ev_time_beats, difference, difference.to_double() / (double)ppqn())
432 			        << endmsg;
433 			return;
434 		}
435 	}
436 
437 	Evoral::event_id_t event_id;
438 
439 	if (ev.id() < 0) {
440 		event_id  = Evoral::next_event_id();
441 	} else {
442 		event_id = ev.id();
443 	}
444 
445 	if (_model) {
446 		_model->append (ev, event_id);
447 	}
448 
449 	_length_beats = max(_length_beats, time);
450 
451 	const Temporal::Beats delta_time_beats = time - _last_ev_time_beats;
452 	const uint32_t      delta_time_ticks = delta_time_beats.to_ticks(ppqn());
453 
454 	Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
455 	_last_ev_time_beats = time;
456 	_flags = Source::Flag (_flags & ~Empty);
457 	_flags = Source::Flag (_flags & ~Missing);
458 }
459 
460 /** Append an event with a timestamp in samples (samplepos_t) */
461 void
append_event_samples(const Glib::Threads::Mutex::Lock & lock,const Evoral::Event<samplepos_t> & ev,samplepos_t position)462 SMFSource::append_event_samples (const Glib::Threads::Mutex::Lock& lock,
463                                 const Evoral::Event<samplepos_t>&  ev,
464                                 samplepos_t                        position)
465 {
466 	if (!_writing || ev.size() == 0)  {
467 		return;
468 	}
469 
470 	// printf("SMFSource: %s - append_event_samples ID = %d time = %u, size = %u, data = ",
471 	// name().c_str(), ev.id(), ev.time(), ev.size());
472 	// for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");
473 
474 	if (ev.time() < _last_ev_time_samples) {
475 		warning << string_compose(_("Skipping event with unordered sample time %1 < %2"),
476 		                          ev.time(), _last_ev_time_samples)
477 		        << endmsg;
478 		return;
479 	}
480 
481 	BeatsSamplesConverter converter(_session.tempo_map(), position);
482 	const Temporal::Beats  ev_time_beats = converter.from(ev.time());
483 	Evoral::event_id_t   event_id;
484 
485 	if (ev.id() < 0) {
486 		event_id  = Evoral::next_event_id();
487 	} else {
488 		event_id = ev.id();
489 	}
490 
491 	if (_model) {
492 		const Evoral::Event<Temporal::Beats> beat_ev (ev.event_type(),
493 		                                            ev_time_beats,
494 		                                            ev.size(),
495 		                                            const_cast<uint8_t*>(ev.buffer()));
496 		_model->append (beat_ev, event_id);
497 	}
498 
499 	_length_beats = max(_length_beats, ev_time_beats);
500 
501 	const Temporal::Beats last_time_beats  = converter.from (_last_ev_time_samples);
502 	const Temporal::Beats delta_time_beats = ev_time_beats - last_time_beats;
503 	const uint32_t      delta_time_ticks = delta_time_beats.to_ticks(ppqn());
504 
505 	Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
506 	_last_ev_time_samples = ev.time();
507 	_flags = Source::Flag (_flags & ~Empty);
508 	_flags = Source::Flag (_flags & ~Missing);
509 }
510 
511 XMLNode&
get_state()512 SMFSource::get_state ()
513 {
514 	XMLNode& node = MidiSource::get_state();
515 	node.set_property (X_("origin"), _origin);
516 	return node;
517 }
518 
519 int
set_state(const XMLNode & node,int version)520 SMFSource::set_state (const XMLNode& node, int version)
521 {
522 	if (Source::set_state (node, version)) {
523 		return -1;
524 	}
525 
526 	if (MidiSource::set_state (node, version)) {
527 		return -1;
528 	}
529 
530 	if (FileSource::set_state (node, version)) {
531 		return -1;
532 	}
533 
534 	return 0;
535 }
536 
537 void
mark_streaming_midi_write_started(const Lock & lock,NoteMode mode)538 SMFSource::mark_streaming_midi_write_started (const Lock& lock, NoteMode mode)
539 {
540 	if (!_open && open_for_write()) {
541 		error << string_compose (_("cannot open MIDI file %1 for write"), _path) << endmsg;
542 		/* XXX should probably throw or return something */
543 		return;
544 	}
545 
546 	MidiSource::mark_streaming_midi_write_started (lock, mode);
547 	Evoral::SMF::begin_write ();
548 	_last_ev_time_beats  = Temporal::Beats();
549 	_last_ev_time_samples = 0;
550 }
551 
552 void
mark_streaming_write_completed(const Lock & lock)553 SMFSource::mark_streaming_write_completed (const Lock& lock)
554 {
555 	mark_midi_streaming_write_completed (lock, Evoral::Sequence<Temporal::Beats>::DeleteStuckNotes);
556 }
557 
558 void
mark_midi_streaming_write_completed(const Lock & lm,Evoral::Sequence<Temporal::Beats>::StuckNoteOption stuck_notes_option,Temporal::Beats when)559 SMFSource::mark_midi_streaming_write_completed (const Lock& lm, Evoral::Sequence<Temporal::Beats>::StuckNoteOption stuck_notes_option, Temporal::Beats when)
560 {
561 	MidiSource::mark_midi_streaming_write_completed (lm, stuck_notes_option, when);
562 
563 	if (!writable()) {
564 		warning << string_compose ("attempt to write to unwritable SMF file %1", _path) << endmsg;
565 		return;
566 	}
567 
568 	if (_model) {
569 		_model->set_edited(false);
570 	}
571 
572 	try {
573 		Evoral::SMF::end_write (_path);
574 	} catch (std::exception & e) {
575 		error << string_compose (_("Exception while writing %1, file may be corrupt/unusable"), _path) << endmsg;
576 	}
577 
578 	/* data in the file now, not removable */
579 
580 	mark_nonremovable ();
581 }
582 
583 bool
valid_midi_file(const string & file)584 SMFSource::valid_midi_file (const string& file)
585 {
586 	if (safe_midi_file_extension (file) ) {
587 		return (SMF::test (file) );
588 	}
589 	return false;
590 }
591 
592 bool
safe_midi_file_extension(const string & file)593 SMFSource::safe_midi_file_extension (const string& file)
594 {
595 	static regex_t compiled_pattern;
596 	static bool compile = true;
597 	const int nmatches = 2;
598 	regmatch_t matches[nmatches];
599 
600 	if (Glib::file_test (file, Glib::FILE_TEST_EXISTS)) {
601 		if (!Glib::file_test (file, Glib::FILE_TEST_IS_REGULAR)) {
602 			/* exists but is not a regular file */
603 			return false;
604 		}
605 	}
606 
607 	if (compile && regcomp (&compiled_pattern, "\\.[mM][iI][dD][iI]?$", REG_EXTENDED)) {
608 		return false;
609 	} else {
610 		compile = false;
611 	}
612 
613 	if (regexec (&compiled_pattern, file.c_str(), nmatches, matches, 0)) {
614 		return false;
615 	}
616 
617 	return true;
618 }
619 
compare_eventlist(const std::pair<const Evoral::Event<Temporal::Beats> *,gint> & a,const std::pair<const Evoral::Event<Temporal::Beats> *,gint> & b)620 static bool compare_eventlist (
621 	const std::pair< const Evoral::Event<Temporal::Beats>*, gint >& a,
622 	const std::pair< const Evoral::Event<Temporal::Beats>*, gint >& b) {
623 	return ( a.first->time() < b.first->time() );
624 }
625 
626 void
load_model(const Glib::Threads::Mutex::Lock & lock,bool force_reload)627 SMFSource::load_model (const Glib::Threads::Mutex::Lock& lock, bool force_reload)
628 {
629 	if (_writing) {
630 		return;
631 	}
632 
633 	if (_model && !force_reload) {
634 		return;
635 	}
636 
637 	if (!_model) {
638 		boost::shared_ptr<SMFSource> smf = boost::dynamic_pointer_cast<SMFSource> ( shared_from_this () );
639 		_model = boost::shared_ptr<MidiModel> (new MidiModel (smf));
640 	} else {
641 		_model->clear();
642 	}
643 
644 	invalidate(lock);
645 
646 	if (writable() && !_open) {
647 		return;
648 	}
649 
650 	_model->start_write();
651 	Evoral::SMF::seek_to_start();
652 
653 	uint64_t time = 0; /* in SMF ticks */
654 	Evoral::Event<Temporal::Beats> ev;
655 
656 	uint32_t scratch_size = 0; // keep track of scratch and minimize reallocs
657 
658 	uint32_t delta_t = 0;
659 	uint32_t size    = 0;
660 	uint8_t* buf     = NULL;
661 	int ret;
662 	Evoral::event_id_t event_id;
663 	bool have_event_id;
664 
665 	// TODO simplify event allocation
666 	std::list< std::pair< Evoral::Event<Temporal::Beats>*, gint > > eventlist;
667 
668 	for (unsigned i = 1; i <= num_tracks(); ++i) {
669 		if (seek_to_track(i)) continue;
670 
671 		time = 0;
672 		have_event_id = false;
673 
674 		while ((ret = read_event (&delta_t, &size, &buf, &event_id)) >= 0) {
675 
676 			time += delta_t;
677 
678 			if (ret == 0) {
679 				/* meta-event : did we get an event ID ?  */
680 				if (event_id >= 0) {
681 					have_event_id = true;
682 				}
683 				continue;
684 			}
685 
686 			if (ret > 0) {
687 				/* not a meta-event */
688 
689 				if (!have_event_id) {
690 					event_id = Evoral::next_event_id();
691 				}
692 				const Temporal::Beats event_time = Temporal::Beats::ticks_at_rate(time, ppqn());
693 #ifndef NDEBUG
694 				std::string ss;
695 
696 				for (uint32_t xx = 0; xx < size; ++xx) {
697 					char b[8];
698 					snprintf (b, sizeof (b), "0x%x ", buf[xx]);
699 					ss += b;
700 				}
701 
702 				DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF %7 load model delta %1, time %2, size %3 buf %4, id %6\n",
703 							delta_t, time, size, ss, event_id, name()));
704 #endif
705 
706 				eventlist.push_back(make_pair (
707 							new Evoral::Event<Temporal::Beats> (
708 								Evoral::MIDI_EVENT, event_time,
709 								size, buf, true)
710 							, event_id));
711 
712 				// Set size to max capacity to minimize allocs in read_event
713 				scratch_size = std::max(size, scratch_size);
714 				size = scratch_size;
715 
716 				_length_beats = max(_length_beats, event_time);
717 			}
718 
719 			/* event ID's must immediately precede the event they are for */
720 			have_event_id = false;
721 		}
722 	}
723 
724 	eventlist.sort(compare_eventlist);
725 
726 	std::list< std::pair< Evoral::Event<Temporal::Beats>*, gint > >::iterator it;
727 	for (it=eventlist.begin(); it!=eventlist.end(); ++it) {
728 		_model->append (*it->first, it->second);
729 		delete it->first;
730 	}
731 
732         // cerr << "----SMF-SRC-----\n";
733         // _playback_buf->dump (cerr);
734         // cerr << "----------------\n";
735 
736 	_model->end_write (Evoral::Sequence<Temporal::Beats>::ResolveStuckNotes, _length_beats);
737 	_model->set_edited (false);
738 	invalidate(lock);
739 
740 	free(buf);
741 }
742 
743 void
destroy_model(const Glib::Threads::Mutex::Lock & lock)744 SMFSource::destroy_model (const Glib::Threads::Mutex::Lock& lock)
745 {
746 	//cerr << _name << " destroying model " << _model.get() << endl;
747 	_model.reset();
748 	invalidate(lock);
749 }
750 
751 void
flush_midi(const Lock & lock)752 SMFSource::flush_midi (const Lock& lock)
753 {
754 	if (!writable() || _length_beats == 0.0) {
755 		return;
756 	}
757 
758 	ensure_disk_file (lock);
759 
760 	Evoral::SMF::end_write (_path);
761 	/* data in the file means its no longer removable */
762 	mark_nonremovable ();
763 
764 	invalidate(lock);
765 }
766 
767 void
set_path(const string & p)768 SMFSource::set_path (const string& p)
769 {
770 	FileSource::set_path (p);
771 }
772 
773 /** Ensure that this source has some file on disk, even if it's just a SMF header */
774 void
ensure_disk_file(const Lock & lock)775 SMFSource::ensure_disk_file (const Lock& lock)
776 {
777 	if (!writable()) {
778 		return;
779 	}
780 
781 	if (_model) {
782 		/* We have a model, so write it to disk; see MidiSource::session_saved
783 		   for an explanation of what we are doing here.
784 		*/
785 		boost::shared_ptr<MidiModel> mm = _model;
786 		_model.reset ();
787 		mm->sync_to_source (lock);
788 		_model = mm;
789 		invalidate(lock);
790 	} else {
791 		/* No model; if it's not already open, it's an empty source, so create
792 		   and open it for writing.
793 		*/
794 		if (!_open) {
795 			open_for_write ();
796 		}
797 	}
798 }
799 
800 void
prevent_deletion()801 SMFSource::prevent_deletion ()
802 {
803 	/* Unlike the audio case, the MIDI file remains mutable (because we can
804 	   edit MIDI data)
805 	*/
806 
807 	_flags = Flag (_flags & ~(Removable|RemovableIfEmpty|RemoveAtDestroy));
808 }
809