-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.lua
120 lines (102 loc) · 3.49 KB
/
main.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
local gens, effects = require("units").gens, require("units").effects
local function showHelpFor(unit, name)
local unitType = "a generator"
if unit.new().process then unitType = "an effect" end
io.stderr:write(name .. " is " .. unitType .. "\n\n")
for k,v in pairs(unit.knobInfo) do
io.stderr:write("-" .. k .. ": " .. v.label .. "\n")
if v.options then
io.stderr:write(" Options: ")
for k_,v_ in pairs(v.options) do
io.stderr:write('"' .. v_ .. '" ')
end
io.stderr:write("\n")
else
io.stderr:write(" Minimum: " .. v.min .. "\n")
io.stderr:write(" Maximum: " .. v.max .. "\n")
end
io.stderr:write(" Default: " .. v.default .. "\n")
end
end
local function showHelp(topic)
if not topic then
io.stderr:write("usage: luasynth unitname [-param val] ...\n")
io.stderr:write("for a list of units, run 'luasynth help units'\n")
elseif topic == 'units' then
io.stderr:write("generators:\n")
for k,_ in pairs(gens) do
io.stderr:write(" " .. k .. "\n")
end
io.stderr:write("effects:\n")
for k,_ in pairs(effects) do
io.stderr:write(" " .. k .. "\n")
end
io.stderr:write("for info on a unit, run 'luasynth help unitname'\n")
else
local unit = gens[topic] or effects[topic]
if unit then
showHelpFor(unit, topic)
else
io.stderr:write(topic .. " is not a unit\n")
end
end
os.exit(1)
end
if #arg < 1 or arg[1] == 'help' or arg[1] == '--help' then
showHelp(arg[2])
end
local isEffect = true
local rate = os.getenv('RATE') or 44100
local unit = effects[arg[1]] and effects[arg[1]].new({sampleRate = rate})
if not unit then
unit = gens[arg[1]] and gens[arg[1]].new({sampleRate = rate})
if not unit then
error("No such unit: " .. arg[1])
end
isEffect = false
end
local lengthLimitSecs = -1
for i=3,#arg,2 do
local param = arg[i-1]
if string.sub(param, 1, 1) ~= "-" then
error("Expected a knob name starting with `-`: " .. param)
end
local knobName = string.sub(param, 2)
if knobName == "length" then
lengthLimitSecs = tonumber(arg[i])
elseif unit.knobInfo[knobName] then
unit[knobName] = arg[i]
else
error("Unit `" .. unit.name .. "` has no knob called `"
.. knobName .. "`")
end
end
-- Use LuaJIT's FFI to handle reading and writing floats.
local ffi = require "ffi"
ffi.cdef[[
typedef struct { float f[2]; } sample_pair;
size_t fread(void *ptr, size_t size, size_t nmemb, void *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, void *stream);
int isatty(int fd);
]]
-- Refuse to run if stdout is a terminal.
if ffi.C.isatty(1) ~= 0 then
error("Stdout should not be a terminal. Try redirecting to a file")
end
local samplePair = ffi.new("sample_pair[?]", 1)
local plainSamples = {}
local pairsLeft = math.floor(lengthLimitSecs * rate)
while pairsLeft ~= 0 do
if isEffect then
if ffi.C.fread(samplePair, 8, 1, io.stdin) <= 0 then break end
plainSamples[1] = samplePair[0].f[0]
plainSamples[2] = samplePair[0].f[1]
unit.process(plainSamples)
else
plainSamples = unit.generate(1)
end
samplePair[0].f[0] = plainSamples[1]
samplePair[0].f[1] = plainSamples[2]
if ffi.C.fwrite(samplePair, 8, 1, io.stdout) < 1 then break end
pairsLeft = pairsLeft - 1
end