*/
#include <cmath>
-#include <cassert>
#include <algorithm>
#include <ostream>
#include "pbd/memento_command.h"
#include "pbd/stateful_diff_command.h"
-#include "ardour/midi_region.h"
-#include "ardour/midi_source.h"
#include "ardour/midi_model.h"
#include "ardour/midi_patch_manager.h"
+#include "ardour/midi_region.h"
+#include "ardour/midi_source.h"
+#include "ardour/midi_track.h"
#include "ardour/session.h"
#include "evoral/Parameter.hpp"
#include "canvas-hit.h"
#include "canvas-note.h"
#include "canvas_patch_change.h"
+#include "canvas-sysex.h"
#include "debug.h"
#include "editor.h"
#include "editor_drag.h"
#include "mouse_cursors.h"
#include "note_player.h"
#include "public_editor.h"
+#include "route_time_axis.h"
#include "rgb_macros.h"
#include "selection.h"
#include "simpleline.h"
MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
: RegionView (parent, tv, r, spu, basic_color)
- , _last_channel_selection(0xFFFF)
, _current_range_min(0)
, _current_range_max(0)
, _active_notes(0)
, _last_event_y (0)
, pre_enter_cursor (0)
, pre_press_cursor (0)
+ , _note_player (0)
{
_note_group->raise_to_top();
PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
- /* Look up MIDNAM details from our MidiTimeAxisView */
- MidiTimeAxisView& mtv = dynamic_cast<MidiTimeAxisView&> (tv);
- midi_patch_settings_changed (mtv.midi_patch_model (), mtv.midi_patch_custom_device_node ());
-
Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
connect_to_diskstream ();
boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
TimeAxisViewItem::Visibility visibility)
: RegionView (parent, tv, r, spu, basic_color, false, visibility)
- , _last_channel_selection(0xFFFF)
, _current_range_min(0)
, _current_range_max(0)
, _active_notes(0)
, _last_event_y (0)
, pre_enter_cursor (0)
, pre_press_cursor (0)
+ , _note_player (0)
{
_note_group->raise_to_top();
PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
- /* Look up MIDNAM details from our MidiTimeAxisView */
- MidiTimeAxisView& mtv = dynamic_cast<MidiTimeAxisView&> (tv);
- midi_patch_settings_changed (mtv.midi_patch_model (), mtv.midi_patch_custom_device_node ());
-
connect_to_diskstream ();
SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
MidiRegionView::MidiRegionView (const MidiRegionView& other)
: sigc::trackable(other)
, RegionView (other)
- , _last_channel_selection(0xFFFF)
, _current_range_min(0)
, _current_range_max(0)
, _active_notes(0)
, _last_event_y (0)
, pre_enter_cursor (0)
, pre_press_cursor (0)
+ , _note_player (0)
{
Gdk::Color c;
int r,g,b,a;
MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
: RegionView (other, boost::shared_ptr<Region> (region))
- , _last_channel_selection(0xFFFF)
, _current_range_min(0)
, _current_range_max(0)
, _active_notes(0)
, _last_event_y (0)
, pre_enter_cursor (0)
, pre_press_cursor (0)
+ , _note_player (0)
{
Gdk::Color c;
int r,g,b,a;
region_resized (ARDOUR::bounds_change);
region_locked ();
- reset_width_dependent_items (_pixel_width);
-
set_colors ();
_enable_display = true;
}
}
+ reset_width_dependent_items (_pixel_width);
+
group->raise_to_top();
- group->signal_event().connect(
- sigc::mem_fun(this, &MidiRegionView::canvas_event), false);
+ group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
- midi_view()->signal_channel_mode_changed().connect(
- sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
- midi_view()->signal_midi_patch_settings_changed().connect(
- sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
+ midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
+ boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
+ gui_context ());
+
+ instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
+ boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
boost::bind (&MidiRegionView::snap_changed, this),
SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
}
+InstrumentInfo&
+MidiRegionView::instrument_info () const
+{
+ RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
+ return route_ui->route()->instrument_info();
+}
+
const boost::shared_ptr<ARDOUR::MidiRegion>
MidiRegionView::midi_region() const
{
bool
MidiRegionView::canvas_event(GdkEvent* ev)
{
+ bool r;
+
switch (ev->type) {
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
}
if (ev->type == GDK_2BUTTON_PRESS) {
- return trackview.editor().toggle_internal_editing_from_double_click (ev);
+ // cannot use double-click to exit internal mode if single-click is being used
+ MouseMode m = trackview.editor().current_mouse_mode();
+
+ if ((m != MouseObject || !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier())) && (m != MouseDraw)) {
+ return trackview.editor().toggle_internal_editing_from_double_click (ev);
+ }
}
if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) ||
return button_press (&ev->button);
case GDK_BUTTON_RELEASE:
- return button_release (&ev->button);
+ r = button_release (&ev->button);
+ delete _note_player;
+ _note_player = 0;
+ return r;
case GDK_ENTER_NOTIFY:
return enter_notify (&ev->crossing);
change_velocities (true, fine, false, together);
} else if (ev->direction == GDK_SCROLL_DOWN) {
change_velocities (false, fine, false, together);
+ } else {
+ /* left, right: we don't use them */
+ return false;
}
+
return true;
}
return true;
- } else if (ev->keyval == GDK_Delete && unmodified) {
+ } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
+
+ if (_selection.empty()) {
+ return false;
+ }
delete_selection();
return true;
void
MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap_t)
{
+ if (length < 2 * DBL_EPSILON) {
+ return;
+ }
+
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
MidiStreamView* const view = mtv->midi_view();
- double note = view->y_to_note(y);
-
- assert(note >= 0.0);
- assert(note <= 127.0);
+ const double note = view->y_to_note(y);
// Start of note in frames relative to region start
if (snap_t) {
t = snap_frame_to_grid_underneath (t, grid_frames);
}
- assert (t >= 0);
- assert (length != 0);
-
- const boost::shared_ptr<NoteType> new_note (new NoteType (mtv->get_channel_for_add (),
- region_frames_to_region_beats(t + _region->start()),
- length,
- (uint8_t)note, 0x40));
+ const boost::shared_ptr<NoteType> new_note (
+ new NoteType (mtv->get_channel_for_add (),
+ region_frames_to_region_beats(t + _region->start()),
+ length,
+ (uint8_t)note, 0x40));
if (_model->contains (new_note)) {
return;
}
void
-MidiRegionView::clear_events()
+MidiRegionView::clear_events (bool with_selection_signal)
{
- clear_selection();
+ clear_selection (with_selection_signal);
MidiGhostRegion* gr;
for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
MidiRegionView::display_patch_changes ()
{
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
- uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
+ uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
for (uint8_t i = 0; i < 16; ++i) {
- if (chn_mask & (1<<i)) {
- display_patch_changes_on_channel (i);
- }
- /* TODO gray-out patch instad of not displaying it */
+ display_patch_changes_on_channel (i, chn_mask & (1 << i));
}
}
+/** @param active_channel true to display patch changes fully, false to display
+ * them `greyed-out' (as on an inactive channel)
+ */
void
-MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
+MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
{
for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
continue;
}
- MIDI::Name::PatchPrimaryKey patch_key ((*i)->bank_msb (), (*i)->bank_lsb (), (*i)->program ());
-
- boost::shared_ptr<MIDI::Name::Patch> patch =
- MIDI::Name::MidiPatchManager::instance().find_patch(
- _model_name, _custom_device_mode, channel, patch_key);
-
- if (patch != 0) {
- add_canvas_patch_change (*i, patch->name());
- } else {
- char buf[16];
- /* program and bank numbers are zero-based: convert to one-based: MIDI_BP_ZERO */
- snprintf (buf, 16, "%d %d", (*i)->program() + MIDI_BP_ZERO , (*i)->bank() + MIDI_BP_ZERO);
- add_canvas_patch_change (*i, buf);
- }
+ const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
+ add_canvas_patch_change (*i, patch_name, active_channel);
}
}
boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
Evoral::MusicalTime time = (*i)->time();
- assert (time >= 0);
if (mev) {
if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
}
string text = str.str();
- const double x = trackview.editor().frame_to_pixel(source_beats_to_absolute_frames(time));
+ const double x = trackview.editor().frame_to_pixel(source_beats_to_region_frames(time));
double height = midi_stream_view()->contents_height();
boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
- new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
+ new CanvasSysEx(*this, *_note_group, text, height, x, 1.0, (*i)));
// Show unless message is beyond the region bounds
if (time - _region->start() >= _region->length() || time < _region->start()) {
_selection_cleared_connection.disconnect ();
_selection.clear();
- clear_events();
+ clear_events (false);
delete _note_group;
delete _note_diff_command;
MidiRegionView::reset_width_dependent_items (double pixel_width)
{
RegionView::reset_width_dependent_items(pixel_width);
- assert(_pixel_width == pixel_width);
if (_enable_display) {
redisplay_model();
}
+ for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
+ if ((*x)->width() >= _pixel_width) {
+ (*x)->hide();
+ } else {
+ (*x)->show();
+ }
+ }
+
move_step_edit_cursor (_step_edit_cursor_position);
set_step_edit_cursor_width (_step_edit_cursor_width);
}
void
MidiRegionView::begin_write()
{
- assert(!_active_notes);
+ if (_active_notes) {
+ delete[] _active_notes;
+ }
_active_notes = new CanvasNote*[128];
- for (unsigned i=0; i < 128; ++i) {
+ for (unsigned i = 0; i < 128; ++i) {
_active_notes[i] = 0;
}
}
return;
}
- NotePlayer* np = new NotePlayer (route_ui->midi_track());
+ NotePlayer* np = new NotePlayer (route_ui->midi_track ());
np->add (note);
np->play ();
+
+ /* NotePlayer deletes itself */
+}
+
+void
+MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
+{
+ if (_no_sound_notes || !Config->get_sound_midi_notes()) {
+ return;
+ }
+
+ RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
+
+ if (!route_ui || !route_ui->midi_track()) {
+ return;
+ }
+
+ delete _note_player;
+ _note_player = new NotePlayer (route_ui->midi_track ());
+ _note_player->add (note);
+ _note_player->on ();
}
void
-MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
+MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
{
if (_no_sound_notes || !Config->get_sound_midi_notes()) {
return;
return;
}
- NotePlayer* np = new NotePlayer (route_ui->midi_track());
+ delete _note_player;
+ _note_player = new NotePlayer (route_ui->midi_track());
for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
- np->add (*n);
+ _note_player->add (*n);
}
- np->play ();
+ _note_player->on ();
}
ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
if (note->length() == 0) {
- if (_active_notes) {
- assert(note->note() < 128);
+ if (_active_notes && note->note() < 128) {
// If this note is already active there's a stuck note,
// finish the old note rectangle
if (_active_notes[note->note()]) {
/* outline all edges */
ev->property_outline_what() = (guint32) 0xF;
}
-
+
if (update_ghost_regions) {
for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
{
CanvasNoteEvent* event = 0;
- assert(note->time() >= 0);
- assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
-
//ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
if (midi_view()->note_mode() == Sustained) {
event->show_velocity();
}
- event->on_channel_selection_change(_last_channel_selection);
+ event->on_channel_selection_change (get_selected_channels());
_events.push_back(event);
if (visible) {
change_note_lengths (false, false, beats, false, true);
}
+/** Add a new patch change flag to the canvas.
+ * @param patch the patch change to add
+ * @param the text to display in the flag
+ * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
+ */
void
-MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext)
+MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool active_channel)
{
- assert (patch->time() >= 0);
-
framecnt_t region_frames = source_beats_to_region_frames (patch->time());
const double x = trackview.editor().frame_to_pixel (region_frames);
double const height = midi_stream_view()->contents_height();
boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
- new CanvasPatchChange(*this, *_note_group,
+ new CanvasPatchChange(*this, *group,
displaytext,
height,
x, 1.0,
- _model_name,
- _custom_device_mode,
- patch)
- );
-
- // Show unless patch change is beyond the region bounds
- if (region_frames < 0 || region_frames >= _region->length()) {
- patch_change->hide();
+ instrument_info(),
+ patch,
+ active_channel));
+
+ if (patch_change->width() < _pixel_width) {
+ // Show unless patch change is beyond the region bounds
+ if (region_frames < 0 || region_frames >= _region->length()) {
+ patch_change->hide();
+ } else {
+ patch_change->show();
+ }
} else {
- patch_change->show();
+ patch_change->hide ();
}
_patch_changes.push_back (patch_change);
}
-void
-MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
+MIDI::Name::PatchPrimaryKey
+MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
{
+ return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
+}
+
+/// Return true iff @p pc applies to the given time on the given channel.
+static bool
+patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, double time, uint8_t channel)
+{
+ return pc->time() <= time && pc->channel() == channel;
+}
+
+void
+MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
+{
+ // The earliest event not before time
MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
- while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
- ++i;
+
+ // Go backwards until we find the latest PC for this channel, or the start
+ while (i != _model->patch_changes().begin() &&
+ (i == _model->patch_changes().end() ||
+ !patch_applies(*i, time, channel))) {
+ --i;
}
- if (i != _model->patch_changes().end()) {
- key.msb = (*i)->bank_msb ();
- key.lsb = (*i)->bank_lsb ();
+ if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
+ key.bank_number = (*i)->bank();
key.program_number = (*i)->program ();
} else {
- key.msb = key.lsb = key.program_number = 0;
+ key.bank_number = key.program_number = 0;
}
- assert (key.is_sane());
+ if (!key.is_sane()) {
+ error << string_compose(_("insane MIDI patch key %1:%2"),
+ key.bank_number, key.program_number) << endmsg;
+ }
}
-
void
MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
{
c->change_program (pc.patch (), new_patch.program_number);
}
- int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
+ int const new_bank = new_patch.bank_number;
if (pc.patch()->bank() != new_bank) {
c->change_bank (pc.patch (), new_bank);
}
MidiRegionView::previous_patch (CanvasPatchChange& patch)
{
if (patch.patch()->program() < 127) {
- MIDI::Name::PatchPrimaryKey key;
- get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
+ MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
key.program_number++;
change_patch_change (patch, key);
}
MidiRegionView::next_patch (CanvasPatchChange& patch)
{
if (patch.patch()->program() > 0) {
- MIDI::Name::PatchPrimaryKey key;
- get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
+ MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
key.program_number--;
change_patch_change (patch, key);
}
}
void
-MidiRegionView::previous_bank (CanvasPatchChange& patch)
+MidiRegionView::next_bank (CanvasPatchChange& patch)
{
if (patch.patch()->program() < 127) {
- MIDI::Name::PatchPrimaryKey key;
- get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
- if (key.lsb > 0) {
- key.lsb--;
+ MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
+ if (key.bank_number > 0) {
+ key.bank_number--;
change_patch_change (patch, key);
- } else {
- if (key.msb > 0) {
- key.lsb = 127;
- key.msb--;
- change_patch_change (patch, key);
- }
}
}
}
void
-MidiRegionView::next_bank (CanvasPatchChange& patch)
+MidiRegionView::previous_bank (CanvasPatchChange& patch)
{
if (patch.patch()->program() > 0) {
- MIDI::Name::PatchPrimaryKey key;
- get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
- if (key.lsb < 127) {
- key.lsb++;
+ MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
+ if (key.bank_number < 127) {
+ key.bank_number++;
change_patch_change (patch, key);
- } else {
- if (key.msb < 127) {
- key.lsb = 0;
- key.msb++;
- change_patch_change (patch, key);
- }
}
}
}
void
MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend)
{
- if (x1 > x2) {
- swap (x1, x2);
- }
-
- if (y1 > y2) {
- swap (y1, y2);
- }
-
// TODO: Make this faster by storing the last updated selection rect, and only
// adjusting things that are in the area that appears/disappeared.
// We probably need a tree to be able to find events in O(log(n)) time.
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-
- /* check if any corner of the note is inside the rect
-
- Notes:
- 1) this is computing "touched by", not "contained by" the rect.
- 2) this does not require that events be sorted in time.
- */
-
- const double ix1 = (*i)->x1();
- const double ix2 = (*i)->x2();
- const double iy1 = (*i)->y1();
- const double iy2 = (*i)->y2();
-
- if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
- (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
- (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
- (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
-
- // Inside rectangle
+ if ((*i)->x1() < x2 && (*i)->x2() > x1 && (*i)->y1() < y2 && (*i)->y2() > y1) {
+ // Rectangles intersect
if (!(*i)->selected()) {
add_to_selection (*i);
}
} else if ((*i)->selected() && !extend) {
- // Not inside rectangle
+ // Rectangles do not intersect
remove_from_selection (*i);
}
}
// We probably need a tree to be able to find events in O(log(n)) time.
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-
- /* check if any corner of the note is inside the rect
-
- Notes:
- 1) this is computing "touched by", not "contained by" the rect.
- 2) this does not require that events be sorted in time.
- */
-
if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
// within y- (note-) range
if (!(*i)->selected()) {
add_to_selection (*i);
}
} else if ((*i)->selected() && !extend) {
- // Not inside rectangle
remove_from_selection (*i);
}
}
if (_selection.insert (ev).second) {
ev->set_selected (true);
- play_midi_note ((ev)->note());
+ start_playing_midi_note ((ev)->note());
}
if (add_mrv_selection) {
shifted.push_back (moved_note);
}
- play_midi_chord (shifted);
+ start_playing_midi_chord (shifted);
} else if (!to_play.empty()) {
boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
moved_note->set_note (moved_note->note() + cumulative_dy);
- play_midi_note (moved_note);
+ start_playing_midi_note (moved_note);
}
}
}
void
MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
{
- assert (!_selection.empty());
-
uint8_t lowest_note_in_selection = 127;
uint8_t highest_note_in_selection = 0;
- uint8_t highest_note_difference = 0;
+ uint8_t highest_note_difference = 0;
// find highest and lowest notes first
MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
{
int8_t delta;
- int8_t value;
+ int8_t value = 0;
if (_selection.empty()) {
return;
if (!allow_smush) {
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
- if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
- return;
+ if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
+ goto cursor_label;
}
}
}
apply_diff();
+ cursor_label:
if (!_selection.empty()) {
char buf[24];
snprintf (buf, sizeof (buf), "Vel %d",
}
void
-MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
+MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* p)
{
ostringstream s;
/* XXX should get patch name if we can */
- s << _("Bank:") << (ev->patch()->bank() + MIDI_BP_ZERO) << '\n' << _("Program:") << ((int) ev->patch()->program()) + MIDI_BP_ZERO << '\n' << _("Channel:") << ((int) ev->patch()->channel() + 1);
+ s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
+ << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
+ << _("Channel ") << ((int) p->patch()->channel() + 1);
show_verbose_cursor (s.str(), 10, 20);
+ p->grab_focus();
}
void
MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
{
trackview.editor().verbose_cursor()->hide ();
+ /* focus will transfer back via the enter-notify event sent to this
+ * midi region view.
+ */
+}
+
+void
+MidiRegionView::sysex_entered (ArdourCanvas::CanvasSysEx* p)
+{
+ ostringstream s;
+ s << p->text();
+ show_verbose_cursor (s.str(), 10, 20);
+ p->grab_focus();
+}
+
+void
+MidiRegionView::sysex_left (ArdourCanvas::CanvasSysEx *)
+{
+ trackview.editor().verbose_cursor()->hide ();
+ /* focus will transfer back via the enter-notify event sent to this
+ * midi region view.
+ */
}
void
}
void
-MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
+MidiRegionView::midi_channel_mode_changed ()
{
+ MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
+ uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
+ ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
+
if (mode == ForceChannel) {
mask = 0xFFFF; // Show all notes as active (below)
}
// Update notes for selection
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- (*i)->on_channel_selection_change(mask);
+ (*i)->on_channel_selection_change (mask);
}
- _last_channel_selection = mask;
-
_patch_changes.clear ();
display_patch_changes ();
}
void
-MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
+MidiRegionView::instrument_settings_changed ()
{
- _model_name = model;
- _custom_device_mode = custom_device_mode;
redisplay_model();
}
time_sort_events ();
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
- uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
+ uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
if ((*i)->selected()) {
time_sort_events ();
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
- uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
+ uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
if ((*i)->selected()) {
Events e;
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
- uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
+ uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
- assert (ev.buffer ());
+
+ if (ev.is_channel_event()) {
+ if (get_channel_mode() == FilterChannels) {
+ if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
+ continue;
+ }
+ }
+ }
/* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
frames from the start of the source, and so time_beats is in terms of the
Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
if (ev.type() == MIDI_CMD_NOTE_ON) {
-
boost::shared_ptr<NoteType> note (
- new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
- );
+ new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity()));
add_note (note, true);
void
MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
{
- PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), _model_name, _custom_device_mode, Gtk::Stock::APPLY);
- if (d.run () != Gtk::RESPONSE_ACCEPT) {
+ PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
+
+ int response = d.run();
+
+ switch (response) {
+ case Gtk::RESPONSE_ACCEPT:
+ break;
+ case Gtk::RESPONSE_REJECT:
+ delete_patch_change (pc);
+ return;
+ default:
return;
}
change_patch_change (pc->patch(), d.patch ());
}
+void
+MidiRegionView::delete_sysex (CanvasSysEx* sysex)
+{
+ MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
+ c->remove (sysex->sysex());
+ _model->apply_command (*trackview.session(), c);
+
+ _sys_exes.clear ();
+ display_sysexes();
+}
void
MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
{
- char buf[24];
- snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
- Evoral::midi_note_name (n->note()).c_str(),
+ using namespace MIDI::Name;
+
+ std::string name;
+
+ MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
+ if (mtv) {
+ boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
+ if (device_names) {
+ MIDI::Name::PatchPrimaryKey patch_key;
+ get_patch_key_at(n->time(), n->channel(), patch_key);
+ name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
+ n->channel(),
+ patch_key.bank_number,
+ patch_key.program_number,
+ n->note());
+ }
+ }
+
+ char buf[128];
+ snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
(int) n->note (),
+ name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
(int) n->channel() + 1,
(int) n->velocity());
- show_verbose_cursor (buf, 10, 20);
+ show_verbose_cursor(buf, 10, 20);
}
void
/* Clear our selection in sympathy; but don't signal the fact */
clear_selection (false);
}
+
+void
+MidiRegionView::note_button_release ()
+{
+ delete _note_player;
+ _note_player = 0;
+}
+
+ChannelMode
+MidiRegionView::get_channel_mode () const
+{
+ RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
+ return rtav->midi_track()->get_playback_channel_mode();
+}
+
+uint16_t
+MidiRegionView::get_selected_channels () const
+{
+ RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
+ return rtav->midi_track()->get_playback_channel_mask();
+}
+