ardour
smf_source.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 Paul Davis
3  Author: David Robillard
4 
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.
9 
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.
14 
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.
18 
19 */
20 
21 #include <vector>
22 
23 #include <sys/time.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <regex.h>
28 
29 #include "pbd/file_utils.h"
30 #include "pbd/stl_delete.h"
31 #include "pbd/strsplit.h"
32 
33 #include <glib/gstdio.h>
34 #include <glibmm/miscutils.h>
35 #include <glibmm/fileutils.h>
36 
37 #include "evoral/Control.hpp"
38 #include "evoral/SMF.hpp"
39 
40 #include "ardour/debug.h"
42 #include "ardour/midi_model.h"
45 #include "ardour/parameter_types.h"
46 #include "ardour/session.h"
47 #include "ardour/smf_source.h"
48 
49 #include "i18n.h"
50 
51 using namespace ARDOUR;
52 using namespace Glib;
53 using namespace PBD;
54 using namespace Evoral;
55 using namespace std;
56 
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)
62  , Evoral::SMF()
63  , _open (false)
64  , _last_ev_time_beats(0.0)
65  , _last_ev_time_frames(0)
66  , _smf_last_read_end (0)
67  , _smf_last_read_time (0)
68 {
69  /* note that origin remains empty */
70 
71  if (init (_path, false)) {
72  throw failed_constructor ();
73  }
74 
75  assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
76  existence_check ();
77 
78  _flags = Source::Flag (_flags | Empty);
79 
80  /* file is not opened until write */
81 
82  if (flags & Writable) {
83  return;
84  }
85 
86  if (open (_path)) {
87  throw failed_constructor ();
88  }
89 
90  _open = true;
91 }
92 
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))
98  , Evoral::SMF()
99  , _open (false)
100  , _last_ev_time_beats(0.0)
101  , _last_ev_time_frames(0)
102  , _smf_last_read_end (0)
103  , _smf_last_read_time (0)
104 {
105  /* note that origin remains empty */
106 
107  if (init (_path, true)) {
108  throw failed_constructor ();
109  }
110 
111  assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
112  existence_check ();
113 
114  if (_flags & Writable) {
115  /* file is not opened until write */
116  return;
117  }
118 
119  if (open (_path)) {
120  throw failed_constructor ();
121  }
122 
123  _open = true;
124 }
125 
127 SMFSource::SMFSource (Session& s, const XMLNode& node, bool must_exist)
128  : Source(s, node)
129  , MidiSource(s, node)
130  , FileSource(s, node, must_exist)
131  , _open (false)
132  , _last_ev_time_beats(0.0)
133  , _last_ev_time_frames(0)
134  , _smf_last_read_end (0)
135  , _smf_last_read_time (0)
136 {
138  throw failed_constructor ();
139  }
140 
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.
144  */
145 
146  try {
147 
148  if (init (_path, true)) {
149  throw failed_constructor ();
150  }
151 
152  } catch (MissingSource& err) {
153 
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.
161  */
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 */
165  _file_is_new = true;
166  } else {
167  /* pass it on */
168  throw;
169  }
170  }
171 
172  if (!(_flags & Source::Empty)) {
173  assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
174  existence_check ();
175  } else {
176  assert (_flags & Source::Writable);
177  /* file will be opened on write */
178  return;
179  }
180 
181  if (open (_path)) {
182  throw failed_constructor ();
183  }
184 
185  _open = true;
186 }
187 
189 {
190  if (removable()) {
191  ::g_unlink (_path.c_str());
192  }
193 }
194 
195 int
197 {
198  if (create (_path)) {
199  return -1;
200  }
201  _open = true;
202  return 0;
203 }
204 
205 void
207 {
208  /* nothing to do: file descriptor is never kept open */
209 }
210 
214  Evoral::EventSink<framepos_t>& destination,
215  framepos_t const source_start,
217  framecnt_t duration,
218  MidiStateTracker* tracker,
219  MidiChannelFilter* filter) const
220 {
221  int ret = 0;
222  uint64_t time = 0; // in SMF ticks, 1 tick per _ppqn
223 
224  if (writable() && !_open) {
225  /* nothing to read since nothing has ben written */
226  return duration;
227  }
228 
229  DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: start %1 duration %2\n", start, duration));
230 
231  // Output parameters for read_event (which will allocate scratch in buffer as needed)
232  uint32_t ev_delta_t = 0;
233  uint32_t ev_type = 0;
234  uint32_t ev_size = 0;
235  uint8_t* ev_buffer = 0;
236 
237  size_t scratch_size = 0; // keep track of scratch to minimize reallocs
238 
239  BeatsFramesConverter converter(_session.tempo_map(), source_start);
240 
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));
243 
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));
247  while (time < start_ticks) {
248  gint ignored;
249 
250  ret = read_event(&ev_delta_t, &ev_size, &ev_buffer, &ignored);
251  if (ret == -1) { // EOF
252  _smf_last_read_end = start + duration;
253  return duration;
254  }
255  time += ev_delta_t; // accumulate delta time
256  }
257  } else {
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;
260  }
261 
262  _smf_last_read_end = start + duration;
263 
264  while (true) {
265  gint ignored; /* XXX don't ignore note id's ??*/
266 
267  ret = read_event(&ev_delta_t, &ev_size, &ev_buffer, &ignored);
268  if (ret == -1) { // EOF
269  break;
270  }
271 
272  time += ev_delta_t; // accumulate delta time
273  _smf_last_read_time = time;
274 
275  if (ret == 0) { // meta-event (skipped, just accumulate time)
276  continue;
277  }
278 
279  ev_type = midi_parameter_type(ev_buffer[0]);
280 
281  DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked delta %1, time %2, buf[0] %3, type %4\n",
282  ev_delta_t, time, ev_buffer[0], ev_type));
283 
284  assert(time >= start_ticks);
285 
286  /* Note that we add on the source start time (in session frames) here so that ev_frame_time
287  is in session frames.
288  */
289  const framepos_t ev_frame_time = converter.to(Evoral::Beats::ticks_at_rate(time, ppqn())) + source_start;
290 
291  if (ev_frame_time < start + duration) {
292  if (!filter || !filter->filter(ev_buffer, ev_size)) {
293  destination.write (ev_frame_time, ev_type, ev_size, ev_buffer);
294  if (tracker) {
295  tracker->track(ev_buffer);
296  }
297  }
298  } else {
299  break;
300  }
301 
302  if (ev_size > scratch_size) {
303  scratch_size = ev_size;
304  }
305  ev_size = scratch_size; // ensure read_event only allocates if necessary
306  }
307 
308  return duration;
309 }
310 
315  framecnt_t cnt)
316 {
317  if (!_writing) {
319  }
320 
321  framepos_t time;
323  uint32_t size;
324 
325  size_t buf_capacity = 4;
326  uint8_t* buf = (uint8_t*)malloc(buf_capacity);
327 
328  if (_model && !_model->writing()) {
329  _model->start_write();
330  }
331 
333  while (true) {
334  /* Get the event time, in frames since session start but ignoring looping. */
335  bool ret;
336  if (!(ret = source.peek ((uint8_t*)&time, sizeof (time)))) {
337  /* Ring is empty, no more events. */
338  break;
339  }
340 
341  if ((cnt != max_framecnt) &&
342  (time > position + _capture_length + cnt)) {
343  /* The diskstream doesn't want us to write everything, and this
344  event is past the end of this block, so we're done for now. */
345  break;
346  }
347 
348  /* Read the time, type, and size of the event. */
349  if (!(ret = source.read_prefix (&time, &type, &size))) {
350  error << _("Unable to read event prefix, corrupt MIDI ring") << endmsg;
351  break;
352  }
353 
354  /* Enlarge body buffer if necessary now that we know the size. */
355  if (size > buf_capacity) {
356  buf_capacity = size;
357  buf = (uint8_t*)realloc(buf, size);
358  }
359 
360  /* Read the event body into buffer. */
361  ret = source.read_contents(size, buf);
362  if (!ret) {
363  error << _("Event has time and size but no body, corrupt MIDI ring") << endmsg;
364  break;
365  }
366 
367  /* Convert event time from absolute to source relative. */
368  if (time < position) {
369  error << _("Event time is before MIDI source position") << endmsg;
370  break;
371  }
372  time -= position;
373 
374  ev.set(buf, size, time);
377 
378  if (!(ev.is_channel_event() || ev.is_smf_meta_event() || ev.is_sysex())) {
379  continue;
380  }
381 
382  append_event_frames(lock, ev, position);
383  }
384 
386  free (buf);
387 
388  return cnt;
389 }
390 
392 void
395 {
396  if (!_writing || ev.size() == 0) {
397  return;
398  }
399 
400  /*printf("SMFSource: %s - append_event_beats ID = %d time = %lf, size = %u, data = ",
401  name().c_str(), ev.id(), ev.time(), ev.size());
402  for (size_t i = 0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/
403 
404  Evoral::Beats time = ev.time();
405  if (time < _last_ev_time_beats) {
406  const Evoral::Beats difference = _last_ev_time_beats - time;
407  if (difference.to_double() / (double)ppqn() < 1.0) {
408  /* Close enough. This problem occurs because Sequence is not
409  actually ordered due to fuzzy time comparison. I'm pretty sure
410  this is inherently a bad idea which causes problems all over the
411  place, but tolerate it here for now anyway. */
412  time = _last_ev_time_beats;
413  } else {
414  /* Out of order by more than a tick. */
415  warning << string_compose(_("Skipping event with unordered beat time %1 < %2 (off by %3 beats, %4 ticks)"),
416  ev.time(), _last_ev_time_beats, difference, difference.to_double() / (double)ppqn())
417  << endmsg;
418  return;
419  }
420  }
421 
422  Evoral::event_id_t event_id;
423 
424  if (ev.id() < 0) {
425  event_id = Evoral::next_event_id();
426  } else {
427  event_id = ev.id();
428  }
429 
430  if (_model) {
431  _model->append (ev, event_id);
432  }
433 
434  _length_beats = max(_length_beats, time);
435 
436  const Evoral::Beats delta_time_beats = time - _last_ev_time_beats;
437  const uint32_t delta_time_ticks = delta_time_beats.to_ticks(ppqn());
438 
439  Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
440  _last_ev_time_beats = time;
442 }
443 
445 void
447  const Evoral::Event<framepos_t>& ev,
449 {
450  if (!_writing || ev.size() == 0) {
451  return;
452  }
453 
454  // printf("SMFSource: %s - append_event_frames ID = %d time = %u, size = %u, data = ",
455  // name().c_str(), ev.id(), ev.time(), ev.size());
456  // for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");
457 
458  if (ev.time() < _last_ev_time_frames) {
459  warning << string_compose(_("Skipping event with unordered frame time %1 < %2"),
461  << endmsg;
462  return;
463  }
464 
466  const Evoral::Beats ev_time_beats = converter.from(ev.time());
467  Evoral::event_id_t event_id;
468 
469  if (ev.id() < 0) {
470  event_id = Evoral::next_event_id();
471  } else {
472  event_id = ev.id();
473  }
474 
475  if (_model) {
476  const Evoral::Event<Evoral::Beats> beat_ev (ev.event_type(),
477  ev_time_beats,
478  ev.size(),
479  const_cast<uint8_t*>(ev.buffer()));
480  _model->append (beat_ev, event_id);
481  }
482 
483  _length_beats = max(_length_beats, ev_time_beats);
484 
485  const Evoral::Beats last_time_beats = converter.from (_last_ev_time_frames);
486  const Evoral::Beats delta_time_beats = ev_time_beats - last_time_beats;
487  const uint32_t delta_time_ticks = delta_time_beats.to_ticks(ppqn());
488 
489  Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
490  _last_ev_time_frames = ev.time();
492 }
493 
494 XMLNode&
496 {
497  XMLNode& node = MidiSource::get_state();
498  node.add_property (X_("origin"), _origin);
499  return node;
500 }
501 
502 int
503 SMFSource::set_state (const XMLNode& node, int version)
504 {
505  if (Source::set_state (node, version)) {
506  return -1;
507  }
508 
509  if (MidiSource::set_state (node, version)) {
510  return -1;
511  }
512 
513  if (FileSource::set_state (node, version)) {
514  return -1;
515  }
516 
517  return 0;
518 }
519 
520 void
522 {
523  if (!_open && open_for_write()) {
524  error << string_compose (_("cannot open MIDI file %1 for write"), _path) << endmsg;
525  /* XXX should probably throw or return something */
526  return;
527  }
528 
533 }
534 
535 void
537 {
539 }
540 
541 void
543 {
544  MidiSource::mark_midi_streaming_write_completed (lm, stuck_notes_option, when);
545 
546  if (!writable()) {
547  warning << string_compose ("attempt to write to unwritable SMF file %1", _path) << endmsg;
548  return;
549  }
550 
551  if (_model) {
552  _model->set_edited(false);
553  }
554 
556 
557  /* data in the file now, not removable */
558 
560 }
561 
562 bool
563 SMFSource::valid_midi_file (const string& file)
564 {
565  if (safe_midi_file_extension (file) ) {
566  return (SMF::test (file) );
567  }
568  return false;
569 }
570 
571 bool
573 {
574  static regex_t compiled_pattern;
575  static bool compile = true;
576  const int nmatches = 2;
577  regmatch_t matches[nmatches];
578 
579  if (Glib::file_test (file, Glib::FILE_TEST_EXISTS)) {
580  if (!Glib::file_test (file, Glib::FILE_TEST_IS_REGULAR)) {
581  /* exists but is not a regular file */
582  return false;
583  }
584  }
585 
586  if (compile && regcomp (&compiled_pattern, "\\.[mM][iI][dD][iI]?$", REG_EXTENDED)) {
587  return false;
588  } else {
589  compile = false;
590  }
591 
592  if (regexec (&compiled_pattern, file.c_str(), nmatches, matches, 0)) {
593  return false;
594  }
595 
596  return true;
597 }
598 
599 static bool compare_eventlist (
600  const std::pair< Evoral::Event<Evoral::Beats>*, gint >& a,
601  const std::pair< Evoral::Event<Evoral::Beats>*, gint >& b) {
602  return ( a.first->time() < b.first->time() );
603 }
604 
605 void
606 SMFSource::load_model (const Glib::Threads::Mutex::Lock& lock, bool force_reload)
607 {
608  if (_writing) {
609  return;
610  }
611 
612  if (_model && !force_reload) {
613  return;
614  }
615 
616  if (!_model) {
617  _model = boost::shared_ptr<MidiModel> (new MidiModel (shared_from_this ()));
618  } else {
619  _model->clear();
620  }
621 
622  invalidate(lock);
623 
624  if (writable() && !_open) {
625  return;
626  }
627 
628  _model->start_write();
630 
631  uint64_t time = 0; /* in SMF ticks */
633 
634  uint32_t scratch_size = 0; // keep track of scratch and minimize reallocs
635 
636  uint32_t delta_t = 0;
637  uint32_t size = 0;
638  uint8_t* buf = NULL;
639  int ret;
640  gint event_id;
641  bool have_event_id;
642 
643  // TODO simplify event allocation
644  std::list< std::pair< Evoral::Event<Evoral::Beats>*, gint > > eventlist;
645 
646  for (unsigned i = 1; i <= num_tracks(); ++i) {
647  if (seek_to_track(i)) continue;
648 
649  time = 0;
650  have_event_id = false;
651 
652  while ((ret = read_event (&delta_t, &size, &buf, &event_id)) >= 0) {
653 
654  time += delta_t;
655 
656  if (ret == 0) {
657  /* meta-event : did we get an event ID ? */
658  if (event_id >= 0) {
659  have_event_id = true;
660  }
661  continue;
662  }
663 
664  if (ret > 0) {
665  /* not a meta-event */
666 
667  if (!have_event_id) {
668  event_id = Evoral::next_event_id();
669  }
670  const uint32_t event_type = midi_parameter_type(buf[0]);
671  const Evoral::Beats event_time = Evoral::Beats::ticks_at_rate(time, ppqn());
672 #ifndef NDEBUG
673  std::string ss;
674 
675  for (uint32_t xx = 0; xx < size; ++xx) {
676  char b[8];
677  snprintf (b, sizeof (b), "0x%x ", buf[xx]);
678  ss += b;
679  }
680 
681  DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF %6 load model delta %1, time %2, size %3 buf %4, type %5\n",
682  delta_t, time, size, ss , event_type, name()));
683 #endif
684 
685  eventlist.push_back(make_pair (
687  event_type, event_time,
688  size, buf, true)
689  , event_id));
690 
691  // Set size to max capacity to minimize allocs in read_event
692  scratch_size = std::max(size, scratch_size);
693  size = scratch_size;
694 
695  _length_beats = max(_length_beats, event_time);
696  }
697 
698  /* event ID's must immediately precede the event they are for */
699  have_event_id = false;
700  }
701  }
702 
703  eventlist.sort(compare_eventlist);
704 
705  std::list< std::pair< Evoral::Event<Evoral::Beats>*, gint > >::iterator it;
706  for (it=eventlist.begin(); it!=eventlist.end(); ++it) {
707  _model->append (*it->first, it->second);
708  delete it->first;
709  }
710 
712  _model->set_edited (false);
713  invalidate(lock);
714 
715  free(buf);
716 }
717 
718 void
720 {
721  //cerr << _name << " destroying model " << _model.get() << endl;
722  _model.reset();
723  invalidate(lock);
724 }
725 
726 void
728 {
729  if (!writable() || _length_beats == 0.0) {
730  return;
731  }
732 
733  ensure_disk_file (lock);
734 
736  /* data in the file means its no longer removable */
738 
739  invalidate(lock);
740 }
741 
742 void
743 SMFSource::set_path (const string& p)
744 {
746 }
747 
749 void
751 {
752  if (!writable()) {
753  return;
754  }
755 
756  if (_model) {
757  /* We have a model, so write it to disk; see MidiSource::session_saved
758  for an explanation of what we are doing here.
759  */
761  _model.reset ();
762  mm->sync_to_source (lock);
763  _model = mm;
764  invalidate(lock);
765  } else {
766  /* No model; if it's not already open, it's an empty source, so create
767  and open it for writing.
768  */
769  if (!_open) {
770  open_for_write ();
771  }
772  }
773 }
774 
775 void
777 {
778  /* Unlike the audio case, the MIDI file remains mutable (because we can
779  edit MIDI data)
780  */
781 
783 }
784 
785 
Flag _flags
Definition: source.h:119
EventType event_type() const
Definition: Event.hpp:131
ARDOUR::Session & _session
virtual uint32_t write(Time time, EventType type, uint32_t size, const uint8_t *buf)=0
int read_event(uint32_t *delta_t, uint32_t *size, uint8_t **buf) const
Definition: OldSMF.cpp:212
virtual void mark_streaming_midi_write_started(const Lock &lock, NoteMode mode)
Definition: midi_source.cc:272
framecnt_t read_unlocked(const Lock &lock, Evoral::EventSink< framepos_t > &dst, framepos_t position, framepos_t start, framecnt_t cnt, MidiStateTracker *tracker, MidiChannelFilter *filter) const
Definition: smf_source.cc:213
int32_t event_id_t
Definition: types.hpp:40
framepos_t _smf_last_read_end
Definition: smf_source.h:84
NoteMode
Definition: types.h:204
Glib::Threads::Mutex::Lock Lock
Definition: source.h:54
bool read_contents(uint32_t size, uint8_t *buf)
XMLNode & get_state()
Definition: midi_source.cc:90
bool is_smf_meta_event() const
Definition: MIDIEvent.hpp:98
event_id_t id() const
Definition: Event.hpp:143
bool is_channel_event() const
Definition: MIDIEvent.hpp:97
void end_write() THROW_FILE_ERROR
Definition: OldSMF.cpp:315
void mark_midi_streaming_write_completed(const Lock &lock, Evoral::Sequence< Evoral::Beats >::StuckNoteOption, Evoral::Beats when=Evoral::Beats())
Definition: smf_source.cc:542
TempoMap & tempo_map()
Definition: session.h:596
DataType type()
Definition: source.h:61
framepos_t _capture_length
Definition: midi_source.h:222
void set_id(event_id_t n)
Definition: Event.hpp:144
void prevent_deletion()
Definition: smf_source.cc:776
void track(const MidiBuffer::const_iterator &from, const MidiBuffer::const_iterator &to)
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
LIBPBD_API Transmitter warning
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
int open(const std::string &path) THROW_FILE_ERROR
Definition: OldSMF.cpp:60
uint16_t ppqn() const
Definition: OldSMF.hpp:38
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > start
Definition: region.cc:63
bool removable() const
Definition: file_source.cc:103
framepos_t _smf_last_read_time
Definition: smf_source.h:86
#define _(Text)
Definition: i18n.h:11
void ensure_disk_file(const Lock &lock)
Definition: smf_source.cc:750
#define X_(Text)
Definition: i18n.h:13
XMLNode & get_state()
Definition: smf_source.cc:495
int64_t framecnt_t
Definition: types.h:76
static Beats ticks_at_rate(uint64_t ticks, uint32_t ppqn)
Definition: Beats.hpp:58
uint16_t num_tracks() const
Definition: SMF.cpp:44
uint32_t size() const
Definition: Event.hpp:134
uint64_t to_ticks() const
Definition: Beats.hpp:186
int set_state(const XMLNode &, int version)
Definition: file_source.cc:141
static bool valid_midi_file(const std::string &path)
Definition: smf_source.cc:563
int set_state(const XMLNode &, int version)
Definition: smf_source.cc:503
void set_event_type(EventType t)
Definition: Event.hpp:138
Definition: amp.h:29
Time time() const
Definition: Event.hpp:132
SMFSource(Session &session, const std::string &path, Source::Flag flags)
Evoral::Beats _length_beats
Definition: midi_source.h:218
int seek_to_track(int track)
Definition: SMF.cpp:61
LIBEVORAL_API event_id_t next_event_id()
Definition: Event.cpp:39
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
Evoral::Beats from(framepos_t frames) const
void destroy_model(const Glib::Threads::Mutex::Lock &lock)
Definition: smf_source.cc:719
static bool compare_eventlist(const std::pair< Evoral::Event< Evoral::Beats > *, gint > &a, const std::pair< Evoral::Event< Evoral::Beats > *, gint > &b)
Definition: smf_source.cc:599
std::string _path
Definition: file_source.h:107
int64_t framepos_t
Definition: types.h:66
void set_path(const std::string &newpath)
Definition: smf_source.cc:743
bool sync_to_source(const Glib::Threads::Mutex::Lock &source_lock)
Definition: midi_model.cc:1443
int create(const std::string &path, int track=1, uint16_t ppqn=19200) THROW_FILE_ERROR
Definition: SMF.cpp:143
bool read_prefix(T *time, Evoral::EventType *type, uint32_t *size)
void filter(BufferSet &bufs)
static int loading_state_version
Definition: stateful.h:90
void flush_midi(const Lock &lock)
Definition: smf_source.cc:727
void mark_streaming_write_completed(const Lock &lock)
Definition: smf_source.cc:536
virtual int init(const std::string &idstr, bool must_exist)
Definition: file_source.cc:113
void begin_write()
Definition: OldSMF.cpp:307
void append_event_beats(const Lock &lock, const Evoral::Event< Evoral::Beats > &ev)
Definition: smf_source.cc:393
uint32_t EventType
Definition: types.hpp:43
virtual ~SMFSource()
Definition: smf_source.cc:188
void set(const uint8_t *buf, uint32_t size, Time t)
static bool safe_midi_file_extension(const std::string &path)
Definition: smf_source.cc:572
XMLProperty * add_property(const char *name, const std::string &value)
bool is_sysex() const
Definition: MIDIEvent.hpp:99
virtual void mark_streaming_write_started(const Lock &lock)
Definition: midi_source.cc:307
Writable
Definition: selectable.h:36
framecnt_t write_unlocked(const Lock &lock, MidiRingBuffer< framepos_t > &src, framepos_t position, framecnt_t cnt)
Definition: smf_source.cc:312
std::string _origin
Definition: file_source.h:112
void load_model(const Glib::Threads::Mutex::Lock &lock, bool force_reload=false)
Definition: smf_source.cc:606
LIBARDOUR_API uint64_t MidiSourceIO
Definition: debug.cc:28
Definition: xml++.h:95
void seek_to_start() const
Definition: OldSMF.cpp:108
std::string name() const
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > position
Definition: region.cc:65
std::vector< std::string > source_search_path(DataType) const
Definition: session.cc:5125
Definition: debug.h:30
double to_double() const
Definition: Beats.hpp:185
int set_state(const XMLNode &, int version)
Definition: midi_source.cc:114
virtual void set_path(const std::string &)
Definition: file_source.cc:545
void flush()
Definition: OldSMF.cpp:138
void append_event_delta(uint32_t delta_t, const Event< Time > &ev)
Definition: OldSMF.cpp:284
LIBEVORAL_API uint64_t Beats
Evoral::Beats _last_ev_time_beats
Definition: smf_source.h:81
const uint8_t * buffer() const
Definition: Event.hpp:135
bool writable() const
Definition: source.cc:305
static const framecnt_t max_framecnt
Definition: types.h:79
bool peek(uint8_t *, size_t size)
virtual void mark_midi_streaming_write_completed(const Lock &lock, Evoral::Sequence< Evoral::Beats >::StuckNoteOption stuck_option, Evoral::Beats when=Evoral::Beats())
Definition: midi_source.cc:314
int set_state(const XMLNode &, int version)
Definition: source.cc:113
AutomationType midi_parameter_type(uint8_t status)
boost::shared_ptr< MidiModel > _model
Definition: midi_source.h:212
LIBARDOUR_API bool init(bool with_vst, bool try_optimization, const char *localedir)
Definition: globals.cc:376
void mark_streaming_midi_write_started(const Lock &lock, NoteMode mode)
Definition: smf_source.cc:521
framepos_t _last_ev_time_frames
Definition: smf_source.h:82
Definition: ardour.h:41
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
void append_event_frames(const Lock &lock, const Evoral::Event< framepos_t > &ev, framepos_t source_start)
Definition: smf_source.cc:446
void invalidate(const Glib::Threads::Mutex::Lock &lock, std::set< Evoral::Sequence< Evoral::Beats >::WeakNotePtr > *notes=NULL)
Definition: midi_source.cc:181