Compare commits

...

20 commits

Author SHA1 Message Date
Paul Davis
fd0e9e840e when opening a pianoroll for a new empty region, take the note mode from the track 2025-12-01 18:10:19 -07:00
Paul Davis
38368f4dd9 save and restore pianoroll note mode on a per-region basis 2025-12-01 18:10:19 -07:00
Paul Davis
54958f1e94 remove debug output 2025-12-01 18:10:19 -07:00
Paul Davis
36d6200631 remove some debug output associated with loading region UI settings 2025-12-01 18:10:19 -07:00
Robin Gareus
7635707ac2
Disable debug-print in MIDI randomize script 2025-12-02 00:54:42 +01:00
Paul Davis
fca85e01c9 partially revert cf6eff4d9 upon realizing that a lot of the changes were not required
Hiding the overlay text when the trigger rec-enable state changes is sufficient
2025-12-01 15:32:47 -07:00
Paul Davis
cf6eff4d9f ensure count-in for clip recording vanishes at transport stop/rec-disable 2025-12-01 15:17:26 -07:00
Paul Davis
c5c04f38cd audio clip editors: ensure countdown digits are not scrolled out of sight 2025-12-01 15:17:26 -07:00
Paul Davis
04a1ed3c48 pianoroll: ensure countdown digits are not scrolled out of sight 2025-12-01 15:17:26 -07:00
Robin Gareus
21b2985c47
Tweak Stip Import table layout 2025-12-01 22:19:12 +01:00
Robin Gareus
5833fc08f2
Best keep Mixbus state in mixbus-git 2025-12-01 21:23:00 +01:00
Nicolas Koch
34850e514d MCP - Fix iCON P1-M check for RGB update on next redisplay 2025-12-01 13:10:02 -07:00
Nicolas Koch
2db87934dd MCP: iCON P1-M/P1-NANO/V1-M only - Make RGB blink to 20% brightness when track/bus selected 2025-12-01 13:10:02 -07:00
Nicolas Koch
e39eebee57 change iCON RGB color to use newly added p1m device logic rather than erroneous platformMp 2025-12-01 13:10:02 -07:00
Nicolas Koch
669e689f8c MCP - Add iCON Pro Audio P1-M and correct Platform M+ - I forgot to rename to P1-M on previous commit 2025-12-01 13:10:02 -07:00
Nicolas Koch
f71b519c56 MCP - Add RGB color display to iCON P1-M, P-NANO and V1-M 2025-12-01 13:10:02 -07:00
Nicolas Koch
3e2c73258c MCP updates:
* Add iCON Pro Audio P1-NANO device with option of P1-X extenders left or right
* Resolve Metering on P1-M/X and P1-NANO
* Resolve SMPTE/beats display on P1-M/X and P1-NANO
2025-12-01 13:10:02 -07:00
Robin Gareus
4db08ba34f
Fix Mixbus builds (2/2) 2025-12-01 20:53:25 +01:00
Robin Gareus
293a359d10
Fix Mixbus builds (1/2) 2025-12-01 20:53:22 +01:00
Paul Davis
f996808962 triggerbox: fix error during 794888738 that caused crashes when editing MIDI clip boundaries 2025-12-01 12:39:32 -07:00
26 changed files with 372 additions and 46 deletions

View file

@ -702,7 +702,7 @@ AudioClipEditor::set_overlay_text (std::string const & str)
EC_LOCAL_TEMPO_SCOPE;
if (!overlay_text) {
overlay_text = new ArdourCanvas::Text (data_group);
overlay_text = new ArdourCanvas::Text (no_scroll_group);
Pango::FontDescription font ("Sans 200");
overlay_text->set_font_description (font);
overlay_text->set_color (0xff000088);
@ -894,9 +894,12 @@ AudioClipEditor::snap_mode_chosen (Editing::SnapMode)
void
AudioClipEditor::grid_type_chosen (Editing::GridType gt)
{
EC_LOCAL_TEMPO_SCOPE;
if (gt != Editing::GridTypeMinSec && grid_actions[gt] && grid_actions[gt]->get_active()) {
assert (grid_actions[Editing::GridTypeMinSec]);
grid_actions[Editing::GridTypeMinSec]->set_active (false);
grid_actions[Editing::GridTypeMinSec]->set_active (true);
}
}

View file

@ -707,6 +707,7 @@ CueEditor::rec_enable_change ()
break;
case Disabled:
rec_enable_button.set_active_state (Gtkmm2ext::Off);
hide_count_in ();
break;
}
}
@ -1447,6 +1448,7 @@ CueEditor::maybe_set_count_in ()
}
if (ref.box()->record_enabled() == Disabled) {
hide_count_in ();
return;
}

