hi all!!!
I start to learn lua with ardour. I begin to read the docs, examples and help me with IA to understand how to it works.
I found a post confirming that I have to restart Ardour to reload the script if I make changes.
This is very inconvenient during the development process.
It would be great if Ardour had a “Developer mode” option for these situations, to detect script changes and incorporate the new modifications. It wouldn’t matter if it added some processing overhead (or resulted in some optimization loss), as it would only be for script development and not for actual production.
I recently acquired a Novation Launchkey 49 MK2, and this script sends the tempo beat to the first four buttons that light up, essentially creating a visual metronome.
I hope it helps others as it helped me to start learning Lua.
ardour {
["type"] = "dsp",
name = "Beat Pad Flasher",
author = "ragnarok@docksud.com.ar",
category = "Utility",
description = [[
Metrónomo visual para Launchkey MK2 49.
Beat 1 del compás = ROJO. Beats 2/3/4 = VERDE en los 4 pads cuadrados.
SETUP:
1. Crear un Instrument Track (sin instrumento).
2. Agregar este plugin al track.
3. Conectar salida MIDI del track a "Launchkey MK2 49 Launchkey InCo".
4. Play → pads parpadean al ritmo.
]]
}
function dsp_ioconfig ()
return { { midi_in = 1, midi_out = 1, audio_in = 0, audio_out = 0 } }
end
function dsp_options ()
return { time_info = true }
end
-- ============================================================
-- Configuración del Launchkey MK2
-- ============================================================
local PAD_NOTES = { 96, 97, 98, 99 } -- 4 pads cuadrados inferiores
local COLOR_BEAT1 = 5 -- Rojo (beat 1 del compás)
local COLOR_OTHER = 21 -- Verde (beats 2, 3, 4)
local FLASH_FRAC = 0.4 -- El pad se apaga al 40% del beat
-- ============================================================
-- Estado global
-- ============================================================
local srate = 48000
local last_beat = nil
local was_rolling = false
local needs_init = true
local pad_lit = { false, false, false, false }
local pad_off_at = { 0, 0, 0, 0 } -- sample absoluto de apagado
function dsp_init (rate)
srate = rate
needs_init = true
last_beat = nil
was_rolling = false
for i = 1, 4 do
pad_lit[i] = false
pad_off_at[i] = 0
end
end
-- ============================================================
-- Loop principal DSP
-- ============================================================
function dsp_run (_, _, n_samples)
assert (type(midiout) == "table")
assert (type(time) == "table")
local k = 1
-- Enviar Extended Mode + activar pads InControl al arrancar
if needs_init then
midiout[k] = { time = 1, data = { 0x9F, 0x0C, 0x7F } } -- Extended Mode ON
k = k + 1
midiout[k] = { time = 2, data = { 0x9F, 0x0F, 0x7F } } -- Pads → InControl
k = k + 1
needs_init = false
return
end
-- Pass-through del MIDI entrante
for _, ev in ipairs(midiin) do
midiout[k] = ev
k = k + 1
end
-- Estado del transporte
local rolling = Session:transport_state_rolling()
-- Se detuvo → apagar todos los pads
if was_rolling and not rolling then
for i, note in ipairs(PAD_NOTES) do
if pad_lit[i] then
midiout[k] = { time = 1, data = { 0x9F, note, 0 } }
k = k + 1
pad_lit[i] = false
end
end
last_beat = nil
end
was_rolling = rolling
if not rolling then return end
-- Obtener BBT desde el tempo map
local tm = Temporal.TempoMap.read()
local pos = Temporal.timepos_t(time.sample)
local bbt = tm:bbt_at(pos)
local beat = bbt.beats -- beat dentro del compás (1, 2, 3...)
-- Calcular duración del flash en samples
local tempo_qpm = tm:tempo_at(pos):quarter_notes_per_minute()
local beat_samps = math.floor((60.0 / tempo_qpm) * srate * FLASH_FRAC)
-- Pad correspondiente al beat (cicla 1..4)
local pad_idx = ((beat - 1) % 4) + 1
local color = (beat == 1) and COLOR_BEAT1 or COLOR_OTHER
-- Nuevo beat → encender pad
if beat ~= last_beat then
last_beat = beat
-- Apagar pads anteriores
for i, note in ipairs(PAD_NOTES) do
if pad_lit[i] and i ~= pad_idx then
midiout[k] = { time = 1, data = { 0x9F, note, 0 } }
k = k + 1
pad_lit[i] = false
end
end
-- Encender pad actual
midiout[k] = { time = 1, data = { 0x9F, PAD_NOTES[pad_idx], color } }
k = k + 1
pad_lit[pad_idx] = true
pad_off_at[pad_idx] = time.sample + beat_samps
end
-- Apagar pads que cumplieron el flash
local cycle_end = time.sample + n_samples
for i, note in ipairs(PAD_NOTES) do
if pad_lit[i] then
local off_at = pad_off_at[i]
if off_at < cycle_end then
local t_off = off_at - time.sample + 1
if t_off < 1 then t_off = 1 end
midiout[k] = { time = t_off, data = { 0x9F, note, 0 } }
k = k + 1
pad_lit[i] = false
end
end
end
end