libfn/fnvum.c
Steffen Vogel 2ea30ea3d3 rewrite
2011-01-26 17:58:25 +01:00

181 lines
4.2 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <termios.h>
#include <fcntl.h>
#include <complex.h>
/* third party libs */
#include "libfn.h"
#include <SDL/SDL.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <pulse/gccmacro.h>
#include <fftw3.h>
#define BUFFER_SIZE 1024
#define SAMPLING_RATE 44100
#define SCREEN_WIDTH (BUFFER_SIZE/2+1)
#define SCREEN_HEIGHT (SCREEN_WIDTH / 2)
#define TITLE "fnordlicht vumeter & spectrum"
void show_level(SDL_Surface *dst, float level) {
SDL_Rect rect = dst->clip_rect;
Uint32 color = SDL_MapRGB(dst->format, 0xff, 0, 0);
Uint32 background = SDL_MapRGB(dst->format, 0, 0, 0);
SDL_FillRect(dst, &rect, background);
rect.w *= level;
SDL_FillRect(dst, &rect, color);
SDL_Flip(dst);
}
void show_spectrum(SDL_Surface * dst, complex * fft_data) {
int x, p, start, end;
double log_data[BUFFER_SIZE];
SDL_Rect rect;
Uint32 color = SDL_MapRGB(dst->format, 0xff, 0, 0);
Uint32 background = SDL_MapRGB(dst->format, 0, 0, 0);
SDL_FillRect(dst, &dst->clip_rect, background);
double ampl, db;
rect.w = 1;
for (x = 0; x < BUFFER_SIZE/2+1; x++) {
ampl = cabs(fft_data[x]);
rect.x = x;
// rect.h = (ampl/3000000) * SCREEN_HEIGHT;
rect.h = 10 * log10(ampl/300000) * SCREEN_HEIGHT;
rect.y = SCREEN_HEIGHT - rect.h;
SDL_FillRect(dst, &rect, color);
}
SDL_Flip(dst);
}
void fade_level(int fd, float level) {
struct remote_msg_fade_rgb_t fncmd;
memset(&fncmd, 0, sizeof (struct remote_msg_t));
fncmd.color.red = fncmd.color.green = fncmd.color.blue = (uint8_t) 255.0 * level;
fncmd.address = 255;
fncmd.cmd = REMOTE_CMD_FADE_RGB;
fncmd.step = 25;
fncmd.delay = 0;
fn_send(fd, (struct remote_msg_t *) &fncmd);
}
int main(int argc, char *argv[]) {
/* The sample type to use */
static const pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = SAMPLING_RATE,
.channels = 1
};
pa_simple *s = NULL;
SDL_Surface *screen = NULL;
SDL_Event event;
int error, counter = 0, fd = -1;
uint32_t level;
int16_t * pcm_data;
complex * fft_data;
fftw_plan fft_plan;
/* init fnordlichts */
if (argc > 1) {
fd = open(argv[1], O_RDWR | O_NOCTTY);
if (fd < 0) {
perror(argv[0]);
exit(-1);
}
fn_init(fd);
}
/* init screen & window */
if(SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
exit(-1);
}
/* open sdl window */
SDL_WM_SetCaption(TITLE, NULL);
screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 16, SDL_SWSURFACE|SDL_ANYFORMAT|SDL_RESIZABLE);
if (screen == NULL) {
fprintf(stderr, "Unable to set video: %s\n", SDL_GetError());
exit(-1);
}
/* init fftw & get buffers*/
pcm_data = (int16_t *) malloc(BUFFER_SIZE);
fft_data = (complex *) fftw_malloc(BUFFER_SIZE * sizeof (complex));
fft_plan = fftw_plan_dft_1d(BUFFER_SIZE, fft_data, fft_data, FFTW_FORWARD, 0);
/* Create the recording stream */
if (!(s = pa_simple_new(NULL, TITLE, PA_STREAM_RECORD, NULL, "record", &ss, NULL, NULL, &error))) {
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
exit(-1);
}
pa_simple_flush(s, &error); /* flush audio buffer */
while (1) {
counter++;
/* handle SDL events */
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
printf("Good bye!\n");
exit(0);
}
}
/* read PCM audio data */
if (pa_simple_read(s, pcm_data, BUFFER_SIZE, &error) < 0) {
fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
exit(-1);
}
/* analyse audio data */
uint16_t index, max = 0;
uint32_t sum = 0;
float level;
for (index = 0; index < BUFFER_SIZE; index++) {
sum += abs(pcm_data[index]);
if (abs(pcm_data[index]) > max) max = pcm_data[index];
fft_data[index] = (double) pcm_data[index];
}
/* execute fftw plan */
fftw_execute(fft_plan);
level = (float) sum / (BUFFER_SIZE * 32767);
show_spectrum(screen, (complex *) fft_data);
//show_level(screen, level);
//fade_level(fd, level);
//printf("level: %f \tsum: %d\t max: %d\n", level, sum, max);
}
/* housekeeping */
SDL_Quit();
free(pcm_data);
fftw_free(fft_data);
fftw_destroy(fft_plan);
fftw_cleanup();
return 0;
}