View file

@ -467,6 +467,10 @@ EditingContext::register_common_actions (Bindings* common_bindings, std::string
reg_sens (_common_actions, "editor-copy", _("Copy"), sigc::mem_fun (*this, &EditingContext::copy));
reg_sens (_common_actions, "editor-paste", _("Paste"), sigc::mem_fun (*this, &EditingContext::keyboard_paste));
RadioAction::Group note_mode_group;
note_mode_actions[ARDOUR::Sustained] = ActionManager::register_radio_action (_common_actions, note_mode_group, "set-note-mode-sustained", _("Sustained"), sigc::bind (sigc::mem_fun (*this, &EditingContext::note_mode_chosen), ARDOUR::Sustained));
note_mode_actions[ARDOUR::Percussive] = ActionManager::register_radio_action (_common_actions, note_mode_group, "set-note-mode-percussivee", _("Percussive"), sigc::bind (sigc::mem_fun (*this, &EditingContext::note_mode_chosen), ARDOUR::Percussive));
RadioAction::Group mouse_mode_group;
mouse_mode_actions[Editing::MouseObject] = ActionManager::register_radio_action (_common_actions, mouse_mode_group, "set-mouse-mode-object", _("Grab (Object Tool)"), sigc::bind (sigc::mem_fun (*this, &EditingContext::mouse_mode_chosen), Editing::MouseObject));

View file

@ -297,10 +297,12 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider,
void set_draw_length (Editing::GridType);
void set_draw_velocity (int);
void set_draw_channel (int);
virtual void set_note_mode (ARDOUR::NoteMode) {}
Editing::GridType draw_length () const;
int draw_velocity () const;
int draw_channel () const;
virtual ARDOUR::NoteMode note_mode() const { return ARDOUR::Sustained; }
Editing::SnapMode snap_mode () const;
@ -548,6 +550,7 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider,
std::map<Editing::SnapMode, Glib::RefPtr<Gtk::RadioAction> > snap_mode_actions;
std::map<Editing::GridType, Glib::RefPtr<Gtk::RadioAction> > draw_length_actions;
std::map<Editing::MouseMode, Glib::RefPtr<Gtk::RadioAction> > mouse_mode_actions;
std::map<ARDOUR::NoteMode, Glib::RefPtr<Gtk::RadioAction> > note_mode_actions;
std::map<Editing::ZoomFocus, Glib::RefPtr<Gtk::RadioAction> > zoom_focus_actions;
std::map<int, Glib::RefPtr<Gtk::RadioAction> > draw_velocity_actions;
std::map<int, Glib::RefPtr<Gtk::RadioAction> > draw_channel_actions;
@ -555,6 +558,7 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider,
void draw_channel_chosen (int);
void draw_velocity_chosen (int);
void draw_length_chosen (Editing::GridType);
virtual void note_mode_chosen (ARDOUR::NoteMode) {}
sigc::signal<void> DrawLengthChanged;
sigc::signal<void> DrawVelocityChanged;

View file

@ -74,7 +74,6 @@ Pianoroll::Pianoroll (std::string const & name, bool with_transport)
, bg (nullptr)
, view (nullptr)
, bbt_metric (*this)
, _note_mode (Sustained)
, ignore_channel_changes (false)
{
autoscroll_vertical_allowed = false;
@ -558,7 +557,6 @@ Pianoroll::maybe_update ()
}
if (_track->triggerbox()->record_enabled() == Recording) {
_playhead_cursor->set_position (data_capture_duration);
}
@ -1353,7 +1351,6 @@ Pianoroll::trigger_prop_change (PBD::PropertyChange const & what_changed)
EC_LOCAL_TEMPO_SCOPE;
if (what_changed.contains (Properties::region)) {
std::cerr << "PR region changed\n";
std::shared_ptr<MidiRegion> mr = std::dynamic_pointer_cast<MidiRegion> (ref.trigger()->the_region());
set_region (mr);
}
@ -1478,6 +1475,13 @@ Pianoroll::set_region (std::shared_ptr<ARDOUR::Region> region)
bg->display_region (*view);
maybe_set_from_rsu ();
if (r->source()->empty()) {
std::shared_ptr<MidiTrack> mt (std::dynamic_pointer_cast<ARDOUR::MidiTrack> (_track));
if (mt) {
note_mode_actions[mt->note_mode()]->set_active (true);
}
}
}
bool
@ -1589,6 +1593,40 @@ Pianoroll::automation_state_changed ()
}
}
ARDOUR::NoteMode
Pianoroll::note_mode () const
{
return bg->note_mode();
}
void
Pianoroll::note_mode_chosen (ARDOUR::NoteMode mode)
{
EC_LOCAL_TEMPO_SCOPE;
/* this is driven by a toggle on a radio group, and so is invoked twice,
once for the item that became inactive and once for the one that became
active.
*/
Glib::RefPtr<Gtk::RadioAction> ract = note_mode_actions[mode];
if (!ract->get_active()) {
return;
}
if (mode != bg->note_mode()) {
bg->set_note_mode (mode);
if (bg->note_mode() == Percussive) {
note_mode_button.set_active_state (Gtkmm2ext::ExplicitActive);
} else {
note_mode_button.set_active_state (Gtkmm2ext::Off);
}
}
instant_save ();
}
void
Pianoroll::note_mode_clicked ()
{
@ -1596,27 +1634,11 @@ Pianoroll::note_mode_clicked ()
assert (bg);
if (bg->note_mode() == Sustained) {
set_note_mode (Percussive);
note_mode_actions[Percussive]->set_active (true);
} else {
set_note_mode (Sustained);
}
}
void
Pianoroll::set_note_mode (NoteMode nm)
{
EC_LOCAL_TEMPO_SCOPE;
assert (bg);
if (nm != bg->note_mode()) {
bg->set_note_mode (nm);
if (bg->note_mode() == Percussive) {
note_mode_button.set_active_state (Gtkmm2ext::ExplicitActive);
} else {
note_mode_button.set_active_state (Gtkmm2ext::Off);
}
note_mode_actions[Sustained]->set_active (true);
}
}
@ -1870,6 +1892,8 @@ Pianoroll::map_transport_state ()
} else {
loop_button.set_active (false);
}
hide_count_in ();
}
}
@ -2037,6 +2061,13 @@ Pianoroll::hide_count_in ()
}
}
void
Pianoroll::set_from_rsu (RegionUISettings& region_ui_settings)
{
note_mode_actions[region_ui_settings.note_mode]->set_active (true);
CueEditor::set_from_rsu (region_ui_settings);
}
void
Pianoroll::instant_save ()
{
@ -2047,6 +2078,7 @@ Pianoroll::instant_save ()
region_ui_settings.channel = draw_channel();
region_ui_settings.note_min = bg->lowest_note ();
region_ui_settings.note_max = bg->highest_note();
region_ui_settings.note_mode = note_mode ();
CueEditor::instant_save ();
}

