2012-07-13 21:18:35 +02:00
|
|
|
/*
|
|
|
|
* vim: sw=2 ts=2 expandtab
|
|
|
|
*
|
2013-02-02 21:54:53 +01:00
|
|
|
* "THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"):
|
|
|
|
* Thomas Kolb <cfr34k@tkolb.de> wrote this file. As long as you retain this
|
|
|
|
* notice you can do whatever you want with this stuff. If we meet some day,
|
|
|
|
* and you think this stuff is worth it, you can buy me a pizza in return.
|
|
|
|
* - Thomas Kolb
|
2012-07-13 21:18:35 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <semaphore.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdint.h>
|
2012-07-28 01:30:20 +02:00
|
|
|
#include <malloc.h>
|
|
|
|
|
2019-08-22 15:30:51 +02:00
|
|
|
#include <lua5.3/lua.h>
|
|
|
|
#include <lua5.3/lualib.h>
|
|
|
|
#include <lua5.3/lauxlib.h>
|
2012-07-28 01:30:20 +02:00
|
|
|
|
|
|
|
#include "lua_utils.h"
|
|
|
|
#include "lua_wrappers.h"
|
2012-07-13 21:18:35 +02:00
|
|
|
|
|
|
|
#include "fft.h"
|
|
|
|
#include "utils.h"
|
2018-06-24 22:23:39 +02:00
|
|
|
#include "sk6812.h"
|
2012-07-13 21:18:35 +02:00
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
// Frames per second
|
2012-09-03 17:16:14 +02:00
|
|
|
#define FPS ((value_type)BUFFER_PARTS * SAMPLE_RATE / BLOCK_LEN)
|
2012-07-13 21:18:35 +02:00
|
|
|
|
|
|
|
// Number of new samples put into the buffer each frame
|
|
|
|
#define READ_SAMPLES (BLOCK_LEN / BUFFER_PARTS)
|
|
|
|
|
2012-09-03 17:16:14 +02:00
|
|
|
value_type fft[BLOCK_LEN];
|
|
|
|
value_type rms;
|
|
|
|
value_type lastUpdateTime = 0;
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
sample signal[BLOCK_LEN];
|
2012-07-13 21:18:35 +02:00
|
|
|
|
|
|
|
sem_t fftSemaphore;
|
|
|
|
|
2018-08-14 21:52:33 +02:00
|
|
|
struct sk6812_ctx sk6812;
|
2018-06-30 01:42:34 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
int num_modules;
|
|
|
|
int num_strips;
|
|
|
|
|
2012-07-13 21:18:35 +02:00
|
|
|
int running = 1;
|
|
|
|
|
|
|
|
void* fft_thread(void *param) {
|
|
|
|
sample buffer[BLOCK_LEN];
|
|
|
|
sample block[BLOCK_LEN];
|
2012-09-03 17:16:14 +02:00
|
|
|
value_type fftOutReal[BLOCK_LEN], fftOutImag[BLOCK_LEN];
|
|
|
|
value_type tmpFFT[BLOCK_LEN];
|
|
|
|
value_type tmpRMS;
|
2012-07-13 21:18:35 +02:00
|
|
|
|
|
|
|
double nextFrame = get_hires_time() + 0.05;
|
|
|
|
double curTime;
|
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
int i;
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
init_fft();
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
while(running) {
|
|
|
|
// shift the buffer left
|
|
|
|
memmove(buffer, buffer + READ_SAMPLES,
|
|
|
|
(BLOCK_LEN - READ_SAMPLES) * sizeof(sample));
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
size_t ret = fread(buffer + (BLOCK_LEN - READ_SAMPLES),
|
|
|
|
sizeof(sample), READ_SAMPLES, stdin);
|
|
|
|
if(ret != READ_SAMPLES) {
|
|
|
|
break;
|
|
|
|
}
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
memcpy(block, buffer, BLOCK_LEN * sizeof(sample));
|
2012-07-13 21:18:35 +02:00
|
|
|
|
|
|
|
tmpRMS = 0;
|
|
|
|
for(i = 0; i < BLOCK_LEN; i++) {
|
2012-07-28 01:30:20 +02:00
|
|
|
tmpRMS += buffer[i]*buffer[i];
|
2012-07-13 21:18:35 +02:00
|
|
|
}
|
|
|
|
tmpRMS = sqrt(tmpRMS/BLOCK_LEN);
|
|
|
|
|
2018-06-24 22:23:39 +02:00
|
|
|
if(tmpRMS == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
apply_hanning(block);
|
|
|
|
fft_transform(block, fftOutReal, fftOutImag);
|
|
|
|
complex_to_absolute(fftOutReal, fftOutImag, tmpFFT);
|
2018-06-24 22:23:39 +02:00
|
|
|
|
2012-07-13 21:18:35 +02:00
|
|
|
// --- SAFE SECTION ---
|
|
|
|
sem_wait(&fftSemaphore);
|
|
|
|
|
|
|
|
memcpy(fft, tmpFFT, sizeof(fft));
|
2012-07-28 01:30:20 +02:00
|
|
|
memcpy(signal, buffer, sizeof(signal));
|
2012-07-13 21:18:35 +02:00
|
|
|
rms = tmpRMS;
|
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
curTime = get_hires_time();
|
2012-07-13 21:18:35 +02:00
|
|
|
lastUpdateTime = curTime;
|
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
sem_post(&fftSemaphore);
|
2012-07-13 21:18:35 +02:00
|
|
|
// --- END SAFE SECTION ---
|
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
if(curTime > nextFrame + 0.05) {
|
|
|
|
printf("Frame too late! Skipping.\n");
|
|
|
|
nextFrame = -1;
|
|
|
|
}
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
if(curTime < nextFrame - 0.05) {
|
|
|
|
printf("Frame too early.\n");
|
|
|
|
nextFrame = -1;
|
|
|
|
}
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
if(nextFrame < 0) {
|
|
|
|
printf("Frame time reset.\n");
|
|
|
|
nextFrame = curTime;
|
|
|
|
}
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
nextFrame += 1.000/FPS;
|
|
|
|
//sleep_until(nextFrame);
|
|
|
|
}
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
return NULL;
|
2012-07-13 21:18:35 +02:00
|
|
|
}
|
|
|
|
|
2012-11-22 19:53:49 +01:00
|
|
|
value_type gamma_correct(value_type d, value_type gamma) {
|
2012-07-28 01:30:20 +02:00
|
|
|
return pow(d, gamma);
|
2012-07-13 21:18:35 +02:00
|
|
|
}
|
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
int main(int argc, char **argv) {
|
|
|
|
double nextFrame = get_hires_time() + LED_INTERVAL;
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
int i;
|
|
|
|
pthread_t fftThread;
|
2012-07-28 01:30:20 +02:00
|
|
|
|
|
|
|
int active = 1;
|
2018-06-24 22:23:39 +02:00
|
|
|
//int frame = 0;
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
double *red;
|
|
|
|
double *green;
|
|
|
|
double *blue;
|
2018-06-24 22:23:39 +02:00
|
|
|
double *white;
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
int useFading, fadeStep;
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
if(argc < 2) {
|
|
|
|
fprintf(stderr, "LUA script file must be given as command line argument!\n");
|
|
|
|
return 1;
|
2012-07-13 21:18:35 +02:00
|
|
|
}
|
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
// initialize lua
|
2018-06-19 22:13:22 +02:00
|
|
|
lua_State *L = luaL_newstate();
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
// load the lua libraries
|
|
|
|
luaL_openlibs(L);
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
// register local functions
|
|
|
|
lua_register_funcs(L);
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
// load the configuration from "config.lua"
|
|
|
|
if(luaL_dofile(L, "config.lua")) {
|
|
|
|
lua_showerror(L, "luaL_dofile(config.lua) failed.");
|
|
|
|
return 1;
|
|
|
|
}
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
lua_getglobal(L, "WS2801_HOST");
|
|
|
|
if(!lua_isstring(L, -1)) return 2;
|
|
|
|
const char *host = lua_tostring(L, -1);
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
lua_getglobal(L, "WS2801_PORT");
|
|
|
|
if(!lua_isnumber(L, -1)) return 2;
|
|
|
|
unsigned short port = lua_tointeger(L, -1);
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
lua_getglobal(L, "GAMMA");
|
|
|
|
if(!lua_isnumber(L, -1)) return 2;
|
|
|
|
double gamma = lua_tonumber(L, -1);
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
lua_getglobal(L, "NUM_MODULES");
|
|
|
|
if(!lua_isnumber(L, -1)) return 2;
|
2020-05-15 23:17:26 +02:00
|
|
|
num_modules = lua_tointeger(L, -1);
|
|
|
|
|
|
|
|
lua_getglobal(L, "NUM_STRIPS");
|
|
|
|
if(!lua_isnumber(L, -1)) return 2;
|
|
|
|
num_strips = lua_tointeger(L, -1);
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
lua_getglobal(L, "CENTER_MODULE");
|
|
|
|
if(!lua_isnumber(L, -1)) return 2;
|
|
|
|
int center_module = lua_tointeger(L, -1);
|
|
|
|
|
2012-11-26 17:51:30 +01:00
|
|
|
// export some global constants
|
|
|
|
lua_pushnumber(L, SAMPLE_RATE);
|
|
|
|
lua_setglobal(L, "SAMPLE_RATE");
|
|
|
|
|
|
|
|
lua_pushnumber(L, BLOCK_LEN);
|
|
|
|
lua_setglobal(L, "BLOCK_LEN");
|
|
|
|
|
|
|
|
lua_pushnumber(L, DATALEN);
|
|
|
|
lua_setglobal(L, "DATALEN");
|
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
// allocate arrays
|
2020-05-15 23:17:26 +02:00
|
|
|
red = malloc(num_strips * num_modules * sizeof(double));
|
|
|
|
green = malloc(num_strips * num_modules * sizeof(double));
|
|
|
|
blue = malloc(num_strips * num_modules * sizeof(double));
|
|
|
|
white = malloc(num_strips * num_modules * sizeof(double));
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
// load and initialize the script
|
|
|
|
if(luaL_loadfile(L, argv[1])) {
|
|
|
|
lua_showerror(L, "luaL_loadfile(cmdline_argument) failed.");
|
|
|
|
}
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
// priming call: read the lua file to make functions known
|
|
|
|
if(lua_pcall(L, 0, 0, 0)) {
|
|
|
|
lua_showerror(L, "lua_pcall failed.");
|
|
|
|
}
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
// call the init function
|
|
|
|
lua_getglobal(L, "init");
|
2020-05-15 23:17:26 +02:00
|
|
|
lua_pushnumber(L, num_strips);
|
2012-07-28 01:30:20 +02:00
|
|
|
lua_pushnumber(L, num_modules);
|
|
|
|
lua_pushnumber(L, center_module);
|
2020-05-15 23:17:26 +02:00
|
|
|
if(lua_pcall(L, 3, 1, 0)) {
|
2012-07-28 01:30:20 +02:00
|
|
|
lua_showerror(L, "lua_pcall(init) failed.");
|
|
|
|
}
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
fadeStep = lua_tointeger(L, -1);
|
|
|
|
useFading = fadeStep > 0;
|
|
|
|
if(useFading) {
|
2018-08-14 21:52:33 +02:00
|
|
|
sk6812_set_fadestep(&sk6812, fadeStep);
|
2012-07-28 01:30:20 +02:00
|
|
|
printf("Fading enabled with fadestep %i.\n", fadeStep);
|
|
|
|
}
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
// initialize the WS2801 library
|
|
|
|
printf("Connecting to %s:%i\n", host, port);
|
2018-08-14 21:52:33 +02:00
|
|
|
sk6812_init(&sk6812, host, port);
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
// create semaphores
|
|
|
|
sem_init(&fftSemaphore, 0, 1);
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
// run the fft thread
|
|
|
|
pthread_create(&fftThread, NULL, fft_thread, NULL);
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
while(running) {
|
2012-07-13 21:18:35 +02:00
|
|
|
if(active) {
|
2012-07-28 01:30:20 +02:00
|
|
|
// call the periodic() function from LUA
|
|
|
|
lua_getglobal(L, "periodic");
|
2018-06-24 22:23:39 +02:00
|
|
|
if(lua_pcall(L, 0, 4, 0)) { // no arguments, 4 return values
|
2012-07-28 01:30:20 +02:00
|
|
|
lua_showerror(L, "lua_pcall(periodic) failed.");
|
2012-07-13 21:18:35 +02:00
|
|
|
}
|
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
// read the return values (reverse order, as lua uses a stack)
|
2020-05-15 23:17:26 +02:00
|
|
|
lua_readdoublearray(L, white, num_strips*num_modules);
|
|
|
|
lua_readdoublearray(L, blue, num_strips*num_modules);
|
|
|
|
lua_readdoublearray(L, green, num_strips*num_modules);
|
|
|
|
lua_readdoublearray(L, red, num_strips*num_modules);
|
2012-07-28 01:30:20 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
for(int s = 0; s < num_strips; s++) {
|
2018-06-19 22:13:22 +02:00
|
|
|
if(useFading) {
|
|
|
|
for(i = 0; i < num_modules; i++) {
|
2020-05-15 23:17:26 +02:00
|
|
|
int lidx = idx(s, i);
|
2018-08-14 21:52:33 +02:00
|
|
|
sk6812_fade_color(&sk6812, s, i,
|
2020-05-15 23:17:26 +02:00
|
|
|
255 * gamma_correct(red[lidx], gamma),
|
|
|
|
255 * gamma_correct(green[lidx], gamma),
|
|
|
|
255 * gamma_correct(blue[lidx], gamma),
|
|
|
|
255 * gamma_correct(white[lidx], gamma));
|
2018-06-19 22:13:22 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for(i = 0; i < num_modules; i++) {
|
2020-05-15 23:17:26 +02:00
|
|
|
int lidx = idx(s, i);
|
2018-08-14 21:52:33 +02:00
|
|
|
sk6812_set_color(&sk6812, s, i,
|
2020-05-15 23:17:26 +02:00
|
|
|
255 * gamma_correct(red[lidx], gamma),
|
|
|
|
255 * gamma_correct(green[lidx], gamma),
|
|
|
|
255 * gamma_correct(blue[lidx], gamma),
|
|
|
|
255 * gamma_correct(white[lidx], gamma));
|
2018-06-19 22:13:22 +02:00
|
|
|
}
|
2012-07-28 01:30:20 +02:00
|
|
|
}
|
2012-07-13 21:18:35 +02:00
|
|
|
}
|
|
|
|
|
2018-08-14 21:52:33 +02:00
|
|
|
sk6812_commit(&sk6812);
|
|
|
|
|
2012-07-13 21:18:35 +02:00
|
|
|
if(lastUpdateTime < nextFrame - 1) {
|
|
|
|
printf("Idle for 1 second -> stopping updates.\n");
|
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
for(i = 0; i < num_modules; i++) {
|
2020-05-15 23:17:26 +02:00
|
|
|
for(int s = 0; s < num_strips; s++) {
|
2018-08-14 21:52:33 +02:00
|
|
|
sk6812_fade_color(&sk6812, s, i, 0, 0, 0, 20);
|
2018-06-30 01:42:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-14 21:52:33 +02:00
|
|
|
sk6812_commit(&sk6812);
|
2012-07-13 21:18:35 +02:00
|
|
|
|
|
|
|
active = 0;
|
|
|
|
}
|
|
|
|
} else if(lastUpdateTime > nextFrame - 1) {
|
|
|
|
printf("Resuming updates.\n");
|
|
|
|
active = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
nextFrame += LED_INTERVAL;
|
|
|
|
sleep_until(nextFrame);
|
2018-06-30 01:42:34 +02:00
|
|
|
}
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
sk6812_shutdown(&sk6812);
|
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
// free arrays
|
|
|
|
free(red);
|
|
|
|
free(green);
|
|
|
|
free(blue);
|
2018-06-24 22:23:39 +02:00
|
|
|
free(white);
|
2012-07-28 01:30:20 +02:00
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
pthread_join(fftThread, NULL);
|
2012-07-13 21:18:35 +02:00
|
|
|
|
2012-07-28 01:30:20 +02:00
|
|
|
lua_close(L);
|
|
|
|
|
2020-05-15 23:17:26 +02:00
|
|
|
return 0;
|
2012-07-13 21:18:35 +02:00
|
|
|
}
|