Skip to content

Instantly share code, notes, and snippets.

@Melanpan
Created February 26, 2020 22:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Melanpan/c87bfa16dc1edb02a674ac84858f3d80 to your computer and use it in GitHub Desktop.
Save Melanpan/c87bfa16dc1edb02a674ac84858f3d80 to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#include <signal.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <fftw3.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//gcc -Ofast test2.c -o test3 -lm -lpulse -lpulse-simple -lfftw3
//Tnx to https://gitlab.com/nitroxis/pasa/ for providing a good example of how to do this
#define COLS 150
#define LINES 255
#define PIXELS 150
struct Pixel {
uint8_t id;
uint8_t r;
uint8_t g;
uint8_t b;
} pixel;
struct Packet {
uint8_t protocol;
uint8_t timeout;
struct Pixel pixel[PIXELS];
} packet;
#define peakHoldTimeout 500
uint8_t peakHold[COLS];
long long peakHoldTime[COLS];
struct sigaction old_sigint;
volatile bool run;
int framesPerSecond = 25;
double upperFrequency = 3520.0; // A7
double gain = 10.0;
long map(long x, long in_min, long in_max, long out_min, long out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
void onSigInt()
{
sigaction(SIGINT, &old_sigint, NULL);
run = false;
}
// hanning window.
double windowFunction(int n, int N)
{
return 0.5 * (1.0 - cosf(2.0 * M_PI * n / (N - 1.0)));
}
void print_bytes(const void *object, size_t size)
{
// This is for C++; in C just drop the static_cast<>() and assign.
const unsigned char * const bytes = static_cast<const unsigned char *>(object);
size_t i;
printf(">> ");
for(i = 0; i < size; i++)
{
printf("%d ", bytes[i]);
}
printf("\n");
}
void calculateBars(fftw_complex* fft, int fftSize, int* bars, int numBars)
{
// todo: use the float-point value and implement proper interpolation.
double barWidthD = upperFrequency / (framesPerSecond * numBars);
int barWidth = (int)ceil(barWidthD);
double scale = 2.0 / fftSize * gain;
// interpolate bars.
int i = 0;
for(int bar = 0; bar < numBars; bar++)
{
// get average.
double power = 0.0;
for(int j = 0; j < barWidth && i < fftSize; i++, j++)
{
double re = fft[i][0] * scale;
double im = fft[i][1] * scale;
power += re * re + im * im; // abs(c)
}
power *= (1.0 / barWidth); // average.
if(power < 1e-15) power = 1e-15; // prevent overflows.
// compute decibels.
int dB = LINES + (int)(10.0 * log10(power));
if(dB > LINES) dB = LINES;
if(dB < 0) dB = 0;
// set bar.
bars[bar] = dB;
}
}
int main(int argc, char* argv[])
{
static const pa_sample_spec ss =
{
.format = PA_SAMPLE_FLOAT32LE,
.rate = 44100,
.channels = 2
};
int clientSocket, portNum;
struct sockaddr_in serverAddr;
socklen_t addr_size;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(21324);
serverAddr.sin_addr.s_addr = inet_addr("192.168.3.4");
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
addr_size = sizeof serverAddr;
clientSocket = socket(PF_INET, SOCK_DGRAM, 0);
// open record device
int error;
pa_simple *s = pa_simple_new(NULL, "pasa", PA_STREAM_RECORD, NULL, "record", &ss, NULL, NULL, &error);
// check error
if (!s)
{
fprintf(stderr, "pa_simple_new() failed: %s\n", pa_strerror(error));
return 1;
}
// input buffer.
const int size = ss.rate / framesPerSecond;
float window[size];
float buffer[ss.channels * size];
// compute window.
for(int n = 0; n < size; n++)
window[n] = windowFunction(n, size);
// fftw setup
double *in = (double*)fftw_malloc(sizeof(double) * size);
fftw_complex *out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * size);
fftw_plan plan = fftw_plan_dft_r2c_1d(size, in, out, FFTW_MEASURE);
run = true;
int barHeight;
struct Packet packet;
packet.protocol = 1;
packet.timeout = 10;
// record loop
while(run)
{
int barsL[COLS / 2];
int barsR[COLS / 2];
// read from device.
if (pa_simple_read(s, buffer, sizeof(buffer), &error) < 0)
{
pa_simple_free(s);
fprintf(stderr, "pa_simple_read() failed: %s\n", pa_strerror(error));
return 1;
}
// left input.
for (int i = 0; i < size; i++) {
in[i] = (double)(window[i] * buffer[i * 2]);
}
fftw_execute(plan);
calculateBars(out, size, barsL, COLS / 2);
// // right input.
// for (int i = 0; i < size; i++) {
// in[i] = (double)(window[i] * buffer[i * 2 + 1]);
// }
// fftw_execute(plan);
// calculateBars(out, size, barsR, COLS / 2);
packet.protocol = 1;
packet.timeout = 5;
// draw left
for(int i = 0; i < PIXELS; i++)
{
barHeight = (int)map((int)barsL[i], 0, 128, 15, 0);
packet.pixel[i].id = i;
packet.pixel[i].r = 0;
packet.pixel[i].g = map((int)barsL[i], 255, 230, 0, 255);
packet.pixel[i].b = map((int)barsL[i], 200, 230, 0, 255);
}
int ret = sendto(clientSocket, &packet, sizeof(packet), 0, (struct sockaddr *)&serverAddr, addr_size);
print_bytes(&packet, sizeof(packet));
//printf("\n----------\n");
}
// clean up fftw
fftw_destroy_plan(plan);
fftw_free(in);
fftw_free(out);
// clean up pulseaudio
pa_simple_free(s);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment