Browse Source

Example on how to use the FFI in LuaJIT to increase the performance

tags/v1.3.0
Leonardo Laguna Ruiz Andrew Belt 5 years ago
parent
commit
9dcb0f4445
4 changed files with 78 additions and 192 deletions
  1. +5
    -5
      examples/gain.lua
  2. +3
    -3
      examples/rainbow.lua
  3. +4
    -4
      examples/vco.lua
  4. +66
    -180
      src/LuaJITEngine.cpp

+ 5
- 5
examples/gain.lua View File

@@ -6,23 +6,23 @@ config.bufferSize = 32

function process(block)
-- Loop through each column
for i=1,6 do
for i=0,5 do
-- Get gain knob
gain = block.knobs[i]
-- Set gain light (red = 1)
block.lights[i][1] = gain
block.lights[i][0] = gain
-- Check mute switch
if block.switches[i] then
-- Mute output
gain = 0
-- Enable mute light (red = 1)
block.switchLights[i][1] = 1
block.switchLights[i][0] = 1
else
-- Disable mute light
block.switchLights[i][1] = 0
block.switchLights[i][0] = 0
end
-- Iterate input/output buffer
for j=1,block.bufferSize do
for j=0,block.bufferSize-1 do
-- Get input
x = block.inputs[i][j]
-- Apply gain to input


+ 3
- 3
examples/rainbow.lua View File

@@ -34,10 +34,10 @@ function process(block)
h = (1 - i / 6 + phase) % 1
rgb = hsvToRgb(h, 1, 1)
for c=1,3 do
block.lights[i][c] = rgb[c]
block.switchLights[i][c] = rgb[c]
block.lights[i-1][c-1] = rgb[c]
block.switchLights[i-1][c-1] = rgb[c]
end
block.outputs[i][1] = math.sin(2 * math.pi * h) * 5 + 5
block.outputs[i-1][0] = math.sin(2 * math.pi * h) * 5 + 5
end
end



+ 4
- 4
examples/vco.lua View File

@@ -9,10 +9,10 @@ config.bufferSize = 16
phase = 0
function process(block)
-- Knob ranges from -5 to 5 octaves
pitch = block.knobs[1] * 10 - 5
pitch = block.knobs[0] * 10 - 5
-- Input follows 1V/oct standard
-- Take the first input's first buffer value
pitch = pitch + block.inputs[1][1]
pitch = pitch + block.inputs[0][0]

-- The relationship between 1V/oct pitch and frequency is `freq = 2^pitch`.
-- Default frequency is middle C (C4) in Hz.
@@ -22,13 +22,13 @@ function process(block)

-- Set all samples in output buffer
deltaPhase = config.frameDivider * block.sampleTime * freq
for i=1,block.bufferSize do
for i=0,block.bufferSize-1 do
-- Accumulate phase
phase = phase + deltaPhase
-- Wrap phase around range [0, 1]
phase = phase % 1

-- Convert phase to sine output
block.outputs[1][i] = math.sin(2 * math.pi * phase) * 5
block.outputs[0][i] = math.sin(2 * math.pi * phase) * 5
end
end

+ 66
- 180
src/LuaJITEngine.cpp View File

@@ -1,6 +1,28 @@
#include "ScriptEngine.hpp"
#include <luajit-2.0/lua.hpp>

static const luaL_Reg lj_lib_load[] = {
{ "", luaopen_base },
{ LUA_LOADLIBNAME, luaopen_package },
{ LUA_TABLIBNAME, luaopen_table },
{ LUA_STRLIBNAME, luaopen_string },
{ LUA_MATHLIBNAME, luaopen_math },
{ LUA_BITLIBNAME, luaopen_bit },
{ LUA_JITLIBNAME, luaopen_jit },
{ LUA_FFILIBNAME, luaopen_ffi },
{ NULL, NULL }
};

LUALIB_API void custom_openlibs(lua_State *L)
{
const luaL_Reg *lib;
for (lib = lj_lib_load; lib->func; lib++) {
lua_pushcfunction(L, lib->func);
lua_pushstring(L, lib->name);
lua_call(L, 1, 0);
}
lua_pop(L, 1);
}

