ardour/share/scripts/a_dtmf_phone.lua

199 lines
4.3 KiB
Lua

ardour {
["type"] = "dsp",
name = "ACE DTMF Phone",
category = "Instrument",
license = "MIT",
author = "Ardour Team",
description = [[Ardour phone home]]
}
local r = 48000
local p1 = 0
local p2 = 0
local tau = 2 * math.pi
local active_note = -1
local ringsize = 8
function dsp_ioconfig ()
return {
{ midi_in = 1, audio_in = 0, audio_out = 1},
{ midi_in = 1, audio_in = 0, audio_out = 2}
}
end
function dsp_init (rate)
r = rate
-- allocate DSP -> GUI ringbuffer
self:shmem():allocate (1 + ringsize)
self:shmem():atomic_set_int (0, 0)
local buffer = self:shmem():to_int(1):array()
for i = 1, ringsize do
buffer[i] = -1 -- empty slot
end
end
-- https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling#Keypad
local dtmf = {
["1"] = {1209, 697},
["2"] = {1336, 697},
["3"] = {1447, 697},
["A"] = {1633, 697},
["4"] = {1209, 770},
["5"] = {1336, 770},
["6"] = {1447, 770},
["B"] = {1633, 770},
["7"] = {1209, 852},
["8"] = {1336, 852},
["9"] = {1447, 852},
["C"] = {1633, 852},
["*"] = {1209, 941},
["0"] = {1336, 941},
["#"] = {1447, 941},
["D"] = {1633, 941},
[" "] = {0, 0},
}
local map = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "*", "#", "A", "B", "C", "D"}
function midi_note_to_pad (n)
-- map [1..9 0 * #] to 12TET, ignore A..D
-- start at C == 1
return map [1 + n % 12]
end
function dsp_run (ins, outs, n_samples)
-- clear output buffer
local a = {}
for s = 1, n_samples do a[s] = 0 end
local function synth (s_start, s_end)
if active_note == -1 then
return
end
local f = dtmf[midi_note_to_pad (active_note)]
if f[1] == 0 then
return
end
local i1 = f[1] / r
local i2 = f[2] / r
for s = s_start,s_end do
a[s] = 0.5 * (math.sin (p1 * tau) + math.sin (p2 * tau))
p1 = p1 + i1
p2 = p2 + i2
end
end
local newdata = false
local tme = 1
for _,b in pairs (midiin) do
local t = b["time"] -- t = [ 1 .. n_samples ]
-- synth sound until event
synth (tme, t)
tme = t + 1
-- process MIDI events (ignore MIDI channel)
local d = b["data"] -- get midi-event
if (#d == 3 and (d[1] & 240) == 144) then -- note on
--if active_note == -1 then
active_note = d[2]
p1, p2 = 0, 0
-- inform UI
local pos = self:shmem():atomic_get_int (0)
local buffer = self:shmem():to_int(1):array()
buffer[1 + pos] = active_note
pos = (pos + 1) % ringsize
self:shmem():atomic_set_int (0, pos)
newdata = true
--end
end
if (#d == 3 and (d[1] & 240) == 128) then -- note off
if active_note == d[2] then
active_note = -1
end
end
if (#d == 3 and (d[1] & 240) == 176) then -- CC
if (d[2] == 120 or d[2] == 123) then -- panic
active_note = -1
-- clear UI
self:shmem():atomic_set_int (0, 0)
local buffer = self:shmem():to_int(1):array()
for i = 1, ringsize do
buffer[i] = -1 -- empty slot
end
newdata = true
end
end
end
-- synth rest of cycle
synth (tme, n_samples)
-- keep phase between 0..tau
p1 = math.fmod (p1, 1)
p2 = math.fmod (p2, 1)
-- set all outputs
for c = 1,#outs do
outs[c]:set_table (a, n_samples)
end
if newdata then
self:queue_draw ()
end
end
-----------------
-- inline display
-----------------
local txt = nil -- a pango context
local vpadding = 2
local displayheight = 0
local linewidth = 0
function render_inline (ctx, displaywidth, max_h)
local pos = self:shmem():atomic_get_int(0)
local buffer = self:shmem():to_int(1):array()
local str = ""
for i = 1, ringsize do
local p = (i + pos + ringsize - 1) % ringsize
local n = buffer[1 + p]
if n ~= -1 and " " ~= midi_note_to_pad (n) then
str = str .. midi_note_to_pad (n)
end
end
if not txt then
txt = Cairo.PangoLayout (ctx, "Mono 10")
-- compute the size of the display
local siz = ""
for i = 1, ringsize do
siz = siz .. "0"
end
txt:set_text (siz)
local lineheight
linewidth, lineheight = txt:get_pixel_size()
displayheight = math.min (2 * vpadding + lineheight, max_h)
end
-- clear background
ctx:rectangle (0, 0, displaywidth, displayheight)
ctx:set_source_rgba (.2, .2, .2, 1.0)
ctx:fill ()
-- show dialed number
ctx:set_source_rgba (1.0, 1.0, 1.0, 1.0)
txt:set_text (str)
ctx:move_to (math.floor (.5 * (displaywidth - linewidth)), vpadding)
txt:show_in_cairo_context (ctx)
return {displaywidth, displayheight}
end