Skip to content

Commit

Permalink
PFFFT Lua wrapper
Browse files Browse the repository at this point in the history
Draft, do not merge yet
  • Loading branch information
nuoun committed Dec 18, 2024
1 parent 6dea7c3 commit a6b40c2
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ target_link_libraries(${PROJECT_NAME}
surge::sqlite
surge-common-binary
tuning-library
pffft
PRIVATE
surge-lua-src
surge-platform
Expand Down
140 changes: 139 additions & 1 deletion src/common/LuaSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ static int lua_sandboxPrint(lua_State *L)
for (int i = 1; i <= n; i++)
{
if (!lua_isstring(L, i))
return luaL_error(L, "Error: print() only accepts strings or numbers!");
return luaL_error(L, "print() error: only accepts strings or numbers!");
const char *s = lua_tostring(L, i); // get the string
fputs(s, stdout); // print the string
}
Expand All @@ -155,6 +155,126 @@ static int lua_sandboxPrint(lua_State *L)
return 0;
}

// Wrapper function for PFFFT submodule
// From pffft.h:
// Transforms are not scaled: PFFFT_BACKWARD(PFFFT_FORWARD(x)) = N*x.
// Typically you will want to scale the backward transform by 1/N.
static int pffft_Transform(lua_State *L, pffft_direction_t direction, pffft_transform_t transform,
bool unordered, const char *functionName)
{
#if HAS_LUA
// Check for an input table
if (lua_istable(L, 1) != 1)
return luaL_error(L, "%s error: table expected", functionName);

int tableLength = lua_objlen(L, 1);

// Check if we're doing a complex transformation, if so, the FFT size is determined by only one
// set of numbers but we still need the full array length to iterate and allocate buffers
int transformLength = tableLength;
if (transform == PFFFT_COMPLEX)
transformLength /= 2;

// Enforce size constraints: minimum 32 FFT size, maximum 524,288 table size
if (transformLength < 32 || tableLength > (1 << 19))
return luaL_error(
L, "%s error: table must contain between 32 and 524,288 elements, received %d elements",
functionName, transformLength);

// PFFFT supports sizes in the form N=(2^a)*(3^b)*(5^c) where a >= 5, b >=0, c >= 0 but a
// size like 180 seems to result in undefined behaviour so restricting N to a power of two is
// the safe option
if ((tableLength & (tableLength - 1)) != 0)
return luaL_error(L,
"%s error: input table length must be a power of 2, received %d elements",
functionName, transformLength);

// Create PFFFT setup
PFFFT_Setup *setup = pffft_new_setup(transformLength, transform);
if (!setup)
return luaL_error(L, "%s error: failed to create setup for %d elements", functionName,
transformLength);

// Allocate memory for aligned buffers for SIMD
// TODO: Consider whether the temporary buffers should be managed as static objects or
// alternatively, a state table with an initialization step, followed by calls to a
// type-specific API
float *input = (float *)pffft_aligned_malloc(tableLength * sizeof(float));
float *output = (float *)pffft_aligned_malloc(tableLength * sizeof(float));
float *work = (float *)pffft_aligned_malloc(tableLength * sizeof(float));

// Populate the input buffer from the Lua table
for (int i = 0; i < tableLength; i++)
{
lua_rawgeti(L, 1, i + 1);
input[i] = luaL_checknumber(L, -1);
lua_pop(L, 1);
}

// Perform the ordered or unordered transform
if (unordered)
pffft_transform_ordered(setup, input, output, work, direction);
else
pffft_transform(setup, input, output, work, direction);

// Push the output back to Lua as a table
lua_newtable(L);
for (int i = 0; i < tableLength; i++)
{
lua_pushnumber(L, output[i]);
lua_rawseti(L, -2, i + 1);
}

// Clean up
pffft_aligned_free(input);
pffft_aligned_free(output);
pffft_aligned_free(work);
pffft_destroy_setup(setup);
#endif
return 1;
}

// Different modes for pffft_Transform
static int pffft_ForwardReal(lua_State *L)
{
return pffft_Transform(L, PFFFT_FORWARD, PFFFT_REAL, false, "fft_real()");
}

static int pffft_ForwardComplex(lua_State *L)
{
return pffft_Transform(L, PFFFT_FORWARD, PFFFT_COMPLEX, false, "fft_complex()");
}

static int pffft_BackwardReal(lua_State *L)
{
return pffft_Transform(L, PFFFT_BACKWARD, PFFFT_REAL, false, "ifft_real()");
}

static int pffft_BackwardComplex(lua_State *L)
{
return pffft_Transform(L, PFFFT_BACKWARD, PFFFT_COMPLEX, false, "ifft_complex()");
}

static int pffft_ForwardRealUnordered(lua_State *L)
{
return pffft_Transform(L, PFFFT_FORWARD, PFFFT_REAL, true, "fft_real_unordered()");
}

static int pffft_ForwardComplexUnordered(lua_State *L)
{
return pffft_Transform(L, PFFFT_FORWARD, PFFFT_COMPLEX, true, "fft_complex_unordered()");
}

static int pffft_BackwardRealUnordered(lua_State *L)
{
return pffft_Transform(L, PFFFT_BACKWARD, PFFFT_REAL, true, "ifft_real_unordered()");
}

static int pffft_BackwardComplexUnordered(lua_State *L)
{
return pffft_Transform(L, PFFFT_BACKWARD, PFFFT_COMPLEX, true, "ifft_complex_unordered()");
}

bool Surge::LuaSupport::setSurgeFunctionEnvironment(lua_State *L)
{
#if HAS_LUA
Expand All @@ -174,6 +294,24 @@ bool Surge::LuaSupport::setSurgeFunctionEnvironment(lua_State *L)
lua_pushcfunction(L, lua_sandboxPrint);
lua_setfield(L, eidx, "print");

// FFT functions
lua_pushcfunction(L, pffft_ForwardReal);
lua_setfield(L, eidx, "fft_real");
lua_pushcfunction(L, pffft_ForwardComplex);
lua_setfield(L, eidx, "fft_complex");
lua_pushcfunction(L, pffft_BackwardReal);
lua_setfield(L, eidx, "ifft_real");
lua_pushcfunction(L, pffft_BackwardComplex);
lua_setfield(L, eidx, "ifft_complex");
lua_pushcfunction(L, pffft_ForwardRealUnordered);
lua_setfield(L, eidx, "fft_real_unordered");
lua_pushcfunction(L, pffft_ForwardComplexUnordered);
lua_setfield(L, eidx, "fft_complex_unordered");
lua_pushcfunction(L, pffft_BackwardRealUnordered);
lua_setfield(L, eidx, "ifft_real_unordered");
lua_pushcfunction(L, pffft_BackwardComplexUnordered);
lua_setfield(L, eidx, "ifft_complex_unordered");

// add global tables
lua_getglobal(L, surgeTableName);
lua_setfield(L, eidx, surgeTableName);
Expand Down
2 changes: 2 additions & 0 deletions src/common/LuaSupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ extern "C"
#include "luajit.h"

#include "lj_arch.h"

#include "pffft.h"
}
#else
typedef int lua_State;
Expand Down

0 comments on commit a6b40c2

Please sign in to comment.