Initial commit von Musiclight 2
This commit is contained in:
commit
cf8193e421
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
*.o
|
||||||
|
*.swp
|
||||||
|
musiclight2
|
||||||
|
core
|
||||||
|
tags
|
22
Makefile
Normal file
22
Makefile
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
CC=gcc
|
||||||
|
CFLAGS+=-O3 -Wall -march=native -pedantic -std=c99 -D_POSIX_C_SOURCE=20120607L -D_XOPEN_SOURCE
|
||||||
|
LIBS=-lm -lpthread -lrt
|
||||||
|
|
||||||
|
TARGET=musiclight2
|
||||||
|
SOURCE=main.c fft.c utils.c ws2801.c
|
||||||
|
DEPS=config.h fft.h utils.h ws2801.h
|
||||||
|
|
||||||
|
OBJ=$(patsubst %.c, %.o, $(SOURCE))
|
||||||
|
|
||||||
|
$(TARGET): $(OBJ) $(DEPS)
|
||||||
|
$(CC) -o $(TARGET) $(OBJ) $(LIBS)
|
||||||
|
|
||||||
|
%.o: %.c $(DEPS)
|
||||||
|
$(CC) -c $(CFLAGS) -o $@ $< $(INCLUDES)
|
||||||
|
|
||||||
|
doc:
|
||||||
|
doxygen doxygen.conf
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(TARGET)
|
||||||
|
rm -f $(OBJ)
|
54
config.h
Normal file
54
config.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* vim: sw=2 ts=2 expandtab
|
||||||
|
*
|
||||||
|
* THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"):
|
||||||
|
* <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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// configuration variables for musiclight2
|
||||||
|
|
||||||
|
// networking
|
||||||
|
#define HOST "192.168.23.222"
|
||||||
|
#define PORT 2703
|
||||||
|
|
||||||
|
// FFT transformation parameters
|
||||||
|
#define FFT_EXPONENT 10
|
||||||
|
#define BLOCK_LEN (1 << FFT_EXPONENT) // 2^FFT_EXPONENT
|
||||||
|
#define SAMPLE_RATE 44100
|
||||||
|
#define DATALEN (BLOCK_LEN / 2)
|
||||||
|
|
||||||
|
// Number of parts in the sample buffer
|
||||||
|
#define BUFFER_PARTS 2
|
||||||
|
|
||||||
|
// update rate for the led strip (in seconds)
|
||||||
|
#define LED_INTERVAL 0.03
|
||||||
|
|
||||||
|
// number of modules in LED strip
|
||||||
|
#define NUM_MODULES 20
|
||||||
|
|
||||||
|
// frequency ranges for the base colors
|
||||||
|
#define RED_MIN_FREQ 0
|
||||||
|
#define RED_MAX_FREQ 400
|
||||||
|
#define GREEN_MIN_FREQ 400
|
||||||
|
#define GREEN_MAX_FREQ 4000
|
||||||
|
#define BLUE_MIN_FREQ 4000
|
||||||
|
#define BLUE_MAX_FREQ SAMPLE_RATE/2
|
||||||
|
|
||||||
|
#define COLOR_MAX_REDUCTION_FACTOR 0.9998
|
||||||
|
|
||||||
|
#define CENTER_MODULE 10
|
||||||
|
|
||||||
|
#define GAMMA 2.0
|
||||||
|
|
||||||
|
// sample data types
|
||||||
|
typedef int16_t sample;
|
||||||
|
typedef int64_t sample_sum;
|
||||||
|
|
||||||
|
#endif // CONFIG_H
|
151
fft.c
Normal file
151
fft.c
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* vim: sw=2 ts=2 expandtab
|
||||||
|
*
|
||||||
|
* THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"):
|
||||||
|
* <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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "fft.h"
|
||||||
|
|
||||||
|
double hanning_buffer[BLOCK_LEN];
|
||||||
|
int lookup_table[BLOCK_LEN];
|
||||||
|
|
||||||
|
|
||||||
|
void init_fft(void) {
|
||||||
|
int i = 0;
|
||||||
|
int ri, b;
|
||||||
|
|
||||||
|
for(i = 0; i < BLOCK_LEN; i++) {
|
||||||
|
hanning_buffer[i] = 0.5 * (1 - cos(2 * M_PI * i / BLOCK_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < BLOCK_LEN; i++) {
|
||||||
|
ri = 0;
|
||||||
|
|
||||||
|
for(b = 0; b < FFT_EXPONENT; b++)
|
||||||
|
ri |= ((i >> b) & 1) << (FFT_EXPONENT - b - 1);
|
||||||
|
|
||||||
|
lookup_table[i] = ri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void complex_to_absolute(double *re, double *im, double *result) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < DATALEN; i++)
|
||||||
|
{
|
||||||
|
result[i] = sqrt( re[i]*re[i] + im[i]*im[i] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void apply_hanning(sample *dftinput) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < BLOCK_LEN; i++)
|
||||||
|
dftinput[i] *= hanning_buffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void fft_transform(sample *samples, double *resultRe, double *resultIm) {
|
||||||
|
int i;
|
||||||
|
int layer, part, element;
|
||||||
|
int num_parts, num_elements;
|
||||||
|
|
||||||
|
int left, right;
|
||||||
|
|
||||||
|
double x_left_re, x_left_im, x_right_re, x_right_im;
|
||||||
|
double param;
|
||||||
|
double sinval, cosval;
|
||||||
|
|
||||||
|
// re-arrange the input array according to the lookup table
|
||||||
|
// and store it into the real output array (as the input is obviously real).
|
||||||
|
// zero the imaginary output
|
||||||
|
for(i = 0; i < BLOCK_LEN; i++)
|
||||||
|
{
|
||||||
|
resultRe[lookup_table[i]] = samples[i];
|
||||||
|
resultIm[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// walk layers
|
||||||
|
for(layer = 0; layer < FFT_EXPONENT; layer++)
|
||||||
|
{
|
||||||
|
// number of parts in current layer
|
||||||
|
num_parts = 1 << (FFT_EXPONENT - layer - 1);
|
||||||
|
|
||||||
|
// walk parts of layer
|
||||||
|
for(part = 0; part < num_parts; part++)
|
||||||
|
{
|
||||||
|
// number of elements in current part
|
||||||
|
num_elements = (1 << layer);
|
||||||
|
|
||||||
|
// walk elements in part
|
||||||
|
for(element = 0; element < num_elements; element++)
|
||||||
|
{
|
||||||
|
// calculate index of element in left and right half of part
|
||||||
|
left = (1 << (layer + 1)) * part + element;
|
||||||
|
right = left + (1 << layer);
|
||||||
|
|
||||||
|
// buffer the elements for the calculation
|
||||||
|
x_left_re = resultRe[left];
|
||||||
|
x_left_im = resultIm[left];
|
||||||
|
x_right_re = resultRe[right];
|
||||||
|
x_right_im = resultIm[right];
|
||||||
|
|
||||||
|
// precalculate the parameter for sin and cos
|
||||||
|
param = -M_PI * element / (1 << layer);
|
||||||
|
|
||||||
|
// precalculate sinus and cosinus values for param
|
||||||
|
sinval = sin(param);
|
||||||
|
cosval = cos(param);
|
||||||
|
|
||||||
|
// combine the values according to a butterfly diagram
|
||||||
|
resultRe[left] = x_right_re + x_left_re * cosval - x_left_im * sinval;
|
||||||
|
resultIm[left] = x_right_im + x_left_im * cosval + x_left_re * sinval;
|
||||||
|
resultRe[right] = x_right_re - x_left_re * cosval + x_left_im * sinval;
|
||||||
|
resultIm[right] = x_right_im - x_left_im * cosval - x_left_re * sinval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t find_loudest_frequency(double *absFFT) {
|
||||||
|
int maxPos = 0;
|
||||||
|
double maxVal = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < BLOCK_LEN; i++) {
|
||||||
|
if(absFFT[i] > maxVal) {
|
||||||
|
maxPos = i;
|
||||||
|
maxVal = absFFT[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (double)maxPos * SAMPLE_RATE / BLOCK_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_energy_in_band(double *fft, uint32_t minFreq, uint32_t maxFreq) {
|
||||||
|
int firstBlock = minFreq * BLOCK_LEN / SAMPLE_RATE;
|
||||||
|
int lastBlock = maxFreq * BLOCK_LEN / SAMPLE_RATE;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
double energy = 0;
|
||||||
|
for(i = firstBlock; i < lastBlock; i++) {
|
||||||
|
energy += fft[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return energy;
|
||||||
|
}
|
22
fft.h
Normal file
22
fft.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* vim: sw=2 ts=2 expandtab
|
||||||
|
*
|
||||||
|
* THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"):
|
||||||
|
* <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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FFT_H
|
||||||
|
#define FFT_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
void init_fft(void);
|
||||||
|
void complex_to_absolute(double *re, double *im, double *result);
|
||||||
|
void apply_hanning(sample *dftinput);
|
||||||
|
void fft_transform(sample *samples, double *resultRe, double *resultIm);
|
||||||
|
uint32_t find_loudest_frequency(double *absFFT);
|
||||||
|
double get_energy_in_band(double *fft, uint32_t minFreq, uint32_t maxFreq);
|
||||||
|
|
||||||
|
#endif // FFT_H
|
263
main.c
Normal file
263
main.c
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
/*
|
||||||
|
* vim: sw=2 ts=2 expandtab
|
||||||
|
*
|
||||||
|
* THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"):
|
||||||
|
* <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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "fft.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "ws2801.h"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
// Frames per second
|
||||||
|
#define FPS ((double)BUFFER_PARTS * SAMPLE_RATE / BLOCK_LEN)
|
||||||
|
|
||||||
|
// Number of new samples put into the buffer each frame
|
||||||
|
#define READ_SAMPLES (BLOCK_LEN / BUFFER_PARTS)
|
||||||
|
|
||||||
|
#define COLORBUF_SIZE (2*(NUM_MODULES+1))
|
||||||
|
#define CENTER_POS (2*CENTER_MODULE)
|
||||||
|
|
||||||
|
double fft[BLOCK_LEN];
|
||||||
|
double rms;
|
||||||
|
double redEnergy, greenEnergy, blueEnergy;
|
||||||
|
double lastUpdateTime = 0;
|
||||||
|
|
||||||
|
sem_t fftSemaphore;
|
||||||
|
|
||||||
|
int running = 1;
|
||||||
|
|
||||||
|
void* fft_thread(void *param) {
|
||||||
|
sample buffer[BLOCK_LEN];
|
||||||
|
sample block[BLOCK_LEN];
|
||||||
|
double fftOutReal[BLOCK_LEN], fftOutImag[BLOCK_LEN];
|
||||||
|
double tmpFFT[BLOCK_LEN];
|
||||||
|
double tmpRMS;
|
||||||
|
|
||||||
|
double nextFrame = get_hires_time() + 0.05;
|
||||||
|
double curTime;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
init_fft();
|
||||||
|
|
||||||
|
while(running) {
|
||||||
|
// shift the buffer left
|
||||||
|
memmove(buffer, buffer + READ_SAMPLES,
|
||||||
|
(BLOCK_LEN - READ_SAMPLES) * sizeof(sample));
|
||||||
|
|
||||||
|
size_t ret = fread(buffer + (BLOCK_LEN - READ_SAMPLES),
|
||||||
|
sizeof(sample), READ_SAMPLES, stdin);
|
||||||
|
if(ret != READ_SAMPLES) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(block, buffer, BLOCK_LEN * sizeof(sample));
|
||||||
|
|
||||||
|
apply_hanning(block);
|
||||||
|
fft_transform(block, fftOutReal, fftOutImag);
|
||||||
|
complex_to_absolute(fftOutReal, fftOutImag, tmpFFT);
|
||||||
|
|
||||||
|
tmpRMS = 0;
|
||||||
|
for(i = 0; i < BLOCK_LEN; i++) {
|
||||||
|
tmpRMS += block[i]*block[i];
|
||||||
|
}
|
||||||
|
tmpRMS = sqrt(tmpRMS/BLOCK_LEN);
|
||||||
|
|
||||||
|
// --- SAFE SECTION ---
|
||||||
|
sem_wait(&fftSemaphore);
|
||||||
|
|
||||||
|
memcpy(fft, tmpFFT, sizeof(fft));
|
||||||
|
rms = tmpRMS;
|
||||||
|
redEnergy = get_energy_in_band(fft, RED_MIN_FREQ, RED_MAX_FREQ);
|
||||||
|
greenEnergy = get_energy_in_band(fft, GREEN_MIN_FREQ, GREEN_MAX_FREQ);
|
||||||
|
blueEnergy = get_energy_in_band(fft, BLUE_MIN_FREQ, BLUE_MAX_FREQ);
|
||||||
|
|
||||||
|
curTime = get_hires_time();
|
||||||
|
lastUpdateTime = curTime;
|
||||||
|
|
||||||
|
sem_post(&fftSemaphore);
|
||||||
|
// --- END SAFE SECTION ---
|
||||||
|
|
||||||
|
if(curTime > nextFrame + 0.05) {
|
||||||
|
printf("Frame too late! Skipping.\n");
|
||||||
|
nextFrame = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(curTime < nextFrame - 0.05) {
|
||||||
|
printf("Frame too early.\n");
|
||||||
|
nextFrame = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nextFrame < 0) {
|
||||||
|
printf("Frame time reset.\n");
|
||||||
|
nextFrame = curTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextFrame += 1.000/FPS;
|
||||||
|
sleep_until(nextFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
double gamma_correct(double d) {
|
||||||
|
return pow(d, GAMMA);
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_bar(double fill) {
|
||||||
|
int fillCnt = 10 * fill;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < fillCnt; i++) {
|
||||||
|
printf("|");
|
||||||
|
}
|
||||||
|
|
||||||
|
for(; i < 10; i++) {
|
||||||
|
printf("-");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double weighted_avg(uint8_t colorBuf[COLORBUF_SIZE][3], int channel, int centerPos) {
|
||||||
|
return 0.20 * colorBuf[centerPos - 1][channel] +
|
||||||
|
0.60 * colorBuf[centerPos][channel] +
|
||||||
|
0.20 * colorBuf[centerPos + 1][channel];
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_status(double curRed, double maxRed, double curGreen, double maxGreen, double curBlue, double maxBlue) {
|
||||||
|
printf("\r");
|
||||||
|
|
||||||
|
printf("[\033[31m");
|
||||||
|
text_bar(curRed / maxRed);
|
||||||
|
printf("\033[0m] ");
|
||||||
|
printf("\033[1;31m%7.2e\033[0m ", maxRed / (RED_MAX_FREQ - RED_MIN_FREQ));
|
||||||
|
|
||||||
|
printf("[\033[32m");
|
||||||
|
text_bar(curGreen / maxGreen);
|
||||||
|
printf("\033[0m] ");
|
||||||
|
printf("\033[1;32m%7.2e\033[0m ", maxGreen / (GREEN_MAX_FREQ - GREEN_MIN_FREQ));
|
||||||
|
|
||||||
|
printf("[\033[34m");
|
||||||
|
text_bar(curBlue / maxBlue);
|
||||||
|
printf("\033[0m] ");
|
||||||
|
printf("\033[1;34m%7.2e\033[0m ", maxBlue / (BLUE_MAX_FREQ - BLUE_MIN_FREQ));
|
||||||
|
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
double nextFrame = get_hires_time() + LED_INTERVAL;
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
pthread_t fftThread;
|
||||||
|
|
||||||
|
int active = 1;
|
||||||
|
|
||||||
|
uint8_t colorBuf[COLORBUF_SIZE][3];
|
||||||
|
|
||||||
|
double curRedEnergy, curGreenEnergy, curBlueEnergy;
|
||||||
|
double maxRedEnergy = 1, maxGreenEnergy = 1, maxBlueEnergy = 1;
|
||||||
|
|
||||||
|
memset(colorBuf, 0, sizeof(colorBuf));
|
||||||
|
|
||||||
|
// create semaphores
|
||||||
|
sem_init(&fftSemaphore, 0, 1);
|
||||||
|
|
||||||
|
// run the fft thread
|
||||||
|
pthread_create(&fftThread, NULL, fft_thread, NULL);
|
||||||
|
|
||||||
|
ws2801_init(HOST, PORT);
|
||||||
|
|
||||||
|
while(running) {
|
||||||
|
for(i = COLORBUF_SIZE-1; i >= 0; i--) {
|
||||||
|
int pos = CENTER_POS + i;
|
||||||
|
if(pos < COLORBUF_SIZE-1) {
|
||||||
|
for(j = 0; j < 3; j++) {
|
||||||
|
colorBuf[pos][j] = colorBuf[pos - 1][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = CENTER_POS - i;
|
||||||
|
if(pos >= 0) {
|
||||||
|
for(j = 0; j < 3; j++) {
|
||||||
|
colorBuf[pos][j] = colorBuf[pos + 1][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(active) {
|
||||||
|
sem_wait(&fftSemaphore);
|
||||||
|
curRedEnergy = redEnergy;
|
||||||
|
curGreenEnergy = greenEnergy;
|
||||||
|
curBlueEnergy = blueEnergy;
|
||||||
|
sem_post(&fftSemaphore);
|
||||||
|
|
||||||
|
maxRedEnergy *= COLOR_MAX_REDUCTION_FACTOR;
|
||||||
|
if(curRedEnergy > maxRedEnergy) {
|
||||||
|
maxRedEnergy = curRedEnergy;
|
||||||
|
}
|
||||||
|
|
||||||
|
maxGreenEnergy *= COLOR_MAX_REDUCTION_FACTOR;
|
||||||
|
if(curGreenEnergy > maxGreenEnergy) {
|
||||||
|
maxGreenEnergy = curGreenEnergy;
|
||||||
|
}
|
||||||
|
|
||||||
|
maxBlueEnergy *= COLOR_MAX_REDUCTION_FACTOR;
|
||||||
|
if(curBlueEnergy > maxBlueEnergy) {
|
||||||
|
maxBlueEnergy = curBlueEnergy;
|
||||||
|
}
|
||||||
|
|
||||||
|
colorBuf[CENTER_POS][0] = 255 * gamma_correct(curRedEnergy / maxRedEnergy);
|
||||||
|
colorBuf[CENTER_POS][1] = 255 * gamma_correct(curGreenEnergy / maxGreenEnergy);
|
||||||
|
colorBuf[CENTER_POS][2] = 255 * gamma_correct(curBlueEnergy / maxBlueEnergy);
|
||||||
|
|
||||||
|
/*
|
||||||
|
show_status(curRedEnergy, maxRedEnergy, curGreenEnergy, maxGreenEnergy,
|
||||||
|
curBlueEnergy, maxBlueEnergy);
|
||||||
|
*/
|
||||||
|
|
||||||
|
for(i = 0; i < NUM_MODULES; i++) {
|
||||||
|
ws2801_set_color(i,
|
||||||
|
weighted_avg(colorBuf, 0, 2 * i + 1),
|
||||||
|
weighted_avg(colorBuf, 1, 2 * i + 1),
|
||||||
|
weighted_avg(colorBuf, 2, 2 * i + 1));
|
||||||
|
}
|
||||||
|
ws2801_commit();
|
||||||
|
|
||||||
|
if(lastUpdateTime < nextFrame - 1) {
|
||||||
|
printf("Idle for 1 second -> stopping updates.\n");
|
||||||
|
|
||||||
|
for(i = 0; i < NUM_MODULES; i++) {
|
||||||
|
ws2801_fade_color(i, 20, 20, 20);
|
||||||
|
}
|
||||||
|
ws2801_commit();
|
||||||
|
|
||||||
|
active = 0;
|
||||||
|
}
|
||||||
|
} else if(lastUpdateTime > nextFrame - 1) {
|
||||||
|
printf("Resuming updates.\n");
|
||||||
|
active = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextFrame += LED_INTERVAL;
|
||||||
|
sleep_until(nextFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
ws2801_shutdown();
|
||||||
|
|
||||||
|
pthread_join(fftThread, NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
3
run_alsa.sh
Executable file
3
run_alsa.sh
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
arecord -c 1 -f s16 -r 22050 | ./rtfft
|
4
run_mpd.sh
Executable file
4
run_mpd.sh
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#dd if=/tmp/mpd.fifo bs=1024 | ./musiclight2
|
||||||
|
./musiclight2 < /tmp/mpd.fifo
|
16
run_pa.sh
Executable file
16
run_pa.sh
Executable file
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#parec -d "alsa_output.pci-0000_00_1b.0.analog-stereo.monitor" --channels=1 --format=s16 | mbuffer -R 88200 | ./musiclight2
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
mic)
|
||||||
|
#mikro
|
||||||
|
parec -d "alsa_input.pci-0000_00_1b.0.analog-stereo" --raw --rate=44100 --channels=1 --format=s16 | ./musiclight2
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
# soundkarte
|
||||||
|
parec -d "alsa_output.pci-0000_00_1b.0.analog-stereo.monitor" --raw --rate=44100 --channels=1 --format=s16 | ./musiclight2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
42
utils.c
Normal file
42
utils.c
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* vim: sw=2 ts=2 expandtab
|
||||||
|
*
|
||||||
|
* THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"):
|
||||||
|
* <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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
double get_hires_time(void) {
|
||||||
|
struct timespec clk;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &clk);
|
||||||
|
return clk.tv_sec + 1e-9 * clk.tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsleep(double d) {
|
||||||
|
struct timespec ts;
|
||||||
|
|
||||||
|
ts.tv_sec = (time_t)d;
|
||||||
|
ts.tv_nsec = (long)(1e9 * (d - (long)d));
|
||||||
|
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sleep_until(double hires_time) {
|
||||||
|
struct timespec tv;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
tv.tv_sec = hires_time;
|
||||||
|
tv.tv_nsec = (uint64_t)(1e9 * hires_time) % 1000000000;
|
||||||
|
do {
|
||||||
|
ret = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &tv, NULL);
|
||||||
|
} while(ret == EINTR);
|
||||||
|
}
|
||||||
|
|
17
utils.h
Normal file
17
utils.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* vim: sw=2 ts=2 expandtab
|
||||||
|
*
|
||||||
|
* THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"):
|
||||||
|
* <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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
|
double get_hires_time(void);
|
||||||
|
void fsleep(double d);
|
||||||
|
void sleep_until(double hires_time);
|
||||||
|
|
||||||
|
#endif // UTILS_H
|
112
ws2801.c
Normal file
112
ws2801.c
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* vim: sw=2 ts=2 expandtab
|
||||||
|
*
|
||||||
|
* THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"):
|
||||||
|
* <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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "ws2801.h"
|
||||||
|
|
||||||
|
#define SET_COLOR 0
|
||||||
|
#define FADE_COLOR 1
|
||||||
|
#define ADD_COLOR 2
|
||||||
|
#define SET_FADESTEP 3
|
||||||
|
|
||||||
|
struct WS2801Packet {
|
||||||
|
uint8_t metadata;
|
||||||
|
uint8_t data[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
int ws2801_socket = -1;
|
||||||
|
struct WS2801Packet packetQueue[50];
|
||||||
|
int queueIndex = 0;
|
||||||
|
|
||||||
|
// creates the socket needed for steering the LED strip
|
||||||
|
int ws2801_init(char *host, unsigned short port) {
|
||||||
|
struct addrinfo hints;
|
||||||
|
struct addrinfo *result;
|
||||||
|
char portstr[6];
|
||||||
|
|
||||||
|
memset(&hints, 0, sizeof(struct addrinfo));
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_DGRAM;
|
||||||
|
hints.ai_flags = 0;
|
||||||
|
hints.ai_protocol = 0;
|
||||||
|
|
||||||
|
sprintf(portstr, "%u", port);
|
||||||
|
|
||||||
|
if(getaddrinfo(host, portstr, &hints, &result) != 0) {
|
||||||
|
perror("getaddrinfo() failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ws2801_socket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
|
||||||
|
if (ws2801_socket == -1) {
|
||||||
|
perror("socket() failed");
|
||||||
|
freeaddrinfo(result);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connect(ws2801_socket, result->ai_addr, result->ai_addrlen) == -1) {
|
||||||
|
perror("connect() failed");
|
||||||
|
freeaddrinfo(result);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(result);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws2801_set_color(uint8_t module, uint8_t r, uint8_t g, uint8_t b) {
|
||||||
|
packetQueue[queueIndex].metadata = (SET_COLOR << 6) | (module & 0x3F);
|
||||||
|
packetQueue[queueIndex].data[0] = r;
|
||||||
|
packetQueue[queueIndex].data[1] = g;
|
||||||
|
packetQueue[queueIndex].data[2] = b;
|
||||||
|
queueIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws2801_fade_color(uint8_t module, uint8_t r, uint8_t g, uint8_t b) {
|
||||||
|
packetQueue[queueIndex].metadata = (FADE_COLOR << 6) | (module & 0x3F);
|
||||||
|
packetQueue[queueIndex].data[0] = r;
|
||||||
|
packetQueue[queueIndex].data[1] = g;
|
||||||
|
packetQueue[queueIndex].data[2] = b;
|
||||||
|
queueIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws2801_add_color(uint8_t module, uint8_t r, uint8_t g, uint8_t b) {
|
||||||
|
packetQueue[queueIndex].metadata = (ADD_COLOR << 6) | (module & 0x3F);
|
||||||
|
packetQueue[queueIndex].data[0] = r;
|
||||||
|
packetQueue[queueIndex].data[1] = g;
|
||||||
|
packetQueue[queueIndex].data[2] = b;
|
||||||
|
queueIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws2801_set_fadestep(uint8_t fadestep) {
|
||||||
|
packetQueue[queueIndex].metadata = (SET_FADESTEP << 6);
|
||||||
|
packetQueue[queueIndex].data[0] = fadestep;
|
||||||
|
queueIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ws2801_commit(void) {
|
||||||
|
if(send(ws2801_socket, packetQueue, queueIndex * sizeof(struct WS2801Packet), 0) == -1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
queueIndex = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws2801_shutdown() {
|
||||||
|
close(ws2801_socket);
|
||||||
|
}
|
21
ws2801.h
Normal file
21
ws2801.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* vim: sw=2 ts=2 expandtab
|
||||||
|
*
|
||||||
|
* THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"):
|
||||||
|
* <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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WS2801_H
|
||||||
|
#define WS2801_H
|
||||||
|
|
||||||
|
int ws2801_init(char *host, unsigned short port);
|
||||||
|
void ws2801_set_color(uint8_t module, uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
void ws2801_fade_color(uint8_t module, uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
void ws2801_add_color(uint8_t module, uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
void ws2801_set_fadestep(uint8_t fadestep);
|
||||||
|
int ws2801_commit(void);
|
||||||
|
void ws2801_shutdown(void);
|
||||||
|
|
||||||
|
#endif // WS2801_H
|
Loading…
Reference in a new issue