From f99680896274c9e5182e2ae187b055656381861f Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 1 Dec 2025 12:39:15 -0700 Subject: [PATCH 01/20] triggerbox: fix error during 794888738 that caused crashes when editing MIDI clip boundaries --- libs/ardour/ardour/triggerbox.h | 2 +- libs/ardour/triggerbox.cc | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libs/ardour/ardour/triggerbox.h b/libs/ardour/ardour/triggerbox.h index d3839dc0c1..68457874ea 100644 --- a/libs/ardour/ardour/triggerbox.h +++ b/libs/ardour/ardour/triggerbox.h @@ -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; } }; diff --git a/libs/ardour/triggerbox.cc b/libs/ardour/triggerbox.cc index 286b8531ad..bf95500cb1 100644 --- a/libs/ardour/triggerbox.cc +++ b/libs/ardour/triggerbox.cc @@ -3180,15 +3180,20 @@ MIDITrigger::load_pending_data (PendingSwap& ps) { MIDIPendingSwap* mps (dynamic_cast (&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; From 293a359d10fac98d8022aed7082c00b0b3dd1ed6 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 1 Dec 2025 20:47:08 +0100 Subject: [PATCH 02/20] Fix Mixbus builds (1/2) --- libs/ardour/session_state.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index ab34667110..7aaef61762 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -1276,7 +1276,7 @@ Session::parse_route_state (const string& path, bool& match_pbd_id) int mixbus = 0; #ifdef MIXBUS - rxml->get_property (X_("mixbus-num"), mixbus) + rxml->get_property (X_("mixbus-num"), mixbus); #endif rv.emplace (id, RouteImportInfo (name, pi, mixbus)); From 4db08ba34f486ed3feb82606481952b5032e68d3 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 1 Dec 2025 20:47:46 +0100 Subject: [PATCH 03/20] Fix Mixbus builds (2/2) --- gtk2_ardour/strip_import_dialog.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtk2_ardour/strip_import_dialog.cc b/gtk2_ardour/strip_import_dialog.cc index bd60c375c1..d61375d9e5 100644 --- a/gtk2_ardour/strip_import_dialog.cc +++ b/gtk2_ardour/strip_import_dialog.cc @@ -698,7 +698,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 From 3e2c73258c0d999c7af543259e7493395c2cf053 Mon Sep 17 00:00:00 2001 From: Nicolas Koch Date: Thu, 27 Nov 2025 21:27:14 -0500 Subject: [PATCH 04/20] 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 --- libs/surfaces/mackie/device_info.cc | 11 +++++++++++ libs/surfaces/mackie/device_info.h | 2 ++ share/mcp/p1_nano+p1-x.device | 20 ++++++++++++++++++++ share/mcp/p1_nano.device | 20 ++++++++++++++++++++ share/mcp/p1_x+p1_nano.device | 20 ++++++++++++++++++++ share/mcp/platform_m+.device | 12 ++++++++---- share/mcp/platform_m+_platformx+.device | 10 ++++++---- share/mcp/platform_x+_platform_m+.device | 10 ++++++---- 8 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 share/mcp/p1_nano+p1-x.device create mode 100644 share/mcp/p1_nano.device create mode 100644 share/mcp/p1_x+p1_nano.device diff --git a/libs/surfaces/mackie/device_info.cc b/libs/surfaces/mackie/device_info.cc index 71eb29963a..7a0b0e3935 100644 --- a/libs/surfaces/mackie/device_info.cc +++ b/libs/surfaces/mackie/device_info.cc @@ -366,6 +366,12 @@ DeviceInfo::set_state (const XMLNode& node, int /* version */) _is_platformMp = 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 +549,11 @@ bool DeviceInfo::is_platformMp () const return _is_platformMp; } +bool DeviceInfo::is_p1nano () const +{ + return _is_p1nano; +} + bool DeviceInfo::is_proG2 () const { return _is_proG2; diff --git a/libs/surfaces/mackie/device_info.h b/libs/surfaces/mackie/device_info.h index 1e22214740..0badaa2599 100644 --- a/libs/surfaces/mackie/device_info.h +++ b/libs/surfaces/mackie/device_info.h @@ -82,6 +82,7 @@ class DeviceInfo bool is_qcon() const; bool is_v1m() const; bool is_platformMp() const; + bool is_p1nano() const; bool is_proG2() const; bool is_xtouch() const; bool has_qcon_second_lcd() const; @@ -119,6 +120,7 @@ class DeviceInfo bool _is_qcon; bool _is_v1m; bool _is_platformMp; + bool _is_p1nano; bool _is_proG2; bool _is_xtouch; bool _has_qcon_second_lcd; diff --git a/share/mcp/p1_nano+p1-x.device b/share/mcp/p1_nano+p1-x.device new file mode 100644 index 0000000000..d1f0bdb00e --- /dev/null +++ b/share/mcp/p1_nano+p1-x.device @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/share/mcp/p1_nano.device b/share/mcp/p1_nano.device new file mode 100644 index 0000000000..82a7613247 --- /dev/null +++ b/share/mcp/p1_nano.device @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/share/mcp/p1_x+p1_nano.device b/share/mcp/p1_x+p1_nano.device new file mode 100644 index 0000000000..33f83142af --- /dev/null +++ b/share/mcp/p1_x+p1_nano.device @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/share/mcp/platform_m+.device b/share/mcp/platform_m+.device index 3363eddce3..41c250441d 100644 --- a/share/mcp/platform_m+.device +++ b/share/mcp/platform_m+.device @@ -2,15 +2,19 @@ - - - + + + + + + - + + diff --git a/share/mcp/platform_m+_platformx+.device b/share/mcp/platform_m+_platformx+.device index 8b26926b63..739f7272b3 100644 --- a/share/mcp/platform_m+_platformx+.device +++ b/share/mcp/platform_m+_platformx+.device @@ -5,14 +5,16 @@ - + - - + + + - + + diff --git a/share/mcp/platform_x+_platform_m+.device b/share/mcp/platform_x+_platform_m+.device index fd73e44097..5430cc97a1 100644 --- a/share/mcp/platform_x+_platform_m+.device +++ b/share/mcp/platform_x+_platform_m+.device @@ -5,14 +5,16 @@ - + - - + + + - + + From f71b519c569b1aeb9d933b840ea00d12614fd428 Mon Sep 17 00:00:00 2001 From: Nicolas Koch Date: Thu, 27 Nov 2025 23:37:48 -0500 Subject: [PATCH 05/20] MCP - Add RGB color display to iCON P1-M, P-NANO and V1-M --- .../mackie/mackie_control_protocol.cc | 5 ++ libs/surfaces/mackie/surface.cc | 71 ++++++++++++++++++- libs/surfaces/mackie/surface.h | 18 +++++ 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index aa4438ed2c..3db64ae1e6 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -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_platformMp() || _device_info.is_p1nano()) { + (*si)->force_icon_rgb_update(); + } } } diff --git a/libs/surfaces/mackie/surface.cc b/libs/surfaces/mackie/surface.cc index 7990ddd56c..80140aed5c 100644 --- a/libs/surfaces/mackie/surface.cc +++ b/libs/surfaces/mackie/surface.cc @@ -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_platformMp (false) + , is_p1nano (false) , input_source (0) { 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 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 */ /* lets use master_position instead */ 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 ) { _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 +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 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) { (*s)->redisplay (now, force); } @@ -1639,6 +1688,26 @@ Surface::display_message_for (string const& msg, uint64_t msecs) (*s)->block_screen_display_for (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& 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 * @@ -1656,7 +1725,7 @@ Surface::display_colors_on_xtouch (const XTouchColors color_values[]) const for (uint8_t i = 0; i < displaycount; ++i) { midi_msg << color_values[i]; } - + midi_msg << MIDI::eox; return midi_msg; diff --git a/libs/surfaces/mackie/surface.h b/libs/surfaces/mackie/surface.h index 2d0a98f447..ba871a13a0 100644 --- a/libs/surfaces/mackie/surface.h +++ b/libs/surfaces/mackie/surface.h @@ -205,6 +205,12 @@ public: 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 (); 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 _pending_icon_rgb{}; + std::array _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,14 @@ 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_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& rgb_values) const; + public: /* IP MIDI devices need to keep a handle on this and destroy it */ GSource* input_source; From 669e689f8cab092f7d6698b8b4581099ce178cf2 Mon Sep 17 00:00:00 2001 From: Nicolas Koch Date: Fri, 28 Nov 2025 22:49:26 -0500 Subject: [PATCH 06/20] MCP - Add iCON Pro Audio P1-M and correct Platform M+ - I forgot to rename to P1-M on previous commit --- libs/surfaces/mackie/device_info.cc | 11 +++++++++++ libs/surfaces/mackie/device_info.h | 2 ++ share/mcp/p1-m+p1-x.device | 20 ++++++++++++++++++++ share/mcp/p1-m.device | 20 ++++++++++++++++++++ share/mcp/p1-x+p1-m.device | 20 ++++++++++++++++++++ share/mcp/platform_m+.device | 12 ++++-------- share/mcp/platform_m+_platformx+.device | 10 ++++------ share/mcp/platform_x+_platform_m+.device | 10 ++++------ 8 files changed, 85 insertions(+), 20 deletions(-) create mode 100644 share/mcp/p1-m+p1-x.device create mode 100644 share/mcp/p1-m.device create mode 100644 share/mcp/p1-x+p1-m.device diff --git a/libs/surfaces/mackie/device_info.cc b/libs/surfaces/mackie/device_info.cc index 7a0b0e3935..3d6a84b341 100644 --- a/libs/surfaces/mackie/device_info.cc +++ b/libs/surfaces/mackie/device_info.cc @@ -366,6 +366,12 @@ 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 { @@ -549,6 +555,11 @@ bool DeviceInfo::is_platformMp () const return _is_platformMp; } +bool DeviceInfo::is_p1m () const +{ + return _is_p1m; +} + bool DeviceInfo::is_p1nano () const { return _is_p1nano; diff --git a/libs/surfaces/mackie/device_info.h b/libs/surfaces/mackie/device_info.h index 0badaa2599..0d930ba9e2 100644 --- a/libs/surfaces/mackie/device_info.h +++ b/libs/surfaces/mackie/device_info.h @@ -82,6 +82,7 @@ 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; @@ -120,6 +121,7 @@ class DeviceInfo bool _is_qcon; bool _is_v1m; bool _is_platformMp; + bool _is_p1m; bool _is_p1nano; bool _is_proG2; bool _is_xtouch; diff --git a/share/mcp/p1-m+p1-x.device b/share/mcp/p1-m+p1-x.device new file mode 100644 index 0000000000..739f7272b3 --- /dev/null +++ b/share/mcp/p1-m+p1-x.device @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/share/mcp/p1-m.device b/share/mcp/p1-m.device new file mode 100644 index 0000000000..41c250441d --- /dev/null +++ b/share/mcp/p1-m.device @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/share/mcp/p1-x+p1-m.device b/share/mcp/p1-x+p1-m.device new file mode 100644 index 0000000000..5430cc97a1 --- /dev/null +++ b/share/mcp/p1-x+p1-m.device @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/share/mcp/platform_m+.device b/share/mcp/platform_m+.device index 41c250441d..3363eddce3 100644 --- a/share/mcp/platform_m+.device +++ b/share/mcp/platform_m+.device @@ -2,19 +2,15 @@ + + + - - - - - - + - - diff --git a/share/mcp/platform_m+_platformx+.device b/share/mcp/platform_m+_platformx+.device index 739f7272b3..8b26926b63 100644 --- a/share/mcp/platform_m+_platformx+.device +++ b/share/mcp/platform_m+_platformx+.device @@ -5,16 +5,14 @@ - + - + + - - + - - diff --git a/share/mcp/platform_x+_platform_m+.device b/share/mcp/platform_x+_platform_m+.device index 5430cc97a1..fd73e44097 100644 --- a/share/mcp/platform_x+_platform_m+.device +++ b/share/mcp/platform_x+_platform_m+.device @@ -5,16 +5,14 @@ - + - + + - - + - - From e39eebee5759ca8b2e59ed00bee7267513d3238f Mon Sep 17 00:00:00 2001 From: Nicolas Koch Date: Fri, 28 Nov 2025 23:13:58 -0500 Subject: [PATCH 07/20] change iCON RGB color to use newly added p1m device logic rather than erroneous platformMp --- libs/surfaces/mackie/surface.cc | 8 ++++---- libs/surfaces/mackie/surface.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libs/surfaces/mackie/surface.cc b/libs/surfaces/mackie/surface.cc index 80140aed5c..4d4a707db7 100644 --- a/libs/surfaces/mackie/surface.cc +++ b/libs/surfaces/mackie/surface.cc @@ -119,7 +119,7 @@ Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, ui , connection_state (0) , is_qcon (false) , is_v1m (false) - , is_platformMp (false) + , is_p1m (false) , is_p1nano (false) , input_source (0) { @@ -136,12 +136,12 @@ Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, ui //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_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_platformMp |= device_name.find("Platform M+") != std::string::npos; + is_p1m |= device_name.find("P1-M") != std::string::npos; is_p1nano |= device_name.find("P1-NANO") != std::string::npos; _pending_icon_rgb.fill(0); @@ -1180,7 +1180,7 @@ 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) { + if (is_v1m || is_p1m || is_p1nano) { std::array pending_rgb{}; for (size_t i = 0; i < 8 && i < strips.size(); ++i) { diff --git a/libs/surfaces/mackie/surface.h b/libs/surfaces/mackie/surface.h index ba871a13a0..525a0266db 100644 --- a/libs/surfaces/mackie/surface.h +++ b/libs/surfaces/mackie/surface.h @@ -206,7 +206,7 @@ public: bool get_qcon_flag() { return is_qcon; } bool get_v1_flag() { return is_v1m; } - bool get_platformMp_flag() { return is_platformMp; } + bool get_p1m_flag() { return is_p1m; } bool get_p1nano_flag() { return is_p1nano; } void force_icon_rgb_update() { _pending_icon_rgb.fill(0xFF); } @@ -268,7 +268,7 @@ public: // iCON Flags bool is_v1m; - bool is_platformMp; + bool is_p1m; bool is_p1nano; /** Send RGB colors to P1-M and V1-M scribble strips (iCON-specific SysEx) */ From 2db87934dddd4c1f8db1aef9e6314a8c12e5d8ea Mon Sep 17 00:00:00 2001 From: Nicolas Koch Date: Mon, 1 Dec 2025 08:58:21 -0500 Subject: [PATCH 08/20] MCP: iCON P1-M/P1-NANO/V1-M only - Make RGB blink to 20% brightness when track/bus selected --- libs/surfaces/mackie/surface.cc | 39 ++++++++++++++++++++++++++++++++- libs/surfaces/mackie/surface.h | 3 +++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/libs/surfaces/mackie/surface.cc b/libs/surfaces/mackie/surface.cc index 4d4a707db7..029c1be937 100644 --- a/libs/surfaces/mackie/surface.cc +++ b/libs/surfaces/mackie/surface.cc @@ -144,8 +144,11 @@ Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, ui is_p1m |= device_name.find("P1-M") != std::string::npos; is_p1nano |= device_name.find("P1-NANO") != std::string::npos; - _pending_icon_rgb.fill(0); + _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 */ @@ -1183,6 +1186,13 @@ Surface::redisplay (PBD::microseconds_t now, bool force) if (is_v1m || is_p1m || is_p1nano) { std::array 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(); @@ -1198,6 +1208,33 @@ Surface::redisplay (PBD::microseconds_t now, bool force) 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; + } } } diff --git a/libs/surfaces/mackie/surface.h b/libs/surfaces/mackie/surface.h index 525a0266db..dd0e2b0e08 100644 --- a/libs/surfaces/mackie/surface.h +++ b/libs/surfaces/mackie/surface.h @@ -273,6 +273,9 @@ public: /** Send RGB colors to P1-M and V1-M scribble strips (iCON-specific SysEx) */ MidiByteArray display_colors_on_p1m_v1m (const std::array& rgb_values) const; + std::array _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 */ From 34850e514d6655a0c770db8ed3a1080be6ae8781 Mon Sep 17 00:00:00 2001 From: Nicolas Koch Date: Mon, 1 Dec 2025 09:30:15 -0500 Subject: [PATCH 09/20] MCP - Fix iCON P1-M check for RGB update on next redisplay --- libs/surfaces/mackie/mackie_control_protocol.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index 3db64ae1e6..d7b29011dd 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -454,7 +454,7 @@ MackieControlProtocol::switch_banks (uint32_t initial, bool force) (*si)->map_stripables (stripables); // Force RGB update on next redisplay - if (_device_info.is_v1m() || _device_info.is_platformMp() || _device_info.is_p1nano()) { + if (_device_info.is_v1m() || _device_info.is_p1m() || _device_info.is_p1nano()) { (*si)->force_icon_rgb_update(); } } From 5833fc08f29a96955b8e9ec88fd6cc22db645751 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 1 Dec 2025 21:20:49 +0100 Subject: [PATCH 10/20] Best keep Mixbus state in mixbus-git --- libs/ardour/session_state.cc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 7aaef61762..4d98c033dc 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -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; From 21b2985c47fc4c50cf1fe28e714bda3a90450166 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 1 Dec 2025 22:19:12 +0100 Subject: [PATCH 11/20] Tweak Stip Import table layout --- gtk2_ardour/strip_import_dialog.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gtk2_ardour/strip_import_dialog.cc b/gtk2_ardour/strip_import_dialog.cc index d61375d9e5..2d2515e30b 100644 --- a/gtk2_ardour/strip_import_dialog.cc +++ b/gtk2_ardour/strip_import_dialog.cc @@ -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 (); From 04a1ed3c48d5be8428c13c59972ef7dac70d2f55 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 1 Dec 2025 13:27:11 -0700 Subject: [PATCH 12/20] pianoroll: ensure countdown digits are not scrolled out of sight --- gtk2_ardour/pianoroll_midi_view.cc | 3 ++- gtk2_ardour/pianoroll_midi_view.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/gtk2_ardour/pianoroll_midi_view.cc b/gtk2_ardour/pianoroll_midi_view.cc index d7a203b074..bf5f5647a5 100644 --- a/gtk2_ardour/pianoroll_midi_view.cc +++ b/gtk2_ardour/pianoroll_midi_view.cc @@ -55,6 +55,7 @@ PianorollMidiView::PianorollMidiView (std::shared_ptr 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); diff --git a/gtk2_ardour/pianoroll_midi_view.h b/gtk2_ardour/pianoroll_midi_view.h index fb8b8011bd..9855d0427f 100644 --- a/gtk2_ardour/pianoroll_midi_view.h +++ b/gtk2_ardour/pianoroll_midi_view.h @@ -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; From c5c04f38cdacedb428f7c3b05b4e367d413cfc21 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 1 Dec 2025 13:29:07 -0700 Subject: [PATCH 13/20] audio clip editors: ensure countdown digits are not scrolled out of sight --- gtk2_ardour/audio_clip_editor.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtk2_ardour/audio_clip_editor.cc b/gtk2_ardour/audio_clip_editor.cc index 17dc08094d..0a81376bfb 100644 --- a/gtk2_ardour/audio_clip_editor.cc +++ b/gtk2_ardour/audio_clip_editor.cc @@ -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); From cf6eff4d9f4d547a7400fa52bf32ff7335973d4f Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 1 Dec 2025 15:17:20 -0700 Subject: [PATCH 14/20] ensure count-in for clip recording vanishes at transport stop/rec-disable --- gtk2_ardour/audio_clip_editor.cc | 33 ++++++++++++++++++++++++++++++++ gtk2_ardour/audio_clip_editor.h | 4 ++++ gtk2_ardour/cue_editor.cc | 2 ++ gtk2_ardour/pianoroll.cc | 17 ++++++++-------- 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/gtk2_ardour/audio_clip_editor.cc b/gtk2_ardour/audio_clip_editor.cc index 0a81376bfb..3dabd084e4 100644 --- a/gtk2_ardour/audio_clip_editor.cc +++ b/gtk2_ardour/audio_clip_editor.cc @@ -894,9 +894,42 @@ 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); } } + +void +AudioClipEditor::set_session (ARDOUR::Session* s) +{ + EC_LOCAL_TEMPO_SCOPE; + + CueEditor::set_session (s); + + if (_session) { + _session->TransportStateChange.connect (_session_connections, MISSING_INVALIDATOR, std::bind (&AudioClipEditor::map_transport_state, this), gui_context()); + } else { + _session_connections.drop_connections(); + } + + map_transport_state (); +} + +void +AudioClipEditor::map_transport_state () +{ + EC_LOCAL_TEMPO_SCOPE; + + if (!_session) { + return; + } + + if (!_session->transport_rolling()) { + hide_count_in (); + } +} + diff --git a/gtk2_ardour/audio_clip_editor.h b/gtk2_ardour/audio_clip_editor.h index 30790b7e1a..1234ecf93d 100644 --- a/gtk2_ardour/audio_clip_editor.h +++ b/gtk2_ardour/audio_clip_editor.h @@ -110,6 +110,8 @@ public: samplecnt_t current_page_samples() const; void set_samples_per_pixel (samplecnt_t); + void set_session (ARDOUR::Session* s); + Gdk::Cursor* which_track_cursor () const { return nullptr; } Gdk::Cursor* which_mode_cursor () const { return nullptr; } Gdk::Cursor* which_trim_cursor (bool left_side) const { return nullptr; } @@ -209,4 +211,6 @@ public: void grid_type_chosen (Editing::GridType); Gtk::Box* pack_mouse_mode_box () { return nullptr; } + + void map_transport_state (); }; diff --git a/gtk2_ardour/cue_editor.cc b/gtk2_ardour/cue_editor.cc index 0bb301b8ae..b6cae5e0e2 100644 --- a/gtk2_ardour/cue_editor.cc +++ b/gtk2_ardour/cue_editor.cc @@ -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; } diff --git a/gtk2_ardour/pianoroll.cc b/gtk2_ardour/pianoroll.cc index 51b4a8148a..557ecdb329 100644 --- a/gtk2_ardour/pianoroll.cc +++ b/gtk2_ardour/pianoroll.cc @@ -558,7 +558,6 @@ Pianoroll::maybe_update () } if (_track->triggerbox()->record_enabled() == Recording) { - _playhead_cursor->set_position (data_capture_duration); } @@ -1812,16 +1811,14 @@ Pianoroll::set_session (ARDOUR::Session* s) CueEditor::set_session (s); - if (with_transport_controls) { - if (_session) { - _session->TransportStateChange.connect (_session_connections, MISSING_INVALIDATOR, std::bind (&Pianoroll::map_transport_state, this), gui_context()); - } else { - _session_connections.drop_connections(); - } - - map_transport_state (); + if (_session) { + _session->TransportStateChange.connect (_session_connections, MISSING_INVALIDATOR, std::bind (&Pianoroll::map_transport_state, this), gui_context()); + } else { + _session_connections.drop_connections(); } + map_transport_state (); + if (_session) { zoom_to_show (timecnt_t (timepos_t (max_extents_scale() * max_zoom_extent ().second.samples()))); } @@ -1870,6 +1867,8 @@ Pianoroll::map_transport_state () } else { loop_button.set_active (false); } + + hide_count_in (); } } From fca85e01c959c7e2c5089116248f75fdf9bab9ea Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 1 Dec 2025 15:32:47 -0700 Subject: [PATCH 15/20] 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 --- gtk2_ardour/audio_clip_editor.cc | 30 ------------------------------ gtk2_ardour/audio_clip_editor.h | 4 ---- gtk2_ardour/pianoroll.cc | 14 ++++++++------ 3 files changed, 8 insertions(+), 40 deletions(-) diff --git a/gtk2_ardour/audio_clip_editor.cc b/gtk2_ardour/audio_clip_editor.cc index 3dabd084e4..4ec8a61627 100644 --- a/gtk2_ardour/audio_clip_editor.cc +++ b/gtk2_ardour/audio_clip_editor.cc @@ -903,33 +903,3 @@ AudioClipEditor::grid_type_chosen (Editing::GridType gt) } } -void -AudioClipEditor::set_session (ARDOUR::Session* s) -{ - EC_LOCAL_TEMPO_SCOPE; - - CueEditor::set_session (s); - - if (_session) { - _session->TransportStateChange.connect (_session_connections, MISSING_INVALIDATOR, std::bind (&AudioClipEditor::map_transport_state, this), gui_context()); - } else { - _session_connections.drop_connections(); - } - - map_transport_state (); -} - -void -AudioClipEditor::map_transport_state () -{ - EC_LOCAL_TEMPO_SCOPE; - - if (!_session) { - return; - } - - if (!_session->transport_rolling()) { - hide_count_in (); - } -} - diff --git a/gtk2_ardour/audio_clip_editor.h b/gtk2_ardour/audio_clip_editor.h index 1234ecf93d..30790b7e1a 100644 --- a/gtk2_ardour/audio_clip_editor.h +++ b/gtk2_ardour/audio_clip_editor.h @@ -110,8 +110,6 @@ public: samplecnt_t current_page_samples() const; void set_samples_per_pixel (samplecnt_t); - void set_session (ARDOUR::Session* s); - Gdk::Cursor* which_track_cursor () const { return nullptr; } Gdk::Cursor* which_mode_cursor () const { return nullptr; } Gdk::Cursor* which_trim_cursor (bool left_side) const { return nullptr; } @@ -211,6 +209,4 @@ public: void grid_type_chosen (Editing::GridType); Gtk::Box* pack_mouse_mode_box () { return nullptr; } - - void map_transport_state (); }; diff --git a/gtk2_ardour/pianoroll.cc b/gtk2_ardour/pianoroll.cc index 557ecdb329..71e25b8f1c 100644 --- a/gtk2_ardour/pianoroll.cc +++ b/gtk2_ardour/pianoroll.cc @@ -1811,13 +1811,15 @@ Pianoroll::set_session (ARDOUR::Session* s) CueEditor::set_session (s); - if (_session) { - _session->TransportStateChange.connect (_session_connections, MISSING_INVALIDATOR, std::bind (&Pianoroll::map_transport_state, this), gui_context()); - } else { - _session_connections.drop_connections(); - } + if (with_transport_controls) { + if (_session) { + _session->TransportStateChange.connect (_session_connections, MISSING_INVALIDATOR, std::bind (&Pianoroll::map_transport_state, this), gui_context()); + } else { + _session_connections.drop_connections(); + } - map_transport_state (); + map_transport_state (); + } if (_session) { zoom_to_show (timecnt_t (timepos_t (max_extents_scale() * max_zoom_extent ().second.samples()))); From 7635707ac28d5c7a6c5740d7f138aaa9a22c78bb Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 2 Dec 2025 00:53:40 +0100 Subject: [PATCH 16/20] Disable debug-print in MIDI randomize script --- share/scripts/brutalize_midi.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/scripts/brutalize_midi.lua b/share/scripts/brutalize_midi.lua index 7692982aca..7a8e734431 100644 --- a/share/scripts/brutalize_midi.lua +++ b/share/scripts/brutalize_midi.lua @@ -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) From 36d62006310b6dc4f6a67b7a574da33e0d8eefe1 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 1 Dec 2025 17:54:30 -0700 Subject: [PATCH 17/20] remove some debug output associated with loading region UI settings --- gtk2_ardour/region_ui_settings.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gtk2_ardour/region_ui_settings.cc b/gtk2_ardour/region_ui_settings.cc index adb10e8bc9..69c8909644 100644 --- a/gtk2_ardour/region_ui_settings.cc +++ b/gtk2_ardour/region_ui_settings.cc @@ -171,15 +171,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 +185,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)); } From 54958f1e9430958f930f30c2d90828097f43d6ec Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 1 Dec 2025 17:55:14 -0700 Subject: [PATCH 18/20] remove debug output --- gtk2_ardour/pianoroll.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/gtk2_ardour/pianoroll.cc b/gtk2_ardour/pianoroll.cc index 71e25b8f1c..f21b3b3509 100644 --- a/gtk2_ardour/pianoroll.cc +++ b/gtk2_ardour/pianoroll.cc @@ -1352,7 +1352,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 mr = std::dynamic_pointer_cast (ref.trigger()->the_region()); set_region (mr); } From 38368f4dd968d78394053a17ca1be31ea92aa49c Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 1 Dec 2025 17:55:25 -0700 Subject: [PATCH 19/20] save and restore pianoroll note mode on a per-region basis --- gtk2_ardour/editing_context.cc | 4 ++ gtk2_ardour/editing_context.h | 4 ++ gtk2_ardour/pianoroll.cc | 65 +++++++++++++++++++++---------- gtk2_ardour/pianoroll.h | 6 +-- gtk2_ardour/region_ui_settings.cc | 3 ++ gtk2_ardour/region_ui_settings.h | 3 +- 6 files changed, 61 insertions(+), 24 deletions(-) diff --git a/gtk2_ardour/editing_context.cc b/gtk2_ardour/editing_context.cc index 2afa6480c3..9b1796c787 100644 --- a/gtk2_ardour/editing_context.cc +++ b/gtk2_ardour/editing_context.cc @@ -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)); diff --git a/gtk2_ardour/editing_context.h b/gtk2_ardour/editing_context.h index 4a983dc1ab..81a0c56ecb 100644 --- a/gtk2_ardour/editing_context.h +++ b/gtk2_ardour/editing_context.h @@ -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 > snap_mode_actions; std::map > draw_length_actions; std::map > mouse_mode_actions; + std::map > note_mode_actions; std::map > zoom_focus_actions; std::map > draw_velocity_actions; std::map > 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 DrawLengthChanged; sigc::signal DrawVelocityChanged; diff --git a/gtk2_ardour/pianoroll.cc b/gtk2_ardour/pianoroll.cc index f21b3b3509..0a6b0a6a88 100644 --- a/gtk2_ardour/pianoroll.cc +++ b/gtk2_ardour/pianoroll.cc @@ -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; @@ -1587,6 +1586,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 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 () { @@ -1594,27 +1627,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); } } @@ -2037,6 +2054,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 +2071,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 (); } diff --git a/gtk2_ardour/pianoroll.h b/gtk2_ardour/pianoroll.h index 4ea5600032..f51de28cd2 100644 --- a/gtk2_ardour/pianoroll.h +++ b/gtk2_ardour/pianoroll.h @@ -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 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&); }; diff --git a/gtk2_ardour/region_ui_settings.cc b/gtk2_ardour/region_ui_settings.cc index 69c8909644..424e3f6534 100644 --- a/gtk2_ardour/region_ui_settings.cc +++ b/gtk2_ardour/region_ui_settings.cc @@ -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); diff --git a/gtk2_ardour/region_ui_settings.h b/gtk2_ardour/region_ui_settings.h index 3dadd4c856..dd0f3dccd0 100644 --- a/gtk2_ardour/region_ui_settings.h +++ b/gtk2_ardour/region_ui_settings.h @@ -39,13 +39,14 @@ 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; int height; int x; int y; - + /* MIDI specific */ Editing::GridType draw_length; From fd0e9e840e7fec0f5ddc9b5909f7ee76d583aa7c Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 1 Dec 2025 18:10:09 -0700 Subject: [PATCH 20/20] when opening a pianoroll for a new empty region, take the note mode from the track --- gtk2_ardour/pianoroll.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gtk2_ardour/pianoroll.cc b/gtk2_ardour/pianoroll.cc index 0a6b0a6a88..47d585db93 100644 --- a/gtk2_ardour/pianoroll.cc +++ b/gtk2_ardour/pianoroll.cc @@ -1475,6 +1475,13 @@ Pianoroll::set_region (std::shared_ptr region) bg->display_region (*view); maybe_set_from_rsu (); + + if (r->source()->empty()) { + std::shared_ptr mt (std::dynamic_pointer_cast (_track)); + if (mt) { + note_mode_actions[mt->note_mode()]->set_active (true); + } + } } bool