View file

@ -100,8 +100,8 @@ class Pianoroll : public CueEditor
int visible_channel () const { return _visible_channel; }
void note_mode_clicked();
ARDOUR::NoteMode note_mode() const { return _note_mode; }
void set_note_mode (ARDOUR::NoteMode);
ARDOUR::NoteMode note_mode() const;
void note_mode_chosen (ARDOUR::NoteMode);
void set_trigger_start (Temporal::timepos_t const &);
void set_trigger_end (Temporal::timepos_t const &);
@ -213,7 +213,6 @@ class Pianoroll : public CueEditor
int _visible_channel;
ARDOUR::NoteMode _note_mode;
sigc::signal<void> NoteModeChanged;
void automation_state_changed ();
@ -253,4 +252,5 @@ class Pianoroll : public CueEditor
void instant_save ();
void parameter_changed (std::string param);
void set_from_rsu (RegionUISettings&);
};

View file

@ -55,6 +55,7 @@ PianorollMidiView::PianorollMidiView (std::shared_ptr<ARDOUR::MidiTrack> mt,
MidiViewBackground& bg,
uint32_t basic_color)
: MidiView (mt, parent, ec, bg, basic_color)
, _noscroll_parent (&noscroll_parent)
, overlay_text (nullptr)
, active_automation (nullptr)
, velocity_display (nullptr)
@ -748,7 +749,7 @@ void
PianorollMidiView::set_overlay_text (std::string const & str)
{
if (!overlay_text) {
overlay_text = new ArdourCanvas::Text (_note_group->parent());
overlay_text = new ArdourCanvas::Text (_noscroll_parent);
Pango::FontDescription font ("Sans 200");
overlay_text->set_font_description (font);
overlay_text->set_color (0xff000088);

View file

@ -95,6 +95,7 @@ class PianorollMidiView : public MidiView
protected:
bool scroll (GdkEventScroll* ev);
ArdourCanvas::Item* _noscroll_parent;
ArdourCanvas::Rectangle* automation_group;
ArdourCanvas::Text* overlay_text;

View file

@ -40,6 +40,7 @@ RegionUISettings::RegionUISettings ()
, snap_mode (Editing::SnapMagnetic)
, zoom_focus (ZoomFocusLeft)
, mouse_mode (MouseContent)
, note_mode (ARDOUR::Sustained)
, x_origin (0)
, recording_length (1, 0, 0)
, width (-1)
@ -65,6 +66,7 @@ RegionUISettings::get_state () const
node->set_property (X_("snap-mode"), snap_mode);
node->set_property (X_("zoom-focus"), zoom_focus);
node->set_property (X_("mouse-mode"), mouse_mode);
node->set_property (X_("note-mode"), note_mode);
node->set_property (X_("x-origin"), x_origin);
node->set_property (X_("recording_length"), recording_length);
@ -95,6 +97,7 @@ RegionUISettings::set_state (XMLNode const & state, int)
state.get_property (X_("snap-mode"), snap_mode);
state.get_property (X_("zoom-focus"), zoom_focus);
state.get_property (X_("mouse-mode"), mouse_mode);
state.get_property (X_("note-mode"), note_mode);
state.get_property (X_("x-origin"), x_origin);
state.get_property (X_("recording_length"), recording_length);
@ -171,15 +174,12 @@ RegionUISettingsManager::load (std::string const & xmlpath)
}
if (!state_tree.read (xmlpath)) {
std::cerr << "bad xmlpath " << xmlpath << std::endl;
return -1;
}
std::cerr << "loading " << xmlpath << std::endl;
XMLNode const & root (*state_tree.root());
if (root.name() != X_("RegionUISettings")) {
std::cerr << "bad root\n";
return -1;
}
@ -188,8 +188,6 @@ RegionUISettingsManager::load (std::string const & xmlpath)
PBD::ID id;
node->get_property ("id", id);
std::cerr << "loaded RSU for " << id << std::endl;
if (rsu.set_state (*node, 0) == 0) {
insert (std::make_pair (id, rsu));
}

View file

@ -39,6 +39,7 @@ struct RegionUISettings
Editing::SnapMode snap_mode;
Editing::ZoomFocus zoom_focus;
Editing::MouseMode mouse_mode;
ARDOUR::NoteMode note_mode;
Temporal::timepos_t x_origin;
Temporal::BBT_Offset recording_length;
int width;

View file

@ -497,8 +497,9 @@ StripImportDialog::refill_import_table ()
}
ArdourButton* rm = manage (new ArdourButton ());
rm->set_icon (ArdourIcon::CloseCross);
rm->set_tweaks (ArdourButton::TrackHeader);
rm->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &StripImportDialog::remove_mapping), rid));
_strip_table.attach (*rm, 3, 4, r, r + 1, Gtk::SHRINK, Gtk::SHRINK, 4, 2);
_strip_table.attach (*rm, 3, 4, r, r + 1, Gtk::SHRINK, Gtk::SHRINK, 4, 0);
}
if (r > 1) {
@ -554,12 +555,13 @@ StripImportDialog::refill_import_table ()
_add_new_mapping = manage (new ArdourButton ());
_add_new_mapping->set_icon (ArdourIcon::PlusSign);
_add_new_mapping->set_tweaks (ArdourButton::TrackHeader);
_add_new_mapping->signal_clicked.connect (sigc::mem_fun (*this, &StripImportDialog::add_mapping));
/* clang-format off */
_strip_table.attach (*_add_rid_dropdown, 0, 1, r, r + 1, EXPAND | FILL, SHRINK);
_strip_table.attach (*_add_eid_dropdown, 2, 3, r, r + 1, EXPAND | FILL, SHRINK);
_strip_table.attach (*_add_new_mapping, 3, 4, r, r + 1, Gtk::SHRINK, SHRINK);
_strip_table.attach (*_add_new_mapping, 3, 4, r, r + 1, Gtk::SHRINK, SHRINK, 4, 0);
/* clang-format on */
bool can_add = !_add_rid_dropdown->items ().empty () && !_add_eid_dropdown->items ().empty ();
@ -698,7 +700,7 @@ StripImportDialog::setup_strip_import_page ()
for (auto const& r : *_session->get_routes ()) {
#ifdef MIXBUS
_route_map.emplace (r->id (), Session::RouteImportInfo (r->name (), r->presentation_info (), c->mixbus ()));
_route_map.emplace (r->id (), Session::RouteImportInfo (r->name (), r->presentation_info (), r->mixbus ()));
#else
_route_map.emplace (r->id (), Session::RouteImportInfo (r->name (), r->presentation_info (), 0));
#endif

View file

@ -729,7 +729,7 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
struct MIDIPendingSwap : public PendingSwap {
RTMidiBufferBeats* rt_midibuffer;
MIDIPendingSwap() : rt_midibuffer (nullptr) {}
MIDIPendingSwap();
~MIDIPendingSwap() { delete rt_midibuffer; }
};

View file

@ -1274,12 +1274,7 @@ Session::parse_route_state (const string& path, bool& match_pbd_id)
continue;
}
int mixbus = 0;
#ifdef MIXBUS
rxml->get_property (X_("mixbus-num"), mixbus)
#endif
rv.emplace (id, RouteImportInfo (name, pi, mixbus));
rv.emplace (id, RouteImportInfo (name, pi, 0));
}
}
return rv;

