Code:
/* gcc kissfft_xlib_spectrum_v1.c kiss_fft.c -O2 -Wall -o xkissfft_alsa -lm -lX11 -lasound
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <alsa/asoundlib.h> // For audio capture
#include <X11/X.h> // For X11 graphics
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include "kiss_fft.h" // For FFT calculations
#define FFT_SIZE 1024 // Adjust as needed
// Map magnitudes to visual representation (lines and bars)
void draw_spectrum(Display *display, Window window, double *magnitudes)
{
// Get window dimensions
int width, height;
XWindowAttributes wa;
XGetWindowAttributes(display, window, &wa);
width = wa.width;
height = wa.height;
// Calculate bar width and spacing
int bar_width = width / (FFT_SIZE / 2 + 1);
int bar_spacing = 1; // Adjust spacing as needed
GC gc = XCreateGC(display, window, 0, NULL); // Create a graphics context
// Clear the window (optional)
XSetForeground(display, gc, WhitePixel(display, DefaultScreen(display)));
XFillRectangle(display, window, gc, 0, 0, width, height);
// Set drawing color for the bars
XSetForeground(display, gc, BlackPixel(display, DefaultScreen(display)));
// Draw bars for each magnitude value
for (int i = 0; i < FFT_SIZE / 2 + 1; i++)
{
int bar_height = (int)(magnitudes[i] *height / 256); // Scale magnitude to screen height
XFillRectangle(display, window, gc, i *(bar_width + bar_spacing), height - bar_height, bar_width, bar_height);
}
XFreeGC(display, gc); // Release the graphics context
// Handle events and refresh display
XEvent xevent;
XNextEvent(display, &xevent);
XFlush(display);
// Handle events
if (xevent.type == ClientMessage) /*how is this wrong? */
{
if (xevent.xclient.message_type == XInternAtom(display, "WM_PROTOCOLS", False) && (Atom) xevent.xclient.data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", False))
{
XCloseDisplay(display);
exit(0);
}
}
}
int main(int argc, char *argv[])
{
//Start of program code
double *magnitudes = (double*) malloc(sizeof(double) *(FFT_SIZE / 2 + 1));
/// Initialize audio capture and X11 display:
// Open audio device
snd_pcm_t * pcm_handle;
snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_CAPTURE, 0);
// Set audio parameters (sample rate, channels, etc.)
snd_pcm_set_params(pcm_handle, SND_PCM_FORMAT_U8, SND_PCM_ACCESS_RW_INTERLEAVED, 1, 48000, 1, 500000);
// Open X11 display
Display *display = XOpenDisplay(NULL);
// Window window = XCreateSimpleWindow(display, window, 100, 100, 800, 600, 1, BlackPixel(display, DefaultScreen(display)), WhitePixel(display, DefaultScreen(display))); //display, parent, x, y, width, height, border_width, border, background
Window window = XCreateSimpleWindow(display, RootWindow(display, DefaultScreen(display)), 100, 100, 1080, 240, 1, BlackPixel(display, DefaultScreen(display)), WhitePixel(display, DefaultScreen(display)));
XStoreName(display, window, "kissfft_alsa-spectrum");
XSelectInput(display, window, ExposureMask | StructureNotifyMask | KeyPressMask); // Set event mask for exposure, resize, and key press
XEvent xevent;
XMapWindow(display, window);
/// Create buffers for audio samples and FFT output:
kiss_fft_cfg cfg = kiss_fft_alloc(FFT_SIZE, 0, 0, 0);
kiss_fft_cpx in[FFT_SIZE];
kiss_fft_cpx out[FFT_SIZE];
/// Main loop:
while (1)
{
// Check for events
XNextEvent(display, &xevent);
// Capture audio samples
snd_pcm_readi(pcm_handle, in, FFT_SIZE);
// Apply window function (Hann window)
for (int i = 0; i < FFT_SIZE; i++)
{
in[i].r *= 0.5 *(1 - cos(2 *M_PI *i / (FFT_SIZE - 1)));
}
// Perform FFT
kiss_fft(cfg, in, out);
// Calculate magnitudes
double magnitudes[FFT_SIZE / 2 + 1];
for (int i = 0; i < FFT_SIZE / 2 + 1; i++)
{
magnitudes[i] = sqrt(out[i].r *out[i].r + out[i].i *out[i].i);
}
// Map magnitudes to visual representation (lines and bars)
draw_spectrum(display, window, magnitudes);
}
/// Clean up:
free(magnitudes);
// Close audio device
snd_pcm_close(pcm_handle);
// Close X11 display
XDestroyWindow(display, window);
XCloseDisplay(display);
return 0;
}