Unify program change and bank handling so that they are manipulated together.
git-svn-id: svn://localhost/ardour2/branches/3.0@8346 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
390f18c115
commit
f8ebb4582d
|
@ -258,7 +258,7 @@
|
|||
<menuitem action='lower-region-to-bottom'/>
|
||||
</menu>
|
||||
<menu action='RegionMenuMIDI'>
|
||||
<menuitem action='insert-program-change'/>
|
||||
<menuitem action='insert-patch-change'/>
|
||||
<menuitem action='quantize-region'/>
|
||||
<menuitem action='fork-region'/>
|
||||
<menuitem action='show-region-list-editor'/>
|
||||
|
@ -593,7 +593,7 @@
|
|||
<menuitem action='lower-region-to-bottom'/>
|
||||
</menu>
|
||||
<menu action='RegionMenuMIDI'>
|
||||
<menuitem action='insert-program-change'/>
|
||||
<menuitem action='insert-patch-change'/>
|
||||
<menuitem action='quantize-region'/>
|
||||
<menuitem action='fork-region'/>
|
||||
<menuitem action='show-region-list-editor'/>
|
||||
|
|
|
@ -71,8 +71,8 @@
|
|||
<Option name="selected midi note color max" value="ff0000ff"/>
|
||||
<Option name="midi note selected" value="ff3a48ff"/>
|
||||
<Option name="midi note velocity text" value="f4f214bc"/>
|
||||
<Option name="midi program change fill" value="0000ffa0"/>
|
||||
<Option name="midi program change outline" value="a7a7d4ff"/>
|
||||
<Option name="midi patch change fill" value="0000ffa0"/>
|
||||
<Option name="midi patch change outline" value="a7a7d4ff"/>
|
||||
<Option name="midi sysex fill" value="f1e139a0"/>
|
||||
<Option name="midi sysex outline" value="a7a7d4ff"/>
|
||||
<Option name="midi select rect fill" value="8888ff88"/>
|
||||
|
|
|
@ -65,8 +65,8 @@ CANVAS_VARIABLE(canvasvar_SelectedMidiNoteColorMid, "selected midi note color mi
|
|||
CANVAS_VARIABLE(canvasvar_SelectedMidiNoteColorTop, "selected midi note color max")
|
||||
CANVAS_VARIABLE(canvasvar_MidiNoteSelected, "midi note selected")
|
||||
CANVAS_VARIABLE(canvasvar_MidiNoteVelocityText, "midi note velocity text")
|
||||
CANVAS_VARIABLE(canvasvar_MidiProgramChangeFill, "midi program change fill")
|
||||
CANVAS_VARIABLE(canvasvar_MidiProgramChangeOutline, "midi program change outline")
|
||||
CANVAS_VARIABLE(canvasvar_MidiPatchChangeFill, "midi patch change fill")
|
||||
CANVAS_VARIABLE(canvasvar_MidiPatchChangeOutline, "midi patch change outline")
|
||||
CANVAS_VARIABLE(canvasvar_MidiSysExFill, "midi sysex fill")
|
||||
CANVAS_VARIABLE(canvasvar_MidiSysExOutline, "midi sysex outline")
|
||||
CANVAS_VARIABLE(canvasvar_MidiSelectRectFill, "midi select rect fill")
|
||||
|
|
|
@ -1090,7 +1090,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
|
|||
void reset_region_scale_amplitude ();
|
||||
void adjust_region_gain (bool up);
|
||||
void quantize_region ();
|
||||
void insert_program_change ();
|
||||
void insert_patch_change ();
|
||||
void fork_region ();
|
||||
|
||||
void do_insert_time ();
|
||||
|
|
|
@ -1469,7 +1469,7 @@ Editor::register_region_actions ()
|
|||
);
|
||||
|
||||
reg_sens (_region_actions, "quantize-region", _("Quantize"), sigc::mem_fun (*this, &Editor::quantize_region));
|
||||
reg_sens (_region_actions, "insert-program-change", _("Insert Program Change..."), sigc::mem_fun (*this, &Editor::insert_program_change));
|
||||
reg_sens (_region_actions, "insert-patch-change", _("Insert Patch Change..."), sigc::mem_fun (*this, &Editor::insert_patch_change));
|
||||
reg_sens (_region_actions, "fork-region", _("Fork"), sigc::mem_fun (*this, &Editor::fork_region));
|
||||
reg_sens (_region_actions, "strip-region-silence", _("Strip Silence..."), sigc::mem_fun (*this, &Editor::strip_region_silence));
|
||||
reg_sens (_region_actions, "set-selection-from-region", _("Set Range Selection"), sigc::mem_fun (*this, &Editor::set_selection_from_region));
|
||||
|
|
|
@ -4093,17 +4093,17 @@ DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
|
|||
initial_end = v->region()->position () + v->region()->length ();
|
||||
}
|
||||
|
||||
ProgramChangeDrag::ProgramChangeDrag (Editor* e, CanvasProgramChange* i, MidiRegionView* r)
|
||||
PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
|
||||
: Drag (e, i)
|
||||
, _region_view (r)
|
||||
, _program_change (i)
|
||||
, _patch_change (i)
|
||||
, _cumulative_dx (0)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::Drags, "New ProgramChangeDrag\n");
|
||||
DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
|
||||
}
|
||||
|
||||
void
|
||||
ProgramChangeDrag::motion (GdkEvent* ev, bool)
|
||||
PatchChangeDrag::motion (GdkEvent* ev, bool)
|
||||
{
|
||||
framepos_t f = adjusted_current_frame (ev);
|
||||
boost::shared_ptr<Region> r = _region_view->region ();
|
||||
|
@ -4112,12 +4112,12 @@ ProgramChangeDrag::motion (GdkEvent* ev, bool)
|
|||
|
||||
framecnt_t const dxf = f - grab_frame();
|
||||
double const dxu = _editor->frame_to_unit (dxf);
|
||||
_program_change->move (dxu - _cumulative_dx, 0);
|
||||
_patch_change->move (dxu - _cumulative_dx, 0);
|
||||
_cumulative_dx = dxu;
|
||||
}
|
||||
|
||||
void
|
||||
ProgramChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
|
||||
PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
|
||||
{
|
||||
if (!movement_occurred) {
|
||||
return;
|
||||
|
@ -4129,22 +4129,22 @@ ProgramChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
|
|||
f = max (f, r->position ());
|
||||
f = min (f, r->last_frame ());
|
||||
|
||||
_region_view->move_program_change (
|
||||
MidiRegionView::PCEvent (_program_change->event_time(), _program_change->program(), _program_change->channel()),
|
||||
_region_view->move_patch_change (
|
||||
*_patch_change,
|
||||
_region_view->frames_to_beats (f - r->position() - r->start())
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
ProgramChangeDrag::aborted ()
|
||||
PatchChangeDrag::aborted ()
|
||||
{
|
||||
_program_change->move (-_cumulative_dx, 0);
|
||||
_patch_change->move (-_cumulative_dx, 0);
|
||||
}
|
||||
|
||||
void
|
||||
ProgramChangeDrag::setup_pointer_frame_offset ()
|
||||
PatchChangeDrag::setup_pointer_frame_offset ()
|
||||
{
|
||||
boost::shared_ptr<Region> region = _region_view->region ();
|
||||
_pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_program_change->event_time()) - region->position() + region->start();
|
||||
_pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_patch_change->patch()->time()) - region->position() + region->start();
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace PBD {
|
|||
namespace Gnome {
|
||||
namespace Canvas {
|
||||
class CanvasNoteEvent;
|
||||
class CanvasProgramChange;
|
||||
class CanvasPatchChange;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -451,11 +451,11 @@ class NoteDrag : public Drag
|
|||
double _note_height;
|
||||
};
|
||||
|
||||
/** Drag to move MIDI program changes */
|
||||
class ProgramChangeDrag : public Drag
|
||||
/** Drag to move MIDI patch changes */
|
||||
class PatchChangeDrag : public Drag
|
||||
{
|
||||
public:
|
||||
ProgramChangeDrag (Editor *, ArdourCanvas::CanvasProgramChange *, MidiRegionView *);
|
||||
PatchChangeDrag (Editor *, ArdourCanvas::CanvasPatchChange *, MidiRegionView *);
|
||||
|
||||
void motion (GdkEvent *, bool);
|
||||
void finished (GdkEvent *, bool);
|
||||
|
@ -469,7 +469,7 @@ public:
|
|||
|
||||
private:
|
||||
MidiRegionView* _region_view;
|
||||
ArdourCanvas::CanvasProgramChange* _program_change;
|
||||
ArdourCanvas::CanvasPatchChange* _patch_change;
|
||||
double _cumulative_dx;
|
||||
};
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
#include "normalize_dialog.h"
|
||||
#include "editor_cursors.h"
|
||||
#include "mouse_cursors.h"
|
||||
#include "program_change_dialog.h"
|
||||
#include "patch_change_dialog.h"
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
|
@ -4706,25 +4706,27 @@ Editor::quantize_region ()
|
|||
}
|
||||
|
||||
void
|
||||
Editor::insert_program_change ()
|
||||
Editor::insert_patch_change ()
|
||||
{
|
||||
RegionSelection rs = get_regions_from_selection_and_entered ();
|
||||
if (rs.empty ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProgramChangeDialog d;
|
||||
framepos_t const p = get_preferred_edit_position (false);
|
||||
|
||||
Evoral::PatchChange<Evoral::MusicalTime> empty (0, 0, 0, 0);
|
||||
PatchChangeDialog d (0, _session, empty, Gtk::Stock::ADD);
|
||||
|
||||
if (d.run() == RESPONSE_CANCEL) {
|
||||
return;
|
||||
}
|
||||
|
||||
framepos_t const p = get_preferred_edit_position (false);
|
||||
|
||||
for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
|
||||
MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
|
||||
if (mrv) {
|
||||
if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
|
||||
mrv->add_program_change (p - mrv->region()->position(), d.program ());
|
||||
mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
#include "automation_time_axis.h"
|
||||
#include "canvas-hit.h"
|
||||
#include "canvas-note.h"
|
||||
#include "canvas-program-change.h"
|
||||
#include "canvas_patch_change.h"
|
||||
#include "editor.h"
|
||||
#include "ghostregion.h"
|
||||
#include "gui_thread.h"
|
||||
|
@ -67,6 +67,7 @@
|
|||
#include "streamview.h"
|
||||
#include "utils.h"
|
||||
#include "mouse_cursors.h"
|
||||
#include "patch_change_dialog.h"
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
|
@ -778,7 +779,7 @@ MidiRegionView::clear_events()
|
|||
}
|
||||
|
||||
_events.clear();
|
||||
_pgm_changes.clear();
|
||||
_patch_changes.clear();
|
||||
_sys_exes.clear();
|
||||
_optimization_iterator = _events.end();
|
||||
}
|
||||
|
@ -1019,11 +1020,11 @@ MidiRegionView::redisplay_model()
|
|||
}
|
||||
}
|
||||
|
||||
_pgm_changes.clear();
|
||||
_patch_changes.clear();
|
||||
_sys_exes.clear();
|
||||
|
||||
display_sysexes();
|
||||
display_program_changes();
|
||||
display_patch_changes ();
|
||||
|
||||
_marked_for_selection.clear ();
|
||||
_marked_for_velocity.clear ();
|
||||
|
@ -1037,66 +1038,40 @@ MidiRegionView::redisplay_model()
|
|||
}
|
||||
|
||||
void
|
||||
MidiRegionView::display_program_changes()
|
||||
MidiRegionView::display_patch_changes ()
|
||||
{
|
||||
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);
|
||||
display_patch_changes_on_channel (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::display_program_changes_on_channel(uint8_t channel)
|
||||
MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
|
||||
{
|
||||
boost::shared_ptr<Evoral::Control> control =
|
||||
_model->control(Evoral::MIDI::ProgramChange (MidiPgmChangeAutomation, channel));
|
||||
for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
|
||||
|
||||
if (!control) {
|
||||
return;
|
||||
}
|
||||
|
||||
Glib::Mutex::Lock lock (control->list()->lock());
|
||||
|
||||
for (AutomationList::const_iterator event = control->list()->begin();
|
||||
event != control->list()->end(); ++event) {
|
||||
double event_time = (*event)->when;
|
||||
double program_number = floor((*event)->value + 0.5);
|
||||
|
||||
// Get current value of bank select MSB at time of the program change
|
||||
Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
|
||||
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_double(true, event_time) + 0.5));
|
||||
if ((*i)->channel() != channel) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get current value of bank select LSB at time of the program change
|
||||
Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
|
||||
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_double(true, event_time) + 0.5));
|
||||
}
|
||||
|
||||
MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
|
||||
|
||||
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);
|
||||
|
||||
PCEvent program_change(event_time, uint8_t(program_number), channel);
|
||||
|
||||
if (patch != 0) {
|
||||
add_canvas_program_change (program_change, patch->name());
|
||||
add_canvas_patch_change (*i, patch->name());
|
||||
} else {
|
||||
char buf[4];
|
||||
// program_number is zero-based: convert to one-based
|
||||
snprintf(buf, 4, "%d", int(program_number+1));
|
||||
add_canvas_program_change (program_change, buf);
|
||||
snprintf (buf, 4, "%d", (*i)->program() + 1);
|
||||
add_canvas_patch_change (*i, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1125,7 +1100,7 @@ MidiRegionView::display_sysexes()
|
|||
boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
|
||||
new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
|
||||
|
||||
// Show unless program change is beyond the region bounds
|
||||
// Show unless patch change is beyond the region bounds
|
||||
if (time - _region->start() >= _region->length() || time < _region->start()) {
|
||||
sysex->hide();
|
||||
} else {
|
||||
|
@ -1205,7 +1180,7 @@ MidiRegionView::set_height (double height)
|
|||
name_pixbuf->raise_to_top();
|
||||
}
|
||||
|
||||
for (PgmChanges::iterator x = _pgm_changes.begin(); x != _pgm_changes.end(); ++x) {
|
||||
for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
|
||||
(*x)->set_height (midi_stream_view()->contents_height());
|
||||
}
|
||||
|
||||
|
@ -1563,163 +1538,163 @@ MidiRegionView::step_sustain (Evoral::MusicalTime beats)
|
|||
}
|
||||
|
||||
void
|
||||
MidiRegionView::add_canvas_program_change (PCEvent& program, const string& displaytext)
|
||||
MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext)
|
||||
{
|
||||
assert(program.time >= 0);
|
||||
assert (patch->time() >= 0);
|
||||
|
||||
const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
|
||||
const double x = trackview.editor().frame_to_pixel (beats_to_frames (patch->time()));
|
||||
|
||||
double height = midi_stream_view()->contents_height();
|
||||
double const height = midi_stream_view()->contents_height();
|
||||
|
||||
boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
|
||||
new CanvasProgramChange(*this, *_note_group,
|
||||
boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
|
||||
new CanvasPatchChange(*this, *_note_group,
|
||||
displaytext,
|
||||
height,
|
||||
x, 1.0,
|
||||
_model_name,
|
||||
_custom_device_mode,
|
||||
program.time, program.channel, program.value));
|
||||
|
||||
// Show unless program change is beyond the region bounds
|
||||
if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
|
||||
pgm_change->hide();
|
||||
} else {
|
||||
pgm_change->show();
|
||||
}
|
||||
|
||||
_pgm_changes.push_back(pgm_change);
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
|
||||
{
|
||||
Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
|
||||
boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
|
||||
double msb = 0.0;
|
||||
if (msb_control != 0) {
|
||||
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);
|
||||
double lsb = 0.0;
|
||||
if (lsb_control != 0) {
|
||||
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);
|
||||
double program_number = -1.0;
|
||||
if (program_control != 0) {
|
||||
program_number = program_control->get_double(true, time);
|
||||
}
|
||||
|
||||
key.msb = (int) floor(msb + 0.5);
|
||||
key.lsb = (int) floor(lsb + 0.5);
|
||||
key.program_number = (int) floor(program_number + 0.5);
|
||||
assert(key.is_sane());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
|
||||
{
|
||||
// TODO: Get the real event here and alter them at the original times
|
||||
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_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_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_double(float(new_patch.program_number), true, old_program.time);
|
||||
|
||||
_pgm_changes.clear ();
|
||||
display_program_changes (); // XXX would be nice to limit to just old_program.channel
|
||||
}
|
||||
|
||||
/** @param t Time in frames relative to region position */
|
||||
void
|
||||
MidiRegionView::add_program_change (framecnt_t t, uint8_t value)
|
||||
{
|
||||
boost::shared_ptr<Evoral::Control> control = midi_region()->model()->control (
|
||||
Evoral::Parameter (MidiPgmChangeAutomation, get_channel_for_add (), 0), true
|
||||
patch)
|
||||
);
|
||||
|
||||
// Show unless patch change is beyond the region bounds
|
||||
if (patch->time() - _region->start() >= _region->length() || patch->time() < _region->start()) {
|
||||
patch_change->hide();
|
||||
} else {
|
||||
patch_change->show();
|
||||
}
|
||||
|
||||
_patch_changes.push_back (patch_change);
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
|
||||
{
|
||||
MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
|
||||
while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
|
||||
++i;
|
||||
}
|
||||
|
||||
assert (control);
|
||||
if (i != _model->patch_changes().end()) {
|
||||
key.msb = (*i)->bank_msb ();
|
||||
key.lsb = (*i)->bank_lsb ();
|
||||
key.program_number = (*i)->program ();
|
||||
} else {
|
||||
key.msb = key.lsb = key.program_number = 0;
|
||||
}
|
||||
|
||||
assert (key.is_sane());
|
||||
}
|
||||
|
||||
Evoral::MusicalTime const b = frames_to_beats (t + midi_region()->start());
|
||||
|
||||
control->list()->add (b, value);
|
||||
void
|
||||
MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
|
||||
{
|
||||
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
|
||||
|
||||
if (pc.patch()->program() != new_patch.program_number) {
|
||||
c->change_program (pc.patch (), new_patch.program_number);
|
||||
}
|
||||
|
||||
_pgm_changes.clear ();
|
||||
display_program_changes ();
|
||||
int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
|
||||
if (pc.patch()->bank() != new_bank) {
|
||||
c->change_bank (pc.patch (), new_bank);
|
||||
}
|
||||
|
||||
_model->apply_command (*trackview.session(), c);
|
||||
|
||||
_patch_changes.clear ();
|
||||
display_patch_changes ();
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::move_program_change (PCEvent pc, Evoral::MusicalTime t)
|
||||
MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
|
||||
{
|
||||
boost::shared_ptr<Evoral::Control> control = _model->control (Evoral::Parameter (MidiPgmChangeAutomation, pc.channel, 0));
|
||||
assert (control);
|
||||
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
|
||||
|
||||
if (old_change->time() != new_change.time()) {
|
||||
c->change_time (old_change, new_change.time());
|
||||
}
|
||||
|
||||
control->list()->erase (pc.time, pc.value);
|
||||
control->list()->add (t, pc.value);
|
||||
if (old_change->channel() != new_change.channel()) {
|
||||
c->change_channel (old_change, new_change.channel());
|
||||
}
|
||||
|
||||
if (old_change->program() != new_change.program()) {
|
||||
c->change_program (old_change, new_change.program());
|
||||
}
|
||||
|
||||
_pgm_changes.clear ();
|
||||
display_program_changes ();
|
||||
if (old_change->bank() != new_change.bank()) {
|
||||
c->change_bank (old_change, new_change.bank());
|
||||
}
|
||||
|
||||
_model->apply_command (*trackview.session(), c);
|
||||
|
||||
_patch_changes.clear ();
|
||||
display_patch_changes ();
|
||||
}
|
||||
|
||||
/** Add a patch change to the region.
|
||||
* @param t Time in frames relative to region position
|
||||
* @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
|
||||
* get_channel_for_add())
|
||||
*/
|
||||
void
|
||||
MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
|
||||
{
|
||||
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
|
||||
c->add (MidiModel::PatchChangePtr (
|
||||
new Evoral::PatchChange<Evoral::MusicalTime> (
|
||||
frames_to_beats (t + midi_region()->start()), get_channel_for_add(), patch.program(), patch.bank()
|
||||
)
|
||||
));
|
||||
|
||||
_model->apply_command (*trackview.session(), c);
|
||||
|
||||
_patch_changes.clear ();
|
||||
display_patch_changes ();
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::delete_program_change (CanvasProgramChange* pc)
|
||||
MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
|
||||
{
|
||||
boost::shared_ptr<Evoral::Control> control = _model->control (Evoral::Parameter (MidiPgmChangeAutomation, pc->channel(), 0));
|
||||
assert (control);
|
||||
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
|
||||
c->change_time (pc.patch (), t);
|
||||
_model->apply_command (*trackview.session(), c);
|
||||
|
||||
control->list()->erase (pc->event_time(), pc->program());
|
||||
_pgm_changes.clear ();
|
||||
display_program_changes ();
|
||||
_patch_changes.clear ();
|
||||
display_patch_changes ();
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
|
||||
MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
|
||||
{
|
||||
PCEvent program_change_event(program.event_time(), program.program(), program.channel());
|
||||
alter_program_change(program_change_event, new_patch);
|
||||
}
|
||||
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
|
||||
c->remove (pc->patch ());
|
||||
_model->apply_command (*trackview.session(), c);
|
||||
|
||||
_patch_changes.clear ();
|
||||
display_patch_changes ();
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::previous_program(CanvasProgramChange& program)
|
||||
MidiRegionView::previous_patch (CanvasPatchChange& patch)
|
||||
{
|
||||
if (program.program() < 127) {
|
||||
if (patch.patch()->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());
|
||||
|
||||
get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
|
||||
key.program_number++;
|
||||
alter_program_change(program_change_event, key);
|
||||
change_patch_change (patch, key);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::next_program(CanvasProgramChange& program)
|
||||
MidiRegionView::next_patch (CanvasPatchChange& patch)
|
||||
{
|
||||
if (program.program() > 0) {
|
||||
if (patch.patch()->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());
|
||||
|
||||
get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
|
||||
key.program_number--;
|
||||
alter_program_change(program_change_event, key);
|
||||
change_patch_change (patch, key);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2759,6 +2734,20 @@ MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
|
||||
{
|
||||
ostringstream s;
|
||||
s << ((int) ev->patch()->program() + 1) << ":" << (ev->patch()->bank() + 1);
|
||||
trackview.editor().show_verbose_canvas_cursor_with (s.str().c_str(), 10, 20);
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
|
||||
{
|
||||
trackview.editor().hide_verbose_canvas_cursor ();
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
|
||||
{
|
||||
|
@ -2806,6 +2795,9 @@ MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
|
|||
}
|
||||
|
||||
_last_channel_selection = mask;
|
||||
|
||||
_patch_changes.clear ();
|
||||
display_patch_changes ();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -3303,3 +3295,14 @@ MidiRegionView::get_channel_for_add () const
|
|||
|
||||
return channel;
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
|
||||
{
|
||||
PatchChangeDialog d (&_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
|
||||
if (d.run () != Gtk::RESPONSE_ACCEPT) {
|
||||
return;
|
||||
}
|
||||
|
||||
change_patch_change (pc->patch(), d.patch ());
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
#include "canvas-hit.h"
|
||||
#include "canvas-note.h"
|
||||
#include "canvas-note-event.h"
|
||||
#include "canvas-program-change.h"
|
||||
#include "canvas_patch_change.h"
|
||||
#include "canvas-sysex.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
|
@ -120,57 +120,43 @@ class MidiRegionView : public RegionView
|
|||
void cut_copy_clear (Editing::CutCopyOp);
|
||||
void paste (framepos_t pos, float times, const MidiCutBuffer&);
|
||||
|
||||
struct PCEvent {
|
||||
PCEvent(double a_time, uint8_t a_value, uint8_t a_channel)
|
||||
: time(a_time), value(a_value), channel(a_channel) {}
|
||||
|
||||
double time;
|
||||
uint8_t value;
|
||||
uint8_t channel;
|
||||
};
|
||||
|
||||
/** Add a new program change flag to the canvas.
|
||||
* @param program the MidiRegionView::PCEvent to add
|
||||
/** Add a new patch change flag to the canvas.
|
||||
* @param patch the patch change to add
|
||||
* @param the text to display in the flag
|
||||
*/
|
||||
void add_canvas_program_change (PCEvent& program, const std::string& displaytext);
|
||||
void add_canvas_patch_change (ARDOUR::MidiModel::PatchChangePtr patch, const std::string& displaytext);
|
||||
|
||||
/** Look up the given time and channel in the 'automation' and set keys accordingly.
|
||||
* @param time the time of the program change event
|
||||
* @param time the time of the patch change event
|
||||
* @param channel the MIDI channel of the event
|
||||
* @key a reference to an instance of MIDI::Name::PatchPrimaryKey whose fields will
|
||||
* will be set according to the result of the lookup
|
||||
*/
|
||||
void get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key);
|
||||
|
||||
/** Change the 'automation' data of old_program to new values which correspond to new_patch.
|
||||
* @param old_program the program change event which is to be altered
|
||||
* @param new_patch the new lsb, msb and program number which are to be set
|
||||
/** Change old_patch to new_patch.
|
||||
* @param old_patch the canvas patch change which is to be altered
|
||||
* @param new_patch new patch
|
||||
*/
|
||||
void alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch);
|
||||
void change_patch_change (ArdourCanvas::CanvasPatchChange& old_patch, const MIDI::Name::PatchPrimaryKey& new_patch);
|
||||
void change_patch_change (ARDOUR::MidiModel::PatchChangePtr, Evoral::PatchChange<Evoral::MusicalTime> const &);
|
||||
|
||||
void add_program_change (framecnt_t, uint8_t);
|
||||
void move_program_change (PCEvent, Evoral::MusicalTime);
|
||||
void delete_program_change (ArdourCanvas::CanvasProgramChange *);
|
||||
void add_patch_change (framecnt_t, Evoral::PatchChange<Evoral::MusicalTime> const &);
|
||||
void move_patch_change (ArdourCanvas::CanvasPatchChange &, Evoral::MusicalTime);
|
||||
void delete_patch_change (ArdourCanvas::CanvasPatchChange *);
|
||||
void edit_patch_change (ArdourCanvas::CanvasPatchChange *);
|
||||
|
||||
/** Alter a given program to the new given one.
|
||||
* (Called on context menu select on CanvasProgramChange)
|
||||
/** Alter a given patch to be its predecessor in the MIDNAM file.
|
||||
*/
|
||||
void program_selected(
|
||||
ArdourCanvas::CanvasProgramChange& program,
|
||||
const MIDI::Name::PatchPrimaryKey& new_patch);
|
||||
void previous_patch (ArdourCanvas::CanvasPatchChange &);
|
||||
|
||||
/** Alter a given program to be its predecessor in the MIDNAM file.
|
||||
/** Alters a given patch to be its successor in the MIDNAM file.
|
||||
*/
|
||||
void previous_program(ArdourCanvas::CanvasProgramChange& program);
|
||||
void next_patch (ArdourCanvas::CanvasPatchChange &);
|
||||
|
||||
/** Alters a given program to be its successor in the MIDNAM file.
|
||||
/** Displays all patch change events in the region as flags on the canvas.
|
||||
*/
|
||||
void next_program(ArdourCanvas::CanvasProgramChange& program);
|
||||
|
||||
/** Displays all program change events in the region as flags on the canvas.
|
||||
*/
|
||||
void display_program_changes();
|
||||
void display_patch_changes();
|
||||
|
||||
/** Displays all system exclusive events in the region as flags on the canvas.
|
||||
*/
|
||||
|
@ -196,6 +182,8 @@ class MidiRegionView : public RegionView
|
|||
|
||||
void note_entered(ArdourCanvas::CanvasNoteEvent* ev);
|
||||
void note_left(ArdourCanvas::CanvasNoteEvent* ev);
|
||||
void patch_entered (ArdourCanvas::CanvasPatchChange *);
|
||||
void patch_left (ArdourCanvas::CanvasPatchChange *);
|
||||
void note_mouse_position (float xfraction, float yfraction, bool can_set_cursor=true);
|
||||
void unique_select(ArdourCanvas::CanvasNoteEvent* ev);
|
||||
void note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend=false);
|
||||
|
@ -360,12 +348,12 @@ class MidiRegionView : public RegionView
|
|||
std::string _custom_device_mode;
|
||||
|
||||
typedef std::list<ArdourCanvas::CanvasNoteEvent*> Events;
|
||||
typedef std::vector< boost::shared_ptr<ArdourCanvas::CanvasProgramChange> > PgmChanges;
|
||||
typedef std::vector< boost::shared_ptr<ArdourCanvas::CanvasPatchChange> > PatchChanges;
|
||||
typedef std::vector< boost::shared_ptr<ArdourCanvas::CanvasSysEx> > SysExes;
|
||||
|
||||
boost::shared_ptr<ARDOUR::MidiModel> _model;
|
||||
Events _events;
|
||||
PgmChanges _pgm_changes;
|
||||
PatchChanges _patch_changes;
|
||||
SysExes _sys_exes;
|
||||
ArdourCanvas::CanvasNote** _active_notes;
|
||||
ArdourCanvas::Group* _note_group;
|
||||
|
@ -444,7 +432,7 @@ class MidiRegionView : public RegionView
|
|||
void maybe_select_by_position (GdkEventButton* ev, double x, double y);
|
||||
void get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask = 0);
|
||||
|
||||
void display_program_changes_on_channel (uint8_t);
|
||||
void display_patch_changes_on_channel (uint8_t);
|
||||
|
||||
void connect_to_diskstream ();
|
||||
void data_recorded (boost::shared_ptr<ARDOUR::MidiBuffer>, boost::weak_ptr<ARDOUR::MidiSource>);
|
||||
|
|
|
@ -591,7 +591,8 @@ MidiTimeAxisView::build_controller_menu ()
|
|||
}
|
||||
}
|
||||
|
||||
/* loop over all 127 MIDI controllers, in groups of 16 */
|
||||
/* loop over all 127 MIDI controllers, in groups of 16; except don't offer
|
||||
bank select controllers, as they are handled by the `patch' code */
|
||||
|
||||
for (int i = 0; i < 127; i += 16) {
|
||||
|
||||
|
@ -603,6 +604,10 @@ MidiTimeAxisView::build_controller_menu ()
|
|||
|
||||
for (int ctl = i; ctl < i+16; ++ctl) {
|
||||
|
||||
if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chn_cnt > 1) {
|
||||
|
||||
/* multiple channels - create a submenu, with 1 item per channel */
|
||||
|
@ -985,7 +990,7 @@ MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
|
|||
|
||||
no_redraw = false;
|
||||
|
||||
/* TODO: Bender, PgmChange, Pressure */
|
||||
/* TODO: Bender, Pressure */
|
||||
|
||||
/* invalidate the controller menu, so that we rebuilt it next time */
|
||||
_controller_menu_map.clear ();
|
||||
|
|
|
@ -55,7 +55,7 @@ gtk2_ardour_sources = [
|
|||
'canvas-hit.cc',
|
||||
'canvas-note-event.cc',
|
||||
'canvas-note.cc',
|
||||
'canvas-program-change.cc',
|
||||
'canvas_patch_change.cc',
|
||||
'canvas-simpleline.c',
|
||||
'canvas-simplerect.c',
|
||||
'canvas-sysex.cc',
|
||||
|
@ -173,7 +173,7 @@ gtk2_ardour_sources = [
|
|||
'port_matrix_labels.cc',
|
||||
'port_matrix_row_labels.cc',
|
||||
'processor_box.cc',
|
||||
'program_change_dialog.cc',
|
||||
'patch_change_dialog.cc',
|
||||
'progress_reporter.cc',
|
||||
'prompter.cc',
|
||||
'public_editor.cc',
|
||||
|
|
|
@ -172,8 +172,66 @@ public:
|
|||
Change unmarshal_change (XMLNode *);
|
||||
};
|
||||
|
||||
class PatchChangeDiffCommand : public DiffCommand {
|
||||
public:
|
||||
PatchChangeDiffCommand (boost::shared_ptr<MidiModel>, const std::string &);
|
||||
PatchChangeDiffCommand (boost::shared_ptr<MidiModel>, const XMLNode &);
|
||||
|
||||
int set_state (const XMLNode &, int version);
|
||||
XMLNode & get_state ();
|
||||
|
||||
void operator() ();
|
||||
void undo ();
|
||||
|
||||
void add (PatchChangePtr);
|
||||
void remove (PatchChangePtr);
|
||||
void change_time (PatchChangePtr, TimeType);
|
||||
void change_channel (PatchChangePtr, uint8_t);
|
||||
void change_program (PatchChangePtr, uint8_t);
|
||||
void change_bank (PatchChangePtr, int);
|
||||
|
||||
enum Property {
|
||||
Time,
|
||||
Channel,
|
||||
Program,
|
||||
Bank
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
struct Change {
|
||||
PatchChangePtr patch;
|
||||
Property property;
|
||||
union {
|
||||
TimeType old_time;
|
||||
uint8_t old_channel;
|
||||
int old_bank;
|
||||
uint8_t old_program;
|
||||
};
|
||||
union {
|
||||
uint8_t new_channel;
|
||||
TimeType new_time;
|
||||
uint8_t new_program;
|
||||
int new_bank;
|
||||
};
|
||||
};
|
||||
|
||||
typedef std::list<Change> ChangeList;
|
||||
ChangeList _changes;
|
||||
|
||||
std::list<PatchChangePtr> _added;
|
||||
std::list<PatchChangePtr> _removed;
|
||||
|
||||
XMLNode & marshal_change (const Change &);
|
||||
Change unmarshal_change (XMLNode *);
|
||||
|
||||
XMLNode & marshal_patch_change (constPatchChangePtr);
|
||||
PatchChangePtr unmarshal_patch_change (XMLNode *);
|
||||
};
|
||||
|
||||
MidiModel::NoteDiffCommand* new_note_diff_command (const std::string name = "midi edit");
|
||||
MidiModel::SysExDiffCommand* new_sysex_diff_command (const std::string name = "midi edit");
|
||||
MidiModel::PatchChangeDiffCommand* new_patch_change_diff_command (const std::string name = "midi edit");
|
||||
void apply_command (Session& session, Command* cmd);
|
||||
void apply_command_as_subcommand (Session& session, Command* cmd);
|
||||
|
||||
|
@ -193,6 +251,7 @@ public:
|
|||
void set_midi_source (boost::shared_ptr<MidiSource>);
|
||||
|
||||
boost::shared_ptr<Evoral::Note<TimeType> > find_note (NotePtr);
|
||||
PatchChangePtr find_patch_change (Evoral::event_id_t);
|
||||
boost::shared_ptr<Evoral::Note<TimeType> > find_note (gint note_id);
|
||||
boost::shared_ptr<Evoral::Event<TimeType> > find_sysex (gint);
|
||||
|
||||
|
|
|
@ -114,6 +114,7 @@ setup_enum_writer ()
|
|||
MuteMaster::MutePoint _MuteMaster_MutePoint;
|
||||
MidiModel::NoteDiffCommand::Property _MidiModel_NoteDiffCommand_Property;
|
||||
MidiModel::SysExDiffCommand::Property _MidiModel_SysExDiffCommand_Property;
|
||||
MidiModel::PatchChangeDiffCommand::Property _MidiModel_PatchChangeDiffCommand_Property;
|
||||
WaveformScale _WaveformScale;
|
||||
WaveformShape _WaveformShape;
|
||||
QuantizeType _QuantizeType;
|
||||
|
@ -536,6 +537,11 @@ setup_enum_writer ()
|
|||
|
||||
REGISTER_CLASS_ENUM (MidiModel::SysExDiffCommand, Time);
|
||||
REGISTER (_MidiModel_SysExDiffCommand_Property);
|
||||
|
||||
REGISTER_CLASS_ENUM (MidiModel::PatchChangeDiffCommand, Time);
|
||||
REGISTER_CLASS_ENUM (MidiModel::PatchChangeDiffCommand, Program);
|
||||
REGISTER_CLASS_ENUM (MidiModel::PatchChangeDiffCommand, Bank);
|
||||
REGISTER (_MidiModel_PatchChangeDiffCommand_Property);
|
||||
|
||||
REGISTER_ENUM(Linear);
|
||||
REGISTER_ENUM(Logarithmic);
|
||||
|
|
|
@ -70,6 +70,16 @@ MidiModel::new_sysex_diff_command (const string name)
|
|||
return new SysExDiffCommand (ms->model(), name);
|
||||
}
|
||||
|
||||
/** Start a new PatchChangeDiff command */
|
||||
MidiModel::PatchChangeDiffCommand*
|
||||
MidiModel::new_patch_change_diff_command (const string name)
|
||||
{
|
||||
boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
|
||||
assert (ms);
|
||||
|
||||
return new PatchChangeDiffCommand (ms->model(), name);
|
||||
}
|
||||
|
||||
|
||||
/** Apply a command.
|
||||
*
|
||||
|
@ -107,6 +117,10 @@ MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
|
|||
#define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
|
||||
#define SYSEX_DIFF_COMMAND_ELEMENT "SysExDiffCommand"
|
||||
#define DIFF_SYSEXES_ELEMENT "ChangedSysExes"
|
||||
#define PATCH_CHANGE_DIFF_COMMAND_ELEMENT "PatchChangeDiffCommand"
|
||||
#define ADDED_PATCH_CHANGES_ELEMENT "AddedPatchChanges"
|
||||
#define REMOVED_PATCH_CHANGES_ELEMENT "RemovedPatchChanges"
|
||||
#define DIFF_PATCH_CHANGES_ELEMENT "ChangedPatchChanges"
|
||||
|
||||
MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
|
||||
: Command (name)
|
||||
|
@ -890,6 +904,415 @@ MidiModel::SysExDiffCommand::get_state ()
|
|||
return *diff_command;
|
||||
}
|
||||
|
||||
MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const string& name)
|
||||
: DiffCommand (m, name)
|
||||
{
|
||||
assert (_model);
|
||||
}
|
||||
|
||||
MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode & node)
|
||||
: DiffCommand (m, "")
|
||||
{
|
||||
assert (_model);
|
||||
set_state (node, Stateful::loading_state_version);
|
||||
}
|
||||
|
||||
void
|
||||
MidiModel::PatchChangeDiffCommand::add (PatchChangePtr p)
|
||||
{
|
||||
_added.push_back (p);
|
||||
}
|
||||
|
||||
void
|
||||
MidiModel::PatchChangeDiffCommand::remove (PatchChangePtr p)
|
||||
{
|
||||
_removed.push_back (p);
|
||||
}
|
||||
|
||||
void
|
||||
MidiModel::PatchChangeDiffCommand::change_time (PatchChangePtr patch, TimeType t)
|
||||
{
|
||||
Change c;
|
||||
c.property = Time;
|
||||
c.patch = patch;
|
||||
c.old_time = patch->time ();
|
||||
c.new_time = t;
|
||||
|
||||
_changes.push_back (c);
|
||||
}
|
||||
|
||||
void
|
||||
MidiModel::PatchChangeDiffCommand::change_channel (PatchChangePtr patch, uint8_t channel)
|
||||
{
|
||||
Change c;
|
||||
c.property = Channel;
|
||||
c.patch = patch;
|
||||
c.old_channel = patch->channel ();
|
||||
c.new_channel = channel;
|
||||
|
||||
_changes.push_back (c);
|
||||
}
|
||||
|
||||
void
|
||||
MidiModel::PatchChangeDiffCommand::change_program (PatchChangePtr patch, uint8_t program)
|
||||
{
|
||||
Change c;
|
||||
c.property = Program;
|
||||
c.patch = patch;
|
||||
c.old_program = patch->program ();
|
||||
c.new_program = program;
|
||||
|
||||
_changes.push_back (c);
|
||||
}
|
||||
|
||||
void
|
||||
MidiModel::PatchChangeDiffCommand::change_bank (PatchChangePtr patch, int bank)
|
||||
{
|
||||
Change c;
|
||||
c.property = Bank;
|
||||
c.patch = patch;
|
||||
c.old_bank = patch->bank ();
|
||||
c.new_bank = bank;
|
||||
|
||||
_changes.push_back (c);
|
||||
}
|
||||
|
||||
void
|
||||
MidiModel::PatchChangeDiffCommand::operator() ()
|
||||
{
|
||||
{
|
||||
MidiModel::WriteLock lock (_model->edit_lock ());
|
||||
|
||||
for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
|
||||
_model->add_patch_change_unlocked (*i);
|
||||
}
|
||||
|
||||
for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
|
||||
_model->remove_patch_change_unlocked (*i);
|
||||
}
|
||||
|
||||
set<PatchChangePtr> temporary_removals;
|
||||
|
||||
for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
|
||||
switch (i->property) {
|
||||
case Time:
|
||||
if (temporary_removals.find (i->patch) == temporary_removals.end()) {
|
||||
_model->remove_patch_change_unlocked (i->patch);
|
||||
temporary_removals.insert (i->patch);
|
||||
}
|
||||
i->patch->set_time (i->new_time);
|
||||
break;
|
||||
|
||||
case Channel:
|
||||
i->patch->set_channel (i->new_channel);
|
||||
break;
|
||||
|
||||
case Program:
|
||||
i->patch->set_program (i->new_program);
|
||||
break;
|
||||
|
||||
case Bank:
|
||||
i->patch->set_bank (i->new_bank);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
|
||||
_model->add_patch_change_unlocked (*i);
|
||||
}
|
||||
}
|
||||
|
||||
_model->ContentsChanged (); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
void
|
||||
MidiModel::PatchChangeDiffCommand::undo ()
|
||||
{
|
||||
{
|
||||
MidiModel::WriteLock lock (_model->edit_lock());
|
||||
|
||||
for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
|
||||
_model->remove_patch_change_unlocked (*i);
|
||||
}
|
||||
|
||||
for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
|
||||
_model->add_patch_change_unlocked (*i);
|
||||
}
|
||||
|
||||
set<PatchChangePtr> temporary_removals;
|
||||
|
||||
for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
|
||||
switch (i->property) {
|
||||
case Time:
|
||||
if (temporary_removals.find (i->patch) == temporary_removals.end()) {
|
||||
_model->remove_patch_change_unlocked (i->patch);
|
||||
temporary_removals.insert (i->patch);
|
||||
}
|
||||
i->patch->set_time (i->old_time);
|
||||
break;
|
||||
|
||||
case Channel:
|
||||
i->patch->set_channel (i->old_channel);
|
||||
break;
|
||||
|
||||
case Program:
|
||||
i->patch->set_program (i->old_program);
|
||||
break;
|
||||
|
||||
case Bank:
|
||||
i->patch->set_bank (i->old_bank);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
|
||||
_model->add_patch_change_unlocked (*i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_model->ContentsChanged (); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
XMLNode &
|
||||
MidiModel::PatchChangeDiffCommand::marshal_patch_change (constPatchChangePtr p)
|
||||
{
|
||||
XMLNode* n = new XMLNode ("patch-change");
|
||||
|
||||
{
|
||||
ostringstream s (ios::ate);
|
||||
s << int (p->id ());
|
||||
n->add_property ("id", s.str());
|
||||
}
|
||||
|
||||
{
|
||||
ostringstream s (ios::ate);
|
||||
s << p->time ();
|
||||
n->add_property ("time", s.str ());
|
||||
}
|
||||
|
||||
{
|
||||
ostringstream s (ios::ate);
|
||||
s << int (p->channel ());
|
||||
n->add_property ("channel", s.str ());
|
||||
}
|
||||
|
||||
{
|
||||
ostringstream s (ios::ate);
|
||||
s << int (p->program ());
|
||||
n->add_property ("program", s.str ());
|
||||
}
|
||||
|
||||
{
|
||||
ostringstream s (ios::ate);
|
||||
s << int (p->bank ());
|
||||
n->add_property ("bank", s.str ());
|
||||
}
|
||||
|
||||
return *n;
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
MidiModel::PatchChangeDiffCommand::marshal_change (const Change& c)
|
||||
{
|
||||
XMLNode* n = new XMLNode (X_("Change"));
|
||||
|
||||
n->add_property (X_("property"), enum_2_string (c.property));
|
||||
|
||||
{
|
||||
ostringstream s (ios::ate);
|
||||
if (c.property == Time) {
|
||||
s << c.old_time;
|
||||
} else if (c.property == Channel) {
|
||||
s << c.old_channel;
|
||||
} else if (c.property == Program) {
|
||||
s << int (c.old_program);
|
||||
} else if (c.property == Bank) {
|
||||
s << c.old_bank;
|
||||
}
|
||||
|
||||
n->add_property (X_("old"), s.str ());
|
||||
}
|
||||
|
||||
{
|
||||
ostringstream s (ios::ate);
|
||||
|
||||
if (c.property == Time) {
|
||||
s << c.new_time;
|
||||
} else if (c.property == Channel) {
|
||||
s << c.new_channel;
|
||||
} else if (c.property == Program) {
|
||||
s << int (c.new_program);
|
||||
} else if (c.property == Bank) {
|
||||
s << c.new_bank;
|
||||
}
|
||||
|
||||
n->add_property (X_("new"), s.str ());
|
||||
}
|
||||
|
||||
{
|
||||
ostringstream s;
|
||||
s << c.patch->id ();
|
||||
n->add_property ("id", s.str ());
|
||||
}
|
||||
|
||||
return *n;
|
||||
}
|
||||
|
||||
MidiModel::PatchChangePtr
|
||||
MidiModel::PatchChangeDiffCommand::unmarshal_patch_change (XMLNode* n)
|
||||
{
|
||||
XMLProperty* prop;
|
||||
Evoral::event_id_t id;
|
||||
Evoral::MusicalTime time = 0;
|
||||
uint8_t channel = 0;
|
||||
uint8_t program = 0;
|
||||
int bank = 0;
|
||||
|
||||
if ((prop = n->property ("id")) != 0) {
|
||||
istringstream s (prop->value());
|
||||
s >> id;
|
||||
}
|
||||
|
||||
if ((prop = n->property ("time")) != 0) {
|
||||
istringstream s (prop->value ());
|
||||
s >> time;
|
||||
}
|
||||
|
||||
if ((prop = n->property ("channel")) != 0) {
|
||||
istringstream s (prop->value ());
|
||||
s >> channel;
|
||||
}
|
||||
|
||||
if ((prop = n->property ("program")) != 0) {
|
||||
istringstream s (prop->value ());
|
||||
s >> program;
|
||||
}
|
||||
|
||||
if ((prop = n->property ("bank")) != 0) {
|
||||
istringstream s (prop->value ());
|
||||
s >> bank;
|
||||
}
|
||||
|
||||
PatchChangePtr p (new Evoral::PatchChange<TimeType> (time, channel, program, bank));
|
||||
p->set_id (id);
|
||||
return p;
|
||||
}
|
||||
|
||||
MidiModel::PatchChangeDiffCommand::Change
|
||||
MidiModel::PatchChangeDiffCommand::unmarshal_change (XMLNode* n)
|
||||
{
|
||||
XMLProperty* prop;
|
||||
Change c;
|
||||
|
||||
prop = n->property ("property");
|
||||
assert (prop);
|
||||
c.property = (Property) string_2_enum (prop->value(), c.property);
|
||||
|
||||
prop = n->property ("id");
|
||||
assert (prop);
|
||||
Evoral::event_id_t const id = atoi (prop->value().c_str());
|
||||
|
||||
prop = n->property ("old");
|
||||
assert (prop);
|
||||
{
|
||||
istringstream s (prop->value ());
|
||||
if (c.property == Time) {
|
||||
s >> c.old_time;
|
||||
} else if (c.property == Channel) {
|
||||
s >> c.old_channel;
|
||||
} else if (c.property == Program) {
|
||||
s >> c.old_program;
|
||||
} else if (c.property == Bank) {
|
||||
s >> c.old_bank;
|
||||
}
|
||||
}
|
||||
|
||||
prop = n->property ("new");
|
||||
assert (prop);
|
||||
{
|
||||
istringstream s (prop->value ());
|
||||
if (c.property == Time) {
|
||||
s >> c.new_time;
|
||||
} else if (c.property == Channel) {
|
||||
s >> c.new_channel;
|
||||
} else if (c.property == Program) {
|
||||
s >> c.new_program;
|
||||
} else if (c.property == Bank) {
|
||||
s >> c.new_bank;
|
||||
}
|
||||
}
|
||||
|
||||
c.patch = _model->find_patch_change (id);
|
||||
assert (c.patch);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int
|
||||
MidiModel::PatchChangeDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
|
||||
{
|
||||
if (diff_command.name() != PATCH_CHANGE_DIFF_COMMAND_ELEMENT) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
_added.clear ();
|
||||
XMLNode* added = diff_command.child (ADDED_PATCH_CHANGES_ELEMENT);
|
||||
if (added) {
|
||||
XMLNodeList p = added->children ();
|
||||
transform (p.begin(), p.end(), back_inserter (_added), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
|
||||
}
|
||||
|
||||
_removed.clear ();
|
||||
XMLNode* removed = diff_command.child (REMOVED_PATCH_CHANGES_ELEMENT);
|
||||
if (removed) {
|
||||
XMLNodeList p = removed->children ();
|
||||
transform (p.begin(), p.end(), back_inserter (_removed), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
|
||||
}
|
||||
|
||||
_changes.clear ();
|
||||
XMLNode* changed = diff_command.child (DIFF_PATCH_CHANGES_ELEMENT);
|
||||
if (changed) {
|
||||
XMLNodeList p = changed->children ();
|
||||
transform (p.begin(), p.end(), back_inserter (_changes), boost::bind (&PatchChangeDiffCommand::unmarshal_change, this, _1));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XMLNode &
|
||||
MidiModel::PatchChangeDiffCommand::get_state ()
|
||||
{
|
||||
XMLNode* diff_command = new XMLNode (PATCH_CHANGE_DIFF_COMMAND_ELEMENT);
|
||||
diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
|
||||
|
||||
XMLNode* added = diff_command->add_child (ADDED_PATCH_CHANGES_ELEMENT);
|
||||
for_each (_added.begin(), _added.end(),
|
||||
boost::bind (
|
||||
boost::bind (&XMLNode::add_child_nocopy, added, _1),
|
||||
boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
|
||||
)
|
||||
);
|
||||
|
||||
XMLNode* removed = diff_command->add_child (REMOVED_PATCH_CHANGES_ELEMENT);
|
||||
for_each (_removed.begin(), _removed.end(),
|
||||
boost::bind (
|
||||
boost::bind (&XMLNode::add_child_nocopy, removed, _1),
|
||||
boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
|
||||
)
|
||||
);
|
||||
|
||||
XMLNode* changes = diff_command->add_child (DIFF_PATCH_CHANGES_ELEMENT);
|
||||
for_each (_changes.begin(), _changes.end(),
|
||||
boost::bind (
|
||||
boost::bind (&XMLNode::add_child_nocopy, changes, _1),
|
||||
boost::bind (&PatchChangeDiffCommand::marshal_change, this, _1)
|
||||
)
|
||||
);
|
||||
|
||||
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
|
||||
|
@ -1084,6 +1507,18 @@ MidiModel::find_note (gint note_id)
|
|||
return NotePtr();
|
||||
}
|
||||
|
||||
MidiModel::PatchChangePtr
|
||||
MidiModel::find_patch_change (Evoral::event_id_t id)
|
||||
{
|
||||
for (PatchChanges::iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
|
||||
if ((*i)->id() == id) {
|
||||
return *i;
|
||||
}
|
||||
}
|
||||
|
||||
return PatchChangePtr ();
|
||||
}
|
||||
|
||||
boost::shared_ptr<Evoral::Event<MidiModel::TimeType> >
|
||||
MidiModel::find_sysex (gint sysex_id)
|
||||
{
|
||||
|
|
|
@ -3196,6 +3196,28 @@ Session::restore_history (string snapshot_name)
|
|||
error << _("Failed to downcast MidiSource for NoteDiffCommand") << endmsg;
|
||||
}
|
||||
|
||||
} else if (n->name() == "SysExDiffCommand") {
|
||||
|
||||
PBD::ID id (n->property("midi-source")->value());
|
||||
boost::shared_ptr<MidiSource> midi_source =
|
||||
boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
|
||||
if (midi_source) {
|
||||
ut->add_command (new MidiModel::SysExDiffCommand (midi_source->model(), *n));
|
||||
} else {
|
||||
error << _("Failed to downcast MidiSource for SysExDiffCommand") << endmsg;
|
||||
}
|
||||
|
||||
} else if (n->name() == "PatchChangeDiffCommand") {
|
||||
|
||||
PBD::ID id (n->property("midi-source")->value());
|
||||
boost::shared_ptr<MidiSource> midi_source =
|
||||
boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
|
||||
if (midi_source) {
|
||||
ut->add_command (new MidiModel::PatchChangeDiffCommand (midi_source->model(), *n));
|
||||
} else {
|
||||
error << _("Failed to downcast MidiSource for PatchChangeDiffCommand") << endmsg;
|
||||
}
|
||||
|
||||
} else if (n->name() == "StatefulDiffCommand") {
|
||||
if ((c = stateful_diff_command_factory (n))) {
|
||||
ut->add_command (c);
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "evoral/Parameter.hpp"
|
||||
#include "evoral/ControlSet.hpp"
|
||||
#include "evoral/ControlList.hpp"
|
||||
#include "evoral/PatchChange.hpp"
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
|
@ -101,7 +102,7 @@ public:
|
|||
void append(const Event<Time>& ev, Evoral::event_id_t evid);
|
||||
|
||||
inline size_t n_notes() const { return _notes.size(); }
|
||||
inline bool empty() const { return _notes.size() == 0 && ControlSet::controls_empty(); }
|
||||
inline bool empty() const { return _notes.empty() && _sysexes.empty() && _patch_changes.empty() && ControlSet::controls_empty(); }
|
||||
|
||||
inline static bool note_time_comparator(const boost::shared_ptr< const Note<Time> >& a,
|
||||
const boost::shared_ptr< const Note<Time> >& b) {
|
||||
|
@ -177,6 +178,19 @@ public:
|
|||
inline SysExes& sysexes() { return _sysexes; }
|
||||
inline const SysExes& sysexes() const { return _sysexes; }
|
||||
|
||||
typedef boost::shared_ptr<PatchChange<Time> > PatchChangePtr;
|
||||
typedef boost::shared_ptr<const PatchChange<Time> > constPatchChangePtr;
|
||||
|
||||
struct EarlierPatchChangeComparator {
|
||||
inline bool operator() (constPatchChangePtr a, constPatchChangePtr b) const {
|
||||
return a->time() < b->time();
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::multiset<PatchChangePtr, EarlierPatchChangeComparator> PatchChanges;
|
||||
inline PatchChanges& patch_changes () { return _patch_changes; }
|
||||
inline const PatchChanges& patch_changes () const { return _patch_changes; }
|
||||
|
||||
private:
|
||||
typedef std::priority_queue<NotePtr, std::deque<NotePtr>, LaterNoteEndComparator> ActiveNotes;
|
||||
public:
|
||||
|
@ -208,19 +222,24 @@ public:
|
|||
friend class Sequence<Time>;
|
||||
|
||||
typedef std::vector<ControlIterator> ControlIterators;
|
||||
enum MIDIMessageType { NIL, NOTE_ON, NOTE_OFF, CONTROL, SYSEX };
|
||||
enum MIDIMessageType { NIL, NOTE_ON, NOTE_OFF, CONTROL, SYSEX, PATCH_CHANGE };
|
||||
|
||||
const Sequence<Time>* _seq;
|
||||
boost::shared_ptr< Event<Time> > _event;
|
||||
mutable ActiveNotes _active_notes;
|
||||
MIDIMessageType _type;
|
||||
bool _is_end;
|
||||
typename Sequence::ReadLock _lock;
|
||||
typename Notes::const_iterator _note_iter;
|
||||
typename SysExes::const_iterator _sysex_iter;
|
||||
ControlIterators _control_iters;
|
||||
ControlIterators::iterator _control_iter;
|
||||
bool _force_discrete;
|
||||
const Sequence<Time>* _seq;
|
||||
boost::shared_ptr< Event<Time> > _event;
|
||||
mutable ActiveNotes _active_notes;
|
||||
/** If the iterator is pointing at a patch change, this is the index of the
|
||||
* sub-message within that change.
|
||||
*/
|
||||
int _active_patch_change_message;
|
||||
MIDIMessageType _type;
|
||||
bool _is_end;
|
||||
typename Sequence::ReadLock _lock;
|
||||
typename Notes::const_iterator _note_iter;
|
||||
typename SysExes::const_iterator _sysex_iter;
|
||||
typename PatchChanges::const_iterator _patch_change_iter;
|
||||
ControlIterators _control_iters;
|
||||
ControlIterators::iterator _control_iter;
|
||||
bool _force_discrete;
|
||||
};
|
||||
|
||||
const_iterator begin (
|
||||
|
@ -233,6 +252,7 @@ public:
|
|||
const const_iterator& end() const { return _end_iter; }
|
||||
|
||||
typename Notes::const_iterator note_lower_bound (Time t) const;
|
||||
typename PatchChanges::const_iterator patch_change_lower_bound (Time t) const;
|
||||
|
||||
bool control_to_midi_event(boost::shared_ptr< Event<Time> >& ev,
|
||||
const ControlIterator& iter) const;
|
||||
|
@ -247,6 +267,9 @@ public:
|
|||
bool add_note_unlocked (const NotePtr note, void* arg = 0);
|
||||
void remove_note_unlocked(const constNotePtr note);
|
||||
|
||||
void add_patch_change_unlocked (const PatchChangePtr);
|
||||
void remove_patch_change_unlocked (const constPatchChangePtr);
|
||||
|
||||
uint8_t lowest_note() const { return _lowest_note; }
|
||||
uint8_t highest_note() const { return _highest_note; }
|
||||
|
||||
|
@ -276,6 +299,7 @@ private:
|
|||
void append_note_off_unlocked(NotePtr);
|
||||
void append_control_unlocked(const Parameter& param, Time time, double value, Evoral::event_id_t);
|
||||
void append_sysex_unlocked(const MIDIEvent<Time>& ev, Evoral::event_id_t);
|
||||
void append_patch_change_unlocked (const PatchChange<Time>&, Evoral::event_id_t);
|
||||
|
||||
void get_notes_by_pitch (Notes&, NoteOperator, uint8_t val, int chan_mask = 0) const;
|
||||
void get_notes_by_velocity (Notes&, NoteOperator, uint8_t val, int chan_mask = 0) const;
|
||||
|
@ -284,13 +308,20 @@ private:
|
|||
|
||||
const TypeMap& _type_map;
|
||||
|
||||
Notes _notes; // notes indexed by time
|
||||
Pitches _pitches[16]; // notes indexed by channel+pitch
|
||||
SysExes _sysexes;
|
||||
Notes _notes; // notes indexed by time
|
||||
Pitches _pitches[16]; // notes indexed by channel+pitch
|
||||
SysExes _sysexes;
|
||||
PatchChanges _patch_changes;
|
||||
|
||||
typedef std::multiset<NotePtr, EarlierNoteComparator> WriteNotes;
|
||||
WriteNotes _write_notes[16];
|
||||
|
||||
/** Current bank number on each channel so that we know what
|
||||
* to put in PatchChange events when program changes are
|
||||
* seen.
|
||||
*/
|
||||
int _bank[16];
|
||||
|
||||
const const_iterator _end_iter;
|
||||
bool _percussive;
|
||||
|
||||
|
|
|
@ -281,10 +281,18 @@ SMF::append_event_delta(uint32_t delta_t, uint32_t size, const uint8_t* buf, eve
|
|||
|
||||
smf_event_t* event;
|
||||
|
||||
/* XXX july 2010: currently only store event ID's for notes
|
||||
/* XXX july 2010: currently only store event ID's for notes, program changes and bank changes
|
||||
*/
|
||||
|
||||
if (((buf[0] & 0xf0) == MIDI_CMD_NOTE_ON || ((buf[0] & 0xf0) == MIDI_CMD_NOTE_OFF)) && note_id >= 0) {
|
||||
uint8_t const c = buf[0] & 0xf0;
|
||||
bool const store_id = (
|
||||
c == MIDI_CMD_NOTE_ON ||
|
||||
c == MIDI_CMD_NOTE_OFF ||
|
||||
c == MIDI_CMD_PGM_CHANGE ||
|
||||
(c == MIDI_CMD_CONTROL && (buf[1] == MIDI_CTL_MSB_BANK || buf[1] == MIDI_CTL_LSB_BANK))
|
||||
);
|
||||
|
||||
if (store_id && note_id >= 0) {
|
||||
int idlen;
|
||||
int lenlen;
|
||||
uint8_t idbuf[16];
|
||||
|
@ -293,7 +301,6 @@ SMF::append_event_delta(uint32_t delta_t, uint32_t size, const uint8_t* buf, eve
|
|||
event = smf_event_new ();
|
||||
assert(event != NULL);
|
||||
|
||||
|
||||
/* generate VLQ representation of note ID */
|
||||
idlen = smf_format_vlq (idbuf, sizeof(idbuf), note_id);
|
||||
|
||||
|
|
|
@ -64,10 +64,12 @@ Sequence<Time>::const_iterator::const_iterator()
|
|||
template<typename Time>
|
||||
Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t, bool force_discrete, std::set<Evoral::Parameter> const & filtered)
|
||||
: _seq(&seq)
|
||||
, _active_patch_change_message (0)
|
||||
, _type(NIL)
|
||||
, _is_end((t == DBL_MAX) || seq.empty())
|
||||
, _note_iter(seq.notes().end())
|
||||
, _sysex_iter(seq.sysexes().end())
|
||||
, _patch_change_iter(seq.patch_changes().end())
|
||||
, _control_iter(_control_iters.end())
|
||||
, _force_discrete (force_discrete)
|
||||
{
|
||||
|
@ -94,6 +96,15 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t
|
|||
}
|
||||
assert(_sysex_iter == seq.sysexes().end() || (*_sysex_iter)->time() >= t);
|
||||
|
||||
// Find first patch event at or after t
|
||||
for (typename Sequence<Time>::PatchChanges::const_iterator i = seq.patch_changes().begin(); i != seq.patch_changes().end(); ++i) {
|
||||
if ((*i)->time() >= t) {
|
||||
_patch_change_iter = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert (_patch_change_iter == seq.patch_changes().end() || (*_patch_change_iter)->time() >= t);
|
||||
|
||||
// Find first control event after t
|
||||
ControlIterator earliest_control(boost::shared_ptr<ControlList>(), DBL_MAX, 0.0);
|
||||
_control_iters.reserve(seq._controls.size());
|
||||
|
@ -163,6 +174,11 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t
|
|||
earliest_t = (*_sysex_iter)->time();
|
||||
}
|
||||
|
||||
if (_patch_change_iter != seq.patch_changes().end() && ((*_patch_change_iter)->time() < earliest_t || _type == NIL)) {
|
||||
_type = PATCH_CHANGE;
|
||||
earliest_t = (*_patch_change_iter)->time ();
|
||||
}
|
||||
|
||||
if (_control_iter != _control_iters.end()
|
||||
&& earliest_control.list && earliest_control.x >= t
|
||||
&& (earliest_control.x < earliest_t || _type == NIL)) {
|
||||
|
@ -185,6 +201,10 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t
|
|||
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Starting at control event @ %1\n", earliest_t));
|
||||
seq.control_to_midi_event(_event, earliest_control);
|
||||
break;
|
||||
case PATCH_CHANGE:
|
||||
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Starting at patch change event @ %1\n", earliest_t));
|
||||
_event = boost::shared_ptr<Event<Time> > (new Event<Time> ((*_patch_change_iter)->message (_active_patch_change_message), true));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -194,12 +214,14 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t
|
|||
_type = NIL;
|
||||
_is_end = true;
|
||||
} else {
|
||||
DEBUG_TRACE (DEBUG::Sequence, string_compose ("New iterator = 0x%x : 0x%x @ %f\n",
|
||||
DEBUG_TRACE (DEBUG::Sequence, string_compose ("New iterator = 0x%1 : 0x%2 @ %3\n",
|
||||
(int)_event->event_type(),
|
||||
(int)((MIDIEvent<Time>*)_event.get())->type(),
|
||||
_event->time()));
|
||||
|
||||
assert(midi_event_is_valid(_event->buffer(), _event->size()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
|
@ -219,6 +241,8 @@ Sequence<Time>::const_iterator::invalidate()
|
|||
if (_seq) {
|
||||
_note_iter = _seq->notes().end();
|
||||
_sysex_iter = _seq->sysexes().end();
|
||||
_patch_change_iter = _seq->patch_changes().end();
|
||||
_active_patch_change_message = 0;
|
||||
}
|
||||
_control_iter = _control_iters.end();
|
||||
_lock.reset();
|
||||
|
@ -291,6 +315,13 @@ Sequence<Time>::const_iterator::operator++()
|
|||
case SYSEX:
|
||||
++_sysex_iter;
|
||||
break;
|
||||
case PATCH_CHANGE:
|
||||
++_active_patch_change_message;
|
||||
if (_active_patch_change_message == (*_patch_change_iter)->messages()) {
|
||||
++_patch_change_iter;
|
||||
_active_patch_change_message = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
@ -329,6 +360,14 @@ Sequence<Time>::const_iterator::operator++()
|
|||
}
|
||||
}
|
||||
|
||||
// Use the next earliest patch change iff it's earlier than the SysEx
|
||||
if (_patch_change_iter != _seq->patch_changes().end()) {
|
||||
if (_type == NIL || (*_patch_change_iter)->time() < earliest_t) {
|
||||
_type = PATCH_CHANGE;
|
||||
earliest_t = (*_patch_change_iter)->time();
|
||||
}
|
||||
}
|
||||
|
||||
// Set event to reflect new position
|
||||
switch (_type) {
|
||||
case NOTE_ON:
|
||||
|
@ -350,6 +389,10 @@ Sequence<Time>::const_iterator::operator++()
|
|||
DEBUG_TRACE(DEBUG::Sequence, "iterator = sysex\n");
|
||||
*_event = *(*_sysex_iter);
|
||||
break;
|
||||
case PATCH_CHANGE:
|
||||
DEBUG_TRACE(DEBUG::Sequence, "iterator = patch change\n");
|
||||
*_event = (*_patch_change_iter)->message (_active_patch_change_message);
|
||||
break;
|
||||
default:
|
||||
DEBUG_TRACE(DEBUG::Sequence, "iterator = end\n");
|
||||
_is_end = true;
|
||||
|
@ -386,8 +429,10 @@ Sequence<Time>::const_iterator::operator=(const const_iterator& other)
|
|||
_is_end = other._is_end;
|
||||
_note_iter = other._note_iter;
|
||||
_sysex_iter = other._sysex_iter;
|
||||
_patch_change_iter = other._patch_change_iter;
|
||||
_control_iters = other._control_iters;
|
||||
_force_discrete = other._force_discrete;
|
||||
_active_patch_change_message = other._active_patch_change_message;
|
||||
|
||||
if (other._lock)
|
||||
_lock = _seq->read_lock();
|
||||
|
@ -421,6 +466,10 @@ Sequence<Time>::Sequence(const TypeMap& type_map)
|
|||
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Sequence constructed: %1\n", this));
|
||||
assert(_end_iter._is_end);
|
||||
assert( ! _end_iter._lock);
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
_bank[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
|
@ -446,6 +495,15 @@ Sequence<Time>::Sequence(const Sequence<Time>& other)
|
|||
_sysexes.push_back (n);
|
||||
}
|
||||
|
||||
for (typename PatchChanges::const_iterator i = other._patch_changes.begin(); i != other._patch_changes.end(); ++i) {
|
||||
PatchChangePtr n (new PatchChange<Time> (**i));
|
||||
_patch_changes.insert (n);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
_bank[i] = other._bank[i];
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Sequence copied: %1\n", this));
|
||||
assert(_end_iter._is_end);
|
||||
assert(! _end_iter._lock);
|
||||
|
@ -697,6 +755,34 @@ Sequence<Time>::remove_note_unlocked(const constNotePtr note)
|
|||
}
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
void
|
||||
Sequence<Time>::add_patch_change_unlocked (PatchChangePtr p)
|
||||
{
|
||||
_patch_changes.insert (p);
|
||||
if (p->id () < 0) {
|
||||
p->set_id (Evoral::next_event_id ());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
void
|
||||
Sequence<Time>::remove_patch_change_unlocked (const constPatchChangePtr p)
|
||||
{
|
||||
typename Sequence<Time>::PatchChanges::iterator i = patch_change_lower_bound (p->time ());
|
||||
while (i != _patch_changes.end() && (*i)->time() == p->time()) {
|
||||
|
||||
typename Sequence<Time>::PatchChanges::iterator tmp = i;
|
||||
++tmp;
|
||||
|
||||
if (*i == p) {
|
||||
_patch_changes.erase (i);
|
||||
}
|
||||
|
||||
i = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/** Append \a ev to model. NOT realtime safe.
|
||||
*
|
||||
* The timestamp of event is expected to be relative to
|
||||
|
@ -730,14 +816,22 @@ Sequence<Time>::append(const Event<Time>& event, event_id_t evid)
|
|||
append_note_off_unlocked (note);
|
||||
} else if (ev.is_sysex()) {
|
||||
append_sysex_unlocked(ev, evid);
|
||||
} else if (ev.is_cc() && (ev.cc_number() == MIDI_CTL_MSB_BANK || ev.cc_number() == MIDI_CTL_LSB_BANK)) {
|
||||
/* note bank numbers in our _bank[] array, so that we can write an event when the program change arrives */
|
||||
if (ev.cc_number() == MIDI_CTL_MSB_BANK) {
|
||||
_bank[ev.channel()] &= (0x7f << 7);
|
||||
_bank[ev.channel()] |= ev.cc_value() << 7;
|
||||
} else {
|
||||
_bank[ev.channel()] &= 0x7f;
|
||||
_bank[ev.channel()] |= ev.cc_value();
|
||||
}
|
||||
} else if (ev.is_cc()) {
|
||||
append_control_unlocked(
|
||||
Evoral::MIDI::ContinuousController(ev.event_type(), ev.channel(), ev.cc_number()),
|
||||
ev.time(), ev.cc_value(), evid);
|
||||
} else if (ev.is_pgm_change()) {
|
||||
append_control_unlocked(
|
||||
Evoral::MIDI::ProgramChange(ev.event_type(), ev.channel()),
|
||||
ev.time(), ev.pgm_number(), evid);
|
||||
/* write a patch change with this program change and any previously set-up bank number */
|
||||
append_patch_change_unlocked (PatchChange<Time> (ev.time(), ev.channel(), ev.pgm_number(), _bank[ev.channel()]), evid);
|
||||
} else if (ev.is_pitch_bender()) {
|
||||
append_control_unlocked(
|
||||
Evoral::MIDI::PitchBender(ev.event_type(), ev.channel()),
|
||||
|
@ -873,6 +967,19 @@ Sequence<Time>::append_sysex_unlocked(const MIDIEvent<Time>& ev, event_id_t /* e
|
|||
_sysexes.push_back(event);
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
void
|
||||
Sequence<Time>::append_patch_change_unlocked (const PatchChange<Time>& ev, event_id_t id)
|
||||
{
|
||||
PatchChangePtr p (new PatchChange<Time> (ev));
|
||||
|
||||
if (p->id() < 0) {
|
||||
p->set_id (id);
|
||||
}
|
||||
|
||||
_patch_changes.insert (p);
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
bool
|
||||
Sequence<Time>::contains (const NotePtr& note) const
|
||||
|
@ -956,6 +1063,17 @@ Sequence<Time>::note_lower_bound (Time t) const
|
|||
return i;
|
||||
}
|
||||
|
||||
/** Return the earliest patch change with time >= t */
|
||||
template<typename Time>
|
||||
typename Sequence<Time>::PatchChanges::const_iterator
|
||||
Sequence<Time>::patch_change_lower_bound (Time t) const
|
||||
{
|
||||
PatchChangePtr search (new PatchChange<Time> (t, 0, 0, 0));
|
||||
typename Sequence<Time>::PatchChanges::const_iterator i = _patch_changes.lower_bound (search);
|
||||
assert (i == _patch_changes.end() || (*i)->time() >= t);
|
||||
return i;
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
void
|
||||
Sequence<Time>::get_notes (Notes& n, NoteOperator op, uint8_t val, int chan_mask) const
|
||||
|
|
Loading…
Reference in New Issue