From 084dda86a7c4391aba4e9f37f752af4846ded15d Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 15 Sep 2010 18:54:04 +0000 Subject: [PATCH] when there is a chord at the beginning of a note selection, play the whole chord during drags, not just the first note. seems to be able to send a bank swap message - probably an existingbug git-svn-id: svn://localhost/ardour2/branches/3.0@7784 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/midi_region_view.cc | 102 +++++++++++++++++++++++--------- gtk2_ardour/midi_region_view.h | 4 +- gtk2_ardour/note_player.cc | 60 +++++++++++++++++++ gtk2_ardour/note_player.h | 34 +++++++++++ gtk2_ardour/wscript | 1 + 5 files changed, 172 insertions(+), 29 deletions(-) create mode 100644 gtk2_ardour/note_player.cc create mode 100644 gtk2_ardour/note_player.h diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index cd26a0c81f..f3e1b2cd98 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -59,6 +59,7 @@ #include "midi_time_axis.h" #include "midi_time_axis.h" #include "midi_util.h" +#include "note_player.h" #include "public_editor.h" #include "rgb_macros.h" #include "selection.h" @@ -91,6 +92,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & , _step_edit_cursor (0) , _step_edit_cursor_width (1.0) , _step_edit_cursor_position (0.0) + , _earliest_selected_time (Evoral::MaxMusicalTime) , _mouse_state(None) , _pressed_button(0) , _sort_needed (true) @@ -115,6 +117,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & , _diff_command(0) , _ghost_note(0) , _drag_rect (0) + , _earliest_selected_time (Evoral::MaxMusicalTime) , _mouse_state(None) , _pressed_button(0) , _sort_needed (true) @@ -138,6 +141,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other) , _diff_command(0) , _ghost_note(0) , _drag_rect (0) + , _earliest_selected_time (Evoral::MaxMusicalTime) , _mouse_state(None) , _pressed_button(0) , _sort_needed (true) @@ -165,6 +169,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr note) { @@ -1321,36 +1327,34 @@ MidiRegionView::play_midi_note(boost::shared_ptr note) return; } - route_ui->midi_track()->write_immediate_event( - note->on_event().size(), note->on_event().buffer()); - - const double note_length_beats = (note->off_event().time() - note->on_event().time()); - nframes_t note_length_ms = beats_to_frames(note_length_beats) - * (1000 / (double)route_ui->session()->nominal_frame_rate()); - - /* note: we probably should not be binding a shared_ptr - here. Since its a one-shot timeout, its sort of OK, but ... - */ - - Glib::signal_timeout().connect(sigc::bind(sigc::mem_fun(this, &MidiRegionView::play_midi_note_off), note), - note_length_ms, G_PRIORITY_DEFAULT); + NotePlayer* np = new NotePlayer (route_ui->midi_track()); + np->add (note); + np->play (); } -bool -MidiRegionView::play_midi_note_off(boost::shared_ptr note) +void +MidiRegionView::play_midi_chord (vector > notes) { - RouteUI* route_ui = dynamic_cast (&trackview); + if (no_sound_notes || !trackview.editor().sound_notes()) { + return; + } + RouteUI* route_ui = dynamic_cast (&trackview); + if (!route_ui || !route_ui->midi_track()) { - return false; + return; } - route_ui->midi_track()->write_immediate_event( - note->off_event().size(), note->off_event().buffer()); + NotePlayer* np = new NotePlayer (route_ui->midi_track()); - return false; + for (vector >::iterator n = notes.begin(); n != notes.end(); ++n) { + np->add (*n); + } + + np->play (); } + bool MidiRegionView::note_in_region_range(const boost::shared_ptr note, bool& visible) const { @@ -1691,6 +1695,7 @@ MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev) } _selection.clear(); + _earliest_selected_time = Evoral::MaxMusicalTime; } void @@ -1932,9 +1937,25 @@ MidiRegionView::remove_from_selection (CanvasNoteEvent* ev) ev->set_selected (false); ev->hide_velocity (); + + if (Evoral::musical_time_equal (ev->note()->time(), _earliest_selected_time)) { + + _earliest_selected_time = Evoral::MaxMusicalTime; + + /* compute new earliest time */ + + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { + if (!Evoral::musical_time_equal ((*i)->note()->time(), _earliest_selected_time) && + (*i)->note()->time() < _earliest_selected_time) { + _earliest_selected_time = (*i)->note()->time(); + } + } + } + if (_selection.empty()) { PublicEditor& editor (trackview.editor()); editor.get_selection().remove (this); + _earliest_selected_time = Evoral::MaxMusicalTime; } } @@ -1950,7 +1971,11 @@ MidiRegionView::add_to_selection (CanvasNoteEvent* ev) if (_selection.insert (ev).second) { ev->set_selected (true); play_midi_note ((ev)->note()); - } + + if (ev->note()->time() < _earliest_selected_time) { + _earliest_selected_time = ev->note()->time(); + } + } if (add_mrv_selection) { PublicEditor& editor (trackview.editor()); @@ -1961,16 +1986,37 @@ MidiRegionView::add_to_selection (CanvasNoteEvent* ev) void MidiRegionView::move_selection(double dx, double dy, double cumulative_dy) { - for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { - (*i)->move_event(dx, dy); + typedef vector > PossibleChord; + PossibleChord to_play; - if (dy) { - boost::shared_ptr - moved_note (new NoteType (*((*i)->note()))); + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { + if (Evoral::musical_time_equal ((*i)->note()->time(), _earliest_selected_time)) { + to_play.push_back ((*i)->note()); + } + (*i)->move_event(dx, dy); + } + + if (dy && !_selection.empty() && !no_sound_notes && trackview.editor().sound_notes()) { + + if (to_play.size() > 1) { + + PossibleChord shifted; + + for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) { + boost::shared_ptr moved_note (new NoteType (**n)); + moved_note->set_note (moved_note->note() + cumulative_dy); + shifted.push_back (moved_note); + } + + play_midi_chord (shifted); + + } else if (!to_play.empty()) { + + boost::shared_ptr moved_note (new NoteType (*to_play.front())); moved_note->set_note (moved_note->note() + cumulative_dy); play_midi_note (moved_note); } - } + } } void @@ -2755,7 +2801,7 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb) beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats; paste_pos_beats = 0; - _selection.clear (); + clear_selection (); for (int n = 0; n < (int) times; ++n) { diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index b0cc505aee..baf67928c8 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -308,6 +308,7 @@ class MidiRegionView : public RegionView * and schedule the playback of the corresponding NoteOff event. */ void play_midi_note(boost::shared_ptr note); + void play_midi_chord (std::vector > notes); /** Play the NoteOff-Event of the given note immediately * (scheduled by @ref play_midi_note()). @@ -371,7 +372,8 @@ class MidiRegionView : public RegionView ArdourCanvas::SimpleRect* _step_edit_cursor; Evoral::MusicalTime _step_edit_cursor_width; Evoral::MusicalTime _step_edit_cursor_position; - + Evoral::MusicalTime _earliest_selected_time; + MouseState _mouse_state; int _pressed_button; diff --git a/gtk2_ardour/note_player.cc b/gtk2_ardour/note_player.cc new file mode 100644 index 0000000000..3c577633b1 --- /dev/null +++ b/gtk2_ardour/note_player.cc @@ -0,0 +1,60 @@ +#include +#include + +#include "ardour/midi_track.h" +#include "ardour/session.h" + +#include "note_player.h" + +using namespace ARDOUR; +using namespace std; + +NotePlayer::NotePlayer (boost::shared_ptr mt) + : track (mt) +{ +} + +void +NotePlayer::add (boost::shared_ptr note) +{ + notes.push_back (note); +} + +void +NotePlayer::play () +{ + Evoral::MusicalTime longest_duration_beats = 0; + + /* note: if there is more than 1 note, we will silence them all at the same time + */ + + for (NoteList::iterator n = notes.begin(); n != notes.end(); ++n) { + track->write_immediate_event ((*n)->on_event().size(), (*n)->on_event().buffer()); + if ((*n)->length() > longest_duration_beats) { + longest_duration_beats = (*n)->length(); + } + } + + uint32_t note_length_ms = 350; + /* beats_to_frames (longest_duration_beats) + * (1000 / (double)track->session().nominal_frame_rate()); */ + + Glib::signal_timeout().connect(sigc::bind (sigc::ptr_fun (&NotePlayer::_off), this), + note_length_ms, G_PRIORITY_DEFAULT); +} + +bool +NotePlayer::_off (NotePlayer* np) +{ + np->off (); + delete np; + return false; +} + +void +NotePlayer::off () +{ + for (NoteList::iterator n = notes.begin(); n != notes.end(); ++n) { + track->write_immediate_event((*n)->off_event().size(), (*n)->off_event().buffer()); + } +} diff --git a/gtk2_ardour/note_player.h b/gtk2_ardour/note_player.h new file mode 100644 index 0000000000..7df4af4445 --- /dev/null +++ b/gtk2_ardour/note_player.h @@ -0,0 +1,34 @@ +#ifndef __gtk2_ardour_note_player_h__ +#define __gtk2_ardour_note_player_h__ + +#include +#include +#include + +#include "evoral/Note.hpp" + +namespace ARDOUR { + class MidiTrack; +} + +class NotePlayer : public sigc::trackable { + public: + typedef Evoral::Note NoteType; + + NotePlayer (boost::shared_ptr); + ~NotePlayer () {} + + void add (boost::shared_ptr); + void play (); + void off (); + + static bool _off (NotePlayer*); + + private: + typedef std::vector > NoteList; + + boost::shared_ptr track; + NoteList notes; +}; + +#endif /* __gtk2_ardour_note_player_h__ */ diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index f4cf401d5a..bd5ef56690 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -146,6 +146,7 @@ gtk2_ardour_sources = [ 'mixer_ui.cc', 'monitor_section.cc', 'nag.cc', + 'note_player.cc', 'option_editor.cc', 'opts.cc', 'panner.cc',