linn/linndrum.cpp: Made knobs controllable by pointing device. (#13433)

This commit is contained in:
m1macrophage 2025-03-01 13:09:35 -08:00 committed by GitHub
parent 5360641faf
commit e6c3d78568
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 81 additions and 57 deletions

View File

@ -224,7 +224,7 @@ copyright-holders:m1macrophage
<simplecounter maxstate="100" digits="1"><color red="1.0" green="0.5" blue="0.2"/></simplecounter>
</element>
<group name="knob">
<element ref="knob_outer"><bounds x="0" y="0" width="68" height="68"/></element>
<element ref="knob_outer" id="knob_~knob_input~"><bounds x="0" y="0" width="68" height="68"/></element>
<element ref="knob_inner"><bounds x="9" y="9" width="50" height="50"/></element>
<element ref="knob_value_background" clickthrough="no">
<bounds x="17" y="22" width="34" height="24"/>
@ -596,6 +596,15 @@ copyright-holders:m1macrophage
function()
local view = file.views["Default Layout"]
install_slider_callbacks(view)
local sweep_scale = 1.2
add_simplecounter_knob(view, "knob_pot_tempo", "pot_tempo", sweep_scale)
add_simplecounter_knob(view, "knob_pot_volume", "pot_volume", sweep_scale)
for i = 1, 7 do
local knob_input = "pot_tuning_" .. i
add_simplecounter_knob(view, "knob_" .. knob_input, knob_input, sweep_scale)
end
for i = 1, 16 do
local pan_input = "pot_pan_" .. i
add_vertical_slider(view, "slider_" .. pan_input, "slider_knob_" .. pan_input, pan_input)
@ -605,47 +614,57 @@ copyright-holders:m1macrophage
end)
-----------------------------------------------------------------------
-- Slider library starts.
-- Slider and knob library starts.
-- Can be copied as-is to other layouts.
-----------------------------------------------------------------------
local sliders = {} -- Stores slider information.
local widgets = {} -- Stores slider and knob information.
local pointers = {} -- Tracks pointer state.
-- The knob's Y position must be animated using <animate inputtag="{port_name}">.
-- The click area's vertical size must exactly span the range of the
-- knob's movement.
function add_vertical_slider(view, clickarea_id, knob_id, port_name)
local slider = {}
table.insert(widgets, {
clickarea = get_layout_item(view, clickarea_id),
slider_knob = get_layout_item(view, knob_id),
field = get_port_field(port_name),
is_knob = false })
end
slider.clickarea = view.items[clickarea_id]
if slider.clickarea == nil then
emu.print_error("Slider element: '" .. clickarea_id .. "' not found.")
return
end
slider.knob = view.items[knob_id]
if slider.knob == nil then
emu.print_error("Slider knob element: '" .. knob_id .. "' not found.")
return
-- A sweep between 0 and 100 requires moving the pointer by
-- `scale * clickarea.height` pixes.
function add_simplecounter_knob(view, clickarea_id, port_name, scale)
table.insert(widgets, {
clickarea = get_layout_item(view, clickarea_id),
field = get_port_field(port_name),
is_knob = true,
scale = scale })
end
function get_layout_item(view, item_id)
local item = view.items[item_id]
if item == nil then
emu.print_error("Layout element: '" .. item_id .. "' not found.")
end
return item
end
function get_port_field(port_name)
local port = file.device:ioport(port_name)
if port == nil then
emu.print_error("Port: '" .. port_name .. "' not found.")
return
return nil
end
slider.field = nil
local field = nil
for k, val in pairs(port.fields) do
slider.field = val
field = val
break
end
if slider.field == nil then
if field == nil then
emu.print_error("Port: '" .. port_name .."' does not seem to be an IPT_ADJUSTER.")
return
return nil
end
table.insert(sliders, slider)
return field
end
local function pointer_updated(type, id, dev, x, y, btn, dn, up, cnt)
@ -655,56 +674,64 @@ copyright-holders:m1macrophage
return
end
-- If a button was just pressed, find the affected slider, if any.
-- If a button was just pressed, find the affected widget, if any.
if dn & 1 ~= 0 then
for i = 1, #sliders do
if sliders[i].knob.bounds:includes(x, y) then
for i = 1, #widgets do
local found, relative
if widgets[i].slider_knob and widgets[i].slider_knob.bounds:includes(x, y) then
found = true
relative = true
elseif widgets[i].clickarea.bounds:includes(x, y) then
found = true
relative = false
end
if found then
pointers[id] = {
selected_slider = i,
relative = true,
selected_widget = i,
relative = relative,
start_y = y,
start_value = sliders[i].field.user_value }
break
elseif sliders[i].clickarea.bounds:includes(x, y) then
pointers[id] = {
selected_slider = i,
relative = false }
start_value = widgets[i].field.user_value }
break
end
end
end
-- If there is no slider selected by the current pointer, we are done.
-- If there is no widget selected by the current pointer, we are done.
if pointers[id] == nil then
return
end
-- A slider is selected. Update state and, indirectly, slider knob position,
-- based on the pointer's Y position. It is assumed the attached IO field is
-- an IPT_ADJUSTER with a range of 0-100 (the default).
-- A widget is selected. Update its state based on the pointer's Y
-- position. It is assumed the attached IO field is an
-- IPT_ADJUSTER with a range of 0-100 (the default).
local pointer = pointers[id]
local slider = sliders[pointer.selected_slider]
local widget = widgets[pointer.selected_widget]
local knob_half_height = slider.knob.bounds.height / 2
local min_y = slider.clickarea.bounds.y0 + knob_half_height
local max_y = slider.clickarea.bounds.y1 - knob_half_height
local new_value = 0
if pointer.relative then
-- User clicked on the knob. The new value will depend on how much the
-- knob was dragged.
new_value = pointer.start_value - 100 * (y - pointer.start_y) / (max_y - min_y)
local new_value
if widget.is_knob then
local step_y = 100.0 / (widget.scale * widget.clickarea.bounds.height)
new_value = pointer.start_value + (pointer.start_y - y) * step_y
else
-- User clicked elsewhere on the slider. The new value will depend on
-- the absolute position of the click.
new_value = 100 - 100 * (y - min_y) / (max_y - min_y)
local knob_half_height = widget.slider_knob.bounds.height / 2
local min_y = widget.clickarea.bounds.y0 + knob_half_height
local max_y = widget.clickarea.bounds.y1 - knob_half_height
if pointer.relative then
-- User clicked on the knob. The new value will depend on how
-- much the knob was dragged.
new_value = pointer.start_value - 100 * (y - pointer.start_y) / (max_y - min_y)
else
-- User clicked elsewhere on the slider. The new value will depend on
-- the absolute position of the click.
new_value = 100 - 100 * (y - min_y) / (max_y - min_y)
end
end
new_value = math.floor(new_value + 0.5)
if new_value < 0 then new_value = 0 end
if new_value > 100 then new_value = 100 end
slider.field.user_value = new_value
widget.field.user_value = new_value
end
local function pointer_left(type, id, dev, x, y, up, cnt)
@ -726,7 +753,7 @@ copyright-holders:m1macrophage
view:set_forget_pointers_callback(forget_pointers)
end
-----------------------------------------------------------------------
-- Slider library ends.
-- Slider and knob library ends.
-----------------------------------------------------------------------
]]></script>
</mamelayout>

View File

@ -33,12 +33,9 @@ PCBoards:
Usage:
The driver includes a (mostly) interactive layout, including buttons, LEDs and
sliders. The only non-interactive widgets right now are the knobs (such as
tuning and volume). Those will display their current value, but they can only
be modified by the Slider Control menu.
The driver includes an interactive layout.
Since there is no audio, the driver logs triggers and other info. To see these
Since there is no audio, the driver logs triggers and other info. To see those,
run the driver with `-log`:
./mame -window linndrum -log