Class: MusaLCEServer::Surface
- Inherits:
-
Object
- Object
- MusaLCEServer::Surface
- Defined in:
- lib/surface.rb
Overview
Authoritative model of the physical control surface (Stream Deck buttons, encoders, …) as seen from the server.
The surface is the abstraction shared with hardware but surface-agnostic: it knows only about named controls with a type and dynamic state. Each control is keyed by its event name — a Symbol (e.g. +:launch_chorus+) that doubles as the identifier used with the sequencer's +on+/+launch+ mechanism when the control fires. In MusaLCE the unit of interaction is always the event; the surface is the physical face of one or more events.
Ownership of the two data axes:
- Inventory (which controls exist and their type) flows inbound from Pulso Bridge through the DAW extension; the server trusts what it receives (Pulso validates type consistency across physical instances).
- State (message, enabled, value, …) is owned by the server:
the score writes to +surface[:event]+ and changes propagate
outbound on +/musalce/surface/state/
+.
Inventory may arrive as a full dump (between +inventory/begin+ and +inventory/end+, in which case events absent from the dump are purged at end) or as runtime deltas (+inventory/add+ / +inventory/remove+). Re-adding an event with the same type preserves its state; a type change replaces the control and resets state.
All mutating methods are expected to run on the sequencer tick thread (inbound OSC messages are routed there by SurfaceBridge). Score code writing +surface[:event].xxx =+ ... also runs on that thread (inside +at+/+every+/+on+ blocks), which keeps access serial without explicit locking.
Instance Method Summary collapse
-
#[](event) ⇒ Control?
Returns the control for the given event, or +nil+ if unknown.
-
#add_control(event, type) ⇒ Control
private
Registers (or refreshes) a control in the inventory.
-
#begin_inventory ⇒ void
private
Begins a full inventory dump.
-
#emit_full_state ⇒ void
private
Re-emits state for every known control.
-
#emit_state(event, prop, *value) ⇒ void
private
Called by a Control when one of its properties changes; relays to the bridge.
-
#end_inventory ⇒ void
private
Ends a full inventory dump.
-
#events ⇒ Array<Symbol>
All known control events.
-
#initialize(bridge:, logger:) ⇒ Surface
constructor
A new instance of Surface.
-
#known?(event) ⇒ Boolean
Whether a control with this event is in the inventory.
-
#remove_control(event) ⇒ Control?
private
Removes a control from the inventory and drops its state.
Constructor Details
#initialize(bridge:, logger:) ⇒ Surface
Returns a new instance of Surface.
40 41 42 43 44 45 |
# File 'lib/surface.rb', line 40 def initialize(bridge:, logger:) @bridge = bridge @logger = logger @controls = {} @pending_events = nil end |
Instance Method Details
#[](event) ⇒ Control?
Returns the control for the given event, or +nil+ if unknown.
A control becomes known once its inventory entry has been received from the surface. Score code that runs before that should use safe navigation (+surface[:foo]&.enabled = true+) or guard with #known?.
56 57 58 |
# File 'lib/surface.rb', line 56 def [](event) @controls[event.to_sym] end |
#add_control(event, type) ⇒ Control
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Registers (or refreshes) a control in the inventory.
If a control with the same event and type already exists, its state is preserved. If the type differs, the existing control is replaced with a fresh instance (state reset).
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/surface.rb', line 89 def add_control(event, type) event = event.to_sym type = type.to_sym existing = @controls[event] if existing && existing.class.type_name == type ctrl = existing else ctrl = Control.create(type, event: event, surface: self) @controls[event] = ctrl @logger.info "Surface: added control #{event} (#{type})" end @pending_events << event if @pending_events ctrl end |
#begin_inventory ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Begins a full inventory dump. Events that are not re-added before #end_inventory are purged.
75 76 77 |
# File 'lib/surface.rb', line 75 def begin_inventory @pending_events = Set.new end |
#emit_full_state ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Re-emits state for every known control. Used after an inventory dump or in response to a +state_request+ from the surface side.
140 141 142 |
# File 'lib/surface.rb', line 140 def emit_full_state @controls.each_value(&:emit_all_state) end |
#emit_state(event, prop, *value) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Called by a Control when one of its properties changes; relays to the bridge.
151 152 153 |
# File 'lib/surface.rb', line 151 def emit_state(event, prop, *value) @bridge.send_state(event: event, prop: prop, value: value) end |
#end_inventory ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Ends a full inventory dump. Any event present before the dump but not re-added between #begin_inventory and this call is purged. Re-emits all state so the surface re-syncs after the round-trip.
123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/surface.rb', line 123 def end_inventory if @pending_events stale = @controls.keys - @pending_events.to_a stale.each do |event| @controls.delete(event) @logger.info "Surface: purged stale control #{event}" end @pending_events = nil end emit_full_state end |
#events ⇒ Array<Symbol>
Returns all known control events.
61 62 63 |
# File 'lib/surface.rb', line 61 def events @controls.keys end |
#known?(event) ⇒ Boolean
Returns whether a control with this event is in the inventory.
67 68 69 |
# File 'lib/surface.rb', line 67 def known?(event) @controls.key?(event.to_sym) end |
#remove_control(event) ⇒ Control?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Removes a control from the inventory and drops its state.
110 111 112 113 114 115 |
# File 'lib/surface.rb', line 110 def remove_control(event) event = event.to_sym removed = @controls.delete(event) @logger.info "Surface: removed control #{event}" if removed removed end |