struct LuaJITEngine : ScriptEngine {
lua_State* L = NULL;
@@ -29,16 +51,7 @@ struct LuaJITEngine : ScriptEngine {
}

// Import a subset of the standard library
luaopen_base(L);
luaopen_string(L);
luaopen_table(L);
luaopen_math(L);
luaopen_bit(L);
// Loads the JIT package otherwise it will be off
luaopen_jit(L);
// Disables access to the JIT package
lua_pushnil(L);
lua_setglobal(L,"jit");
custom_openlibs(L);

// Set user pointer
lua_pushlightuserdata(L, this);
@@ -63,7 +76,45 @@ struct LuaJITEngine : ScriptEngine {
}
lua_setglobal(L, "config");

// Compile script
// Loads the FFI auxiliary functions.
std::stringstream ffi_stream;
ffi_stream
<< "local ffi = require('ffi')" << std::endl
// Describes the struct 'ProcessBlock' that way LuaJIT knows how to access the data
<< "ffi.cdef[[" << std::endl
<< "struct ProcessBlock {" << std::endl
<< "float sampleRate;" << std::endl
<< "float sampleTime;" << std::endl
<< "int bufferSize;" << std::endl
<< "float inputs[" << NUM_ROWS << "][" << MAX_BUFFER_SIZE << "];" << std::endl
<< "float outputs[" << NUM_ROWS << "][" << MAX_BUFFER_SIZE << "];" << std::endl
<< "float knobs[" << NUM_ROWS << "];" << std::endl
<< "bool switches[" << NUM_ROWS << "];" << std::endl
<< "float lights[" << NUM_ROWS << "][3];" << std::endl
<< "float switchLights[" << NUM_ROWS << "][3];};]]" << std::endl
// Declares the function 'castBlock' used to transform the 'block' pointer into a LuaJIT cdata
<< "function castBlock(b) return ffi.cast('struct ProcessBlock*', b) end";
std::string ffi_script = ffi_stream.str();

// Compile the ffi script
if (luaL_loadbuffer(L, ffi_script.c_str(), ffi_script.size(), "ffi_script.lua")) {
const char* s = lua_tostring(L, -1);
WARN("LuaJIT: %s", s);
display(s);
lua_pop(L, 1);
return -1;
}

// Run the ffi script
if (lua_pcall(L, 0, 0, 0)) {
const char* s = lua_tostring(L, -1);
WARN("LuaJIT: %s", s);
display(s);
lua_pop(L, 1);
return -1;
}

// Compile user script
if (luaL_loadbuffer(L, script.c_str(), script.size(), path.c_str())) {
const char* s = lua_tostring(L, -1);
WARN("LuaJIT: %s", s);
@@ -104,125 +155,16 @@ struct LuaJITEngine : ScriptEngine {
return -1;
}

// FloatArray metatable
lua_newtable(L);
{
// __index
lua_pushcfunction(L, native_FloatArray_index);
lua_setfield(L, -2, "__index");
// __newindex
lua_pushcfunction(L, native_FloatArray_newindex);
lua_setfield(L, -2, "__newindex");
// __len
lua_pushcfunction(L, native_FloatArray_len);
lua_setfield(L, -2, "__len");
}
lua_setglobal(L, "FloatArray");

// BoolArray metatable
lua_newtable(L);
{
// __index
lua_pushcfunction(L, native_BoolArray_index);
lua_setfield(L, -2, "__index");
// __newindex
lua_pushcfunction(L, native_BoolArray_newindex);
lua_setfield(L, -2, "__newindex");
// __len
lua_pushcfunction(L, native_BoolArray_len);
lua_setfield(L, -2, "__len");
}
lua_setglobal(L, "BoolArray");

// Create block object
lua_newtable(L);
{
// inputs
lua_newtable(L);
for (int i = 0; i < NUM_ROWS; i++) {
SafeArray* input = (SafeArray*) lua_newuserdata(L, sizeof(SafeArray));
input->p = &block->inputs[i];
input->len = block->bufferSize;
lua_getglobal(L, "FloatArray");
lua_setmetatable(L, -2);
lua_rawseti(L, -2, i + 1);
}
lua_setfield(L, -2, "inputs");

// outputs
lua_newtable(L);
for (int i = 0; i < NUM_ROWS; i++) {
SafeArray* output = (SafeArray*) lua_newuserdata(L, sizeof(SafeArray));
output->p = &block->outputs[i];
output->len = block->bufferSize;
lua_getglobal(L, "FloatArray");
lua_setmetatable(L, -2);
lua_rawseti(L, -2, i + 1);
}
lua_setfield(L, -2, "outputs");

// knobs
SafeArray* knobs = (SafeArray*) lua_newuserdata(L, sizeof(SafeArray));
knobs->p = &block->knobs;
knobs->len = 6;
lua_getglobal(L, "FloatArray");
lua_setmetatable(L, -2);
lua_setfield(L, -2, "knobs");

// switches
SafeArray* switches = (SafeArray*) lua_newuserdata(L, sizeof(SafeArray));
switches->p = &block->switches;
switches->len = 6;
lua_getglobal(L, "BoolArray");
lua_setmetatable(L, -2);
lua_setfield(L, -2, "switches");

// lights
lua_newtable(L);
for (int i = 0; i < NUM_ROWS; i++) {
SafeArray* light = (SafeArray*) lua_newuserdata(L, sizeof(SafeArray));
light->p = &block->lights[i];
light->len = 3;
lua_getglobal(L, "FloatArray");
lua_setmetatable(L, -2);
lua_rawseti(L, -2, i + 1);
}
lua_setfield(L, -2, "lights");

// switchLights
lua_newtable(L);
for (int i = 0; i < NUM_ROWS; i++) {
SafeArray* switchLight = (SafeArray*) lua_newuserdata(L, sizeof(SafeArray));
switchLight->p = &block->switchLights[i];
switchLight->len = 3;
lua_getglobal(L, "FloatArray");
lua_setmetatable(L, -2);
lua_rawseti(L, -2, i + 1);
}
lua_setfield(L, -2, "switchLights");
}
lua_getglobal(L, "castBlock");
lua_pushlightuserdata(L, (void *)block);
if (lua_pcall(L, 1, 1, 0) != 0)
printf("error running function 'castBlock': %s", lua_tostring(L, -1));

return 0;
}

int process() override {
ProcessBlock* block = getProcessBlock();

// Set block
{
// sampleRate
lua_pushnumber(L, block->sampleRate);
lua_setfield(L, -2, "sampleRate");

// sampleTime
lua_pushnumber(L, block->sampleTime);
lua_setfield(L, -2, "sampleTime");

// bufferSize
lua_pushinteger(L, block->bufferSize);
lua_setfield(L, -2, "bufferSize");
}

// Duplicate process function
lua_pushvalue(L, -2);
// Duplicate block
@@ -264,62 +206,6 @@ struct LuaJITEngine : ScriptEngine {
getEngine(L)->display(s);
return 0;
}

static int native_FloatArray_index(lua_State* L) {
SafeArray* a = (SafeArray*) lua_touserdata(L, 1);
float* data = (float*) a->p;
size_t index = lua_tointeger(L, 2) - 1;
if (index >= a->len) {
lua_pushstring(L, "Array out of bounds");
lua_error(L);
}
lua_pushnumber(L, data[index]);
return 1;
}
static int native_FloatArray_newindex(lua_State* L) {
SafeArray* a = (SafeArray*) lua_touserdata(L, 1);
float* data = (float*) a->p;
size_t index = lua_tointeger(L, 2) - 1;
if (index >= a->len) {
lua_pushstring(L, "Array out of bounds");
lua_error(L);
}
data[index] = lua_tonumber(L, 3);
return 0;
}
static int native_FloatArray_len(lua_State* L) {
SafeArray* a = (SafeArray*) lua_touserdata(L, 1);
lua_pushinteger(L, a->len);
return 1;
}

static int native_BoolArray_index(lua_State* L) {
SafeArray* a = (SafeArray*) lua_touserdata(L, 1);
bool* data = (bool*) a->p;
size_t index = lua_tointeger(L, 2) - 1;
if (index >= a->len) {
lua_pushstring(L, "Array out of bounds");
lua_error(L);
}
lua_pushboolean(L, data[index]);
return 1;
}
static int native_BoolArray_newindex(lua_State* L) {
SafeArray* a = (SafeArray*) lua_touserdata(L, 1);
bool* data = (bool*) a->p;
size_t index = lua_tointeger(L, 2) - 1;
if (index >= a->len) {
lua_pushstring(L, "Array out of bounds");
lua_error(L);
}
data[index] = lua_toboolean(L, 3);
return 0;
}
static int native_BoolArray_len(lua_State* L) {
SafeArray* a = (SafeArray*) lua_touserdata(L, 1);
lua_pushinteger(L, a->len);
return 1;
}
};




Loading…
Cancel
Save