aboutsummaryrefslogtreecommitdiffhomepage
path: root/applause.lua
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2023-11-16 17:50:24 +0300
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2023-11-16 17:50:24 +0300
commitebd9c3c4ec2cfe2f08b4f700dcc5bcb2a8b4b847 (patch)
tree62897dce25fad99544ebd1ddb7257e74e133e477 /applause.lua
parent429991e37fc9ca4aeb49cf05db6819f735efe5b3 (diff)
downloadapplause2-ebd9c3c4ec2cfe2f08b4f700dcc5bcb2a8b4b847.tar.gz
improved interruption (SIGINT, CTRL+C) support
* Just like the original LuaJIT interpreter, this will use a hook to automatically raise an error from Lua code. * Unfortunately, as long as Jit compilation is enabled, this cannot reliably work. Therefore we still set an `interrupted` flag that must be polled from tight loops. * Instead of polling via applause_push_sample() which gave interruption-support only to Stream:play(), we now have a separate checkint() function. * checkint() should be manually added to all tight loops. * Stream:foreach() and everthing based on it is now also supporting interruptions (via checkint()). * This internally works via applause_is_interrupted(). A C function was exposed only because LuaJIT does not support volatile-qualifiers and would optimize away reads to the interrupted-flag. As a side effect, we can also reset the hook. * Flags set in signal handlers should be `volatile`. * Added likely() and unlikely() macros to C code. * Updated sample.ipynb Jupyter notebook: Everything important is now supported, albeit requiring custom ILua patches.
Diffstat (limited to 'applause.lua')
-rw-r--r--applause.lua48
1 files changed, 27 insertions, 21 deletions
diff --git a/applause.lua b/applause.lua
index 702e1ce..702913d 100644
--- a/applause.lua
+++ b/applause.lua
@@ -32,6 +32,22 @@ end
cdef_include "applause.h"
+--- Check whether the process was interrupted (SIGINT received).
+--
+-- This checks and automatically raises an error if CTRL+C was pressed
+-- or SIGINT was received.
+-- The automatic way to handle SIGINT is **not** reliable in LuaJIT.
+-- A call to this function should therefore be added to all tight
+-- loops.
+function checkint()
+ -- This does not poll the interrupted-flag directly
+ -- since LuaJIT does not support volatile qualifiers and could
+ -- optimize the read away.
+ if C.applause_is_interrupted() ~= 0 then
+ error("SIGINT received", 2)
+ end
+end
+
--
-- Define C functions for benchmarking (POSIX libc)
--
@@ -664,8 +680,7 @@ function Stream:play(first_port)
local old_stepmul = collectgarbage("setstepmul", 100)
local channels = self.channels
- local state
- self:foreach(function(frame)
+ local _, err = pcall(Stream.foreach, self, function(frame)
-- Loop should get unrolled automatically
for i = 1, channels do
local sample = tonumber(frame[i])
@@ -674,7 +689,7 @@ function Stream:play(first_port)
-- NOTE: Invalid port Ids are currently silently
-- ignored. Perhaps it's better to check state or
-- to access output_ports_count from applause.c.
- state = C.applause_push_sample(first_port+i, sample)
+ local state = C.applause_push_sample(first_port+i, sample)
-- React to buffer underruns.
-- This is done here instead of in the realtime thread
@@ -683,17 +698,13 @@ function Stream:play(first_port)
if state == C.APPLAUSE_AUDIO_XRUN then
io.stderr:write("WARNING: Buffer underrun detected\n")
end
-
- if state == C.APPLAUSE_AUDIO_INTERRUPTED then return true end
end
end)
collectgarbage("setpause", old_pause)
collectgarbage("setstepmul", old_stepmul)
- if state == C.APPLAUSE_AUDIO_INTERRUPTED then
- error("SIGINT received", 2)
- end
+ if err then error(err, 2) end
end
--- Execute function for each frame generated by the stream.
@@ -702,21 +713,19 @@ end
-- @func fnc
-- Function to execute.
-- It gets passed an array of samples, one for each channel.
--- @fixme This is not currently interruptable and therefore not suitable
--- to be executed dynamically at the command-line.
+-- If it returns true, the loop is terminated.
function Stream:foreach(fnc)
- local clear = table.clear
-
-- NOTE: This implementation is for single-channel streams
-- only. See also MuxStream:foreach().
+ local clear = table.clear
local frame = table.new(1, 0)
local tick = self:gtick()
- while true do
+ repeat
+ checkint()
frame[1] = tick()
clear(sampleCache)
- if not frame[1] or fnc(frame) then break end
- end
+ until not frame[1] or fnc(frame)
end
--- Benchmark stream (time to generate all samples).
@@ -873,8 +882,6 @@ end
-- @string[opt="full"] vbufmode Buffering mode.
-- @int[opt] vbufsize The buffer size.
-- @see file:setvbuf
--- @fixme This is currently allowed for infinite streams as well,
--- but there is no way to interrupt Stream:foreach().
function Stream:pipe(prog, vbufmode, vbufsize)
local hnd = io.popen(prog, "w")
hnd:setvbuf(vbufmode or "full", vbufsize)
@@ -1282,7 +1289,8 @@ function MuxStream:foreach(fnc)
local channels = self.channels
local frame = table.new(channels, 0)
- while true do
+ repeat
+ checkint()
clear(sampleCache)
for i = 1, channels do
@@ -1291,9 +1299,7 @@ function MuxStream:foreach(fnc)
-- length, if one ends all end
if not frame[i] then return end
end
-
- if fnc(frame) then break end
- end
+ until fnc(frame)
end
--- Mux several streams with the source stream into a multi-channel stream.