View file

@ -3180,15 +3180,20 @@ MIDITrigger::load_pending_data (PendingSwap& ps)
{
MIDIPendingSwap* mps (dynamic_cast<MIDIPendingSwap*> (&ps));
assert (mps);
assert (mps->rt_midibuffer);
_model->render (_model->read_lock(), *mps->rt_midibuffer);
return 0;
}
MIDITrigger::MIDIPendingSwap::MIDIPendingSwap ()
: rt_midibuffer (new RTMidiBufferBeats)
{
}
void
MIDITrigger::model_contents_changed ()
{
MIDIPendingSwap* pending = new MIDIPendingSwap;
pending->rt_midibuffer = new RTMidiBufferBeats;
pending->play_start = _play_start;
pending->play_end = _play_end;

View file

@ -366,6 +366,18 @@ DeviceInfo::set_state (const XMLNode& node, int /* version */)
_is_platformMp = false;
}
if ((child = node.child ("IsP1M")) != 0) {
child->get_property ("value", _is_p1m);
} else {
_is_p1m = false;
}
if ((child = node.child ("IsP1Nano")) != 0) {
child->get_property ("value", _is_p1nano);
} else {
_is_p1nano = false;
}
if ((child = node.child ("IsProG2")) != 0) {
child->get_property ("value", _is_proG2);
} else {
@ -543,6 +555,16 @@ bool DeviceInfo::is_platformMp () const
return _is_platformMp;
}
bool DeviceInfo::is_p1m () const
{
return _is_p1m;
}
bool DeviceInfo::is_p1nano () const
{
return _is_p1nano;
}
bool DeviceInfo::is_proG2 () const
{
return _is_proG2;

View file

@ -82,6 +82,8 @@ class DeviceInfo
bool is_qcon() const;
bool is_v1m() const;
bool is_platformMp() const;
bool is_p1m() const;
bool is_p1nano() const;
bool is_proG2() const;
bool is_xtouch() const;
bool has_qcon_second_lcd() const;
@ -119,6 +121,8 @@ class DeviceInfo
bool _is_qcon;
bool _is_v1m;
bool _is_platformMp;
bool _is_p1m;
bool _is_p1nano;
bool _is_proG2;
bool _is_xtouch;
bool _has_qcon_second_lcd;

View file

@ -452,6 +452,11 @@ MackieControlProtocol::switch_banks (uint32_t initial, bool force)
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("give surface %1 stripables\n", stripables.size()));
(*si)->map_stripables (stripables);
// Force RGB update on next redisplay
if (_device_info.is_v1m() || _device_info.is_p1m() || _device_info.is_p1nano()) {
(*si)->force_icon_rgb_update();
}
}
}

