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/event_type_map.h"
41 #include "ardour/midi_model.h"
42 #include "ardour/midi_ring_buffer.h"
43 #include "ardour/midi_state_tracker.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;
55 /** Constructor used for new internal-to-session files. File cannot exist. */
56 SMFSource::SMFSource (Session& s, const string& path, Source::Flag flags)
57 : Source(s, DataType::MIDI, path, flags)
58 , MidiSource(s, path, flags)
59 , FileSource(s, DataType::MIDI, path, string(), flags)
61 , _last_ev_time_beats(0.0)
62 , _last_ev_time_frames(0)
63 , _smf_last_read_end (0)
64 , _smf_last_read_time (0)
66 /* note that origin remains empty */
68 if (init (_path, false)) {
69 throw failed_constructor ();
72 assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
75 _flags = Source::Flag (_flags | Empty);
77 /* file is not opened until write */
79 if (flags & Writable) {
84 throw failed_constructor ();
90 /** Constructor used for external-to-session files. File must exist. */
91 SMFSource::SMFSource (Session& s, const string& path)
92 : Source(s, DataType::MIDI, path, Source::Flag (0))
93 , MidiSource(s, path, Source::Flag (0))
94 , FileSource(s, DataType::MIDI, path, string(), Source::Flag (0))
96 , _last_ev_time_beats(0.0)
97 , _last_ev_time_frames(0)
98 , _smf_last_read_end (0)
99 , _smf_last_read_time (0)
101 /* note that origin remains empty */
103 if (init (_path, true)) {
104 throw failed_constructor ();
107 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
110 if (_flags & Writable) {
111 /* file is not opened until write */
116 throw failed_constructor ();
122 /** Constructor used for existing internal-to-session files. */
123 SMFSource::SMFSource (Session& s, const XMLNode& node, bool must_exist)
125 , MidiSource(s, node)
126 , FileSource(s, node, must_exist)
127 , _last_ev_time_beats(0.0)
128 , _last_ev_time_frames(0)
129 , _smf_last_read_end (0)
130 , _smf_last_read_time (0)
132 if (set_state(node, Stateful::loading_state_version)) {
133 throw failed_constructor ();
136 /* we expect the file to exist, but if no MIDI data was ever added
137 it will have been removed at last session close. so, we don't
138 require it to exist if it was marked Empty.
143 if (init (_path, true)) {
144 throw failed_constructor ();
147 } catch (MissingSource& err) {
149 if (_flags & Source::Empty) {
150 /* we don't care that the file was not found, because
151 it was empty. But FileSource::init() will have
152 failed to set our _path correctly, so we have to do
153 this ourselves. Use the first entry in the search
154 path for MIDI files, which is assumed to be the
155 correct "main" location.
157 std::vector<string> sdirs = s.source_search_path (DataType::MIDI);
158 _path = Glib::build_filename (sdirs.front(), _path);
159 /* This might be important, too */
167 if (!(_flags & Source::Empty)) {
168 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
171 assert (_flags & Source::Writable);
172 /* file will be opened on write */
177 throw failed_constructor ();
183 SMFSource::~SMFSource ()
186 ::g_unlink (_path.c_str());
191 SMFSource::open_for_write ()
193 if (create (_path)) {
200 /** All stamps in audio frames */
202 SMFSource::read_unlocked (Evoral::EventSink<framepos_t>& destination,
203 framepos_t const source_start,
206 MidiStateTracker* tracker) const
209 uint64_t time = 0; // in SMF ticks, 1 tick per _ppqn
211 if (writable() && !_open) {
212 /* nothing to read since nothing has ben written */
216 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: start %1 duration %2\n", start, duration));
218 // Output parameters for read_event (which will allocate scratch in buffer as needed)
219 uint32_t ev_delta_t = 0;
220 uint32_t ev_type = 0;
221 uint32_t ev_size = 0;
222 uint8_t* ev_buffer = 0;
224 size_t scratch_size = 0; // keep track of scratch to minimize reallocs
226 BeatsFramesConverter converter(_session.tempo_map(), source_start);
228 const uint64_t start_ticks = (uint64_t)(converter.from(start) * ppqn());
229 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: start in ticks %1\n", start_ticks));
231 if (_smf_last_read_end == 0 || start != _smf_last_read_end) {
232 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: seek to %1\n", start));
233 Evoral::SMF::seek_to_start();
234 while (time < start_ticks) {
237 ret = read_event(&ev_delta_t, &ev_size, &ev_buffer, &ignored);
238 if (ret == -1) { // EOF
239 _smf_last_read_end = start + duration;
242 time += ev_delta_t; // accumulate delta time
245 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: set time to %1\n", _smf_last_read_time));
246 time = _smf_last_read_time;
249 _smf_last_read_end = start + duration;
252 gint ignored; /* XXX don't ignore note id's ??*/
254 ret = read_event(&ev_delta_t, &ev_size, &ev_buffer, &ignored);
255 if (ret == -1) { // EOF
259 time += ev_delta_t; // accumulate delta time
260 _smf_last_read_time = time;
262 if (ret == 0) { // meta-event (skipped, just accumulate time)
266 ev_type = EventTypeMap::instance().midi_event_type(ev_buffer[0]);
268 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked delta %1, time %2, buf[0] %3, type %4\n",
269 ev_delta_t, time, ev_buffer[0], ev_type));
271 assert(time >= start_ticks);
273 /* Note that we add on the source start time (in session frames) here so that ev_frame_time
274 is in session frames.
276 const framepos_t ev_frame_time = converter.to(time / (double)ppqn()) + source_start;
278 if (ev_frame_time < start + duration) {
279 destination.write (ev_frame_time, ev_type, ev_size, ev_buffer);
282 if (ev_buffer[0] & MIDI_CMD_NOTE_ON) {
283 tracker->add (ev_buffer[1], ev_buffer[0] & 0xf);
284 } else if (ev_buffer[0] & MIDI_CMD_NOTE_OFF) {
285 tracker->remove (ev_buffer[1], ev_buffer[0] & 0xf);
292 if (ev_size > scratch_size) {
293 scratch_size = ev_size;
295 ev_size = scratch_size; // ensure read_event only allocates if necessary
302 SMFSource::write_unlocked (MidiRingBuffer<framepos_t>& source,
307 mark_streaming_write_started ();
311 Evoral::EventType type;
314 size_t buf_capacity = 4;
315 uint8_t* buf = (uint8_t*)malloc(buf_capacity);
317 if (_model && !_model->writing()) {
318 _model->start_write();
321 Evoral::MIDIEvent<framepos_t> ev;
323 /* Get the event time, in frames since session start but ignoring looping. */
325 if (!(ret = source.peek ((uint8_t*)&time, sizeof (time)))) {
326 /* Ring is empty, no more events. */
330 if ((cnt != max_framecnt) &&
331 (time > position + _capture_length + cnt)) {
332 /* The diskstream doesn't want us to write everything, and this
333 event is past the end of this block, so we're done for now. */
337 /* Read the time, type, and size of the event. */
338 if (!(ret = source.read_prefix (&time, &type, &size))) {
339 error << _("Unable to read event prefix, corrupt MIDI ring") << endmsg;
343 /* Enlarge body buffer if necessary now that we know the size. */
344 if (size > buf_capacity) {
346 buf = (uint8_t*)realloc(buf, size);
349 /* Read the event body into buffer. */
350 ret = source.read_contents(size, buf);
352 error << _("Event has time and size but no body, corrupt MIDI ring") << endmsg;
356 /* Convert event time from absolute to source relative. */
357 if (time < position) {
358 error << _("Event time is before MIDI source position") << endmsg;
363 ev.set(buf, size, time);
364 ev.set_event_type(EventTypeMap::instance().midi_event_type(ev.buffer()[0]));
365 ev.set_id(Evoral::next_event_id());
367 if (!(ev.is_channel_event() || ev.is_smf_meta_event() || ev.is_sysex())) {
371 append_event_unlocked_frames(ev, position);
374 Evoral::SMF::flush ();
380 /** Append an event with a timestamp in beats (double) */
382 SMFSource::append_event_unlocked_beats (const Evoral::Event<double>& ev)
384 if (!_writing || ev.size() == 0) {
388 /*printf("SMFSource: %s - append_event_unlocked_beats ID = %d time = %lf, size = %u, data = ",
389 name().c_str(), ev.id(), ev.time(), ev.size());
390 for (size_t i = 0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/
392 if (ev.time() < _last_ev_time_beats) {
393 warning << string_compose(_("Skipping event with unordered time %1"), ev.time())
398 Evoral::event_id_t event_id;
401 event_id = Evoral::next_event_id();
407 _model->append (ev, event_id);
410 _length_beats = max(_length_beats, ev.time());
412 const double delta_time_beats = ev.time() - _last_ev_time_beats;
413 const uint32_t delta_time_ticks = (uint32_t)lrint(delta_time_beats * (double)ppqn());
415 Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
416 _last_ev_time_beats = ev.time();
417 _flags = Source::Flag (_flags & ~Empty);
420 /** Append an event with a timestamp in frames (framepos_t) */
422 SMFSource::append_event_unlocked_frames (const Evoral::Event<framepos_t>& ev, framepos_t position)
424 if (!_writing || ev.size() == 0) {
428 // printf("SMFSource: %s - append_event_unlocked_frames ID = %d time = %u, size = %u, data = ",
429 // name().c_str(), ev.id(), ev.time(), ev.size());
430 // for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");
432 if (ev.time() < _last_ev_time_frames) {
433 warning << string_compose(_("Skipping event with unordered time %1"), ev.time())
438 BeatsFramesConverter converter(_session.tempo_map(), position);
439 const double ev_time_beats = converter.from(ev.time());
440 Evoral::event_id_t event_id;
443 event_id = Evoral::next_event_id();
449 const Evoral::Event<double> beat_ev (ev.event_type(),
452 const_cast<uint8_t*>(ev.buffer()));
453 _model->append (beat_ev, event_id);
456 _length_beats = max(_length_beats, ev_time_beats);
458 const Evoral::MusicalTime last_time_beats = converter.from (_last_ev_time_frames);
459 const Evoral::MusicalTime delta_time_beats = ev_time_beats - last_time_beats;
460 const uint32_t delta_time_ticks = (uint32_t)(lrint(delta_time_beats * (double)ppqn()));
462 Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
463 _last_ev_time_frames = ev.time();
464 _flags = Source::Flag (_flags & ~Empty);
468 SMFSource::get_state ()
470 XMLNode& node = MidiSource::get_state();
471 node.add_property (X_("origin"), _origin);
476 SMFSource::set_state (const XMLNode& node, int version)
478 if (Source::set_state (node, version)) {
482 if (MidiSource::set_state (node, version)) {
486 if (FileSource::set_state (node, version)) {
494 SMFSource::mark_streaming_midi_write_started (NoteMode mode)
496 /* CALLER MUST HOLD LOCK */
498 if (!_open && open_for_write()) {
499 error << string_compose (_("cannot open MIDI file %1 for write"), _path) << endmsg;
500 /* XXX should probably throw or return something */
504 MidiSource::mark_streaming_midi_write_started (mode);
505 Evoral::SMF::begin_write ();
506 _last_ev_time_beats = 0.0;
507 _last_ev_time_frames = 0;
511 SMFSource::mark_streaming_write_completed ()
513 mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
517 SMFSource::mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption stuck_notes_option, Evoral::MusicalTime when)
519 Glib::Threads::Mutex::Lock lm (_lock);
520 MidiSource::mark_midi_streaming_write_completed (stuck_notes_option, when);
523 warning << string_compose ("attempt to write to unwritable SMF file %1", _path) << endmsg;
528 _model->set_edited(false);
531 Evoral::SMF::end_write ();
533 /* data in the file now, not removable */
535 mark_nonremovable ();
539 SMFSource::valid_midi_file (const string& file)
541 if (safe_midi_file_extension (file) ) {
542 return (SMF::test (file) );
548 SMFSource::safe_midi_file_extension (const string& file)
550 static regex_t compiled_pattern;
551 static bool compile = true;
552 const int nmatches = 2;
553 regmatch_t matches[nmatches];
555 if (Glib::file_test (file, Glib::FILE_TEST_EXISTS)) {
556 if (!Glib::file_test (file, Glib::FILE_TEST_IS_REGULAR)) {
557 /* exists but is not a regular file */
562 if (compile && regcomp (&compiled_pattern, "\\.[mM][iI][dD][iI]?$", REG_EXTENDED)) {
568 if (regexec (&compiled_pattern, file.c_str(), nmatches, matches, 0)) {
575 static bool compare_eventlist (
576 const std::pair< Evoral::Event<double>*, gint >& a,
577 const std::pair< Evoral::Event<double>*, gint >& b) {
578 return ( a.first->time() < b.first->time() );
582 SMFSource::load_model (bool lock, bool force_reload)
588 boost::shared_ptr<Glib::Threads::Mutex::Lock> lm;
590 lm = boost::shared_ptr<Glib::Threads::Mutex::Lock>(new Glib::Threads::Mutex::Lock(_lock));
592 if (_model && !force_reload) {
597 _model = boost::shared_ptr<MidiModel> (new MidiModel (shared_from_this ()));
602 if (writable() && !_open) {
606 _model->start_write();
607 Evoral::SMF::seek_to_start();
609 uint64_t time = 0; /* in SMF ticks */
610 Evoral::Event<double> ev;
612 uint32_t scratch_size = 0; // keep track of scratch and minimize reallocs
614 uint32_t delta_t = 0;
621 // TODO simplify event allocation
622 std::list< std::pair< Evoral::Event<double>*, gint > > eventlist;
624 for (unsigned i = 1; i <= num_tracks(); ++i) {
625 if (seek_to_track(i)) continue;
628 have_event_id = false;
630 while ((ret = read_event (&delta_t, &size, &buf, &event_id)) >= 0) {
635 /* meta-event : did we get an event ID ? */
637 have_event_id = true;
643 /* not a meta-event */
645 if (!have_event_id) {
646 event_id = Evoral::next_event_id();
648 uint32_t event_type = EventTypeMap::instance().midi_event_type(buf[0]);
649 double event_time = time / (double) ppqn();
653 for (uint32_t xx = 0; xx < size; ++xx) {
655 snprintf (b, sizeof (b), "0x%x ", buf[xx]);
659 DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF %6 load model delta %1, time %2, size %3 buf %4, type %5\n",
660 delta_t, time, size, ss , event_type, name()));
663 eventlist.push_back(make_pair (
664 new Evoral::Event<double> (
665 event_type, event_time,
669 // Set size to max capacity to minimize allocs in read_event
670 scratch_size = std::max(size, scratch_size);
673 _length_beats = max(_length_beats, event_time);
676 /* event ID's must immediately precede the event they are for */
677 have_event_id = false;
681 eventlist.sort(compare_eventlist);
683 std::list< std::pair< Evoral::Event<double>*, gint > >::iterator it;
684 for (it=eventlist.begin(); it!=eventlist.end(); ++it) {
685 _model->append (*it->first, it->second);
689 _model->end_write (Evoral::Sequence<Evoral::MusicalTime>::ResolveStuckNotes, _length_beats);
690 _model->set_edited (false);
692 _model_iter = _model->begin();
698 SMFSource::destroy_model ()
700 //cerr << _name << " destroying model " << _model.get() << endl;
705 SMFSource::flush_midi ()
707 if (!writable() || _length_beats == 0.0) {
713 Evoral::SMF::end_write ();
714 /* data in the file means its no longer removable */
715 mark_nonremovable ();
719 SMFSource::set_path (const string& p)
721 FileSource::set_path (p);
722 SMF::set_path (_path);
725 /** Ensure that this source has some file on disk, even if it's just a SMF header */
727 SMFSource::ensure_disk_file ()
734 /* We have a model, so write it to disk; see MidiSource::session_saved
735 for an explanation of what we are doing here.
737 boost::shared_ptr<MidiModel> mm = _model;
739 mm->sync_to_source ();
742 /* No model; if it's not already open, it's an empty source, so create
743 and open it for writing.
752 SMFSource::prevent_deletion ()
754 /* Unlike the audio case, the MIDI file remains mutable (because we can
758 _flags = Flag (_flags & ~(Removable|RemovableIfEmpty|RemoveAtDestroy));