summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/SConscript1
-rw-r--r--gtk2_ardour/about.cc1
-rw-r--r--gtk2_ardour/canvas-flag.cc41
-rw-r--r--gtk2_ardour/canvas-flag.h58
-rw-r--r--gtk2_ardour/canvas-program-change.cc38
-rw-r--r--gtk2_ardour/canvas-program-change.h16
-rw-r--r--gtk2_ardour/midi_region_view.cc129
-rw-r--r--gtk2_ardour/midi_region_view.h7
-rw-r--r--gtk2_ardour/midi_time_axis.cc48
-rw-r--r--gtk2_ardour/midi_time_axis.h15
-rw-r--r--libs/ardour/midi_model.cc7
-rw-r--r--libs/midi++2/midi++/midnam_patch.h1
12 files changed, 269 insertions, 93 deletions
diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript
index 89a5040400..f8089d099e 100644
--- a/gtk2_ardour/SConscript
+++ b/gtk2_ardour/SConscript
@@ -131,6 +131,7 @@ axis_view.cc
bundle_manager.cc
canvas-note-event.cc
canvas-note.cc
+canvas-flag.cc
canvas-program-change.cc
canvas-simpleline.c
canvas-simplerect.c
diff --git a/gtk2_ardour/about.cc b/gtk2_ardour/about.cc
index ae8c7f503b..7d41847e14 100644
--- a/gtk2_ardour/about.cc
+++ b/gtk2_ardour/about.cc
@@ -148,6 +148,7 @@ static const char* authors[] = {
N_("Christopher George"),
N_("Robert Jordens"),
N_("Dave Robillard"),
+ N_("Hans Baier"),
N_("Hans Fugal"),
N_("Brian Ahr"),
N_("Nimal Ratnayake"),
diff --git a/gtk2_ardour/canvas-flag.cc b/gtk2_ardour/canvas-flag.cc
new file mode 100644
index 0000000000..2692639da4
--- /dev/null
+++ b/gtk2_ardour/canvas-flag.cc
@@ -0,0 +1,41 @@
+#include "canvas-flag.h"
+#include <iostream>
+#include "ardour_ui.h"
+
+using namespace Gnome::Canvas;
+using namespace std;
+
+
+void
+CanvasFlag::set_text(string a_text)
+{
+ if (_text) {
+ delete _text;
+ }
+
+ _text = new Text(*this, 0.0, 0.0, a_text);
+ _text->property_justification() = Gtk::JUSTIFY_CENTER;
+ _text->property_fill_color_rgba() = _outline_color_rgba;
+ double flagwidth = _text->property_text_width() + 10.0;
+ double flagheight = _text->property_text_height() + 3.0;
+ _text->property_x() = flagwidth / 2.0;
+ _text->property_y() = flagheight / 2.0;
+ _text->show();
+ _line = new SimpleLine(*this, 0.0, 0.0, 0.0, _height);
+ _line->property_color_rgba() = _outline_color_rgba;
+ _rect = new SimpleRect(*this, 0.0, 0.0, flagwidth, flagheight);
+ _rect->property_outline_color_rgba() = _outline_color_rgba;
+ _rect->property_fill_color_rgba() = _fill_color_rgba;
+ _text->lower_to_bottom();
+ _text->raise(2);
+}
+
+CanvasFlag::~CanvasFlag()
+{
+ delete _line;
+ delete _rect;
+ if(_text) {
+ delete _text;
+ }
+}
+
diff --git a/gtk2_ardour/canvas-flag.h b/gtk2_ardour/canvas-flag.h
new file mode 100644
index 0000000000..ec150f685a
--- /dev/null
+++ b/gtk2_ardour/canvas-flag.h
@@ -0,0 +1,58 @@
+#ifndef CANVASFLAG_H_
+#define CANVASFLAG_H_
+
+#include <libgnomecanvasmm/group.h>
+#include <libgnomecanvasmm/text.h>
+#include <libgnomecanvasmm/widget.h>
+
+#include <ardour/midi_model.h>
+
+#include "simplerect.h"
+#include "simpleline.h"
+
+class MidiRegionView;
+
+namespace Gnome {
+namespace Canvas {
+
+class CanvasFlag : public Group
+{
+public:
+ CanvasFlag(
+ MidiRegionView& region,
+ Group& parent,
+ double height,
+ guint outline_color_rgba = 0xc0c0c0ff,
+ guint fill_color_rgba = 0x07070707,
+ double x = 0.0,
+ double y = 0.0
+ ) : Group(parent, x, y)
+ , _text(0)
+ , _height(height)
+ , _outline_color_rgba(outline_color_rgba)
+ , _fill_color_rgba(fill_color_rgba)
+ , _region(region)
+ , _line(0)
+ , _rect(0)
+ {}
+
+ virtual ~CanvasFlag();
+
+ void set_text(string a_text);
+
+protected:
+ Text* _text;
+ double _height;
+ guint _outline_color_rgba;
+ guint _fill_color_rgba;
+
+private:
+ MidiRegionView& _region;
+ SimpleLine* _line;
+ SimpleRect* _rect;
+};
+
+} // namespace Canvas
+} // namespace Gnome
+
+#endif /*CANVASFLAG_H_*/
diff --git a/gtk2_ardour/canvas-program-change.cc b/gtk2_ardour/canvas-program-change.cc
index bd9c0e89db..f48d2e6be9 100644
--- a/gtk2_ardour/canvas-program-change.cc
+++ b/gtk2_ardour/canvas-program-change.cc
@@ -8,40 +8,24 @@ using namespace std;
CanvasProgramChange::CanvasProgramChange(
MidiRegionView& region,
Group& parent,
- boost::shared_ptr<Evoral::Event> event,
+ string text,
double height,
double x,
double y)
- : Group(parent, x, y)
- , _region(region)
- , _event(event)
- , _text(0)
- , _line(0)
- , _rect(0)
+ : CanvasFlag(
+ region,
+ parent,
+ height,
+ ARDOUR_UI::config()->canvasvar_MidiProgramChangeOutline.get(),
+ ARDOUR_UI::config()->canvasvar_MidiProgramChangeFill.get(),
+ x,
+ y
+ )
{
- char pgm_str[4];
- snprintf(pgm_str, 4, "%d", (int)(((Evoral::MIDIEvent*)event.get())->pgm_number()));
- _text = new Text(*this, 0.0, 0.0, pgm_str);
- _text->property_justification() = Gtk::JUSTIFY_CENTER;
- _text->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiProgramChangeOutline.get();
- double flagwidth = _text->property_text_width() + 10.0;
- double flagheight = _text->property_text_height() + 3.0;
- _text->property_x() = flagwidth / 2.0;
- _text->property_y() = flagheight / 2.0;
- _text->show();
- _line = new SimpleLine(*this, 0.0, 0.0, 0.0, height);
- _line->property_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiProgramChangeOutline.get();
- _rect = new SimpleRect(*this, 0.0, 0.0, flagwidth, flagheight);
- _rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiProgramChangeOutline.get();
- _rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiProgramChangeFill.get();
- _text->lower_to_bottom();
- _text->raise(2);
+ set_text(text);
}
CanvasProgramChange::~CanvasProgramChange()
{
- delete _line;
- delete _rect;
- delete _text;
}
diff --git a/gtk2_ardour/canvas-program-change.h b/gtk2_ardour/canvas-program-change.h
index f2a912d506..18f7d9a9d7 100644
--- a/gtk2_ardour/canvas-program-change.h
+++ b/gtk2_ardour/canvas-program-change.h
@@ -1,25 +1,20 @@
#ifndef CANVASPROGRAMCHANGE_H_
#define CANVASPROGRAMCHANGE_H_
-#include <libgnomecanvasmm/group.h>
-#include "simplerect.h"
-#include "simpleline.h"
-#include <libgnomecanvasmm/text.h>
-#include <libgnomecanvasmm/widget.h>
-#include <ardour/midi_model.h>
+#include "canvas-flag.h"
class MidiRegionView;
namespace Gnome {
namespace Canvas {
-class CanvasProgramChange : public Group
+class CanvasProgramChange : public CanvasFlag
{
public:
CanvasProgramChange(
MidiRegionView& region,
Group& parent,
- boost::shared_ptr<Evoral::Event> event,
+ string text,
double height,
double x = 0.0,
double y = 0.0
@@ -28,11 +23,6 @@ public:
virtual ~CanvasProgramChange();
private:
- MidiRegionView& _region;
- boost::shared_ptr<Evoral::Event> _event;
- Text* _text;
- SimpleLine* _line;
- SimpleRect* _rect;
};
} // namespace Canvas
diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc
index 8fe67afdd3..a0e01c7546 100644
--- a/gtk2_ardour/midi_region_view.cc
+++ b/gtk2_ardour/midi_region_view.cc
@@ -33,6 +33,10 @@
#include <ardour/midi_source.h>
#include <ardour/midi_diskstream.h>
#include <ardour/midi_model.h>
+#include <ardour/midi_patch_manager.h>
+
+#include <evoral/Parameter.hpp>
+#include <evoral/Control.hpp>
#include "streamview.h"
#include "midi_region_view.h"
@@ -67,6 +71,8 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
, _default_note_length(0.0)
, _current_range_min(0)
, _current_range_max(0)
+ , _model_name(string())
+ , _custom_device_mode(string())
, _active_notes(0)
, _note_group(new ArdourCanvas::Group(*parent))
, _delta_command(NULL)
@@ -81,6 +87,8 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
, _force_channel(-1)
, _last_channel_selection(0xFFFF)
, _default_note_length(0.0)
+ , _model_name(string())
+ , _custom_device_mode(string())
, _active_notes(0)
, _note_group(new ArdourCanvas::Group(*parent))
, _delta_command(NULL)
@@ -97,6 +105,8 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other)
, _force_channel(-1)
, _last_channel_selection(0xFFFF)
, _default_note_length(0.0)
+ , _model_name(string())
+ , _custom_device_mode(string())
, _active_notes(0)
, _note_group(new ArdourCanvas::Group(*get_canvas_group()))
, _delta_command(NULL)
@@ -117,6 +127,8 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<M
, _force_channel(-1)
, _last_channel_selection(0xFFFF)
, _default_note_length(0.0)
+ , _model_name(string())
+ , _custom_device_mode(string())
, _active_notes(0)
, _note_group(new ArdourCanvas::Group(*get_canvas_group()))
, _delta_command(NULL)
@@ -175,6 +187,9 @@ MidiRegionView::init (Gdk::Color& basic_color, bool wfd)
midi_view()->signal_channel_mode_changed().connect(
mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
+
+ midi_view()->signal_midi_patch_settings_changed().connect(
+ mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
}
bool
@@ -546,7 +561,9 @@ MidiRegionView::redisplay_model()
clear_events();
_model->read_lock();
- /*MidiModel::Notes notes = _model->notes();
+
+ MidiModel::Notes notes = _model->notes();
+ /*
cerr << endl << _model->midi_source()->name() << " : redisplaying " << notes.size() << " notes:" << endl;
for (MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) {
cerr << "NOTE time: " << (*i)->time()
@@ -555,27 +572,14 @@ MidiRegionView::redisplay_model()
<< " end-time: " << (*i)->end_time()
<< " velocity: " << int((*i)->velocity())
<< endl;
- }*/
+ }
+ */
- for (size_t i = 0; i < _model->n_notes(); ++i)
+ for (size_t i = 0; i < _model->n_notes(); ++i) {
add_note(_model->note_at(i));
-
- // Draw program change 'flags'
- for (Automatable::Controls::iterator control = _model->controls().begin();
- control != _model->controls().end(); ++control) {
- if (control->first.type() == MidiPgmChangeAutomation) {
- Glib::Mutex::Lock list_lock (control->second->list()->lock());
-
- for (AutomationList::const_iterator event = control->second->list()->begin();
- event != control->second->list()->end(); ++event) {
- Evoral::ControlIterator iter(control->second->list(), (*event)->when, (*event)->value);
- boost::shared_ptr<Evoral::Event> event(new Evoral::Event());
- _model->control_to_midi_event(event, iter);
- add_pgm_change(event);
- }
- break;
- }
}
+
+ find_and_insert_program_chage_flags();
// Is this necessary?
/*for (Automatable::Controls::const_iterator i = _model->controls().begin();
@@ -606,7 +610,6 @@ MidiRegionView::redisplay_model()
_automation_children.insert(std::make_pair(i->second->parameter(), arv));
}*/
-
_model->read_unlock();
} else {
@@ -614,6 +617,74 @@ MidiRegionView::redisplay_model()
}
}
+void
+MidiRegionView::find_and_insert_program_change_flags()
+{
+ // Draw program change 'flags'
+ for (Automatable::Controls::iterator control = _model->controls().begin();
+ control != _model->controls().end(); ++control) {
+ if (control->first.type() == MidiPgmChangeAutomation) {
+ Glib::Mutex::Lock list_lock (control->second->list()->lock());
+
+ uint8_t channel = control->first.channel();
+
+ for (AutomationList::const_iterator event = control->second->list()->begin();
+ event != control->second->list()->end(); ++event) {
+ double event_time = (*event)->when;
+ double program_number = (*event)->value;
+
+ //cerr << " got program change on channel " << int(channel) << " time: " << event_time << " number: " << program_number << endl;
+
+ // find bank select msb and lsb for the program change
+ Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
+ Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
+
+ boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
+ boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
+
+ boost::shared_ptr<MIDI::Name::MasterDeviceNames> master_device
+ = MIDI::Name::MidiPatchManager::instance().master_device_by_model(_model_name);
+
+ MIDI::Name::Patch patch;
+
+ if (master_device != 0 && _custom_device_mode != "") {
+ uint8_t msb = 0;
+ if (msb_control != 0) {
+ msb = uint8_t(msb_control->get_float(true, event_time));
+ }
+
+ uint8_t lsb = 0;
+ if (lsb_control != 0) {
+ lsb = uint8_t(lsb_control->get_float(true, event_time));
+ }
+
+ //cerr << " got msb " << int(msb) << " and lsb " << int(lsb) << endl;
+
+ patch = master_device->find_patch(
+ _custom_device_mode,
+ channel,
+ msb,
+ lsb,
+ uint8_t(program_number)
+ );
+
+ //cerr << " got patch with name " << patch.name() << " number " << patch.number() << endl;
+ }
+ if (patch.name() != "") {
+ add_pgm_change(event_time, patch.name());
+ } else {
+ char buf[4];
+ snprintf(buf, 4, "%d", int(program_number));
+ add_pgm_change(event_time, buf);
+ }
+ }
+ break;
+ } else if (control->first.type() == MidiCCAutomation) {
+ //cerr << " found CC Automation of channel " << int(control->first.channel()) << " and id " << control->first.id() << endl;
+ }
+ }
+}
+
MidiRegionView::~MidiRegionView ()
{
@@ -925,21 +996,21 @@ MidiRegionView::add_note(const boost::shared_ptr<Evoral::Note> note)
}
void
-MidiRegionView::add_pgm_change(boost::shared_ptr<Evoral::Event> event)
+MidiRegionView::add_pgm_change(nframes_t time, string displaytext)
{
- assert(event->time() >= 0);
+ assert(time >= 0);
// dont display notes beyond the region bounds
- if (event->time() - _region->start() >= _region->length() || event->time() < _region->start())
+ if (time - _region->start() >= _region->length() || time < _region->start())
return;
ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
- const double x = trackview.editor.frame_to_pixel((nframes64_t)event->time() - _region->start());
+ const double x = trackview.editor.frame_to_pixel((nframes64_t)time - _region->start());
double height = midi_stream_view()->contents_height();
_pgm_changes.push_back(
boost::shared_ptr<CanvasProgramChange>(
- new CanvasProgramChange(*this, *group, event, height, x, 1.0)));
+ new CanvasProgramChange(*this, *group, displaytext, height, x, 1.0)));
}
void
@@ -1438,3 +1509,11 @@ MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
_last_channel_selection = mask;
}
+void
+MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
+{
+ _model_name = model;
+ _custom_device_mode = custom_device_mode;
+ redisplay_model();
+}
+
diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h
index 6010e2253f..c76775f9f5 100644
--- a/gtk2_ardour/midi_region_view.h
+++ b/gtk2_ardour/midi_region_view.h
@@ -86,7 +86,8 @@ class MidiRegionView : public RegionView
void add_note(const boost::shared_ptr<Evoral::Note> note);
void resolve_note(uint8_t note_num, double end_time);
- void add_pgm_change(boost::shared_ptr<Evoral::Event> event);
+ void add_pgm_change(nframes_t time, string displaytext);
+ void find_and_insert_program_change_flags();
void begin_write();
void end_write();
@@ -219,6 +220,7 @@ class MidiRegionView : public RegionView
bool note_canvas_event(GdkEvent* ev);
void midi_channel_mode_changed(ARDOUR::ChannelMode mode, uint16_t mask);
+ void midi_patch_settings_changed(std::string model, std::string custom_device_mode);
void clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev);
void clear_selection() { clear_selection_except(NULL); }
@@ -229,6 +231,9 @@ class MidiRegionView : public RegionView
double _default_note_length;
uint8_t _current_range_min;
uint8_t _current_range_max;
+
+ string _model_name;
+ string _custom_device_mode;
typedef std::vector<ArdourCanvas::CanvasNoteEvent*> Events;
typedef std::vector< boost::shared_ptr<ArdourCanvas::CanvasProgramChange> > PgmChanges;
diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc
index ab0f0279f1..a8f6e3a6f4 100644
--- a/gtk2_ardour/midi_time_axis.cc
+++ b/gtk2_ardour/midi_time_axis.cc
@@ -141,36 +141,28 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session& sess, boost::shar
}
HBox* midi_controls_hbox = manage(new HBox());
-
- // Instrument patch selector
- ComboBoxText* model_selector = manage(new ComboBoxText());
- ComboBoxText* custom_device_mode_selector = manage(new ComboBoxText());
-
+
MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
for (MIDI::Name::MasterDeviceNames::Models::const_iterator model = patch_manager.all_models().begin();
model != patch_manager.all_models().end();
++model) {
- model_selector->append_text(model->c_str());
+ _model_selector.append_text(model->c_str());
}
- // TODO: persist the choice
- model_selector->set_active(0);
+ _model_selector.signal_changed().connect(mem_fun(*this, &MidiTimeAxisView::model_changed));
- std::list<std::string> device_modes = patch_manager.custom_device_mode_names_by_model(model_selector->get_active_text());
+ _custom_device_mode_selector.signal_changed().connect(
+ mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
- for (std::list<std::string>::const_iterator i = device_modes.begin(); i != device_modes.end(); ++i) {
- cerr << "found custom device mode " << *i << endl;
- custom_device_mode_selector->append_text(*i);
- }
-
// TODO: persist the choice
- custom_device_mode_selector->set_active(0);
-
+ // this initializes the comboboxes and sends out the signal
+ _model_selector.set_active(0);
+
midi_controls_hbox->pack_start(_channel_selector, true, false);
if (!patch_manager.all_models().empty()) {
- _midi_controls_box.pack_start(*model_selector, true, false);
- _midi_controls_box.pack_start(*custom_device_mode_selector, true, false);
+ _midi_controls_box.pack_start(_model_selector, true, false);
+ _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
}
_midi_controls_box.pack_start(*midi_controls_hbox, true, true);
@@ -195,6 +187,26 @@ MidiTimeAxisView::~MidiTimeAxisView ()
_range_scroomer = 0;
}
+void MidiTimeAxisView::model_changed()
+{
+ std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
+ .custom_device_mode_names_by_model(_model_selector.get_active_text());
+
+ _custom_device_mode_selector.clear_items();
+
+ for (std::list<std::string>::const_iterator i = device_modes.begin(); i != device_modes.end(); ++i) {
+ cerr << "found custom device mode " << *i << endl;
+ _custom_device_mode_selector.append_text(*i);
+ }
+
+ _custom_device_mode_selector.set_active(0);
+}
+
+void MidiTimeAxisView::custom_device_mode_changed()
+{
+ _midi_patch_settings_changed.emit(_model_selector.get_active_text(), _custom_device_mode_selector.get_active_text());
+}
+
MidiStreamView*
MidiTimeAxisView::midi_view()
{
diff --git a/gtk2_ardour/midi_time_axis.h b/gtk2_ardour/midi_time_axis.h
index a09cb5e5f1..8f95697e2d 100644
--- a/gtk2_ardour/midi_time_axis.h
+++ b/gtk2_ardour/midi_time_axis.h
@@ -77,10 +77,19 @@ class MidiTimeAxisView : public RouteTimeAxisView
void update_range();
- sigc::signal<void, ARDOUR::ChannelMode, uint16_t>& signal_channel_mode_changed()
- { return _channel_selector.mode_changed; }
+ sigc::signal<void, ARDOUR::ChannelMode, uint16_t>& signal_channel_mode_changed() {
+ return _channel_selector.mode_changed;
+ }
+
+ sigc::signal<void, string, string>& signal_midi_patch_settings_changed() {
+ return _midi_patch_settings_changed;
+ }
private:
+ sigc::signal<void, string, string> _midi_patch_settings_changed;
+
+ void model_changed();
+ void custom_device_mode_changed();
void append_extra_display_menu_items ();
void build_automation_action_menu ();
@@ -102,6 +111,8 @@ class MidiTimeAxisView : public RouteTimeAxisView
Gtk::RadioMenuItem* _percussion_mode_item;
Gtk::VBox _midi_controls_box;
MidiMultipleChannelSelector _channel_selector;
+ Gtk::ComboBoxText _model_selector;
+ Gtk::ComboBoxText _custom_device_mode_selector;
};
#endif /* __ardour_midi_time_axis_h__ */
diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc
index 539881e51c..c1d53a2553 100644
--- a/libs/ardour/midi_model.cc
+++ b/libs/ardour/midi_model.cc
@@ -278,13 +278,6 @@ XMLNode& MidiModel::DeltaCommand::get_state()
return *delta_command;
}
-struct EventTimeComparator {
- typedef const Evoral::Event* value_type;
- inline bool operator()(const Evoral::Event& a, const Evoral::Event& b) const {
- return a.time() >= b.time();
- }
-};
-
/** Write 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
diff --git a/libs/midi++2/midi++/midnam_patch.h b/libs/midi++2/midi++/midnam_patch.h
index b025268c45..74ab0f701d 100644
--- a/libs/midi++2/midi++/midnam_patch.h
+++ b/libs/midi++2/midi++/midnam_patch.h
@@ -147,6 +147,7 @@ public:
Patch& find_patch(uint8_t msb, uint8_t lsb, uint8_t program_number) {
PatchPrimaryKey key(msb, lsb, program_number);
+ assert(key.is_sane());
return _patch_map[key];
}