View file

@ -118,6 +118,9 @@ Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, ui
, _has_master_meter (false)
, connection_state (0)
, is_qcon (false)
, is_v1m (false)
, is_p1m (false)
, is_p1nano (false)
, input_source (0)
{
DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n");
@ -131,6 +134,22 @@ Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, ui
//Store Qcon flag
is_qcon = mcp.device_info().is_qcon();
//Store iCON P1-M and V1-M flag
is_v1m = _mcp.device_info().is_v1m(); // || device_name.find("V1-M") != std::string::npos;
is_p1m = _mcp.device_info().is_p1m(); // || device_name.find("P1-M") != std::string::npos;
is_p1nano = _mcp.device_info().is_p1nano();
/* extenders are not flagged by device_info() — detect by port name */
is_v1m |= (device_name.find("V1-M") != std::string::npos);
is_p1m |= device_name.find("P1-M") != std::string::npos;
is_p1nano |= device_name.find("P1-NANO") != std::string::npos;
_solid_icon_rgb.fill(0);
_current_icon_rgb.fill(0);
_pending_icon_rgb.fill(0);
_blink_state = false;
_last_blink_toggle = 0;
/* only the first Surface object has global controls */
/* lets use master_position instead */
uint32_t mp = _mcp.device_info().master_position();
@ -140,6 +159,11 @@ Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, ui
if ( is_qcon ) {
_has_master_display = (mcp.device_info().has_master_fader() && mcp.device_info().has_qcon_second_lcd());
}
if ( is_v1m ) {
_has_master_display = (mcp.device_info().has_master_fader() && mcp.device_info().has_qcon_second_lcd());
}
_has_master_meter = mcp.device_info().has_qcon_master_meters();
if (_mcp.device_info().has_global_controls()) {
@ -1158,6 +1182,68 @@ Surface::redisplay (PBD::microseconds_t now, bool force)
}
}
/* iCON P1-M/P1-NANO/V1-M color update: full RGB SysEx for all 8 strips */
if (is_v1m || is_p1m || is_p1nano) {
std::array<uint8_t, 24> pending_rgb{};
// 1-second blink cycle (500 ms on / 500 ms off)
uint64_t now = g_get_monotonic_time();
if (now - _last_blink_toggle >= 500000) { // 500000 µs = 500 ms
_blink_state = !_blink_state;
_last_blink_toggle = now;
}
for (size_t i = 0; i < 8 && i < strips.size(); ++i) {
if (auto sp = strips[i]->stripable()) {
uint32_t c = sp->presentation_info().color();
uint8_t r = ((c >> 24) & 0xFF) >> 1;
uint8_t g = ((c >> 16) & 0xFF) >> 1;
uint8_t b = ((c >> 8) & 0xFF) >> 1;
r = (r < 20) ? 0 : std::min(127, r + 20);
g = (g < 20) ? 0 : std::min(127, g + 20);
b = (b < 20) ? 0 : std::min(127, b + 20);
const size_t o = i * 3;
pending_rgb[o+0] = r;
pending_rgb[o+1] = g;
pending_rgb[o+2] = b;
// Save solid color for blinking reference
_solid_icon_rgb[o+0] = r;
_solid_icon_rgb[o+1] = g;
_solid_icon_rgb[o+2] = b;
// If this strip is selected → apply blink
if (sp->is_selected()) {
if (!_blink_state) {
// Blink OFF phase: dim to ~20% or full black (choose one)
pending_rgb[o+0] = r * 0.2f;
pending_rgb[o+1] = g * 0.2f;
pending_rgb[o+2] = b * 0.2f;
// For full black instead, use:
// pending_rgb[o+0] = pending_rgb[o+1] = pending_rgb[o+2] = 0;
} else {
// Blink ON phase: full solid color
pending_rgb[o+0] = r;
pending_rgb[o+1] = g;
pending_rgb[o+2] = b;
}
} else {
// Not selected → always solid
pending_rgb[o+0] = r;
pending_rgb[o+1] = g;
pending_rgb[o+2] = b;
}
}
}
if (force || pending_rgb != _current_icon_rgb) {
_current_icon_rgb = pending_rgb;
write(display_colors_on_p1m_v1m(pending_rgb));
}
}
for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
(*s)->redisplay (now, force);
}
@ -1640,6 +1726,26 @@ Surface::display_message_for (string const& msg, uint64_t msecs)
}
}
/** display color_values on the 8 scribble strips of the iCON P1-M, P1-NANO and V1-M **/
MidiByteArray
Surface::display_colors_on_p1m_v1m (const std::array<uint8_t, 24>& rgb_values) const
{
/* Icon P1-M, P1-NANO and V1-M color SysEx: F0 00 02 4E 16 14 [8×(R G B)] F7
* rgb_values: 24 bytes (8 strips × 3 RGB, each 0-127 / 0x00-0x7F)
*/
MidiByteArray midi_msg;
midi_msg << MIDI::sysex
<< 0x00 << 0x02 << 0x4E // iCON manufacturer
<< 0x16 << 0x14; // color command
for (uint8_t b : rgb_values) {
midi_msg << b;
}
midi_msg << MIDI::eox;
return midi_msg;
}
/** display @p color_values on the 8 scribble strips of the X-Touch
*
* @param color_values is assumed to be an array with a color value for each of the 8 scribble strips

View file

@ -205,6 +205,12 @@ public:
bool get_qcon_flag() { return is_qcon; }
bool get_v1_flag() { return is_v1m; }
bool get_p1m_flag() { return is_p1m; }
bool get_p1nano_flag() { return is_p1nano; }
void force_icon_rgb_update() { _pending_icon_rgb.fill(0xFF); }
void toggle_master_monitor ();
bool master_stripable_is_master_monitor ();
@ -227,6 +233,10 @@ public:
std::string pending_display[2];
std::string current_display[2];
// iCON P1-M / V1-M RGB — same pattern as master display
std::array<uint8_t, 24> _pending_icon_rgb{};
std::array<uint8_t, 24> _current_icon_rgb{};
void handle_midi_sysex (MIDI::Parser&, MIDI::byte *, size_t count);
MidiByteArray host_connection_query (MidiByteArray& bytes);
MidiByteArray host_connection_confirmation (const MidiByteArray& bytes);
@ -256,6 +266,17 @@ public:
MidiByteArray display_colors_on_xtouch (const XTouchColors color_values[]) const;
uint8_t convert_color_to_xtouch_value (uint32_t color) const;
// iCON Flags
bool is_v1m;
bool is_p1m;
bool is_p1nano;
/** Send RGB colors to P1-M and V1-M scribble strips (iCON-specific SysEx) */
MidiByteArray display_colors_on_p1m_v1m (const std::array<uint8_t, 24>& rgb_values) const;
std::array<uint8_t, 24> _solid_icon_rgb{}; // stores the real solid colors
bool _blink_state = false; // true = full brightness, false = dim/off
uint64_t _last_blink_toggle = 0;
public:
/* IP MIDI devices need to keep a handle on this and destroy it */
GSource* input_source;

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<MackieProtocolDevice>
<Name value="iCON Platform M+ with Platform X+ on right"/>
<Strips value="8"/>
<Extenders value="1"/>
<MasterPosition value="1"/>
<MasterFader value="yes"/>
<TimecodeDisplay value="yes"/>
<TwoCharacterDisplay value="no"/>
<HasSeparateMeters value="yes"/>
<GlobalControls value="yes"/>
<JogWheel value="yes"/>
<TouchSenseFaders value="yes"/>
<LogicControlButtons value="yes"/>
<usesIPMIDI value="no"/>
<NoHandShake value="yes"/>
<IsPlatformMp value="yes"/>
<HasQConSecondLCD value="yes"/>
<HasQConMasterMeters value="yes"/>
</MackieProtocolDevice>

