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 {
|
} else {
|
||||||
/* new selection */
|
/* 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->set (_editor->clicked_axisview);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3361,7 +3361,7 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
|
|||||||
_editor->selection->clear_time();
|
_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);
|
_editor->selection->set (_editor->clicked_axisview);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3747,9 +3747,10 @@ NoteDrag::motion (GdkEvent*, bool)
|
|||||||
region->move_selection (dx, dy);
|
region->move_selection (dx, dy);
|
||||||
|
|
||||||
CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
|
CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
|
||||||
char buf[4];
|
|
||||||
snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
|
char buf[12];
|
||||||
//editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
|
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);
|
_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) {
|
switch (item_type) {
|
||||||
case NoteItem:
|
case NoteItem:
|
||||||
if (internal_editing()) {
|
if (internal_editing()) {
|
||||||
/* Note: we don't get here if not in internal_editing() mode */
|
|
||||||
_drags->set (new NoteDrag (this, item), event);
|
_drags->set (new NoteDrag (this, item), event);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
|
|
||||||
#include "evoral/Parameter.hpp"
|
#include "evoral/Parameter.hpp"
|
||||||
#include "evoral/Control.hpp"
|
#include "evoral/Control.hpp"
|
||||||
|
#include "evoral/midi_util.h"
|
||||||
|
|
||||||
#include "automation_region_view.h"
|
#include "automation_region_view.h"
|
||||||
#include "automation_time_axis.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,
|
frames_to_beats(start_frames + _region->start()), length,
|
||||||
(uint8_t)note, 0x40));
|
(uint8_t)note, 0x40));
|
||||||
|
|
||||||
|
if (_model->contains (new_note)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
view->update_note_range(new_note->note());
|
view->update_note_range(new_note->note());
|
||||||
|
|
||||||
MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
|
MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
|
||||||
@ -797,8 +802,6 @@ MidiRegionView::redisplay_model()
|
|||||||
MidiModel::Notes& notes (_model->notes());
|
MidiModel::Notes& notes (_model->notes());
|
||||||
_optimization_iterator = _events.begin();
|
_optimization_iterator = _events.begin();
|
||||||
|
|
||||||
cerr << "++++++++++ MIDI REdisplay\n";
|
|
||||||
|
|
||||||
for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
|
for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
|
||||||
|
|
||||||
boost::shared_ptr<NoteType> note (*n);
|
boost::shared_ptr<NoteType> note (*n);
|
||||||
@ -2394,12 +2397,10 @@ MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
|
|||||||
note_selected(ev, true);
|
note_selected(ev, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
char buf[4];
|
char buf[12];
|
||||||
snprintf (buf, sizeof (buf), "%d", (int) ev->note()->note());
|
snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (ev->note()->note()).c_str(), (int) ev->note()->note());
|
||||||
// This causes an infinite loop on note add sometimes
|
PublicEditor& editor (trackview.editor());
|
||||||
//PublicEditor& editor (trackview.editor());
|
editor.show_verbose_canvas_cursor_with (buf);
|
||||||
//editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
|
|
||||||
//editor.show_verbose_canvas_cursor_with (buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -151,8 +151,9 @@ public:
|
|||||||
void apply_command(Session& session, Command* cmd);
|
void apply_command(Session& session, Command* cmd);
|
||||||
void apply_command_as_subcommand(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,
|
bool write_to(boost::shared_ptr<MidiSource> source);
|
||||||
Evoral::MusicalTime end = Evoral::MaxMusicalTime);
|
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
|
// MidiModel doesn't use the normal AutomationList serialisation code
|
||||||
// since controller data is stored in the .mid
|
// since controller data is stored in the .mid
|
||||||
|
@ -111,7 +111,7 @@ class MidiSource : virtual public Source
|
|||||||
|
|
||||||
boost::shared_ptr<MidiModel> model() { return _model; }
|
boost::shared_ptr<MidiModel> model() { return _model; }
|
||||||
void set_model(boost::shared_ptr<MidiModel> m) { _model = m; }
|
void set_model(boost::shared_ptr<MidiModel> m) { _model = m; }
|
||||||
void drop_model() { _model.reset(); }
|
void drop_model();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void flush_midi() = 0;
|
virtual void flush_midi() = 0;
|
||||||
|
@ -676,6 +676,36 @@ MidiModel::DiffCommand::get_state ()
|
|||||||
return *diff_command;
|
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).
|
/** 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
|
* 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
|
* 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.
|
* destroying the original note durations.
|
||||||
*/
|
*/
|
||||||
bool
|
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());
|
ReadLock lock(read_lock());
|
||||||
MidiStateTracker mst;
|
MidiStateTracker mst;
|
||||||
|
@ -250,7 +250,11 @@ MidiSource::clone (Evoral::MusicalTime begin, Evoral::MusicalTime end)
|
|||||||
newsrc->set_timeline_position(_timeline_position);
|
newsrc->set_timeline_position(_timeline_position);
|
||||||
|
|
||||||
if (_model) {
|
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 {
|
} else {
|
||||||
error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()"));
|
error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()"));
|
||||||
return boost::shared_ptr<MidiSource>();
|
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; }
|
bool edited() const { return _edited; }
|
||||||
void set_edited(bool yn) { _edited = yn; }
|
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);
|
void remove_note_unlocked(const boost::shared_ptr< const Note<Time> > note);
|
||||||
|
|
||||||
uint8_t lowest_note() const { return _lowest_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>
|
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)
|
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());
|
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;
|
_edited = true;
|
||||||
_notes.insert(note);
|
_notes.insert(note);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Time>
|
template<typename Time>
|
||||||
|
Loading…
Reference in New Issue
Block a user