From 4796f05c431525e69e3f6b85c6545db57615e544 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Wed, 2 Feb 2011 12:33:48 +0100 Subject: [PATCH] changed to fftw3 based fourier analysis --- src/fnvum_fftw.c | 228 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 src/fnvum_fftw.c diff --git a/src/fnvum_fftw.c b/src/fnvum_fftw.c new file mode 100644 index 0000000..5079281 --- /dev/null +++ b/src/fnvum_fftw.c @@ -0,0 +1,228 @@ +#include "../lib/libfn.h" + +/* stdlibs */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* third party libs */ +#include +#include +#include +#include +#include + +#define N 512 +#define SAMPLING_RATE 44100 + +#define MIN_FREQ 50 +#define MAX_FREQ 9000 + +#define MIN_K (MIN_FREQ*N/SAMPLING_RATE)+1 +#define MAX_K (MAX_FREQ*N /SAMPLING_RATE) + +#define LINE_WIDTH 4 +#define SCREEN_WIDTH LINE_WIDTH*(MAX_K - MIN_K) +#define SCREEN_HEIGHT SCREEN_WIDTH/2 + +#define TITLE "fnordlicht visualization" + +double normalize_auditory(double freq, double spl) { + return spl; // TODO implement +} + +void show_level(SDL_Surface *dst, float level) { + SDL_Rect rect = dst->clip_rect; + uint32_t color = SDL_MapRGB(dst->format, 0xff, 0, 0); // TODO define via phase + uint32_t background = SDL_MapRGB(dst->format, 0, 0, 0); + + SDL_FillRect(dst, &rect, background); + + rect.w *= level; + + SDL_FillRect(dst, &rect, color); + SDL_Flip(dst); +} + +struct rgb_color_t hsv2rgb(struct hsv_color_t hsv) { + struct rgb_color_t rgb; + + uint16_t h = hsv.hue % 360; + uint8_t s = hsv.saturation; + uint8_t v = hsv.value; + + uint16_t f = ((h % 60) * 255 + 30)/60; + uint16_t p = (v * (255-s)+128)/255; + uint16_t q = ((v * (255 - (s*f+128)/255))+128)/255; + uint16_t t = (v * (255 - ((s * (255 - f))/255)))/255; + uint8_t i = h/60; + + switch (i) { + case 0: rgb.red = v; rgb.green = t; rgb.blue = p; break; + case 1: rgb.red = q; rgb.green = v; rgb.blue = p; break; + case 2: rgb.red = p; rgb.green = v; rgb.blue = t; break; + case 3: rgb.red = p; rgb.green = q; rgb.blue = v; break; + case 4: rgb.red = t; rgb.green = p; rgb.blue = v; break; + case 5: rgb.red = v; rgb.green = p; rgb.blue = q; break; + } + + return rgb; +} + +uint32_t phasor2color(complex phasor, SDL_PixelFormat *format) { + struct hsv_color_t hsv; + struct rgb_color_t rgb; + + hsv.hue = 180*(carg(phasor)+M_PI)/M_PI; + hsv.saturation = 255; + hsv.value = 255; + + rgb = hsv2rgb(hsv); + + return SDL_MapRGB(format, rgb.red, rgb.green, rgb.blue); +} + +void show_spectrum(SDL_Surface * dst, complex * fft_data) { + SDL_Rect rect; + uint32_t background = SDL_MapRGB(dst->format, 0, 0, 0); + + SDL_FillRect(dst, &dst->clip_rect, background); + + rect.w = LINE_WIDTH; + + int k; + for (k = MIN_K; k <= MAX_K; k++) { + double ampl = cabs(fft_data[k]); + + rect.x = (k - MIN_K + 1) * LINE_WIDTH; + + rect.h = (ampl / 900000) * SCREEN_HEIGHT; + rect.y = SCREEN_HEIGHT - rect.h; + + SDL_FillRect(dst, &rect, phasor2color(fft_data[k], dst->format)); + } + 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); + if (screen == NULL) { + fprintf(stderr, "Unable to set video: %s\n", SDL_GetError()); + exit(-1); + } + + /* init fftw & get buffers*/ + pcm_data = (int16_t *) malloc(N * sizeof (int16_t)); + fft_data = (complex *) fftw_malloc(N * sizeof (complex)); + fft_plan = fftw_plan_dft_1d(N, 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, N * sizeof (int16_t), &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error)); + exit(-1); + } + + /* analyse audio data */ + int16_t index, max = 0; + uint32_t sum = 0; + float level; + for (index = 0; index < N; index++) { + sum += abs(pcm_data[index]); + if (abs(pcm_data[index]) > max) max = pcm_data[index]; + + fft_data[index] = (double) pcm_data[index]; + } + + level = (float) sum / (N * pow(2, 15)); + //show_level(screen, level * 1.8); + //fade_level(fd, level); + + /* execute fftw plan */ + fftw_execute(fft_plan); + show_spectrum(screen, fft_data); + + //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; +}