ardour/share/scripts/ir_capture.lua

189 lines
5 KiB
Lua
Raw Normal View History

ardour {
["type"] = "dsp",
name = "Lua IR Capture",
license = "MIT",
author = "Ardour Team",
description = [[
1. Calibrate I/O Latency (Menu > Window > Audio/MIDI Setup)
2025-01-19 15:39:50 +01:00
2. Create a mono track - this will be used to record the IR.
3. Disconnect the input and output of that track.
4. Add a mono bus and load this plugin on the bus.
5. Connect the left output of the bus to the mono track's input.
2025-01-19 15:39:50 +01:00
The deconvolved Impulse Response will be output on that channel.
6. Connect the right output of the bus to a hardware playback port, matching the system you want to capture.
A sine-sweep will be played on that channel.
2025-01-19 15:39:50 +01:00
7. Connect the bus' input to the return signal (hardware capture) of the system under test.
8. Record Arm the track (from 2)
9. Record Arm the session
2025-01-19 15:39:50 +01:00
10. Open the GUI of this plugin, enable "Capture"
11. Roll the transport.
12. Wait 10 sec (recording stops automatically).
13. Select the region on the mono track: Region > Edit > Strip Silence and/or manually tweak the region start:
move it close to the initial impulse.
14. Region > Gain > Normalize, or manually adjust gain.
15. Disable region-fades of the region: Region > Fades
16. Export the region to save the IR file (Region > Export)
]]
}
function dsp_ioconfig () return
{
connect_all_audio_outputs = true, -- override strict-i/o
{ audio_in = 1, audio_out = 2},
}
end
function dsp_params ()
return {
{ ["type"] = "input", name = "Capture", min = 0, max = 1, default = 0, toggled = true },
{ ["type"] = "output", name = "State", min = 0, max = 2, enum = true, scalepoints =
{
["Idle"] = 0,
["Capturing"] = 1,
["Finalize"] = 2,
}
},
{ ["type"] = "output", name = "IR Signal Level", min = -120, max = 0 },
}
end
local conv
local state
local sweep_sin
local peak
local rec_len
local n_captured
local n_playback
function gen_sweep (fmin, fmax, t_sec, rate)
local n_samples_pre = rate * 0.1
local n_samples_sin = rate * t_sec
local n_samples_end = rate * 0.03
local n_samples = n_samples_pre + n_samples_sin + n_samples_end
local amp = 0.5
local a = math.log (fmax / fmin) / n_samples_sin
local b = fmin / (a * rate)
local r = 4.0 * a * a / amp
local cmem = ARDOUR.DSP.DspShm (n_samples * 2)
local ss = cmem:to_float (0):array()
local si = cmem:to_float (n_samples):array()
for i = 0, n_samples - 1 do
local j = n_samples - i - 1
local gain = 1.0
if i < n_samples_pre then
gain = math.sin (0.5 * math.pi * i / n_samples_pre)
elseif j < n_samples_end then
gain = math.sin (0.5 * math.pi * j / n_samples_end)
end
local d = b * math.exp (a * (i - n_samples_pre))
local p = d - b
local x = gain * math.sin (2 * math.pi * (p - math.floor (p)))
ss[i + 1] = x * amp
si[j + 1] = x * d * r
end
sweep_sin = ARDOUR.AudioRom.new_rom (cmem:to_float (0), n_samples)
-- de-convolver
local sweep_inv = ARDOUR.AudioRom.new_rom (cmem:to_float (n_samples), n_samples)
conv = ARDOUR.DSP.Convolution (Session, 1, 1)
conv:add_impdata (0, 0, sweep_inv, 1.0, 0, 0, 0, 0)
conv:restart ()
sweep_inv = nil
cmem = nil
collectgarbage ()
end
function dsp_init (rate)
local fmax = 20000
local slen = 5 -- sec
local rlen = slen + 5 -- sec
if fmax > rate * .47 then
fmax = rate * .47
end
2025-01-19 15:39:50 +01:00
gen_sweep (20, fmax, slen, rate)
rec_len = rlen * rate
n_captured = 0
n_playback = 0
peak = 0
state = 0
end
function dsp_latency ()
return conv:latency()
end
function reset ()
state = 0
if n_captured > 0 then
conv:restart ()
end
n_captured = 0
n_playback = 0
end
function dsp_runmap (bufs, in_map, out_map, n_samples, offset)
local ctrl = CtrlPorts:array() -- get control port array
local capture = ctrl[1] > 0
if state == 0 and capture and Session:actively_recording () then
state = 1
peak = 0
print ("START CAPTURE")
end
if state == 2 then
Session:request_stop (false, false, ARDOUR.TransportRequestSource.TRS_UI)
reset ()
end
-- check I/O mapping
local input = in_map:get (ARDOUR.DataType ("audio"), 0)
local outir = out_map:get (ARDOUR.DataType ("audio"), 0)
local outsw = out_map:get (ARDOUR.DataType ("audio"), 1)
if input == ARDOUR.ChanMapping.Invalid or outir == ARDOUR.ChanMapping.Invalid or outsw == ARDOUR.ChanMapping.Invalid or input ~= outir then
reset ();
end
if state ~= 1 then
bufs:get_audio (outir):silence (n_samples, offset)
end
if state == 1 then -- capture
conv:run_mono_buffered (bufs:get_audio(input):data (offset), n_samples)
if n_samples + n_captured > rec_len then
state = 2
end
peak = ARDOUR.DSP.compute_peak (bufs:get_audio(input):data (offset), n_samples, peak) -- compute digital peak
n_captured = n_captured + n_samples
end
if state == 1 then
-- play back sine-sweep
local copied = sweep_sin:read (bufs:get_audio (outsw):data (offset), n_playback, n_samples, 0)
n_playback = n_playback + copied
if copied < n_samples then
bufs:get_audio (outsw):silence (n_samples - copied, offset + copied)
end
else
bufs:get_audio (outsw):silence (n_samples, offset)
end
ctrl[2] = state
ctrl[3] = ARDOUR.DSP.accurate_coefficient_to_dB (peak)
end