Created
February 26, 2020 22:11
-
-
Save Melanpan/c87bfa16dc1edb02a674ac84858f3d80 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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