From e6c3d7856895a2fccf4266e30c0de4f3d6bfb104 Mon Sep 17 00:00:00 2001 From: m1macrophage <168948267+m1macrophage@users.noreply.github.com> Date: Sat, 1 Mar 2025 13:09:35 -0800 Subject: [PATCH] linn/linndrum.cpp: Made knobs controllable by pointing device. (#13433) --- src/mame/layout/linn_linndrum.lay | 131 ++++++++++++++++++------------ src/mame/linn/linndrum.cpp | 7 +- 2 files changed, 81 insertions(+), 57 deletions(-) diff --git a/src/mame/layout/linn_linndrum.lay b/src/mame/layout/linn_linndrum.lay index f755488d13f..71fe129471f 100644 --- a/src/mame/layout/linn_linndrum.lay +++ b/src/mame/layout/linn_linndrum.lay @@ -224,7 +224,7 @@ copyright-holders:m1macrophage - + @@ -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 . -- 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. ----------------------------------------------------------------------- ]]> diff --git a/src/mame/linn/linndrum.cpp b/src/mame/linn/linndrum.cpp index 608b3cd4a88..50e066e1fe6 100644 --- a/src/mame/linn/linndrum.cpp +++ b/src/mame/linn/linndrum.cpp @@ -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