20
share/mcp/p1-m.device Normal file
View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<MackieProtocolDevice>
<Name value="iCON Platform M+"/>
<Strips value="8"/>
<Extenders value="0"/>
<MasterPosition value="0"/>
<MasterFader value="yes"/>
<TimecodeDisplay value="yes"/>
<TwoCharacterDisplay value="no"/>
<HasSeparateMeters value="yes"/>
<GlobalControls value="yes"/>
<JogWheel value="yes"/>
<TouchSenseFaders value="yes"/>
<LogicControlButtons value="yes"/>
<usesIPMIDI value="no"/>
<NoHandShake value="yes"/>
<IsPlatformMp value="yes"/>
<HasQConSecondLCD value="yes"/>
<HasQConMasterMeters value="yes"/>
</MackieProtocolDevice>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<MackieProtocolDevice>
<Name value="iCON Platform M+ with Platform X+ on left"/>
<Strips value="8"/>
<Extenders value="1"/>
<MasterPosition value="2"/>
<MasterFader value="yes"/>
<TimecodeDisplay value="yes"/>
<TwoCharacterDisplay value="no"/>
<HasSeparateMeters value="yes"/>
<GlobalControls value="yes"/>
<JogWheel value="yes"/>
<TouchSenseFaders value="yes"/>
<LogicControlButtons value="yes"/>
<usesIPMIDI value="no"/>
<NoHandShake value="yes"/>
<IsPlatformMp value="yes"/>
<HasQConSecondLCD value="yes"/>
<HasQConMasterMeters value="yes"/>
</MackieProtocolDevice>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<MackieProtocolDevice>
<Name value="iCON P1-NANO with P1-X on right"/>
<Strips value="8"/>
<Extenders value="1"/>
<MasterPosition value="1"/>
<MasterFader value="yes"/>
<TimecodeDisplay value="yes"/>
<TwoCharacterDisplay value="no"/>
<HasSeparateMeters value="yes"/>
<GlobalControls value="yes"/>
<JogWheel value="yes"/>
<TouchSenseFaders value="yes"/>
<LogicControlButtons value="yes"/>
<usesIPMIDI value="no"/>
<NoHandShake value="yes"/>
<IsP1Nano value="yes"/>
<HasQConSecondLCD value="yes"/>
<HasQConMasterMeters value="yes"/>
</MackieProtocolDevice>

