aboutsummaryrefslogtreecommitdiffhomepage
path: root/applause.c
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2016-01-05 14:20:16 +0100
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2016-01-05 14:20:16 +0100
commit97df9bd48cb6d4847896377d4eefaead9e2cd75f (patch)
treed41ab27628cc389e12b8bc21bb5a122826398515 /applause.c
parent7d222a52c70cc78e80ad0bab2f7f77dd03ad12b8 (diff)
downloadapplause2-97df9bd48cb6d4847896377d4eefaead9e2cd75f.tar.gz
rewritten Stream:play() as a Lua function
* the low-level C part is now implemented in a normal C function applause_push_sample() which is called using the FFI API * this is supposedly faster than the old Lua/C way, but the speed improvement seems to be miniscule. However changes like this are still good since they simplify the C core. * speed improvements will probably be larger for the MIDI*Stream functions since here we call Lua/C functions at sample rate.
Diffstat (limited to 'applause.c')
-rw-r--r--applause.c147
1 files changed, 43 insertions, 104 deletions
diff --git a/applause.c b/applause.c
index 28210c9..4a5f453 100644
--- a/applause.c
+++ b/applause.c
@@ -394,6 +394,7 @@ init_audio(int buffer_size)
return 0;
}
+/* FIXME: Rewrite as an exported FFI-callable function */
static int
l_MIDIVelocityStream_getValue(lua_State *L)
{
@@ -429,6 +430,7 @@ l_MIDIVelocityStream_getValue(lua_State *L)
return 1;
}
+/* FIXME: Rewrite as an exported FFI-callable function */
static int
l_MIDINoteStream_getValue(lua_State *L)
{
@@ -461,6 +463,7 @@ l_MIDINoteStream_getValue(lua_State *L)
return 1;
}
+/* FIXME: Rewrite as an exported FFI-callable function */
static int
l_MIDICCStream_getValue(lua_State *L)
{
@@ -496,117 +499,54 @@ l_MIDICCStream_getValue(lua_State *L)
return 1;
}
-static int
-l_Stream_play(lua_State *L)
-{
- int top = lua_gettop(L);
-
- luaL_argcheck(L, top == 1, top, "Stream object expected");
- luaL_checktype(L, 1, LUA_TTABLE);
-
- /* get reset() method */
- lua_getfield(L, 1, "reset");
- luaL_checktype(L, -1, LUA_TFUNCTION);
- /* duplicate object table since we have to call self.reset(self) */
- lua_pushvalue(L, 1);
- lua_call(L, 1, 0);
-
- /* get tick() method */
- lua_getfield(L, 1, "tick");
- luaL_checktype(L, -1, LUA_TFUNCTION);
- /* duplicate object table since we have to call self.tick(self) */
- lua_pushvalue(L, 1);
- lua_call(L, 1, 1);
- /* the tick generator function should now be on top of the stack */
- luaL_checktype(L, -1, LUA_TFUNCTION);
-
- /*
- * Make sure JIT compilation is turned on for the generator function
- * and all subfunctions.
- * This should not be necessary theoretically.
- */
- luaJIT_setmode(L, -1, LUAJIT_MODE_ALLFUNC | LUAJIT_MODE_ON);
+enum applause_audio_state {
+ APPLAUSE_AUDIO_OK = 0,
+ APPLAUSE_AUDIO_INTERRUPTED,
+ APPLAUSE_AUDIO_XRUN
+};
- /*
- * Get global cycleClock() function. It exists so we can
- * conveniently advance the clock from the native C method
- * without calling lua_setfield() and access a class table
- * from generator methods at sample rate.
- */
- lua_getglobal(L, "clockCycle");
+/**
+ * Push one Jack sample into the ring buffer.
+ *
+ * This function should be called from the Stream:play()
+ * implementation, which is faster than calling Lua functions
+ * from a C implementation of Stream:play() using the Lua C API
+ * (supposedly, I could not reproduce this).
+ *
+ * @param sample_double The audio sample as a double (compatible
+ * with lua_Number).
+ * This is speed relevant since otherwise
+ * LuaJIT tries to translate to Jack's default
+ * float type.
+ */
+enum applause_audio_state
+applause_push_sample(double sample_double)
+{
+ jack_default_audio_sample_t sample =
+ (jack_default_audio_sample_t)sample_double;
/*
- * Perform garbage collection cycle and turn it off
- * temporarily. This improves the realtime properties
- * of the sample generation loop below.
+ * We are about to "consume" one free sample in the buffer.
+ * This can block when the buffer is full.
+ * After this operation, there is __at least__ one sample free
+ * in buffer since jack_process() will only read from the
+ * buffer.
*/
- lua_gc(L, LUA_GCCOLLECT, 0);
- lua_gc(L, LUA_GCSTOP, 0);
+ svsem_op(buffer_sem, -(int)sizeof(sample));
- interrupted = 0;
+ jack_ringbuffer_write(buffer, (const char *)&sample,
+ sizeof(sample));
- while (!interrupted) {
- jack_default_audio_sample_t sample;
-
- /*
- * React to buffer underruns.
- * This is done here instead of in the realtime thread
- * even though it is already overloaded, so as not to
- * affect other applications in the Jack graph.
- */
- if (buffer_xrun) {
- fprintf(stderr, "WARNING: Buffer underrun detected!\n");
- buffer_xrun = 0;
- }
-
- /*
- * Duplicate and call clockCycle() function.
- * This is more efficient than calling lua_getglobal()
- * at sample rate.
- */
- lua_pushvalue(L, -1);
- lua_call(L, 0, 0);
-
- /* duplicate generator function */
- lua_pushvalue(L, -2);
- /* generate next sample */
- lua_call(L, 0, 1);
-
- if (lua_isnil(L, -1))
- /* stream has ended */
- break;
-
- /* copy sample into ring buffer */
- /*
- * FIXME: What if sample isn't a number. This
- * should be handled. But don't forget to restart
- * the garbage collector
- */
- sample = (jack_default_audio_sample_t)lua_tonumber(L, -1);
-
- /*
- * We are about to "consume" one free sample in the buffer.
- * This can block when the buffer is full.
- * After this operation, there is __at least__ one sample free
- * in buffer since jack_process() will only read from the
- * buffer.
- */
- svsem_op(buffer_sem, -(int)sizeof(sample));
-
- jack_ringbuffer_write(buffer, (const char *)&sample,
- sizeof(sample));
-
- /* pop sample, the function dup has already been popped */
- lua_pop(L, 1);
+ if (interrupted) {
+ interrupted = 0;
+ return APPLAUSE_AUDIO_INTERRUPTED;
+ }
+ if (buffer_xrun) {
+ buffer_xrun = 0;
+ return APPLAUSE_AUDIO_XRUN;
}
- lua_gc(L, LUA_GCRESTART, 0);
-
- if (interrupted)
- return luaL_error(L, "SIGINT received");
-
- /* any remaining stack elements are automatically popped */
- return 0;
+ return APPLAUSE_AUDIO_OK;
}
static int
@@ -879,7 +819,6 @@ typedef struct NativeMethod {
} NativeMethod;
static const NativeMethod native_methods[] = {
- {"Stream", "play", l_Stream_play},
{"Stream", "fork", l_Stream_fork},
{"MIDIVelocityStream", "getValue", l_MIDIVelocityStream_getValue},
{"MIDINoteStream", "getValue", l_MIDINoteStream_getValue},