From 82053a22ce2f80df26ca6a897824cc23b761911b Mon Sep 17 00:00:00 2001 From: paul Date: Tue, 20 Jul 2010 16:27:34 +0000 Subject: [PATCH] add note IDs and use them for looking up notes during a history rebuild. NOTE: INVALIDATES OLDER HISTORY FILES git-svn-id: http://subversion.ardour.org/svn/ardour2/ardour2/branches/3.0@7449 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/midi_time_axis.cc | 1 - libs/ardour/ardour/midi_model.h | 1 + libs/ardour/globals.cc | 3 +- libs/ardour/import.cc | 7 +- libs/ardour/midi_model.cc | 201 ++++++---------- libs/ardour/midi_source.cc | 2 +- libs/ardour/midi_state_tracker.cc | 1 - libs/ardour/midi_stretch.cc | 2 +- libs/ardour/region_factory.cc | 1 - libs/ardour/session_state.cc | 8 + libs/ardour/smf_source.cc | 117 +++++++--- libs/evoral/evoral/Event.hpp | 30 +-- libs/evoral/evoral/EventList.hpp | 4 +- libs/evoral/evoral/EventRingBuffer.hpp | 3 +- libs/evoral/evoral/EventSink.hpp | 2 +- libs/evoral/evoral/MIDIEvent.hpp | 4 +- libs/evoral/evoral/Note.hpp | 10 +- libs/evoral/evoral/SMF.hpp | 5 +- libs/evoral/evoral/Sequence.hpp | 8 +- libs/evoral/evoral/types.hpp | 6 + libs/evoral/src/Event.cpp | 25 +- libs/evoral/src/Note.cpp | 16 +- libs/evoral/src/SMF.cpp | 57 ++++- libs/evoral/src/Sequence.cpp | 411 +++++++++++++++++---------------- 24 files changed, 513 insertions(+), 412 deletions(-) diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index 6281fc811..a4e09871c 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -885,7 +885,6 @@ void MidiTimeAxisView::check_step_edit () { MidiRingBuffer& incoming (midi_track()->step_edit_ring_buffer()); - Evoral::Note note; uint8_t* buf; uint32_t bufsize = 32; diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index a8303539b..e91d6484e 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -144,6 +144,7 @@ public: void set_midi_source (MidiSource *); boost::shared_ptr > find_note (NotePtr); + boost::shared_ptr > find_note (gint note_id); InsertMergePolicy insert_merge_policy () const; void set_insert_merge_policy (InsertMergePolicy); diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index 3aa6d6990..ca2432eda 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -283,8 +283,7 @@ ARDOUR::init (bool use_vst, bool try_optimization) if (Config->load_state ()) { return -1; } - - + Config->set_use_vst (use_vst); Profile = new RuntimeProfile; diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index 021e456bb..7d986539d 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -378,9 +378,12 @@ write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status, bool first = true; while (!status.cancel) { - size = buf_size; + gint ignored; // imported files either don't have NoteID's or + // we ignore them. - int ret = source->read_event(&delta_t, &size, &buf); + size = buf_size; + + int ret = source->read_event(&delta_t, &size, &buf, &ignored); if (size > buf_size) buf_size = size; diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index b5d5d2471..6922384e7 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -382,25 +382,41 @@ MidiModel::DiffCommand::marshal_note(const NotePtr note) cerr << "Marshalling note: " << *note << endl; - ostringstream note_str(ios::ate); - note_str << int(note->note()); - xml_note->add_property("note", note_str.str()); + { + ostringstream id_str(ios::ate); + id_str << int(note->id()); + xml_note->add_property("id", id_str.str()); + } - ostringstream channel_str(ios::ate); - channel_str << int(note->channel()); - xml_note->add_property("channel", channel_str.str()); + { + ostringstream note_str(ios::ate); + note_str << int(note->note()); + xml_note->add_property("note", note_str.str()); + } - ostringstream time_str(ios::ate); - time_str << note->time(); - xml_note->add_property("time", time_str.str()); + { + ostringstream channel_str(ios::ate); + channel_str << int(note->channel()); + xml_note->add_property("channel", channel_str.str()); + } - ostringstream length_str(ios::ate); - length_str << note->length(); - xml_note->add_property("length", length_str.str()); + { + ostringstream time_str(ios::ate); + time_str << note->time(); + xml_note->add_property("time", time_str.str()); + } - ostringstream velocity_str(ios::ate); - velocity_str << (unsigned int) note->velocity(); - xml_note->add_property("velocity", velocity_str.str()); + { + ostringstream length_str(ios::ate); + length_str << note->length(); + xml_note->add_property("length", length_str.str()); + } + + { + ostringstream velocity_str(ios::ate); + velocity_str << (unsigned int) note->velocity(); + xml_note->add_property("velocity", velocity_str.str()); + } return *xml_note; } @@ -414,6 +430,15 @@ MidiModel::DiffCommand::unmarshal_note(XMLNode *xml_note) unsigned int time; unsigned int length; unsigned int velocity; + gint id; + + if ((prop = xml_note->property("id")) != 0) { + istringstream id_str(prop->value()); + id_str >> id; + } else { + error << "note information missing ID value" << endmsg; + id = -1; + } if ((prop = xml_note->property("note")) != 0) { istringstream note_str(prop->value()); @@ -456,6 +481,7 @@ MidiModel::DiffCommand::unmarshal_note(XMLNode *xml_note) } NotePtr note_ptr(new Evoral::Note(channel, time, length, note, velocity)); + note_ptr->set_id (id); return note_ptr; } @@ -463,7 +489,7 @@ MidiModel::DiffCommand::unmarshal_note(XMLNode *xml_note) XMLNode& MidiModel::DiffCommand::marshal_change(const NoteChange& change) { - XMLNode* xml_change = new XMLNode("change"); + XMLNode* xml_change = new XMLNode("Change"); /* first, the change itself */ @@ -489,49 +515,9 @@ MidiModel::DiffCommand::marshal_change(const NoteChange& change) xml_change->add_property ("new", new_value_str.str()); } - /* now the rest of the note */ - - const SMFSource* smf = dynamic_cast (_model->midi_source()); - - if (change.property != NoteNumber) { - ostringstream note_str; - note_str << int(change.note->note()); - xml_change->add_property("note", note_str.str()); - } - - if (change.property != Channel) { - ostringstream channel_str; - channel_str << int(change.note->channel()); - xml_change->add_property("channel", channel_str.str()); - } - - if (change.property != StartTime) { - ostringstream time_str; - if (smf) { - time_str << smf->round_to_file_precision (change.note->time()); - } else { - time_str << change.note->time(); - } - xml_change->add_property("time", time_str.str()); - } - - if (change.property != Length) { - ostringstream length_str; - if (smf) { - length_str << smf->round_to_file_precision (change.note->length()); - } else { - length_str << change.note->length(); - } - xml_change->add_property ("length", length_str.str()); - } - - if (change.property != Velocity) { - ostringstream velocity_str; - velocity_str << int (change.note->velocity()); - xml_change->add_property("velocity", velocity_str.str()); - } - - /* and now notes that were remove as a side-effect */ + ostringstream id_str; + id_str << change.note->id(); + xml_change->add_property ("id", id_str.str()); return *xml_change; } @@ -541,11 +527,6 @@ MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change) { XMLProperty* prop; NoteChange change; - unsigned int note; - unsigned int channel; - unsigned int velocity; - Evoral::MusicalTime time; - Evoral::MusicalTime length; if ((prop = xml_change->property("property")) != 0) { change.property = (Property) string_2_enum (prop->value(), change.property); @@ -554,6 +535,13 @@ MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change) /*NOTREACHED*/ } + if ((prop = xml_change->property ("id")) == 0) { + error << _("No NoteID found for note property change - ignored") << endmsg; + return change; + } + + gint note_id = atoi (prop->value().c_str()); + if ((prop = xml_change->property ("old")) != 0) { istringstream old_str (prop->value()); if (change.property == StartTime || change.property == Length) { @@ -582,78 +570,15 @@ MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change) /*NOTREACHED*/ } - if (change.property != NoteNumber) { - if ((prop = xml_change->property("note")) != 0) { - istringstream note_str(prop->value()); - note_str >> note; - } else { - warning << "note information missing note value" << endmsg; - note = 127; - } - } else { - note = change.new_value; - } - - if (change.property != Channel) { - if ((prop = xml_change->property("channel")) != 0) { - istringstream channel_str(prop->value()); - channel_str >> channel; - } else { - warning << "note information missing channel" << endmsg; - channel = 0; - } - } else { - channel = change.new_value; - } - - if (change.property != StartTime) { - if ((prop = xml_change->property("time")) != 0) { - istringstream time_str(prop->value()); - time_str >> time; - } else { - warning << "note information missing time" << endmsg; - time = 0; - } - } else { - time = change.new_time; - } - - if (change.property != Length) { - if ((prop = xml_change->property("length")) != 0) { - istringstream length_str(prop->value()); - length_str >> length; - } else { - warning << "note information missing length" << endmsg; - length = 1; - } - } else { - length = change.new_time; - } - - if (change.property != Velocity) { - if ((prop = xml_change->property("velocity")) != 0) { - istringstream velocity_str(prop->value()); - velocity_str >> velocity; - } else { - warning << "note information missing velocity" << endmsg; - velocity = 127; - } - } else { - velocity = change.new_value; - } - /* we must point at the instance of the note that is actually in the model. so go look for it ... */ - NotePtr new_note (new Evoral::Note (channel, time, length, note, velocity)); - - change.note = _model->find_note (new_note); + change.note = _model->find_note (note_id); if (!change.note) { - warning << "MIDI note " << *new_note << " not found in model - programmers should investigate this" << endmsg; - /* use the actual new note */ - change.note = new_note; + warning << "MIDI note #" << note_id << " not found in model - programmers should investigate this" << endmsg; + return change; } return change; @@ -925,6 +850,22 @@ MidiModel::find_note (NotePtr other) return NotePtr(); } +Evoral::Sequence::NotePtr +MidiModel::find_note (gint note_id) +{ + /* used only for looking up notes when reloading history from disk, + so we don't care about performance *too* much. + */ + + for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) { + if ((*l)->id() == note_id) { + return *l; + } + } + + return NotePtr(); +} + /** Lock and invalidate the source. * This should be used by commands and editing things */ diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index 3f053ecee..fe6c733a8 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -205,7 +205,7 @@ MidiSource::midi_read (Evoral::EventSink& dst, sframes_t source_start const sframes_t time_frames = converter.to(i->time()); if (time_frames < start + cnt) { dst.write(time_frames + stamp_offset - negative_stamp_offset, - i->event_type(), i->size(), i->buffer()); + i->event_type(), i->size(), i->buffer()); if (tracker) { Evoral::MIDIEvent& ev (*(Evoral::MIDIEvent*) (&(*i))); if (ev.is_note_on()) { diff --git a/libs/ardour/midi_state_tracker.cc b/libs/ardour/midi_state_tracker.cc index da6b8f40b..4e43e7269 100644 --- a/libs/ardour/midi_state_tracker.cc +++ b/libs/ardour/midi_state_tracker.cc @@ -152,7 +152,6 @@ MidiStateTracker::resolve_notes (MidiSource& src, Evoral::MusicalTime time) ev.set_velocity (0); src.append_event_unlocked_beats (ev); _active_notes[note + 128 * channel]--; - cerr << "Resolved " << ev << endl; /* don't stack events up at the same time */ time += 1.0/128.0; diff --git a/libs/ardour/midi_stretch.cc b/libs/ardour/midi_stretch.cc index 21b5453da..dedae6acb 100644 --- a/libs/ardour/midi_stretch.cc +++ b/libs/ardour/midi_stretch.cc @@ -100,7 +100,7 @@ MidiStretch::run (boost::shared_ptr r) // FIXME: double copy Evoral::Event ev(*i, true); ev.time() = new_time; - new_model->append(ev); + new_model->append(ev, Evoral::next_event_id()); } new_model->end_write(); diff --git a/libs/ardour/region_factory.cc b/libs/ardour/region_factory.cc index 7f11b8089..b4beddfb6 100644 --- a/libs/ardour/region_factory.cc +++ b/libs/ardour/region_factory.cc @@ -271,7 +271,6 @@ RegionFactory::map_add (boost::shared_ptr r) { Glib::Mutex::Lock lm (region_map_lock); - cerr << "MAP ADD: " << r->name() << " ID = " << r->id() << endl; region_map.insert (p); } diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 8ad239280..9e4d2f281 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -1043,6 +1043,11 @@ Session::state(bool full_state) snprintf (buf, sizeof (buf), "%" PRIu64, ID::counter()); node->add_property ("id-counter", buf); + /* save the event ID counter */ + + snprintf (buf, sizeof (buf), "%d", Evoral::event_id_counter()); + node->add_property ("event-counter", buf); + /* various options */ node->add_child_nocopy (config.get_variables ()); @@ -1225,6 +1230,9 @@ Session::set_state (const XMLNode& node, int version) ID::init_counter (now); } + if ((prop = node.property (X_("event-counter"))) != 0) { + Evoral::init_event_id_counter (atoi (prop->value())); + } IO::disable_connecting (); diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index f8a2d10e9..405f12881 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -134,7 +134,9 @@ SMFSource::read_unlocked (Evoral::EventSink& destination, sframes_t s DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: seek to %1\n", start)); Evoral::SMF::seek_to_start(); while (time < start_ticks) { - ret = read_event(&ev_delta_t, &ev_size, &ev_buffer); + gint ignored; + + ret = read_event(&ev_delta_t, &ev_size, &ev_buffer, &ignored); if (ret == -1) { // EOF _smf_last_read_end = start + duration; return duration; @@ -149,7 +151,9 @@ SMFSource::read_unlocked (Evoral::EventSink& destination, sframes_t s _smf_last_read_end = start + duration; while (true) { - ret = read_event(&ev_delta_t, &ev_size, &ev_buffer); + gint ignored; /* XXX don't ignore note id's ??*/ + + ret = read_event(&ev_delta_t, &ev_size, &ev_buffer, &ignored); if (ret == -1) { // EOF break; } @@ -247,6 +251,8 @@ SMFSource::write_unlocked (MidiRingBuffer& source, sframes_t position ev.set(buf, size, time); ev.set_event_type(EventTypeMap::instance().midi_event_type(ev.buffer()[0])); + ev.set_id (Evoral::next_event_id()); + if (!(ev.is_channel_event() || ev.is_smf_meta_event() || ev.is_sysex())) { /*cerr << "SMFSource: WARNING: caller tried to write non SMF-Event of type " << std::hex << int(ev.buffer()[0]) << endl;*/ @@ -273,10 +279,10 @@ SMFSource::append_event_unlocked_beats (const Evoral::Event& ev) if (ev.size() == 0) { return; } - - /*printf("SMFSource: %s - append_event_unlocked_beats time = %lf, size = %u, data = ", - name().c_str(), ev.time(), ev.size()); - for (size_t i = 0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/ + + /* printf("SMFSource: %s - append_event_unlocked_beats ID = %d time = %lf, size = %u, data = ", + name().c_str(), ev.id(), ev.time(), ev.size()); + for (size_t i = 0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/ assert(ev.time() >= 0); if (ev.time() < _last_ev_time_beats) { @@ -284,19 +290,28 @@ SMFSource::append_event_unlocked_beats (const Evoral::Event& ev) return; } + Evoral::event_id_t event_id; + + if (ev.id() < 0) { + event_id = Evoral::next_event_id(); + } else { + event_id = ev.id(); + } + + if (_model) { + _model->append (ev, event_id); + } + _length_beats = max(_length_beats, ev.time()); const double delta_time_beats = ev.time() - _last_ev_time_beats; const uint32_t delta_time_ticks = (uint32_t)lrint(delta_time_beats * (double)ppqn()); - Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer()); + Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id); _last_ev_time_beats = ev.time(); _write_data_count += ev.size(); - if (_model) { - _model->append (ev); - } } /** Append an event with a timestamp in frames (nframes_t) */ @@ -308,34 +323,44 @@ SMFSource::append_event_unlocked_frames (const Evoral::Event& ev, sfr return; } - /*printf("SMFSource: %s - append_event_unlocked_frames time = %u, size = %u, data = ", - name().c_str(), ev.time(), ev.size()); - for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/ + /* printf("SMFSource: %s - append_event_unlocked_frames ID = %d time = %u, size = %u, data = ", + name().c_str(), ev.id(), ev.time(), ev.size()); + for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/ if (ev.time() < _last_ev_time_frames) { cerr << "SMFSource: Warning: Skipping event with non-monotonic time" << endl; return; } - + BeatsFramesConverter converter(_session.tempo_map(), position); + const double ev_time_beats = converter.from(ev.time()); + Evoral::event_id_t event_id; + + if (ev.id() < 0) { + event_id = Evoral::next_event_id(); + } else { + event_id = ev.id(); + } + + if (_model) { + const Evoral::Event beat_ev (ev.event_type(), + ev_time_beats, + ev.size(), + (uint8_t*)ev.buffer()); + _model->append (beat_ev, event_id); + } - _length_beats = max(_length_beats, converter.from(ev.time())); + _length_beats = max(_length_beats, ev_time_beats); const sframes_t delta_time_frames = ev.time() - _last_ev_time_frames; const double delta_time_beats = converter.from(delta_time_frames); const uint32_t delta_time_ticks = (uint32_t)(lrint(delta_time_beats * (double)ppqn())); - Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer()); + Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id); _last_ev_time_frames = ev.time(); _write_data_count += ev.size(); - if (_model) { - const double ev_time_beats = converter.from(ev.time()); - const Evoral::Event beat_ev( - ev.event_type(), ev_time_beats, ev.size(), (uint8_t*)ev.buffer()); - _model->append (beat_ev); - } } XMLNode& @@ -435,13 +460,35 @@ SMFSource::load_model (bool lock, bool force_reload) uint32_t size = 0; uint8_t* buf = NULL; int ret; - while ((ret = read_event(&delta_t, &size, &buf)) >= 0) { + gint event_id; + bool have_event_id = false; + + while ((ret = read_event (&delta_t, &size, &buf, &event_id)) >= 0) { + time += delta_t; - ev.set(buf, size, time / (double)ppqn()); + + if (ret == 0) { + + /* meta-event : did we get an event ID ? + */ + + if (event_id >= 0) { + have_event_id = true; + } + + continue; + } + + if (ret > 0) { + + /* not a meta-event */ - if (ret > 0) { // didn't skip (meta) event + ev.set (buf, size, time / (double)ppqn()); ev.set_event_type(EventTypeMap::instance().midi_event_type(buf[0])); + if (!have_event_id) { + event_id = Evoral::next_event_id(); + } #ifndef NDEBUG std::string ss; @@ -455,15 +502,21 @@ SMFSource::load_model (bool lock, bool force_reload) delta_t, time, size, ss , ev.event_type(), name())); #endif - _model->append (ev); - } + _model->append (ev, event_id); - if (ev.size() > scratch_size) { - scratch_size = ev.size(); - } - ev.size() = scratch_size; // ensure read_event only allocates if necessary + if (ev.size() > scratch_size) { + scratch_size = ev.size(); + } + + ev.size() = scratch_size; // ensure read_event only allocates if necessary + + _length_beats = max(_length_beats, ev.time()); + } - _length_beats = max(_length_beats, ev.time()); + /* event ID's must immediately precede the event they are for + */ + + have_event_id = false; } _model->end_write(false); diff --git a/libs/evoral/evoral/Event.hpp b/libs/evoral/evoral/Event.hpp index 75dfbdbe3..2410b3684 100644 --- a/libs/evoral/evoral/Event.hpp +++ b/libs/evoral/evoral/Event.hpp @@ -26,7 +26,6 @@ #include #include "evoral/types.hpp" - /** If this is not defined, all methods of MidiEvent are RT safe * but MidiEvent will never deep copy and (depending on the scenario) * may not be usable in STL containers, signals, etc. @@ -35,6 +34,9 @@ namespace Evoral { +event_id_t event_id_counter(); +event_id_t next_event_id(); +void init_event_id_counter (event_id_t n); /** An event (much like a type generic jack_midi_event_t) * @@ -43,7 +45,7 @@ namespace Evoral { template struct Event { #ifdef EVORAL_EVENT_ALLOC - Event(EventType type=0, Time time=0, uint32_t size=0, uint8_t* buf=NULL, bool alloc=false); + Event (EventType type=0, Time time=0, uint32_t size=0, uint8_t* buf=NULL, bool alloc=false); /** Copy \a copy. * @@ -56,6 +58,7 @@ struct Event { ~Event(); inline const Event& operator=(const Event& copy) { + _id = copy.id(); // XXX is this right? do we want ID copy semantics? _type = copy._type; _original_time = copy._original_time; _nominal_time = copy._nominal_time; @@ -77,20 +80,6 @@ struct Event { return *this; } - inline void shallow_copy(const Event& copy) { - if (_owns_buf) { - free(_buf); - _buf = false; - _owns_buf = false; - } - - _type = copy._type; - _original_time = copy._nominal_time; - _nominal_time = copy._nominal_time; - _size = copy._size; - _buf = copy._buf; - } - inline void set(uint8_t* buf, uint32_t size, Time t) { if (_owns_buf) { if (_size < size) { @@ -181,6 +170,9 @@ struct Event { inline const uint8_t* buffer() const { return _buf; } inline uint8_t*& buffer() { return _buf; } + inline event_id_t id() const { return _id; } + inline void set_id (event_id_t n) { _id = n; } + protected: EventType _type; /**< Type of event (application relative, NOT MIDI 'type') */ Time _original_time; /**< Sample index (or beat time) at which event is valid */ @@ -189,17 +181,17 @@ protected: uint8_t* _buf; /**< Raw MIDI data */ #ifdef EVORAL_EVENT_ALLOC - bool _owns_buf; /**< Whether buffer is locally allocated */ + bool _owns_buf; /**< Whether buffer is locally allocated */ #endif + event_id_t _id; /** UUID for each event, should probably be 64bit or at least unsigned */ }; - } // namespace Evoral template std::ostream& operator<<(std::ostream& o, const Evoral::Event