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 <glib/gstdio.h>
34 #include <glibmm/miscutils.h>
35 #include <glibmm/fileutils.h>
37 #include "evoral/Control.hpp"
38 #include "evoral/SMF.hpp"
40 #include "ardour/midi_model.h"
41 #include "ardour/midi_ring_buffer.h"
42 #include "ardour/midi_state_tracker.h"
43 #include "ardour/parameter_types.h"
44 #include "ardour/session.h"
45 #include "ardour/smf_source.h"
46 #include "ardour/debug.h"
50 using namespace ARDOUR;
53 using namespace Evoral;
56 /** Constructor used for new internal-to-session files. File cannot exist. */
57 SMFSource::SMFSource (Session& s, const string& path, Source::Flag flags)
58 : Source(s, DataType::MIDI, path, flags)
59 , MidiSource(s, path, flags)
60 , FileSource(s, DataType::MIDI, path, string(), flags)
62 , _last_ev_time_beats(0.0)
63 , _last_ev_time_frames(0)
64 , _smf_last_read_end (0)
65 , _smf_last_read_time (0)
67 /* note that origin remains empty */
69 if (init (_path, false)) {
70 throw failed_constructor ();
73 assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
76 _flags = Source::Flag (_flags | Empty);
78 /* file is not opened until write */
80 if (flags & Writable) {
85 throw failed_constructor ();
91 /** Constructor used for external-to-session files. File must exist. */
92 SMFSource::SMFSource (Session& s, const string& path)
93 : Source(s, DataType::MIDI, path, Source::Flag (0))
94 , MidiSource(s, path, Source::Flag (0))
95 , FileSource(s, DataType::MIDI, path, string(), Source::Flag (0))
97 , _last_ev_time_beats(0.0)
98 , _last_ev_time_frames(0)
99 , _smf_last_read_end (0)
100 , _smf_last_read_time (0)
102 /* note that origin remains empty */
104 if (init (_path, true)) {
105 throw failed_constructor ();
108 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
111 if (_flags & Writable) {
112 /* file is not opened until write */
117 throw failed_constructor ();
123 /** Constructor used for existing internal-to-session files. */
124 SMFSource::SMFSource (Session& s, const XMLNode& node, bool must_exist)
126 , MidiSource(s, node)
127 , FileSource(s, node, must_exist)
128 , _last_ev_time_beats(0.0)
129 , _last_ev_time_frames(0)
130 , _smf_last_read_end (0)
131 , _smf_last_read_time (0)
133 if (set_state(node, Stateful::loading_state_version)) {
134 throw failed_constructor ();
137 /* we expect the file to exist, but if no MIDI data was ever added
138 it will have been removed at last session close. so, we don't
139 require it to exist if it was marked Empty.
144 if (init (_path, true)) {
145 throw failed_constructor ();
148 } catch (MissingSource& err) {
150 if (_flags & Source::Empty) {
151 /* we don't care that the file was not found, because
152 it was empty. But FileSource::init() will have
153 failed to set our _path correctly, so we have to do
154 this ourselves. Use the first entry in the search
155 path for MIDI files, which is assumed to be the
156 correct "main" location.
158 std::vector<string> sdirs = s.source_search_path (DataType::MIDI);
159 _path = Glib::build_filename (sdirs.front(), _path);
160 /* This might be important, too */
168 if (!(_flags & Source::Empty)) {
169 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
172 assert (_flags & Source::Writable);
173 /* file will be opened on write */
178 throw failed_constructor ();
184 SMFSource::~SMFSource ()
187 ::g_unlink (_path.c_str());
192 SMFSource::open_for_write ()
194 if (create (_path)) {
201 /** All stamps in audio frames */
203 SMFSource::read_unlocked (Evoral::EventSink<framepos_t>& destination,
204 framepos_t const source_start,
207 MidiStateTracker* tracker) const
210 uint64_t time = 0; // in SMF ticks, 1 tick per _ppqn
212 if (writable() && !_open) {
213 /* nothing to read since nothing has ben written */
217 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: start %1 duration %2\n", start, duration));
219 // Output parameters for read_event (which will allocate scratch in buffer as needed)
220 uint32_t ev_delta_t = 0;
221 uint32_t ev_type = 0;
222 uint32_t ev_size = 0;
223 uint8_t* ev_buffer = 0;
225 size_t scratch_size = 0; // keep track of scratch to minimize reallocs
227 BeatsFramesConverter converter(_session.tempo_map(), source_start);
229 const uint64_t start_ticks = converter.from(start).to_ticks();
230 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: start in ticks %1\n", start_ticks));
232 if (_smf_last_read_end == 0 || start != _smf_last_read_end) {
233 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: seek to %1\n", start));
234 Evoral::SMF::seek_to_start();
235 while (time < start_ticks) {
238 ret = read_event(&ev_delta_t, &ev_size, &ev_buffer, &ignored);
239 if (ret == -1) { // EOF
240 _smf_last_read_end = start + duration;
243 time += ev_delta_t; // accumulate delta time
246 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: set time to %1\n", _smf_last_read_time));
247 time = _smf_last_read_time;
250 _smf_last_read_end = start + duration;
253 gint ignored; /* XXX don't ignore note id's ??*/
255 ret = read_event(&ev_delta_t, &ev_size, &ev_buffer, &ignored);
256 if (ret == -1) { // EOF
260 time += ev_delta_t; // accumulate delta time
261 _smf_last_read_time = time;
263 if (ret == 0) { // meta-event (skipped, just accumulate time)
267 ev_type = midi_parameter_type(ev_buffer[0]);
269 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked delta %1, time %2, buf[0] %3, type %4\n",
270 ev_delta_t, time, ev_buffer[0], ev_type));
272 assert(time >= start_ticks);
274 /* Note that we add on the source start time (in session frames) here so that ev_frame_time
275 is in session frames.
277 const framepos_t ev_frame_time = converter.to(Evoral::MusicalTime::ticks_at_rate(time, ppqn())) + source_start;
279 if (ev_frame_time < start + duration) {
280 destination.write (ev_frame_time, ev_type, ev_size, ev_buffer);
283 if (ev_buffer[0] & MIDI_CMD_NOTE_ON) {
284 tracker->add (ev_buffer[1], ev_buffer[0] & 0xf);
285 } else if (ev_buffer[0] & MIDI_CMD_NOTE_OFF) {
286 tracker->remove (ev_buffer[1], ev_buffer[0] & 0xf);
293 if (ev_size > scratch_size) {
294 scratch_size = ev_size;
296 ev_size = scratch_size; // ensure read_event only allocates if necessary
303 SMFSource::write_unlocked (MidiRingBuffer<framepos_t>& source,
308 mark_streaming_write_started ();
312 Evoral::EventType type;
315 size_t buf_capacity = 4;
316 uint8_t* buf = (uint8_t*)malloc(buf_capacity);
318 if (_model && !_model->writing()) {
319 _model->start_write();
322 Evoral::MIDIEvent<framepos_t> ev;
324 /* Get the event time, in frames since session start but ignoring looping. */
326 if (!(ret = source.peek ((uint8_t*)&time, sizeof (time)))) {
327 /* Ring is empty, no more events. */
331 if ((cnt != max_framecnt) &&
332 (time > position + _capture_length + cnt)) {
333 /* The diskstream doesn't want us to write everything, and this
334 event is past the end of this block, so we're done for now. */
338 /* Read the time, type, and size of the event. */
339 if (!(ret = source.read_prefix (&time, &type, &size))) {
340 error << _("Unable to read event prefix, corrupt MIDI ring") << endmsg;
344 /* Enlarge body buffer if necessary now that we know the size. */
345 if (size > buf_capacity) {
347 buf = (uint8_t*)realloc(buf, size);
350 /* Read the event body into buffer. */
351 ret = source.read_contents(size, buf);
353 error << _("Event has time and size but no body, corrupt MIDI ring") << endmsg;
357 /* Convert event time from absolute to source relative. */
358 if (time < position) {
359 error << _("Event time is before MIDI source position") << endmsg;
364 ev.set(buf, size, time);
365 ev.set_event_type(midi_parameter_type(ev.buffer()[0]));
366 ev.set_id(Evoral::next_event_id());
368 if (!(ev.is_channel_event() || ev.is_smf_meta_event() || ev.is_sysex())) {
372 append_event_unlocked_frames(ev, position);
375 Evoral::SMF::flush ();
381 /** Append an event with a timestamp in beats */
383 SMFSource::append_event_unlocked_beats (const Evoral::Event<Evoral::MusicalTime>& ev)
385 if (!_writing || ev.size() == 0) {
389 /*printf("SMFSource: %s - append_event_unlocked_beats ID = %d time = %lf, size = %u, data = ",
390 name().c_str(), ev.id(), ev.time(), ev.size());
391 for (size_t i = 0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/
393 Evoral::MusicalTime time = ev.time();
394 if (time < _last_ev_time_beats) {
395 const Evoral::MusicalTime difference = _last_ev_time_beats - time;
396 if (difference.to_double() / (double)ppqn() < 1.0) {
397 /* Close enough. This problem occurs because Sequence is not
398 actually ordered due to fuzzy time comparison. I'm pretty sure
399 this is inherently a bad idea which causes problems all over the
400 place, but tolerate it here for now anyway. */
401 time = _last_ev_time_beats;
403 /* Out of order by more than a tick. */
404 warning << string_compose(_("Skipping event with unordered beat time %1 < %2 (off by %3 beats, %4 ticks)"),
405 ev.time(), _last_ev_time_beats, difference, difference.to_double() / (double)ppqn())
411 Evoral::event_id_t event_id;
414 event_id = Evoral::next_event_id();
420 _model->append (ev, event_id);
423 _length_beats = max(_length_beats, time);
425 const Evoral::MusicalTime delta_time_beats = time - _last_ev_time_beats;
426 const uint32_t delta_time_ticks = delta_time_beats.to_ticks(ppqn());
428 Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
429 _last_ev_time_beats = time;
430 _flags = Source::Flag (_flags & ~Empty);
433 /** Append an event with a timestamp in frames (framepos_t) */
435 SMFSource::append_event_unlocked_frames (const Evoral::Event<framepos_t>& ev, framepos_t position)
437 if (!_writing || ev.size() == 0) {
441 // printf("SMFSource: %s - append_event_unlocked_frames ID = %d time = %u, size = %u, data = ",
442 // name().c_str(), ev.id(), ev.time(), ev.size());
443 // for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");
445 if (ev.time() < _last_ev_time_frames) {
446 warning << string_compose(_("Skipping event with unordered frame time %1 < %2"),
447 ev.time(), _last_ev_time_frames)
452 BeatsFramesConverter converter(_session.tempo_map(), position);
453 const Evoral::MusicalTime ev_time_beats = converter.from(ev.time());
454 Evoral::event_id_t event_id;
457 event_id = Evoral::next_event_id();
463 const Evoral::Event<Evoral::MusicalTime> beat_ev (ev.event_type(),
466 const_cast<uint8_t*>(ev.buffer()));
467 _model->append (beat_ev, event_id);
470 _length_beats = max(_length_beats, ev_time_beats);
472 const Evoral::MusicalTime last_time_beats = converter.from (_last_ev_time_frames);
473 const Evoral::MusicalTime delta_time_beats = ev_time_beats - last_time_beats;
474 const uint32_t delta_time_ticks = delta_time_beats.to_ticks(ppqn());
476 Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
477 _last_ev_time_frames = ev.time();
478 _flags = Source::Flag (_flags & ~Empty);
482 SMFSource::get_state ()
484 XMLNode& node = MidiSource::get_state();
485 node.add_property (X_("origin"), _origin);
490 SMFSource::set_state (const XMLNode& node, int version)
492 if (Source::set_state (node, version)) {
496 if (MidiSource::set_state (node, version)) {
500 if (FileSource::set_state (node, version)) {
508 SMFSource::mark_streaming_midi_write_started (NoteMode mode)
510 /* CALLER MUST HOLD LOCK */
512 if (!_open && open_for_write()) {
513 error << string_compose (_("cannot open MIDI file %1 for write"), _path) << endmsg;
514 /* XXX should probably throw or return something */
518 MidiSource::mark_streaming_midi_write_started (mode);
519 Evoral::SMF::begin_write ();
520 _last_ev_time_beats = Evoral::MusicalTime();
521 _last_ev_time_frames = 0;
525 SMFSource::mark_streaming_write_completed ()
527 mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
531 SMFSource::mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption stuck_notes_option, Evoral::MusicalTime when)
533 Glib::Threads::Mutex::Lock lm (_lock);
534 MidiSource::mark_midi_streaming_write_completed (stuck_notes_option, when);
537 warning << string_compose ("attempt to write to unwritable SMF file %1", _path) << endmsg;
542 _model->set_edited(false);
545 Evoral::SMF::end_write ();
547 /* data in the file now, not removable */
549 mark_nonremovable ();
553 SMFSource::valid_midi_file (const string& file)
555 if (safe_midi_file_extension (file) ) {
556 return (SMF::test (file) );
562 SMFSource::safe_midi_file_extension (const string& file)
564 static regex_t compiled_pattern;
565 static bool compile = true;
566 const int nmatches = 2;
567 regmatch_t matches[nmatches];
569 if (Glib::file_test (file, Glib::FILE_TEST_EXISTS)) {
570 if (!Glib::file_test (file, Glib::FILE_TEST_IS_REGULAR)) {
571 /* exists but is not a regular file */
576 if (compile && regcomp (&compiled_pattern, "\\.[mM][iI][dD][iI]?$", REG_EXTENDED)) {
582 if (regexec (&compiled_pattern, file.c_str(), nmatches, matches, 0)) {
589 static bool compare_eventlist (
590 const std::pair< Evoral::Event<Evoral::MusicalTime>*, gint >& a,
591 const std::pair< Evoral::Event<Evoral::MusicalTime>*, gint >& b) {
592 return ( a.first->time() < b.first->time() );
596 SMFSource::load_model (bool lock, bool force_reload)
602 boost::shared_ptr<Glib::Threads::Mutex::Lock> lm;
604 lm = boost::shared_ptr<Glib::Threads::Mutex::Lock>(new Glib::Threads::Mutex::Lock(_lock));
606 if (_model && !force_reload) {
611 _model = boost::shared_ptr<MidiModel> (new MidiModel (shared_from_this ()));
616 if (writable() && !_open) {
620 _model->start_write();
621 Evoral::SMF::seek_to_start();
623 uint64_t time = 0; /* in SMF ticks */
624 Evoral::Event<Evoral::MusicalTime> ev;
626 uint32_t scratch_size = 0; // keep track of scratch and minimize reallocs
628 uint32_t delta_t = 0;
635 // TODO simplify event allocation
636 std::list< std::pair< Evoral::Event<Evoral::MusicalTime>*, gint > > eventlist;
638 for (unsigned i = 1; i <= num_tracks(); ++i) {
639 if (seek_to_track(i)) continue;
642 have_event_id = false;
644 while ((ret = read_event (&delta_t, &size, &buf, &event_id)) >= 0) {
649 /* meta-event : did we get an event ID ? */
651 have_event_id = true;
657 /* not a meta-event */
659 if (!have_event_id) {
660 event_id = Evoral::next_event_id();
662 const uint32_t event_type = midi_parameter_type(buf[0]);
663 const Evoral::MusicalTime event_time = Evoral::MusicalTime::ticks_at_rate(time, ppqn());
667 for (uint32_t xx = 0; xx < size; ++xx) {
669 snprintf (b, sizeof (b), "0x%x ", buf[xx]);
673 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF %6 load model delta %1, time %2, size %3 buf %4, type %5\n",
674 delta_t, time, size, ss , event_type, name()));
677 eventlist.push_back(make_pair (
678 new Evoral::Event<Evoral::MusicalTime> (
679 event_type, event_time,
683 // Set size to max capacity to minimize allocs in read_event
684 scratch_size = std::max(size, scratch_size);
687 _length_beats = max(_length_beats, event_time);
690 /* event ID's must immediately precede the event they are for */
691 have_event_id = false;
695 eventlist.sort(compare_eventlist);
697 std::list< std::pair< Evoral::Event<Evoral::MusicalTime>*, gint > >::iterator it;
698 for (it=eventlist.begin(); it!=eventlist.end(); ++it) {
699 _model->append (*it->first, it->second);
703 _model->end_write (Evoral::Sequence<Evoral::MusicalTime>::ResolveStuckNotes, _length_beats);
704 _model->set_edited (false);
711 SMFSource::destroy_model ()
713 //cerr << _name << " destroying model " << _model.get() << endl;
718 SMFSource::flush_midi ()
720 if (!writable() || _length_beats == 0.0) {
726 Evoral::SMF::end_write ();
727 /* data in the file means its no longer removable */
728 mark_nonremovable ();
734 SMFSource::set_path (const string& p)
736 FileSource::set_path (p);
737 SMF::set_path (_path);
740 /** Ensure that this source has some file on disk, even if it's just a SMF header */
742 SMFSource::ensure_disk_file ()
749 /* We have a model, so write it to disk; see MidiSource::session_saved
750 for an explanation of what we are doing here.
752 boost::shared_ptr<MidiModel> mm = _model;
754 mm->sync_to_source ();
757 /* No model; if it's not already open, it's an empty source, so create
758 and open it for writing.
767 SMFSource::prevent_deletion ()
769 /* Unlike the audio case, the MIDI file remains mutable (because we can
773 _flags = Flag (_flags & ~(Removable|RemovableIfEmpty|RemoveAtDestroy));