Module:Keyboard: Difference between revisions
Jump to navigation
Jump to search
mNo edit summary |
m Automatic keyboard layout |
||
| Line 1: | Line 1: | ||
local rat = require('Module:Rational') | |||
local ET = require('Module:ET') | local ET = require('Module:ET') | ||
local p = {} | local p = {} | ||
local layout_cache = { | |||
['0/1'] = 'b', | |||
['1/1'] = 'w' | |||
} | |||
-- `n`: white keys needed | |||
-- `m`: total keys needed | |||
function p.auto_layout(n, m, convergents) | |||
if m == 0 then | |||
return '' | |||
elseif layout_cache[n .. '/' .. m] then | |||
return layout_cache[n .. '/' .. m] | |||
end | |||
local s = '' | |||
local max_i = -1/0 | |||
for i, ratio in ipairs(convergents) do | |||
local r_n, r_m = rat.as_pair(ratio) | |||
if r_m >= m or r_n > n then | |||
break | |||
elseif n - r_n > m - r_m then | |||
break | |||
else | |||
max_i = i | |||
end | |||
end | |||
local r_n, r_m = rat.as_ratio(convergents[max_i]) | |||
local r_layout = p.auto_layout(r_n, r_m, convergents) | |||
if not layout_cache[r_n .. '/' .. r_m] then | |||
layout_cache[r_n .. '/' .. r_m] = r_layout | |||
end | |||
return r_layout .. p.auto_layout(n - r_n, m - r_m, convergents) | |||
end | |||
function p.ET(frame) | function p.ET(frame) | ||
| Line 9: | Line 42: | ||
local layout = frame.args[2] | local layout = frame.args[2] | ||
if not layout and rat.eq(et.equave, 2) and et.size > 0 then | |||
local fifth = ET.approximate(et, 3/2) | |||
local convergents = rat.convergents( | |||
math.log(3/2)/math.log(2), | |||
function(ratio) | |||
local r_n, r_m = rat.as_pair(ratio) | |||
return r_m > et.size | |||
end | |||
) | |||
layout = p.auto_layout(fifth, et.size, convergents) | |||
end | |||
local key_width = tonumber(frame.args['Key width']) or 30 | local key_width = tonumber(frame.args['Key width']) or 30 | ||
Revision as of 13:19, 17 October 2022
| Introspection summary for Module:Keyboard | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||||||||||||||||||||
No function descriptions were provided. The Lua code may have further information.
local rat = require('Module:Rational')
local ET = require('Module:ET')
local p = {}
local layout_cache = {
['0/1'] = 'b',
['1/1'] = 'w'
}
-- `n`: white keys needed
-- `m`: total keys needed
function p.auto_layout(n, m, convergents)
if m == 0 then
return ''
elseif layout_cache[n .. '/' .. m] then
return layout_cache[n .. '/' .. m]
end
local s = ''
local max_i = -1/0
for i, ratio in ipairs(convergents) do
local r_n, r_m = rat.as_pair(ratio)
if r_m >= m or r_n > n then
break
elseif n - r_n > m - r_m then
break
else
max_i = i
end
end
local r_n, r_m = rat.as_ratio(convergents[max_i])
local r_layout = p.auto_layout(r_n, r_m, convergents)
if not layout_cache[r_n .. '/' .. r_m] then
layout_cache[r_n .. '/' .. r_m] = r_layout
end
return r_layout .. p.auto_layout(n - r_n, m - r_m, convergents)
end
function p.ET(frame)
local et = ET.parse(frame.args[1])
local from = tonumber(frame.args['From']) or 0
local to = tonumber(frame.args['To']) or et.size - 1
local base_freq = tonumber(frame.args['Base frequency']) or 440
local layout = frame.args[2]
if not layout and rat.eq(et.equave, 2) and et.size > 0 then
local fifth = ET.approximate(et, 3/2)
local convergents = rat.convergents(
math.log(3/2)/math.log(2),
function(ratio)
local r_n, r_m = rat.as_pair(ratio)
return r_m > et.size
end
)
layout = p.auto_layout(fifth, et.size, convergents)
end
local key_width = tonumber(frame.args['Key width']) or 30
local key_height = tonumber(frame.args['Key height']) or 60
local dur = tonumber(frame.args['Duration']) or 500
local gain = tonumber(frame.args['Gain']) or 0.1
local instrument = frame.args['Instrument'] or 'sine'
local spectrum_arg = frame.args['Harmonic spectrum']
local harmonic_spectrum = nil
if spectrum_arg then
harmonic_spectrum = {}
for val in spectrum_arg:gmatch('%S+') do
table.insert(harmonic_spectrum, tonumber(val))
end
end
return p.build_ET_keyboard(et, from, to, base_freq, layout, key_width, key_height, dur, gain, instrument, harmonic_spectrum)
end
function p.build(frame)
local freqs_arg = frame.args[1]
local freqs = {}
for freq in freqs_arg:gmatch('%S+') do
table.insert(freqs, tonumber(freq))
end
local colours = frame.args[2]
local key_width = tonumber(frame.args['Key width']) or 30
local key_height = tonumber(frame.args['Key height']) or 60
local dur = tonumber(frame.args['Duration']) or 500
local gain = tonumber(frame.args['Gain']) or 0.1
local instrument = frame.args['Instrument'] or 'sine'
local spectrum_arg = frame.args['Harmonic spectrum']
local harmonic_spectrum = nil
if spectrum_arg then
harmonic_spectrum = {}
for val in spectrum_arg:gmatch('%S+') do
table.insert(harmonic_spectrum, tonumber(val))
end
end
return p.build_keyboard(freqs, colours, {}, key_width, key_height, dur, gain, instrument, harmonic_spectrum)
end
function p.build_ET_keyboard(et, from, to, base_freq, layout, key_width, key_height, dur, gain, instrument, harmonic_spectrum)
if #layout ~= et.size then
return '<span style="color:red;">Layout for ' .. ET.as_string(et) .. ' should have exactly ' .. et.size .. ' elements!</span>'
end
local freqs = {}
local colours = ''
local seps = {}
for i = from, to do
local cents = ET.cents(et, i)
local hz = 2 ^ (math.log(base_freq)/math.log(2) + cents/1200)
table.insert(freqs, hz)
local j = (i % et.size) + 1
colours = colours .. layout:sub(j, j)
if j == 1 and i > from then
seps[i - from + 1] = true
end
end
return p.build_keyboard(freqs, colours, seps, key_width, key_height, dur, gain, instrument, harmonic_spectrum)
end
function p.build_keyboard(freqs, colours, seps, key_width, key_height, dur, gain, instrument, harmonic_spectrum)
if #freqs ~= #colours then
return '<span style="color:red;">Number of keys (' .. (#freqs) .. ') and of colours (' .. (#colours) .. ') are different!</span>'
end
local n = #freqs
local s = '<div style="display: flex; width: fit-content;">'
for i = 1, n do
s = s .. '<div class="sequence-audio sequence-audio-button'
if colours:sub(i, i) == 'w' then
s = s .. ' white-key'
elseif colours:sub(i, i) == 'b' then
s = s .. ' black-key'
end
s = s .. '" data-sequence="' .. freqs[i] .. ':' .. dur .. ':' .. gain .. ':0:' .. instrument .. '"'
if harmonic_spectrum then
s = s .. ' data-timbre-' .. instrument .. '="' .. table.concat(harmonic_spectrum, ' ') .. '"'
end
s = s .. ' style="width: ' .. key_width .. 'px; height: ' .. key_height .. 'px; border: 1px solid #7f7f7f;'
if seps[i] then
s = s .. ' border-left: 1px solid #ff0000;'
end
if seps[i + 1] then
s = s .. ' border-right: 1px solid #ff0000;'
end
s = s .. '"></div>'
end
s = s .. '</div>'
return s
end
return p