20
share/mcp/p1_nano.device Normal file
View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<MackieProtocolDevice>
<Name value="iCON P1-NANO"/>
<Strips value="8"/>
<Extenders value="0"/>
<MasterPosition value="0"/>
<MasterFader value="yes"/>
<TimecodeDisplay value="yes"/>
<TwoCharacterDisplay value="no"/>
<HasSeparateMeters value="yes"/>
<GlobalControls value="yes"/>
<JogWheel value="yes"/>
<TouchSenseFaders value="yes"/>
<LogicControlButtons value="yes"/>
<usesIPMIDI value="no"/>
<NoHandShake value="yes"/>
<IsP1Nano value="yes"/>
<HasQConSecondLCD value="yes"/>
<HasQConMasterMeters value="yes"/>
</MackieProtocolDevice>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<MackieProtocolDevice>
<Name value="iCON P1-NANO with P1-X on left"/>
<Strips value="8"/>
<Extenders value="1"/>
<MasterPosition value="2"/>
<MasterFader value="yes"/>
<TimecodeDisplay value="yes"/>
<TwoCharacterDisplay value="no"/>
<HasSeparateMeters value="yes"/>
<GlobalControls value="yes"/>
<JogWheel value="yes"/>
<TouchSenseFaders value="yes"/>
<LogicControlButtons value="yes"/>
<usesIPMIDI value="no"/>
<NoHandShake value="yes"/>
<IsP1Nano value="yes"/>
<HasQConSecondLCD value="yes"/>
<HasQConMasterMeters value="yes"/>
</MackieProtocolDevice>

View file

@ -58,7 +58,7 @@ function factory () return function ()
-- ..generate random offset..
local tickdiff = math.floor (rv['rand']() * max_distance);
print (old_pos:get_beats (), old_pos:get_ticks (), tickdiff)
--print (old_pos:get_beats (), old_pos:get_ticks (), tickdiff)
-- .. and calculate new position.
local new_pos = Temporal.Beats (old_pos:get_beats (), old_pos:get_ticks () + tickdiff)