Last active
May 7, 2019 22:10
-
-
Save J3698/f32cc88daeefeb607dfdf080030332fb to your computer and use it in GitHub Desktop.
Lights Simple Visualizer
This file contains hidden or 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 <stdlib.h> | |
#include <stdio.h> | |
#include <stdbool.h> | |
#include <errno.h> | |
#include <unistd.h> | |
#include <math.h> | |
#include <string.h> | |
#include "moving_average.h" | |
// for audio | |
#include <jack/jack.h> | |
#include <jack/transport.h> | |
// for LEDs | |
#include <apa102.h> | |
#include <wiringPi.h> | |
#define FILTER_SAMPLES 512 | |
#define STRIP_BRIGHTNESS 31 | |
#define MAX_BRIGHTNESS 0x66 | |
#define BRIGHTNESS_DECAY 10 | |
#define BEAT_FACTOR 1.3 | |
// LED strip(s) | |
struct APA102* led_strip; | |
// the datatype for audio (should be a double) | |
typedef jack_default_audio_sample_t sample_t; | |
// the client that interacts with jack | |
char *client_name = "Visualizer"; | |
jack_client_t *client; | |
// ports for left and right audio sent to speaker | |
jack_port_t *output_port_l; | |
jack_port_t *output_port_r; | |
// ports for left and right audio coming from system (e.g. a connected phone) | |
jack_port_t *input_port_l; | |
jack_port_t *input_port_r; | |
void update_lights(bool); | |
bool has_beat(); | |
double calc_volume(sample_t*, sample_t*, jack_nframes_t); | |
double sum_squares(sample_t*, size_t); | |
// this function is called whenever new data is available | |
int process(jack_nframes_t num_samples, void *arg) { | |
sample_t *out_buffer_left = (sample_t *) jack_port_get_buffer(output_port_l, num_samples); | |
sample_t *out_buffer_right = (sample_t *) jack_port_get_buffer(output_port_r, num_samples); | |
sample_t *in_buffer_left = (sample_t *) jack_port_get_buffer(input_port_l, num_samples); | |
sample_t *in_buffer_right = (sample_t *) jack_port_get_buffer(input_port_r, num_samples); | |
memcpy(out_buffer_left, in_buffer_left, sizeof(sample_t) * num_samples); | |
memcpy(out_buffer_right, in_buffer_right, sizeof(sample_t) * num_samples); | |
bool beat_detected = has_beat(in_buffer_left, in_buffer_right, num_samples); | |
update_lights(beat_detected); | |
return 0; | |
} | |
// set lights full if beat detected, else fade lights | |
void update_lights(bool beat_detected) { | |
static uint8_t blueness = 0; | |
if (beat_detected) { | |
blueness = MAX_BRIGHTNESS; | |
} else if (blueness >= BRIGHTNESS_DECAY) { | |
blueness = blueness - BRIGHTNESS_DECAY; | |
} else { | |
blueness = 0; | |
} | |
APA102_Fill(led_strip, APA102_CreateFrame(STRIP_BRIGHTNESS, 0, 0, blueness)); | |
} | |
bool has_beat(sample_t *audio_left, sample_t *audio_right, jack_nframes_t num_samples) { | |
static movingAverage_t *moving_avg = NULL; | |
if (moving_avg == NULL) { | |
moving_avg = movingAverage_new(FILTER_SAMPLES); | |
} | |
// beat detected if volume much higher than moving avg | |
double volume = calc_volume(audio_left, audio_right, num_samples); | |
bool beat_detected = | |
volume * FILTER_SAMPLES > BEAT_FACTOR * moving_avg->average; | |
movingAverage_update(moving_avg, volume); | |
printf("w: %f t: %f\n", moving_avg->average, volume); | |
return beat_detected; | |
} | |
double calc_volume(sample_t *audio_left, sample_t *audio_right, | |
jack_nframes_t num_samples) { | |
return (sum_squares(audio_left, num_samples) + | |
sum_squares(audio_right, num_samples)) / FILTER_SAMPLES; | |
} | |
double sum_squares(sample_t *buf, jack_nframes_t len) { | |
double total = 0; | |
for (jack_nframes_t i = 0; i < len; i++) { | |
total += pow(buf[i], 2); | |
} | |
return total; | |
} | |
// start a connection to jack | |
bool startClient() { | |
jack_status_t status; | |
while ((client = jack_client_open(client_name, 0, &status)) == 0) { | |
fprintf(stderr, "jack server not running?\n"); | |
return false; | |
} | |
printf("Client started\n"); | |
return true; | |
} | |
// create ports in order to read and write audio data (possibly extraneous) | |
void createPorts() { | |
output_port_l = jack_port_register(client, "out_l", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |
output_port_r = jack_port_register(client, "out_r", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |
input_port_l = jack_port_register(client, "in_l", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); | |
input_port_r = jack_port_register(client, "in_r", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); | |
printf("Ports created\n"); | |
} | |
// connect ports from the system and the speakers to our ports | |
void connectPorts() { | |
jack_connect(client, "PulseAudio JACK Sink:front-left", jack_port_name(input_port_l)); | |
jack_connect(client, "PulseAudio JACK Sink:front-right", jack_port_name(input_port_r)); | |
jack_connect(client, jack_port_name(output_port_l), "system:playback_1"); | |
jack_connect(client, jack_port_name(output_port_r), "system:playback_2"); | |
printf("Ports connected\n"); | |
} | |
/** | |
* In the main function jack gets started, ports get created and connected, | |
* and jack is told to call the process function when more work is to be done. | |
*/ | |
int main(int argc, char *argv[]) { | |
if (!startClient()) { | |
return -1; | |
} | |
createPorts(); | |
led_strip = APA102_Init(60); | |
jack_set_process_callback(client, process, 0); | |
if (jack_activate(client)) { | |
fprintf(stderr, "cannot activate client"); | |
return 1; | |
} | |
printf("Jack activated\n"); | |
connectPorts(); | |
sleep(180); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment