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);
559 Evoral::SMF::end_write (_path);
561 /* data in the file now, not removable */
563 mark_nonremovable ();
567 SMFSource::valid_midi_file (const string& file)
569 if (safe_midi_file_extension (file) ) {
570 return (SMF::test (file) );
576 SMFSource::safe_midi_file_extension (const string& file)
578 static regex_t compiled_pattern;
579 static bool compile = true;
580 const int nmatches = 2;
581 regmatch_t matches[nmatches];
583 if (Glib::file_test (file, Glib::FILE_TEST_EXISTS)) {
584 if (!Glib::file_test (file, Glib::FILE_TEST_IS_REGULAR)) {
585 /* exists but is not a regular file */
590 if (compile && regcomp (&compiled_pattern, "\\.[mM][iI][dD][iI]?$", REG_EXTENDED)) {
596 if (regexec (&compiled_pattern, file.c_str(), nmatches, matches, 0)) {
603 static bool compare_eventlist (
604 const std::pair< const Evoral::Event<Temporal::Beats>*, gint >& a,
605 const std::pair< const Evoral::Event<Temporal::Beats>*, gint >& b) {
606 return ( a.first->time() < b.first->time() );
610 SMFSource::load_model (const Glib::Threads::Mutex::Lock& lock, bool force_reload)
616 if (_model && !force_reload) {
621 _model = boost::shared_ptr<MidiModel> (new MidiModel (shared_from_this ()));
628 if (writable() && !_open) {
632 _model->start_write();
633 Evoral::SMF::seek_to_start();
635 uint64_t time = 0; /* in SMF ticks */
636 Evoral::Event<Temporal::Beats> ev;
638 uint32_t scratch_size = 0; // keep track of scratch and minimize reallocs
640 uint32_t delta_t = 0;
647 // TODO simplify event allocation
648 std::list< std::pair< Evoral::Event<Temporal::Beats>*, gint > > eventlist;
650 for (unsigned i = 1; i <= num_tracks(); ++i) {
651 if (seek_to_track(i)) continue;
654 have_event_id = false;
656 while ((ret = read_event (&delta_t, &size, &buf, &event_id)) >= 0) {
661 /* meta-event : did we get an event ID ? */
663 have_event_id = true;
669 /* not a meta-event */
671 if (!have_event_id) {
672 event_id = Evoral::next_event_id();
674 const Temporal::Beats event_time = Temporal::Beats::ticks_at_rate(time, ppqn());
678 for (uint32_t xx = 0; xx < size; ++xx) {
680 snprintf (b, sizeof (b), "0x%x ", buf[xx]);
684 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF %7 load model delta %1, time %2, size %3 buf %4, id %6\n",
685 delta_t, time, size, ss, event_id, name()));
688 eventlist.push_back(make_pair (
689 new Evoral::Event<Temporal::Beats> (
690 Evoral::MIDI_EVENT, event_time,
694 // Set size to max capacity to minimize allocs in read_event
695 scratch_size = std::max(size, scratch_size);
698 _length_beats = max(_length_beats, event_time);
701 /* event ID's must immediately precede the event they are for */
702 have_event_id = false;
706 eventlist.sort(compare_eventlist);
708 std::list< std::pair< Evoral::Event<Temporal::Beats>*, gint > >::iterator it;
709 for (it=eventlist.begin(); it!=eventlist.end(); ++it) {
710 _model->append (*it->first, it->second);
714 // cerr << "----SMF-SRC-----\n";
715 // _playback_buf->dump (cerr);
716 // cerr << "----------------\n";
718 _model->end_write (Evoral::Sequence<Temporal::Beats>::ResolveStuckNotes, _length_beats);
719 _model->set_edited (false);
726 SMFSource::destroy_model (const Glib::Threads::Mutex::Lock& lock)
728 //cerr << _name << " destroying model " << _model.get() << endl;
734 SMFSource::flush_midi (const Lock& lock)
736 if (!writable() || _length_beats == 0.0) {
740 ensure_disk_file (lock);
742 Evoral::SMF::end_write (_path);
743 /* data in the file means its no longer removable */
744 mark_nonremovable ();
750 SMFSource::set_path (const string& p)
752 FileSource::set_path (p);
755 /** Ensure that this source has some file on disk, even if it's just a SMF header */
757 SMFSource::ensure_disk_file (const Lock& lock)
764 /* We have a model, so write it to disk; see MidiSource::session_saved
765 for an explanation of what we are doing here.
767 boost::shared_ptr<MidiModel> mm = _model;
769 mm->sync_to_source (lock);
773 /* No model; if it's not already open, it's an empty source, so create
774 and open it for writing.
783 SMFSource::prevent_deletion ()
785 /* Unlike the audio case, the MIDI file remains mutable (because we can
789 _flags = Flag (_flags & ~(Removable|RemovableIfEmpty|RemoveAtDestroy));