} else {
/* new selection */
- if (!_editor->selection->selected (_editor->clicked_axisview)) {
+ if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
_editor->selection->set (_editor->clicked_axisview);
}
_editor->selection->clear_time();
}
- if (!_editor->selection->selected (_editor->clicked_axisview)) {
+ if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
_editor->selection->set (_editor->clicked_axisview);
}
region->move_selection (dx, dy);
CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
- char buf[4];
- snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
- //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
+
+ char buf[12];
+ snprintf (buf, sizeof (buf), "%s (%g)", Evoral::midi_note_name (cnote->note()->note()).c_str(),
+ (int) cnote->note()->note() + drag_delta_note);
_editor->show_verbose_canvas_cursor_with (buf);
}
}
switch (item_type) {
case NoteItem:
if (internal_editing()) {
- /* Note: we don't get here if not in internal_editing() mode */
_drags->set (new NoteDrag (this, item), event);
return true;
}
#include "evoral/Parameter.hpp"
#include "evoral/Control.hpp"
+#include "evoral/midi_util.h"
#include "automation_region_view.h"
#include "automation_time_axis.h"
frames_to_beats(start_frames + _region->start()), length,
(uint8_t)note, 0x40));
+ if (_model->contains (new_note)) {
+ return;
+ }
+
view->update_note_range(new_note->note());
MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
MidiModel::Notes& notes (_model->notes());
_optimization_iterator = _events.begin();
- cerr << "++++++++++ MIDI REdisplay\n";
-
for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
boost::shared_ptr<NoteType> note (*n);
note_selected(ev, true);
}
- char buf[4];
- snprintf (buf, sizeof (buf), "%d", (int) ev->note()->note());
- // This causes an infinite loop on note add sometimes
- //PublicEditor& editor (trackview.editor());
- //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
- //editor.show_verbose_canvas_cursor_with (buf);
+ char buf[12];
+ snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (ev->note()->note()).c_str(), (int) ev->note()->note());
+ PublicEditor& editor (trackview.editor());
+ editor.show_verbose_canvas_cursor_with (buf);
}
void
void apply_command(Session& session, Command* cmd);
void apply_command_as_subcommand(Session& session, Command* cmd);
- bool write_to(boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin = Evoral::MinMusicalTime,
- Evoral::MusicalTime end = Evoral::MaxMusicalTime);
+ bool write_to(boost::shared_ptr<MidiSource> source);
+ bool write_section_to(boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin = Evoral::MinMusicalTime,
+ Evoral::MusicalTime end = Evoral::MaxMusicalTime);
// MidiModel doesn't use the normal AutomationList serialisation code
// since controller data is stored in the .mid
boost::shared_ptr<MidiModel> model() { return _model; }
void set_model(boost::shared_ptr<MidiModel> m) { _model = m; }
- void drop_model() { _model.reset(); }
+ void drop_model();
protected:
virtual void flush_midi() = 0;
return *diff_command;
}
+/** Write all of the model to a MidiSource (i.e. save the model).
+ * This is different from manually using read to write to a source in that
+ * note off events are written regardless of the track mode. This is so the
+ * user can switch a recorded track (with note durations from some instrument)
+ * to percussive, save, reload, then switch it back to sustained without
+ * destroying the original note durations.
+ */
+bool
+MidiModel::write_to (boost::shared_ptr<MidiSource> source)
+{
+ ReadLock lock(read_lock());
+
+ const bool old_percussive = percussive();
+ set_percussive(false);
+
+ source->drop_model();
+ source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
+
+ for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
+ source->append_event_unlocked_beats(*i);
+ }
+
+ set_percussive(old_percussive);
+ source->mark_streaming_write_completed();
+
+ set_edited(false);
+
+ return true;
+}
+
/** Write part or all of the model to a MidiSource (i.e. save the model).
* This is different from manually using read to write to a source in that
* note off events are written regardless of the track mode. This is so the
* destroying the original note durations.
*/
bool
-MidiModel::write_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
+MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
{
ReadLock lock(read_lock());
MidiStateTracker mst;
newsrc->set_timeline_position(_timeline_position);
if (_model) {
- _model->write_to (newsrc, begin, end);
+ 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>();
}
}
+void
+MidiSource::drop_model ()
+{
+ cerr << name() << " drop model\n";
+ _model.reset();
+}
bool edited() const { return _edited; }
void set_edited(bool yn) { _edited = yn; }
- void add_note_unlocked(const boost::shared_ptr< Note<Time> > note);
+ bool contains (const boost::shared_ptr< Note<Time> > ev) const;
+ bool add_note_unlocked(const boost::shared_ptr< Note<Time> > note);
void remove_note_unlocked(const boost::shared_ptr< const Note<Time> > note);
uint8_t lowest_note() const { return _lowest_note; }
}
template<typename Time>
-void
+bool
+Sequence<Time>::contains(const boost::shared_ptr< Note<Time> > note) const
+{
+ ReadLock lock (read_lock());
+
+ for (typename Sequence<Time>::Notes::const_iterator i = note_lower_bound(note->time());
+ i != _notes.end() && (*i)->time() == note->time(); ++i) {
+ if (*i == note) {
+ cerr << "Existing note matches: " << *i << endl;
+ return true;
+ }
+ }
+ cerr << "No matching note for " << note << endl;
+ return false;
+}
+
+template<typename Time>
+bool
Sequence<Time>::add_note_unlocked(const boost::shared_ptr< Note<Time> > note)
{
DUMP(format("%1% add note %2% @ %3%\n") % this % (int)note->note() % note->time());
+
+ for (typename Sequence<Time>::Notes::iterator i = note_lower_bound(note->time());
+ i != _notes.end() && (*i)->time() == note->time(); ++i) {
+ if (*i == note) {
+ return false;
+ }
+ }
+
_edited = true;
_notes.insert(note);
+ return true;
}
template<typename Time>