#include <iomanip>
#include <algorithm>
+#include <glibmm/fileutils.h>
+
#include "pbd/xml++.h"
#include "pbd/pthread_utils.h"
#include "pbd/basename.h"
using namespace ARDOUR;
using namespace PBD;
-sigc::signal<void,MidiSource *> MidiSource::MidiSourceCreated;
+PBD::Signal1<void,MidiSource*> MidiSource::MidiSourceCreated;
MidiSource::MidiSource (Session& s, string name, Source::Flag flags)
: Source(s, DataType::MIDI, name, flags)
}
}
+
MidiSource::~MidiSource ()
{
}
return 0;
}
-sframes_t
-MidiSource::length (sframes_t pos) const
+bool
+MidiSource::empty () const
{
+ return _length_beats == 0;
+}
+
+framecnt_t
+MidiSource::length (framepos_t pos) const
+{
+ if (_length_beats == 0) {
+ return 0;
+ }
+
BeatsFramesConverter converter(_session.tempo_map(), pos);
return converter.to(_length_beats);
}
_model_iter.invalidate();
}
+/** @param filtered A set of parameters whose MIDI messages will not be returned */
nframes_t
MidiSource::midi_read (Evoral::EventSink<nframes_t>& dst, sframes_t source_start,
sframes_t start, nframes_t cnt,
sframes_t stamp_offset, sframes_t negative_stamp_offset,
- MidiStateTracker* tracker) const
+ MidiStateTracker* tracker,
+ std::set<Evoral::Parameter> const & filtered) const
{
Glib::Mutex::Lock lm (_lock);
// If the cached iterator is invalid, search for the first event past start
if (_last_read_end == 0 || start != _last_read_end || !_model_iter_valid) {
- DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("****!!!!**** search for relevant iterator for %1 / %2\n", source_start, start));
- for (i = _model->begin(); i != _model->end(); ++i) {
+ DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("*** %1 search for relevant iterator for %1 / %2\n", _name, source_start, start));
+ for (i = _model->begin(0, filtered); i != _model->end(); ++i) {
if (converter.to(i->time()) >= start) {
break;
}
}
_model_iter_valid = true;
} else {
- DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("============ use cached iterator for %1 / %2\n", source_start, start));
+ DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("*** %1 use cached iterator for %1 / %2\n", _name, source_start, start));
}
_last_read_end = start + cnt;
if (tracker) {
Evoral::MIDIEvent<Evoral::MusicalTime>& ev (*(Evoral::MIDIEvent<Evoral::MusicalTime>*) (&(*i)));
if (ev.is_note_on()) {
- DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("\t%1 Added note on %2 @ %3\n", _name, ev.note(), time_frames));
+ DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("\t%1 add note on %2 @ %3\n", _name, ev.note(), time_frames));
tracker->add (ev.note(), ev.channel());
} else if (ev.is_note_off()) {
- DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("\t%1 Added note OFF %2 @ %3\n", _name, ev.note(), time_frames));
+ DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("\t%1 add note off %2 @ %3\n", _name, ev.note(), time_frames));
tracker->remove (ev.note(), ev.channel());
}
}
_writing = false;
}
+boost::shared_ptr<MidiSource>
+MidiSource::clone (Evoral::MusicalTime begin, Evoral::MusicalTime end)
+{
+ string newname = PBD::basename_nosuffix(_name.val());
+ string newpath;
+
+ /* get a new name for the MIDI file we're going to write to
+ */
+
+ do {
+
+ newname = bump_name_once (newname, '-');
+ /* XXX build path safely */
+ newpath = _session.session_directory().midi_path().to_string() +"/"+ newname + ".mid";
+
+ } while (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS));
+
+ boost::shared_ptr<MidiSource> newsrc = boost::dynamic_pointer_cast<MidiSource>(
+ SourceFactory::createWritable(DataType::MIDI, _session,
+ newpath, false, _session.frame_rate()));
+
+ newsrc->set_timeline_position(_timeline_position);
+
+ if (_model) {
+ if (begin == Evoral::MinMusicalTime && end == Evoral::MaxMusicalTime) {
+ _model->write_to (newsrc);
+ } else {
+ _model->write_section_to (newsrc, begin, end);
+ }
+ } else {
+ error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()"));
+ return boost::shared_ptr<MidiSource>();
+ }
+
+ newsrc->flush_midi();
+
+ /* force a reload of the model if the range is partial */
+
+ if (begin != Evoral::MinMusicalTime || end != Evoral::MaxMusicalTime) {
+ newsrc->load_model (true, true);
+ } else {
+ newsrc->set_model (_model);
+ }
+
+ return newsrc;
+}
+
void
MidiSource::session_saved()
{
- flush_midi();
+ /* this writes a copy of the data to disk.
+ XXX do we need to do this every time?
+ */
- if (_model && _model->edited()) {
- string newname;
- const string basename = PBD::basename_nosuffix(_name);
- string::size_type last_dash = basename.find_last_of("-");
- if (last_dash == string::npos || last_dash == basename.find_first_of("-")) {
- newname = basename + "-1";
- } else {
- stringstream ss(basename.substr(last_dash+1));
- unsigned write_count = 0;
- ss >> write_count;
- // cerr << "WRITE COUNT: " << write_count << endl;
- ++write_count; // start at 1
- ss.clear();
- ss << basename.substr(0, last_dash) << "-" << write_count;
- newname = ss.str();
- }
-
- string newpath = _session.session_directory().midi_path().to_string() +"/"+ newname + ".mid";
-
- boost::shared_ptr<MidiSource> newsrc = boost::dynamic_pointer_cast<MidiSource>(
- SourceFactory::createWritable(DataType::MIDI, _session,
- newpath, true, false, _session.frame_rate()));
+ flush_midi();
+ cerr << name() << " @ " << this << " length at save = " << _length_beats << endl;
- newsrc->set_timeline_position(_timeline_position);
- _model->write_to(newsrc);
+#if 0 // old style: clone the source if necessary on every session save
+ // and switch to the new source
- // cyclic dependency here, ugly :(
- newsrc->set_model(_model);
- _model->set_midi_source(newsrc.get());
+ if (_model && _model->edited()) {
+ cerr << "Model exists and is edited\n";
- newsrc->flush_midi();
+ boost::shared_ptr<MidiSource> newsrc = clone ();
- Switched.emit(newsrc);
+ if (newsrc) {
+ _model->set_midi_source (newsrc.get());
+ Switched (newsrc); /* EMIT SIGNAL */
+ }
}
+#endif
}
void
}
}
+void
+MidiSource::drop_model ()
+{
+ cerr << name() << " drop model\n";
+ _model.reset();
+ ModelChanged (); /* EMIT SIGNAL */
+}
+
+void
+MidiSource::set_model (boost::shared_ptr<MidiModel> m)
+{
+ _model = m;
+ ModelChanged (); /* EMIT SIGNAL */
+}