diff --git a/gtk2_ardour/audio_clip_editor.cc b/gtk2_ardour/audio_clip_editor.cc index 17dc08094d..4ec8a61627 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); @@ -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); } } + 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/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 51b4a8148a..47d585db93 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; @@ -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 mr = std::dynamic_pointer_cast (ref.trigger()->the_region()); set_region (mr); } @@ -1478,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 @@ -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 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 (); } 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/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; diff --git a/gtk2_ardour/region_ui_settings.cc b/gtk2_ardour/region_ui_settings.cc index adb10e8bc9..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); @@ -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)); } 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; diff --git a/gtk2_ardour/strip_import_dialog.cc b/gtk2_ardour/strip_import_dialog.cc index bd60c375c1..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 (); @@ -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 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/session_state.cc b/libs/ardour/session_state.cc index ab34667110..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; 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; diff --git a/libs/surfaces/mackie/device_info.cc b/libs/surfaces/mackie/device_info.cc index 71eb29963a..3d6a84b341 100644 --- a/libs/surfaces/mackie/device_info.cc +++ b/libs/surfaces/mackie/device_info.cc @@ -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; diff --git a/libs/surfaces/mackie/device_info.h b/libs/surfaces/mackie/device_info.h index 1e22214740..0d930ba9e2 100644 --- a/libs/surfaces/mackie/device_info.h +++ b/libs/surfaces/mackie/device_info.h @@ -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; diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index aa4438ed2c..d7b29011dd 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_p1m() || _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..029c1be937 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_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 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); } @@ -1639,6 +1725,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 +1762,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..dd0e2b0e08 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_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 _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,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& 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 */ GSource* input_source; 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/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/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)