MCP - Add RGB color display to iCON P1-M, P-NANO and V1-M

This commit is contained in:
Nicolas Koch 2025-11-27 23:37:48 -05:00 committed by Paul Davis
parent 3e2c73258c
commit f71b519c56
3 changed files with 93 additions and 1 deletions

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())); DEBUG_TRACE (DEBUG::MackieControl, string_compose ("give surface %1 stripables\n", stripables.size()));
(*si)->map_stripables (stripables); (*si)->map_stripables (stripables);
// Force RGB update on next redisplay
if (_device_info.is_v1m() || _device_info.is_platformMp() || _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) , _has_master_meter (false)
, connection_state (0) , connection_state (0)
, is_qcon (false) , is_qcon (false)
, is_v1m (false)
, is_platformMp (false)
, is_p1nano (false)
, input_source (0) , input_source (0)
{ {
DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n"); DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n");
@ -131,6 +134,19 @@ Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, ui
//Store Qcon flag //Store Qcon flag
is_qcon = mcp.device_info().is_qcon(); 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_platformMp = _mcp.device_info().is_platformMp(); // || device_name.find("Platform 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_platformMp |= device_name.find("Platform M+") != std::string::npos;
is_p1nano |= device_name.find("P1-NANO") != std::string::npos;
_pending_icon_rgb.fill(0);
_current_icon_rgb.fill(0);
/* only the first Surface object has global controls */ /* only the first Surface object has global controls */
/* lets use master_position instead */ /* lets use master_position instead */
uint32_t mp = _mcp.device_info().master_position(); uint32_t mp = _mcp.device_info().master_position();
@ -140,6 +156,11 @@ Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, ui
if ( is_qcon ) { if ( is_qcon ) {
_has_master_display = (mcp.device_info().has_master_fader() && mcp.device_info().has_qcon_second_lcd()); _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(); _has_master_meter = mcp.device_info().has_qcon_master_meters();
if (_mcp.device_info().has_global_controls()) { if (_mcp.device_info().has_global_controls()) {
@ -1158,6 +1179,34 @@ 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_platformMp || is_p1nano) {
std::array<uint8_t, 24> pending_rgb{};
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;
}
}
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) { for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
(*s)->redisplay (now, force); (*s)->redisplay (now, force);
} }
@ -1640,6 +1689,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 /** 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 * @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_qcon_flag() { return is_qcon; }
bool get_v1_flag() { return is_v1m; }
bool get_platformMp_flag() { return is_platformMp; }
bool get_p1nano_flag() { return is_p1nano; }
void force_icon_rgb_update() { _pending_icon_rgb.fill(0xFF); }
void toggle_master_monitor (); void toggle_master_monitor ();
bool master_stripable_is_master_monitor (); bool master_stripable_is_master_monitor ();
@ -227,6 +233,10 @@ public:
std::string pending_display[2]; std::string pending_display[2];
std::string current_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); void handle_midi_sysex (MIDI::Parser&, MIDI::byte *, size_t count);
MidiByteArray host_connection_query (MidiByteArray& bytes); MidiByteArray host_connection_query (MidiByteArray& bytes);
MidiByteArray host_connection_confirmation (const MidiByteArray& bytes); MidiByteArray host_connection_confirmation (const MidiByteArray& bytes);
@ -256,6 +266,14 @@ public:
MidiByteArray display_colors_on_xtouch (const XTouchColors color_values[]) const; MidiByteArray display_colors_on_xtouch (const XTouchColors color_values[]) const;
uint8_t convert_color_to_xtouch_value (uint32_t color) const; uint8_t convert_color_to_xtouch_value (uint32_t color) const;
// iCON Flags
bool is_v1m;
bool is_platformMp;
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;
public: public:
/* IP MIDI devices need to keep a handle on this and destroy it */ /* IP MIDI devices need to keep a handle on this and destroy it */
GSource* input_source; GSource* input_source;