various minor MIDI fixes: prevent duplicate note entry with mouse, show note info more often with verbose cursor, fix some crashes from click+move on notes ... lots more where this comes from
git-svn-id: svn://localhost/ardour2/branches/3.0@7128 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
e58f6752af
commit
a196405da9
@ -3243,7 +3243,7 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
|
||||
} 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);
|
||||
}
|
||||
|
||||
@ -3361,7 +3361,7 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
|
||||
_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);
|
||||
}
|
||||
|
||||
@ -3747,9 +3747,10 @@ NoteDrag::motion (GdkEvent*, bool)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -669,7 +669,6 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
|
||||
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;
|
||||
}
|
||||
|
@ -41,6 +41,7 @@
|
||||
|
||||
#include "evoral/Parameter.hpp"
|
||||
#include "evoral/Control.hpp"
|
||||
#include "evoral/midi_util.h"
|
||||
|
||||
#include "automation_region_view.h"
|
||||
#include "automation_time_axis.h"
|
||||
@ -566,6 +567,10 @@ MidiRegionView::create_note_at(double x, double y, double length)
|
||||
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");
|
||||
@ -797,8 +802,6 @@ MidiRegionView::redisplay_model()
|
||||
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);
|
||||
@ -2394,12 +2397,10 @@ MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
|
||||
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
|
||||
|
@ -151,8 +151,9 @@ public:
|
||||
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
|
||||
|
@ -111,7 +111,7 @@ class MidiSource : virtual public Source
|
||||
|
||||
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;
|
||||
|
@ -676,6 +676,36 @@ MidiModel::DiffCommand::get_state ()
|
||||
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
|
||||
@ -684,7 +714,7 @@ MidiModel::DiffCommand::get_state ()
|
||||
* 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;
|
||||
|
@ -250,7 +250,11 @@ MidiSource::clone (Evoral::MusicalTime begin, Evoral::MusicalTime end)
|
||||
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>();
|
||||
@ -290,3 +294,9 @@ MidiSource::set_note_mode(NoteMode mode)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiSource::drop_model ()
|
||||
{
|
||||
cerr << name() << " drop model\n";
|
||||
_model.reset();
|
||||
}
|
||||
|
@ -202,7 +202,8 @@ public:
|
||||
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; }
|
||||
|
@ -730,12 +730,38 @@ Sequence<Time>::append_sysex_unlocked(const MIDIEvent<Time>& ev)
|
||||
}
|
||||
|
||||
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>
|
||||
|
Loading…
Reference in New Issue
Block a user