#include "ardour/session.h"
#include "evoral/Parameter.hpp"
+#include "evoral/MIDIParameters.hpp"
#include "evoral/Control.hpp"
#include "evoral/midi_util.h"
#include "midi_time_axis.h"
#include "midi_util.h"
#include "public_editor.h"
+#include "rgb_macros.h"
#include "selection.h"
#include "simpleline.h"
#include "streamview.h"
, _diff_command(0)
, _ghost_note(0)
, _drag_rect (0)
+ , _step_edit_cursor (0)
+ , _step_edit_cursor_width (1.0)
+ , _step_edit_cursor_position (0.0)
, _mouse_state(None)
, _pressed_button(0)
, _sort_needed (true)
bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
- change_note_lengths (fine, shorter, start, end);
+ change_note_lengths (fine, shorter, 0.0, start, end);
return true;
} else if (ev->keyval == GDK_Control_L) {
return true;
-
- } else if (ev->keyval == GDK_r) {
- /* yes, this steals r */
- if (midi_view()->midi_track()->step_editing()) {
- midi_view()->step_edit_rest ();
- return true;
- }
+
}
return false;
_model->apply_command(*trackview.session(), _diff_command);
_diff_command = 0;
midi_view()->midi_track()->playlist_modified();
-
if (add_or_remove) {
_marked_for_selection.clear();
}
}
+ _pgm_changes.clear();
+ _sys_exes.clear();
+
display_sysexes();
display_program_changes();
void
MidiRegionView::display_program_changes()
{
- boost::shared_ptr<Evoral::Control> control = _model->control(MidiPgmChangeAutomation);
+ MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
+ uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
+
+ for (uint8_t i = 0; i < 16; ++i) {
+ if (chn_mask & (1<<i)) {
+ display_program_changes_on_channel (i);
+ }
+ }
+}
+
+void
+MidiRegionView::display_program_changes_on_channel(uint8_t channel)
+{
+ boost::shared_ptr<Evoral::Control> control =
+ _model->control(Evoral::MIDI::ProgramChange (MidiPgmChangeAutomation, channel));
+
if (!control) {
return;
}
Glib::Mutex::Lock lock (control->list()->lock());
- uint8_t channel = control->parameter().channel();
-
for (AutomationList::const_iterator event = control->list()->begin();
event != control->list()->end(); ++event) {
double event_time = (*event)->when;
boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
uint8_t msb = 0;
if (msb_control != 0) {
- msb = uint8_t(floor(msb_control->get_float(true, event_time) + 0.5));
+ msb = uint8_t(floor(msb_control->get_double(true, event_time) + 0.5));
}
// Get current value of bank select LSB at time of the program change
boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
uint8_t lsb = 0;
if (lsb_control != 0) {
- lsb = uint8_t(floor(lsb_control->get_float(true, event_time) + 0.5));
+ lsb = uint8_t(floor(lsb_control->get_double(true, event_time) + 0.5));
}
MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
add_pgm_change(program_change, patch->name());
} else {
char buf[4];
- snprintf(buf, 4, "%d", int(program_number));
+ // program_number is zero-based: convert to one-based
+ snprintf(buf, 4, "%d", int(program_number+1));
add_pgm_change(program_change, buf);
}
}
_selection.clear();
clear_events();
+
delete _note_group;
delete _diff_command;
+ delete _step_edit_cursor;
}
void
if (_enable_display) {
redisplay_model();
}
+
+ move_step_edit_cursor (_step_edit_cursor_position);
+ set_step_edit_cursor_width (_step_edit_cursor_width);
}
void
if (name_pixbuf) {
name_pixbuf->raise_to_top();
}
+
+ for (PgmChanges::iterator x = _pgm_changes.begin(); x != _pgm_changes.end(); ++x) {
+ (*x)->set_height (midi_stream_view()->contents_height());
+ }
+
+ if (_step_edit_cursor) {
+ _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
+ }
}
boost::shared_ptr<NoteType> note = ev->note();
const nframes64_t note_start_frames = beats_to_frames(note->time());
- const nframes64_t note_end_frames = beats_to_frames(note->end_time());
+
+ /* trim note display to not overlap the end of its region */
+ const nframes64_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
const double y1 = midi_stream_view()->note_to_y(note->note());
if (event) {
if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
note_selected(event, true);
- }
+ }
if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
event->show_velocity();
}
void
-MidiRegionView::add_note (uint8_t channel, uint8_t number, uint8_t velocity,
- Evoral::MusicalTime pos, Evoral::MusicalTime len)
+MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
+ Evoral::MusicalTime pos, Evoral::MusicalTime len)
{
boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
- start_diff_command (_("step add"));
- diff_add_note (new_note, true, false);
- apply_diff();
-
/* potentially extend region to hold new note */
nframes64_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
nframes64_t region_end = _region->position() + _region->length() - 1;
if (end_frame > region_end) {
- _region->set_length (end_frame, this);
- } else {
- redisplay_model ();
+ _region->set_length (end_frame - _region->position(), this);
}
+
+ _marked_for_selection.clear ();
+ clear_selection ();
+
+ start_diff_command (_("step add"));
+ diff_add_note (new_note, true, false);
+ apply_diff();
+
+ // last_step_edit_note = new_note;
+}
+
+void
+MidiRegionView::step_sustain (Evoral::MusicalTime beats)
+{
+ change_note_lengths (false, false, beats, false, true);
}
void
void
MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
{
- cerr << "getting patch key at " << time << " for channel " << channel << endl;
Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
- float msb = -1.0;
+ double msb = 0.0;
if (msb_control != 0) {
- msb = int(msb_control->get_float(true, time));
- cerr << "got msb " << msb;
+ msb = int(msb_control->get_double(true, time));
}
Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
- float lsb = -1.0;
+ double lsb = 0.0;
if (lsb_control != 0) {
- lsb = lsb_control->get_float(true, time);
- cerr << " got lsb " << lsb;
+ lsb = lsb_control->get_double(true, time);
}
Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
- float program_number = -1.0;
+ double program_number = -1.0;
if (program_control != 0) {
- program_number = program_control->get_float(true, time);
- cerr << " got program " << program_number << endl;
+ program_number = program_control->get_double(true, time);
}
key.msb = (int) floor(msb + 0.5);
Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
if (msb_control != 0) {
- msb_control->set_float(float(new_patch.msb), true, old_program.time);
+ msb_control->set_double(double(new_patch.msb), true, old_program.time);
}
// TODO: Get the real event here and alter them at the original times
Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
if (lsb_control != 0) {
- lsb_control->set_float(float(new_patch.lsb), true, old_program.time);
+ lsb_control->set_double(double(new_patch.lsb), true, old_program.time);
}
Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
assert(program_control != 0);
- program_control->set_float(float(new_patch.program_number), true, old_program.time);
+ program_control->set_double(float(new_patch.program_number), true, old_program.time);
- redisplay_model();
+ _pgm_changes.clear ();
+ display_program_changes (); // XXX would be nice to limit to just old_program.channel
}
void
void
MidiRegionView::previous_program(CanvasProgramChange& program)
{
- MIDI::Name::PatchPrimaryKey key;
- get_patch_key_at(program.event_time(), program.channel(), key);
-
- boost::shared_ptr<MIDI::Name::Patch> patch =
- MIDI::Name::MidiPatchManager::instance().previous_patch(
- _model_name,
- _custom_device_mode,
- program.channel(),
- key);
+ if (program.program() < 127) {
+ MIDI::Name::PatchPrimaryKey key;
+ get_patch_key_at(program.event_time(), program.channel(), key);
+ PCEvent program_change_event(program.event_time(), program.program(), program.channel());
- PCEvent program_change_event(program.event_time(), program.program(), program.channel());
- if (patch) {
- alter_program_change(program_change_event, patch->patch_primary_key());
- }
+ key.program_number++;
+ alter_program_change(program_change_event, key);
+ }
}
void
MidiRegionView::next_program(CanvasProgramChange& program)
{
- MIDI::Name::PatchPrimaryKey key;
- get_patch_key_at(program.event_time(), program.channel(), key);
+ if (program.program() > 0) {
+ MIDI::Name::PatchPrimaryKey key;
+ get_patch_key_at(program.event_time(), program.channel(), key);
+ PCEvent program_change_event(program.event_time(), program.program(), program.channel());
- boost::shared_ptr<MIDI::Name::Patch> patch =
- MIDI::Name::MidiPatchManager::instance().next_patch(
- _model_name,
- _custom_device_mode,
- program.channel(),
- key);
-
- PCEvent program_change_event(program.event_time(), program.program(), program.channel());
- if (patch) {
- alter_program_change(program_change_event, patch->patch_primary_key());
- }
+ key.program_number--;
+ alter_program_change(program_change_event, key);
+ }
}
void
}
void
-MidiRegionView::note_dropped(CanvasNoteEvent *, double dt, int8_t dnote)
+MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
{
assert (!_selection.empty());
for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
- nframes64_t start_frames = beats_to_frames((*i)->note()->time());
-
- if (dt >= 0) {
- start_frames += snap_frame_to_frame(trackview.editor().pixel_to_frame(dt));
- } else {
- start_frames -= snap_frame_to_frame(trackview.editor().pixel_to_frame(-dt));
- }
-
- Evoral::MusicalTime new_time = frames_to_beats(start_frames);
+ Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
if (new_time < 0) {
continue;
apply_diff();
}
+void
+MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
+{
+ diff_add_change (event, MidiModel::DiffCommand::Channel, (uint8_t) channel);
+}
+
void
MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
{
diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
}
+void
+MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
+{
+ diff_add_change (event, MidiModel::DiffCommand::Length, t);
+}
+
void
MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
{
i = next;
}
+ apply_diff();
+
if (!_selection.empty()) {
char buf[24];
snprintf (buf, sizeof (buf), "Vel %d",
(int) (*_selection.begin())->note()->velocity());
trackview.editor().show_verbose_canvas_cursor_with (buf);
}
-
- apply_diff();
}
}
void
-MidiRegionView::change_note_lengths (bool fine, bool shorter, bool start, bool end)
+MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
{
- Evoral::MusicalTime delta;
-
- if (fine) {
- delta = 1.0/128.0;
- } else {
- /* grab the current grid distance */
- bool success;
- delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
- if (!success) {
- /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
- cerr << "Grid type not available as beats - TO BE FIXED\n";
- return;
- }
- }
+ if (delta == 0.0) {
+ if (fine) {
+ delta = 1.0/128.0;
+ } else {
+ /* grab the current grid distance */
+ bool success;
+ delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
+ if (!success) {
+ /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
+ cerr << "Grid type not available as beats - TO BE FIXED\n";
+ return;
+ }
+ }
+ }
if (shorter) {
delta = -delta;
redisplay_model ();
}
}
+
+void
+MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
+{
+ if (_step_edit_cursor == 0) {
+ ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
+
+ _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
+ _step_edit_cursor->property_y1() = 0;
+ _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
+ _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
+ _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
+ }
+
+ move_step_edit_cursor (pos);
+ _step_edit_cursor->show ();
+}
+
+void
+MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
+{
+ _step_edit_cursor_position = pos;
+
+ if (_step_edit_cursor) {
+ double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
+ _step_edit_cursor->property_x1() = pixel;
+ set_step_edit_cursor_width (_step_edit_cursor_width);
+ }
+}
+
+void
+MidiRegionView::hide_step_edit_cursor ()
+{
+ if (_step_edit_cursor) {
+ _step_edit_cursor->hide ();
+ }
+}
+
+void
+MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
+{
+ _step_edit_cursor_width = beats;
+
+ if (_step_edit_cursor) {
+ _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));
+ }
+}
+