2 Copyright (C) 2006 Paul Davis
3 Author: David Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "pbd/file_utils.h"
30 #include "pbd/stl_delete.h"
31 #include "pbd/strsplit.h"
33 #include "pbd/gstdio_compat.h"
34 #include <glibmm/miscutils.h>
35 #include <glibmm/fileutils.h>
37 #include "evoral/Control.hpp"
38 #include "evoral/SMF.hpp"
40 #include "ardour/debug.h"
41 #include "ardour/midi_channel_filter.h"
42 #include "ardour/midi_model.h"
43 #include "ardour/midi_ring_buffer.h"
44 #include "ardour/midi_state_tracker.h"
45 #include "ardour/parameter_types.h"
46 #include "ardour/session.h"
47 #include "ardour/smf_source.h"
51 using namespace ARDOUR;
54 using namespace Evoral;
57 /** Constructor used for new internal-to-session files. File cannot exist. */
58 SMFSource::SMFSource (Session& s, const string& path, Source::Flag flags)
59 : Source(s, DataType::MIDI, path, flags)
60 , MidiSource(s, path, flags)
61 , FileSource(s, DataType::MIDI, path, string(), flags)
64 , _last_ev_time_beats(0.0)
65 , _last_ev_time_samples(0)
66 , _smf_last_read_end (0)
67 , _smf_last_read_time (0)
69 /* note that origin remains empty */
71 if (init (_path, false)) {
72 throw failed_constructor ();
75 assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
78 _flags = Source::Flag (_flags | Empty);
80 /* file is not opened until write */
82 if (flags & Writable) {
87 throw failed_constructor ();
93 /** Constructor used for external-to-session files. File must exist. */
94 SMFSource::SMFSource (Session& s, const string& path)
95 : Source(s, DataType::MIDI, path, Source::Flag (0))
96 , MidiSource(s, path, Source::Flag (0))
97 , FileSource(s, DataType::MIDI, path, string(), Source::Flag (0))
100 , _last_ev_time_beats(0.0)
101 , _last_ev_time_samples(0)
102 , _smf_last_read_end (0)
103 , _smf_last_read_time (0)
105 /* note that origin remains empty */
107 if (init (_path, true)) {
108 throw failed_constructor ();
111 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
114 if (_flags & Writable) {
115 /* file is not opened until write */
120 throw failed_constructor ();
126 /** Constructor used for existing internal-to-session files. */
127 SMFSource::SMFSource (Session& s, const XMLNode& node, bool must_exist)
129 , MidiSource(s, node)
130 , FileSource(s, node, must_exist)
132 , _last_ev_time_beats(0.0)
133 , _last_ev_time_samples(0)
134 , _smf_last_read_end (0)
135 , _smf_last_read_time (0)
137 if (set_state(node, Stateful::loading_state_version)) {
138 throw failed_constructor ();
141 /* we expect the file to exist, but if no MIDI data was ever added
142 it will have been removed at last session close. so, we don't
143 require it to exist if it was marked Empty.
148 if (init (_path, true)) {
149 throw failed_constructor ();
152 } catch (MissingSource& err) {
154 if (_flags & Source::Empty) {
155 /* we don't care that the file was not found, because
156 it was empty. But FileSource::init() will have
157 failed to set our _path correctly, so we have to do
158 this ourselves. Use the first entry in the search
159 path for MIDI files, which is assumed to be the
160 correct "main" location.
162 std::vector<string> sdirs = s.source_search_path (DataType::MIDI);
163 _path = Glib::build_filename (sdirs.front(), _path);
164 /* This might be important, too */
172 if (!(_flags & Source::Empty)) {
173 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
176 assert (_flags & Source::Writable);
177 /* file will be opened on write */
182 throw failed_constructor ();
188 SMFSource::~SMFSource ()
191 ::g_unlink (_path.c_str());
196 SMFSource::open_for_write ()
198 if (create (_path)) {
208 /* nothing to do: file descriptor is never kept open */
211 /** All stamps in audio samples */
213 SMFSource::read_unlocked (const Lock& lock,
214 Evoral::EventSink<samplepos_t>& destination,
215 samplepos_t const source_start,
217 samplecnt_t duration,
218 Evoral::Range<samplepos_t>* loop_range,
219 MidiStateTracker* tracker,
220 MidiChannelFilter* filter) const
223 uint64_t time = 0; // in SMF ticks, 1 tick per _ppqn
225 if (writable() && !_open) {
226 /* nothing to read since nothing has ben written */
230 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: start %1 duration %2\n", start, duration));
232 // Output parameters for read_event (which will allocate scratch in buffer as needed)
233 uint32_t ev_delta_t = 0;
234 uint32_t ev_size = 0;
235 uint8_t* ev_buffer = 0;
237 size_t scratch_size = 0; // keep track of scratch to minimize reallocs
239 BeatsSamplesConverter converter(_session.tempo_map(), source_start);
241 const uint64_t start_ticks = converter.from(start).to_ticks();
242 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: start in ticks %1\n", start_ticks));
244 if (_smf_last_read_end == 0 || start != _smf_last_read_end) {
245 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: seek to %1\n", start));
246 Evoral::SMF::seek_to_start();
247 while (time < start_ticks) {
250 ret = read_event(&ev_delta_t, &ev_size, &ev_buffer, &ignored);
251 if (ret == -1) { // EOF
252 _smf_last_read_end = start + duration;
255 time += ev_delta_t; // accumulate delta time
258 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: set time to %1\n", _smf_last_read_time));
259 time = _smf_last_read_time;
262 _smf_last_read_end = start + duration;
265 gint ignored; /* XXX don't ignore note id's ??*/
267 ret = read_event(&ev_delta_t, &ev_size, &ev_buffer, &ignored);
268 if (ret == -1) { // EOF
272 time += ev_delta_t; // accumulate delta time
273 _smf_last_read_time = time;
275 if (ret == 0) { // meta-event (skipped, just accumulate time)
279 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked delta %1, time %2, buf[0] %3\n",
280 ev_delta_t, time, ev_buffer[0]));
282 assert(time >= start_ticks);
284 /* Note that we add on the source start time (in session samples) here so that ev_sample_time
285 is in session samples.
287 const samplepos_t ev_sample_time = converter.to(Temporal::Beats::ticks_at_rate(time, ppqn())) + source_start;
290 loop_range->squish (ev_sample_time);
293 if (ev_sample_time < start + duration) {
294 if (!filter || !filter->filter(ev_buffer, ev_size)) {
295 destination.write (ev_sample_time, Evoral::MIDI_EVENT, ev_size, ev_buffer);
297 tracker->track(ev_buffer);
304 if (ev_size > scratch_size) {
305 scratch_size = ev_size;
307 ev_size = scratch_size; // ensure read_event only allocates if necessary
314 SMFSource::write_unlocked (const Lock& lock,
315 MidiRingBuffer<samplepos_t>& source,
316 samplepos_t position,
320 mark_streaming_write_started (lock);
324 Evoral::EventType type;
327 size_t buf_capacity = 4;
328 uint8_t* buf = (uint8_t*)malloc(buf_capacity);
330 if (_model && !_model->writing()) {
331 _model->start_write();
334 Evoral::Event<samplepos_t> ev;
336 /* Get the event time, in samples since session start but ignoring looping. */
338 if (!(ret = source.peek ((uint8_t*)&time, sizeof (time)))) {
339 /* Ring is empty, no more events. */
343 if ((cnt != max_samplecnt) &&
344 (time > position + _capture_length + cnt)) {
345 /* The diskstream doesn't want us to write everything, and this
346 event is past the end of this block, so we're done for now. */
350 /* Read the time, type, and size of the event. */
351 if (!(ret = source.read_prefix (&time, &type, &size))) {
352 error << _("Unable to read event prefix, corrupt MIDI ring") << endmsg;
356 /* Enlarge body buffer if necessary now that we know the size. */
357 if (size > buf_capacity) {
359 buf = (uint8_t*)realloc(buf, size);
362 /* Read the event body into buffer. */
363 ret = source.read_contents(size, buf);
365 error << _("Event has time and size but no body, corrupt MIDI ring") << endmsg;
369 /* Convert event time from absolute to source relative. */
370 if (time < position) {
371 error << _("Event time is before MIDI source position") << endmsg;
376 ev.set(buf, size, time);
377 ev.set_event_type(Evoral::MIDI_EVENT);
378 ev.set_id(Evoral::next_event_id());
380 if (!(ev.is_channel_event() || ev.is_smf_meta_event() || ev.is_sysex())) {
384 append_event_samples(lock, ev, position);
387 Evoral::SMF::flush ();
393 /** Append an event with a timestamp in beats */
395 SMFSource::append_event_beats (const Glib::Threads::Mutex::Lock& lock,
396 const Evoral::Event<Temporal::Beats>& ev)
398 if (!_writing || ev.size() == 0) {
403 printf("SMFSource: %s - append_event_beats ID = %d time = %lf, size = %u, data = ",
404 name().c_str(), ev.id(), ev.time(), ev.size());
405 for (size_t i = 0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");
408 Temporal::Beats time = ev.time();
409 if (time < _last_ev_time_beats) {
410 const Temporal::Beats difference = _last_ev_time_beats - time;
411 if (difference.to_double() / (double)ppqn() < 1.0) {
412 /* Close enough. This problem occurs because Sequence is not
413 actually ordered due to fuzzy time comparison. I'm pretty sure
414 this is inherently a bad idea which causes problems all over the
415 place, but tolerate it here for now anyway. */
416 time = _last_ev_time_beats;
418 /* Out of order by more than a tick. */
419 warning << string_compose(_("Skipping event with unordered beat time %1 < %2 (off by %3 beats, %4 ticks)"),
420 ev.time(), _last_ev_time_beats, difference, difference.to_double() / (double)ppqn())
426 Evoral::event_id_t event_id;
429 event_id = Evoral::next_event_id();
435 _model->append (ev, event_id);
438 _length_beats = max(_length_beats, time);
440 const Temporal::Beats delta_time_beats = time - _last_ev_time_beats;
441 const uint32_t delta_time_ticks = delta_time_beats.to_ticks(ppqn());
443 Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
444 _last_ev_time_beats = time;
445 _flags = Source::Flag (_flags & ~Empty);
448 /** Append an event with a timestamp in samples (samplepos_t) */
450 SMFSource::append_event_samples (const Glib::Threads::Mutex::Lock& lock,
451 const Evoral::Event<samplepos_t>& ev,
452 samplepos_t position)
454 if (!_writing || ev.size() == 0) {
458 // printf("SMFSource: %s - append_event_samples ID = %d time = %u, size = %u, data = ",
459 // name().c_str(), ev.id(), ev.time(), ev.size());
460 // for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");
462 if (ev.time() < _last_ev_time_samples) {
463 warning << string_compose(_("Skipping event with unordered sample time %1 < %2"),
464 ev.time(), _last_ev_time_samples)
469 BeatsSamplesConverter converter(_session.tempo_map(), position);
470 const Temporal::Beats ev_time_beats = converter.from(ev.time());
471 Evoral::event_id_t event_id;
474 event_id = Evoral::next_event_id();
480 const Evoral::Event<Temporal::Beats> beat_ev (ev.event_type(),
483 const_cast<uint8_t*>(ev.buffer()));
484 _model->append (beat_ev, event_id);
487 _length_beats = max(_length_beats, ev_time_beats);
489 const Temporal::Beats last_time_beats = converter.from (_last_ev_time_samples);
490 const Temporal::Beats delta_time_beats = ev_time_beats - last_time_beats;
491 const uint32_t delta_time_ticks = delta_time_beats.to_ticks(ppqn());
493 Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
494 _last_ev_time_samples = ev.time();
495 _flags = Source::Flag (_flags & ~Empty);
499 SMFSource::get_state ()
501 XMLNode& node = MidiSource::get_state();
502 node.set_property (X_("origin"), _origin);
507 SMFSource::set_state (const XMLNode& node, int version)
509 if (Source::set_state (node, version)) {
513 if (MidiSource::set_state (node, version)) {
517 if (FileSource::set_state (node, version)) {
525 SMFSource::mark_streaming_midi_write_started (const Lock& lock, NoteMode mode)
527 if (!_open && open_for_write()) {
528 error << string_compose (_("cannot open MIDI file %1 for write"), _path) << endmsg;
529 /* XXX should probably throw or return something */
533 MidiSource::mark_streaming_midi_write_started (lock, mode);
534 Evoral::SMF::begin_write ();
535 _last_ev_time_beats = Temporal::Beats();
536 _last_ev_time_samples = 0;
540 SMFSource::mark_streaming_write_completed (const Lock& lock)
542 mark_midi_streaming_write_completed (lock, Evoral::Sequence<Temporal::Beats>::DeleteStuckNotes);
546 SMFSource::mark_midi_streaming_write_completed (const Lock& lm, Evoral::Sequence<Temporal::Beats>::StuckNoteOption stuck_notes_option, Temporal::Beats when)
548 MidiSource::mark_midi_streaming_write_completed (lm, stuck_notes_option, when);
551 warning << string_compose ("attempt to write to unwritable SMF file %1", _path) << endmsg;
556 _model->set_edited(false);
560 Evoral::SMF::end_write (_path);
561 } catch (std::exception & e) {
562 error << string_compose (_("Exception while writing %1, file may be corrupt/unusable"), _path) << endmsg;
565 /* data in the file now, not removable */
567 mark_nonremovable ();
571 SMFSource::valid_midi_file (const string& file)
573 if (safe_midi_file_extension (file) ) {
574 return (SMF::test (file) );
580 SMFSource::safe_midi_file_extension (const string& file)
582 static regex_t compiled_pattern;
583 static bool compile = true;
584 const int nmatches = 2;
585 regmatch_t matches[nmatches];
587 if (Glib::file_test (file, Glib::FILE_TEST_EXISTS)) {
588 if (!Glib::file_test (file, Glib::FILE_TEST_IS_REGULAR)) {
589 /* exists but is not a regular file */
594 if (compile && regcomp (&compiled_pattern, "\\.[mM][iI][dD][iI]?$", REG_EXTENDED)) {
600 if (regexec (&compiled_pattern, file.c_str(), nmatches, matches, 0)) {
607 static bool compare_eventlist (
608 const std::pair< const Evoral::Event<Temporal::Beats>*, gint >& a,
609 const std::pair< const Evoral::Event<Temporal::Beats>*, gint >& b) {
610 return ( a.first->time() < b.first->time() );
614 SMFSource::load_model (const Glib::Threads::Mutex::Lock& lock, bool force_reload)
620 if (_model && !force_reload) {
625 _model = boost::shared_ptr<MidiModel> (new MidiModel (shared_from_this ()));
632 if (writable() && !_open) {
636 _model->start_write();
637 Evoral::SMF::seek_to_start();
639 uint64_t time = 0; /* in SMF ticks */
640 Evoral::Event<Temporal::Beats> ev;
642 uint32_t scratch_size = 0; // keep track of scratch and minimize reallocs
644 uint32_t delta_t = 0;
651 // TODO simplify event allocation
652 std::list< std::pair< Evoral::Event<Temporal::Beats>*, gint > > eventlist;
654 for (unsigned i = 1; i <= num_tracks(); ++i) {
655 if (seek_to_track(i)) continue;
658 have_event_id = false;
660 while ((ret = read_event (&delta_t, &size, &buf, &event_id)) >= 0) {
665 /* meta-event : did we get an event ID ? */
667 have_event_id = true;
673 /* not a meta-event */
675 if (!have_event_id) {
676 event_id = Evoral::next_event_id();
678 const Temporal::Beats event_time = Temporal::Beats::ticks_at_rate(time, ppqn());
682 for (uint32_t xx = 0; xx < size; ++xx) {
684 snprintf (b, sizeof (b), "0x%x ", buf[xx]);
688 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF %7 load model delta %1, time %2, size %3 buf %4, id %6\n",
689 delta_t, time, size, ss, event_id, name()));
692 eventlist.push_back(make_pair (
693 new Evoral::Event<Temporal::Beats> (
694 Evoral::MIDI_EVENT, event_time,
698 // Set size to max capacity to minimize allocs in read_event
699 scratch_size = std::max(size, scratch_size);
702 _length_beats = max(_length_beats, event_time);
705 /* event ID's must immediately precede the event they are for */
706 have_event_id = false;
710 eventlist.sort(compare_eventlist);
712 std::list< std::pair< Evoral::Event<Temporal::Beats>*, gint > >::iterator it;
713 for (it=eventlist.begin(); it!=eventlist.end(); ++it) {
714 _model->append (*it->first, it->second);
718 // cerr << "----SMF-SRC-----\n";
719 // _playback_buf->dump (cerr);
720 // cerr << "----------------\n";
722 _model->end_write (Evoral::Sequence<Temporal::Beats>::ResolveStuckNotes, _length_beats);
723 _model->set_edited (false);
730 SMFSource::destroy_model (const Glib::Threads::Mutex::Lock& lock)
732 //cerr << _name << " destroying model " << _model.get() << endl;
738 SMFSource::flush_midi (const Lock& lock)
740 if (!writable() || _length_beats == 0.0) {
744 ensure_disk_file (lock);
746 Evoral::SMF::end_write (_path);
747 /* data in the file means its no longer removable */
748 mark_nonremovable ();
754 SMFSource::set_path (const string& p)
756 FileSource::set_path (p);
759 /** Ensure that this source has some file on disk, even if it's just a SMF header */
761 SMFSource::ensure_disk_file (const Lock& lock)
768 /* We have a model, so write it to disk; see MidiSource::session_saved
769 for an explanation of what we are doing here.
771 boost::shared_ptr<MidiModel> mm = _model;
773 mm->sync_to_source (lock);
777 /* No model; if it's not already open, it's an empty source, so create
778 and open it for writing.
787 SMFSource::prevent_deletion ()
789 /* Unlike the audio case, the MIDI file remains mutable (because we can
793 _flags = Flag (_flags & ~(Removable|RemovableIfEmpty|RemoveAtDestroy));