initial commit

This commit is contained in:
Jarcode
2017-11-25 22:00:49 -08:00
parent dce58721aa
commit ca7e6c8ba3
20 changed files with 2235 additions and 0 deletions

38
Makefile Normal file
View File

@@ -0,0 +1,38 @@
src = $(wildcard *.c)
obj = $(src:.c=.o)
LDFLAGS = -lpulse -lpulse-simple -pthread -lOpenGL -lglfw -ldl -lm -lX11
PYTHON = python
GLAD_INSTALL_DIR = glad
GLAD_SRCFILE = ./glad/src/glad.c
GLAD_ARGS_RELEASE = --generator=c --extensions=GL_EXT_framebuffer_multisample,GL_EXT_texture_filter_anisotropic
GLAD_ARGS_DEBUG = --generator=c-debug --extensions=GL_EXT_framebuffer_multisample,GL_EXT_texture_filter_anisotropic
CFLAGS_COMMON = -I glad/include
CFLAGS_USE = $(CFLAGS_COMMON) $(CFLAGS)
all: glad glava
%.o: %.c
$(CC) $(CFLAGS_USE) -o $@ -c $<
glava: $(obj)
$(CC) -o $@ $^ glad.o $(LDFLAGS)
.PHONY: glad
glad:
cd $(GLAD_INSTALL_DIR) && $(PYTHON) -m glad $(GLAD_ARGS_RELEASE) --out-path=.
$(CC) $(CFLAGS_USE) -o glad.o $(GLAD_SRCFILE) -c
.PHONY: glad-debug
glad-debug:
cd $(GLAD_INSTALL_DIR) && $(PYTHON) -m glad $(GLAD_ARGS_DEBUG) --out-path=.
$(CC) $(CFLAGS_USE) -o glad.o $(GLAD_SRCFILE) -c
.PHONY: clean
clean:
rm -f $(obj) glava glad.o
CFLAGS = -ggdb

85
fifo.c Normal file
View File

@@ -0,0 +1,85 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <math.h>
#include <time.h>
#include "fifo.h"
//input: FIFO
void* input_fifo(void* data)
{
struct audio_data *audio = (struct audio_data *)data;
int fd;
int n = 0;
signed char buf[1024];
int tempr, templ, lo;
int q, i;
int t = 0;
int size = 1024;
int bytes = 0;
int flags;
struct timespec req = { .tv_sec = 0, .tv_nsec = 10000000 };
fd = open(audio->source, O_RDONLY);
flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
while (1) {
bytes = read(fd, buf, sizeof(buf));
if (bytes == -1) { //if no bytes read sleep 10ms and zero shared buffer
nanosleep (&req, NULL);
t++;
if (t > 10) {
for (i = 0; i < 2048; i++)audio->audio_out_l[i] = 0;
for (i = 0; i < 2048; i++)audio->audio_out_r[i] = 0;
t = 0;
}
} else { //if bytes read go ahead
t = 0;
for (q = 0; q < (size / 4); q++) {
tempr = ( buf[ 4 * q + 3] << 2);
lo = ( buf[4 * q + 2] >> 6);
if (lo < 0)lo = abs(lo) + 1;
if (tempr >= 0)tempr = tempr + lo;
else tempr = tempr - lo;
templ = ( buf[ 4 * q + 1] << 2);
lo = ( buf[ 4 * q] >> 6);
if (lo < 0)lo = abs(lo) + 1;
if (templ >= 0)templ = templ + lo;
else templ = templ - lo;
if (audio->channels == 1) audio->audio_out_l[n] = (tempr +
templ) /
2;
//stereo storing channels in buffer
if (audio->channels == 2) {
audio->audio_out_l[n] = templ;
audio->audio_out_r[n] = tempr;
}
n++;
if (n == 2048 - 1)n = 0;
}
}
if (audio->terminate == 1) {
close(fd);
break;
}
}
return 0;
}

17
fifo.h Normal file
View File

@@ -0,0 +1,17 @@
#include <pthread.h>
struct audio_data {
volatile float* audio_out_r;
volatile float* audio_out_l;
bool modified;
size_t audio_buf_sz, sample_sz;
int format;
unsigned int rate;
char *source; // pulse source
int channels;
int terminate; // shared variable used to terminate audio thread
pthread_mutex_t mutex;
};
void* input_fifo(void* data);

83
glava.c Normal file
View File

@@ -0,0 +1,83 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include "fifo.h"
#include "pulse_input.h"
#include "render.h"
#include "xwincheck.h"
int main(int argc, char** argv) {
const char* audio_source = argc >= 2 ? argv[1] : NULL; //TODO: change
renderer* r = rd_new(0, 0, 400, 500, "shaders");
float b0[r->bufsize_request], b1[r->bufsize_request];
size_t t;
for (t = 0; t < r->bufsize_request; ++t) {
b0[t] = 0.0F;
b1[t] = 0.0F;
}
struct audio_data audio = {
.source = ({
char* src = NULL;
if (audio_source && strcmp(audio_source, "auto") != 0) {
src = malloc(1 + strlen(audio_source));
strcpy(src, audio_source);
}
src;
}),
.rate = (unsigned int) r->rate_request,
.format = -1,
.terminate = 0,
.channels = 2,
.audio_out_r = b0,
.audio_out_l = b1,
.mutex = PTHREAD_MUTEX_INITIALIZER,
.audio_buf_sz = r->bufsize_request,
.sample_sz = r->samplesize_request,
.modified = false
};
if (!audio.source) {
get_pulse_default_sink(&audio);
printf("Using default PulseAudio sink: %s\n", audio.source);
}
pthread_t thread;
int thread_id = pthread_create(&thread, NULL, input_pulse, (void*) &audio);
float lb[r->bufsize_request], rb[r->bufsize_request];
while (r->alive) {
bool modified; /* if the audio buffer has been updated by the streaming thread */
/* lock the audio mutex and read our data */
pthread_mutex_lock(&audio.mutex);
modified = audio.modified;
if (modified) {
/* create our own copies of the audio buffers, so the streaming thread can continue to append to it */
memcpy(lb, (void*) audio.audio_out_l, r->bufsize_request * sizeof(float));
memcpy(rb, (void*) audio.audio_out_r, r->bufsize_request * sizeof(float));
audio.modified = false; /* set this flag to false until the next time we read */
}
pthread_mutex_unlock(&audio.mutex);
/* Only render if needed (ie. stop rendering when fullscreen windows are focused) */
if (xwin_should_render()) {
rd_update(r, lb, rb, r->bufsize_request, modified);
} else {
/* Sleep for 50ms and then attempt to render again */
struct timespec tv = {
.tv_sec = 0, .tv_nsec = 50 * 1000000
};
nanosleep(&tv, NULL);
}
}
rd_destroy(r);
}

371
glsl_ext.c Normal file
View File

@@ -0,0 +1,371 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "glsl_ext.h"
struct sbuf {
char* buf;
size_t at; /* index of final null character */
size_t bsize; /* actual buffer size */
};
#define append(sbuf, str) n_append(sbuf, strlen(str), str)
static inline void expand_for(struct sbuf* sbuf, size_t len) {
bool resize = false;
while (len + 1 > sbuf->bsize - sbuf->at) {
sbuf->bsize *= 2;
resize = true;
}
if (resize)
sbuf->buf = realloc(sbuf->buf, sbuf->bsize);
}
/* append 'n' bytes from 'str' to the resizable buffer */
static void n_append(struct sbuf* sbuf, size_t len, const char* str) {
expand_for(sbuf, len);
memcpy(sbuf->buf + sbuf->at, str, len);
sbuf->at += len;
sbuf->buf[sbuf->at] = '\0';
}
#define s_append(sbuf, fmt, ...) se_append(sbuf, 64, fmt, __VA_ARGS__)
/* append the formatted string to the resizable buffer, where elen is extra space for formatted chars */
static void se_append(struct sbuf* sbuf, size_t elen, const char* fmt, ...) {
size_t space = strlen(fmt) + elen;
expand_for(sbuf, space);
va_list args;
va_start(args, fmt);
int written;
if ((written = vsnprintf(sbuf->buf + sbuf->at, space, fmt, args)) < 0)
abort();
sbuf->at += space;
va_end(args);
}
#define parse_error(line, f, fmt, ...) \
fprintf(stderr, "[%s:%d] " fmt "\n", f, (int) line, __VA_ARGS__); \
abort()
#define parse_error_s(line, f, s) \
fprintf(stderr, "[%s:%d] " s "\n", f, (int) line); \
abort()
struct schar {
char* buf;
size_t sz;
};
/* handle raw arguments for #include and #request directives */
/* NOTE: munmap needs to be called on the result */
static struct schar directive(struct glsl_ext* ext, char** args,
size_t args_sz, bool include,
size_t line, const char* f) {
if (include) {
if (args_sz == 0) {
parse_error_s(line, f, "No arguments provided to #include directive!");
}
char* target = args[0];
char path[strlen(ext->cd) + strlen(target) + 2];
snprintf(path, sizeof(path) / sizeof(char), "%s/%s", ext->cd, target);
int fd = open(path, O_RDONLY);
if (fd == -1) {
parse_error(line, f, "failed to load GLSL shader source specified by #include directive '%s': %s\n",
path, strerror(errno));
}
struct stat st;
fstat(fd, &st);
char* map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (!map) {
parse_error(line, f, "failed to map GLSL shader source specified by #include directive '%s': %s\n",
path, strerror(errno));
}
struct glsl_ext next = {
.source = map,
.source_len = st.st_size,
.cd = ext->cd,
.handlers = ext->handlers
};
/* recursively process */
ext_process(&next, target);
struct schar ret = {
.buf = next.processed,
.sz = next.p_len
};
return ret;
} else {
if (args_sz > 0) {
char* request = args[0];
struct request_handler* handler;
bool found = false;
size_t t, i;
for (t = 0; (handler = &ext->handlers[t])->name != NULL; ++t) {
if(!strcmp(handler->name, request)) {
found = true;
void** processed_args = malloc(strlen(handler->fmt) * sizeof(void*));
char c;
size_t i;
for (i = 0; (c = handler->fmt[i]) != '\0'; ++i) {
if (args_sz <= 1 + i) {
parse_error(line, f,
"failed to execute request '%s': expected format '%s'\n",
request, handler->fmt);
}
char* raw = args[1 + i];
switch (c) {
case 'i':
{
int v = (int) strtol(raw, NULL, 0);
processed_args[i] = malloc(sizeof(int));
*(int*) processed_args[i] = v;
break;
}
case 'f':
{
float f = strtof(raw, NULL);
processed_args[i] = malloc(sizeof(float));
*(float*) processed_args[i] = f;
break;
}
case 's': { *(char**) &processed_args[i] = raw; break; }
case 'b':
{
bool v;
if (!strcmp(raw, "true")) {
v = true;
} else if (!strcmp(raw, "false")) {
v = false;
} else if (strlen(raw) == 1) {
switch (raw[0]) {
case 't': { v = true; break; }
case 'f': { v = false; break; }
case '1': { v = true; break; }
case '0': { v = false; break; }
default:
parse_error_s(line, f, "tried to parse invalid raw string into a boolean");
}
} else {
parse_error_s(line, f, "tried to parse invalid raw string into a boolean");
}
processed_args[i] = malloc(sizeof(bool));
*(bool*) processed_args[i] = v;
break;
}
}
}
handler->handler(request, processed_args);
for (i = 0; (c = handler->fmt[i]) != '\0'; ++i)
if (c != 's')
free(processed_args[i]);
free(processed_args);
}
}
if (!found) {
parse_error(line, f, "unknown request type '%s'", request);
}
}
struct schar ret = {
.buf = NULL,
.sz = 0
};
return ret;
}
}
/* state machine parser */
void ext_process(struct glsl_ext* ext, const char* f) {
#define LINE_START 0
#define IGNORING 1
#define MACRO 2
#define REQUEST 3
#define INCLUDE 4
struct sbuf sbuf = {
.buf = malloc(256),
.at = 0,
.bsize = 256
};
size_t source_len = ext->source_len;
size_t t;
char at;
int state = LINE_START;
size_t macro_start_idx, arg_start_idx;
size_t line = 1;
bool quoted = false, arg_start;
char** args = NULL;
size_t args_sz = 0;
for (t = 0; t <= source_len; ++t) {
at = source_len == t ? '\0' : ext->source[t];
if (at == '\n')
++line;
switch (state) {
case LINE_START: /* processing start of line */
{
switch (at) {
case '#': {
macro_start_idx = t;
state = MACRO;
continue; }
case ' ':
case '\t':
case '\n':
goto copy;
default: {
state = IGNORING;
goto copy; }
}
}
case IGNORING: /* copying GLSL source or unrelated preprocessor syntax */
if (at == '\n') {
state = LINE_START;
}
goto copy;
case MACRO: /* processing start of macro */
{
switch (at) {
case '\n':
case ' ':
case '\t':
case '\0':
{
/* end parsing directive */
if (!strncmp("#request", &ext->source[macro_start_idx], t - macro_start_idx)
|| !strncmp("#REQUEST", &ext->source[macro_start_idx], t - macro_start_idx)) {
state = REQUEST;
goto prepare_arg_parse;
} else if (!strncmp("#include", &ext->source[macro_start_idx], t - macro_start_idx)
|| !strncmp("#INCLUDE", &ext->source[macro_start_idx], t - macro_start_idx)) {
state = INCLUDE;
goto prepare_arg_parse;
} else {
n_append(&sbuf, t - macro_start_idx, &ext->source[macro_start_idx]);
state = IGNORING;
goto copy;
}
prepare_arg_parse:
{
arg_start_idx = t + 1;
arg_start = true;
args = malloc(sizeof(char*));
args_sz = 0;
*args = NULL;
}
}
case 'a' ... 'z':
case 'A' ... 'Z':
continue;
default:
/* invalid char, malformed! */
parse_error(line, f, "Unexpected character '%c' while parsing GLSL directive", at);
}
}
/* scope-violating macro to copy the result of the currently parsed argument */
#define copy_arg(end) \
do { if (end - arg_start_idx > 0) { \
++args_sz; \
args = realloc(args, sizeof(char*) * args_sz); \
args[args_sz - 1] = malloc((end - arg_start_idx) + 1); \
memcpy(args[args_sz - 1], &ext->source[arg_start_idx], end - arg_start_idx); \
args[args_sz - 1][end - arg_start_idx] = '\0'; \
} } while (0)
case REQUEST:
case INCLUDE:
{
switch (at) {
case ' ':
case '\t':
case '\n':
case '\0':
if (!quoted) {
/* end arg */
copy_arg(t);
arg_start = true;
arg_start_idx = t + 1;
} else arg_start = false;
if (at == '\n') {
/* end directive */
size_t a;
printf("[DEBUG] handling request/include for %d args\n", args_sz);
for (a = 0; a < args_sz; ++a) {
printf("[DEBUG] [%d]: \"%s\"\n", a, args[a]);
}
struct schar r = directive(ext, args, args_sz, state == INCLUDE, line, f);
for (a = 0; a < args_sz; ++a) {
printf("[DEBUG POST] [%d]: \"%s\"\n", a, args[a]);
}
for (a = 0; a < args_sz; ++a) {
free(args[a]);
}
args_sz = 0;
/* if something was returned (ie. included file), paste the results */
if (r.buf) {
n_append(&sbuf, r.sz, r.buf);
append(&sbuf, "\n");
}
munmap(r.buf, r.sz);
state = LINE_START;
}
break;
case '"':
if (quoted) {
/* end arg */
copy_arg(t);
quoted = false;
arg_start = true;
arg_start_idx = t + 1;
} else if (arg_start) {
++arg_start_idx;
quoted = true;
} else arg_start = false;
break;
default:
arg_start = false;
}
continue;
}
}
copy:
if (at != '\0')
n_append(&sbuf, 1, &at);
}
ext->processed = sbuf.buf;
ext->p_len = sbuf.at;
if (args)
free(args);
}
void ext_free(struct glsl_ext* ext) {
free(ext->processed);
}

36
glsl_ext.h Normal file
View File

@@ -0,0 +1,36 @@
#define RHANDLER(name, args, ...) \
({ void _handler(const char* name, void** args) __VA_ARGS__ _handler; })
struct request_handler {
const char* name;
/*
handler format:
'i' - signed integer (void* -> int*)
'f' - float (void* -> float*)
's' - string (void* -> const char*)
'b' - bool (void* -> bool*)
example:
.fmt = "sii" // takes a string, and then two integers
.fmt = "ffb" // takes two floats, then a boolean
*/
const char* fmt;
void (*handler)(const char* name, void** args);
};
struct glsl_ext {
char* processed; /* OUT: null terminated processed source */
size_t p_len; /* OUT: length of processed buffer, excluding null char */
const char* source; /* IN: raw data passed via ext_process */
size_t source_len; /* IN: raw source len */
const char* cd; /* IN: current directory */
/* IN: NULL (where the last element's 'name' member is NULL) terminated
array of request handlers */
struct request_handler* handlers;
};
void ext_process(struct glsl_ext* ext, const char* f);
void ext_free (struct glsl_ext* ext);

205
pulse_input.c Normal file
View File

@@ -0,0 +1,205 @@
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <pulse/pulseaudio.h>
#include "fifo.h"
static pa_mainloop *m_pulseaudio_mainloop;
static void cb(__attribute__((unused)) pa_context *pulseaudio_context,
const pa_server_info *i,
void *userdata) {
//getting default sink name
struct audio_data *audio = (struct audio_data *)userdata;
audio->source = malloc(sizeof(char) * 1024);
strcpy(audio->source,i->default_sink_name);
//appending .monitor suffix
audio->source = strcat(audio->source, ".monitor");
//quiting mainloop
pa_context_disconnect(pulseaudio_context);
pa_context_unref(pulseaudio_context);
pa_mainloop_quit(m_pulseaudio_mainloop, 0);
pa_mainloop_free(m_pulseaudio_mainloop);
}
static void pulseaudio_context_state_callback(pa_context *pulseaudio_context,
void *userdata) {
//make sure loop is ready
switch (pa_context_get_state(pulseaudio_context))
{
case PA_CONTEXT_UNCONNECTED:
//printf("UNCONNECTED\n");
break;
case PA_CONTEXT_CONNECTING:
//printf("CONNECTING\n");
break;
case PA_CONTEXT_AUTHORIZING:
//printf("AUTHORIZING\n");
break;
case PA_CONTEXT_SETTING_NAME:
//printf("SETTING_NAME\n");
break;
case PA_CONTEXT_READY://extract default sink name
//printf("READY\n");
pa_operation_unref(pa_context_get_server_info(
pulseaudio_context, cb, userdata));
break;
case PA_CONTEXT_FAILED:
printf("failed to connect to pulseaudio server\n");
exit(EXIT_FAILURE);
break;
case PA_CONTEXT_TERMINATED:
//printf("PulseAudio context terminated!\n");
pa_mainloop_quit(m_pulseaudio_mainloop, 0);
break;
}
}
void get_pulse_default_sink(struct audio_data* audio) {
pa_mainloop_api *mainloop_api;
pa_context *pulseaudio_context;
int ret;
// Create a mainloop API and connection to the default server
m_pulseaudio_mainloop = pa_mainloop_new();
mainloop_api = pa_mainloop_get_api(m_pulseaudio_mainloop);
pulseaudio_context = pa_context_new(mainloop_api, "cava device list");
// This function connects to the pulse server
pa_context_connect(pulseaudio_context, NULL, PA_CONTEXT_NOFLAGS,
NULL);
// printf("connecting to server\n");
//This function defines a callback so the server will tell us its state.
pa_context_set_state_callback(pulseaudio_context,
pulseaudio_context_state_callback,
(void*)audio);
// starting a mainloop to get default sink
// starting with one non blokng iteration in case pulseaudio is not able to run
if (!(ret = pa_mainloop_iterate(m_pulseaudio_mainloop, 0, &ret))){
printf("Could not open pulseaudio mainloop to "
"find default device name: %d\n"
"check if pulseaudio is running\n",
ret);
exit(EXIT_FAILURE);
}
pa_mainloop_run(m_pulseaudio_mainloop, &ret);
}
/* Sample format for native 'float' type */
#ifndef __STDC_IEC_559__
#error "IEC 60559 standard unsupported on target system"
#endif
#ifdef __ORDER_LITTLE_ENDIAN__
#define FSAMPLE_FORMAT PA_SAMPLE_FLOAT32LE
#elif __ORDER_BIG_ENDIAN__
#define FSAMPLE_FORMAT PA_SAMPLE_FLOAT32BE
#else
#error "Unsupported float format (requires 32 bit IEEE (little or big endian) floating point support)"
#endif
void* input_pulse(void* data) {
struct audio_data *audio = (struct audio_data *)data;
int i, n;
size_t ssz = audio->sample_sz;
float buf[ssz / 2];
/* The sample type to use */
const pa_sample_spec ss = {
.format = FSAMPLE_FORMAT,
.rate = audio->rate,
.channels = 2
};
const pa_buffer_attr pb = {
.maxlength = ssz * 2,
.fragsize = ssz
};
pa_simple *s = NULL;
int error;
if (!(s = pa_simple_new(NULL, "glava", PA_STREAM_RECORD,
audio->source, "audio for glava",
&ss, NULL, &pb, &error))) {
fprintf(stderr, __FILE__ ": Could not open pulseaudio source: %s, %s. "
"To find a list of your pulseaudio sources run 'pacmd list-sources'\n",
audio->source, pa_strerror(error));
exit(EXIT_FAILURE);
}
n = 0;
float* bl = (float*) audio->audio_out_l;
float* br = (float*) audio->audio_out_r;
size_t fsz = audio->audio_buf_sz;
while (1) {
/* Record some data ... */
if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) {
fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
exit(EXIT_FAILURE);
}
pthread_mutex_lock(&audio->mutex);
/* progressing the audio buffer, making space for new write */
memmove(bl, &bl[ssz / 4], (fsz - (ssz / 4)) * sizeof(float));
memmove(br, &br[ssz / 4], (fsz - (ssz / 4)) * sizeof(float));
// sorting out channelss
for (n = 0, i = 0; i < ssz / 2; i += 2) {
// size_t idx = (i / 2) + (at * (BUFSIZE / 2));
int idx = (fsz - (ssz / 4)) + n;
if (audio->channels == 1) bl[idx] = (buf[i] + buf[i + 1]) / 2;
// stereo storing channels in buffer
if (audio->channels == 2) {
bl[idx] = buf[i];
br[idx] = buf[i + 1];
}
++n;
}
audio->modified = true;
pthread_mutex_unlock(&audio->mutex);
if (audio->terminate == 1) {
pa_simple_free(s);
break;
}
}
return 0;
}

3
pulse_input.h Normal file
View File

@@ -0,0 +1,3 @@
void get_pulse_default_sink(struct audio_data* audio);
void* input_pulse(void* data);

1064
render.c Normal file

File diff suppressed because it is too large Load Diff

12
render.h Normal file
View File

@@ -0,0 +1,12 @@
struct gl_data;
typedef struct renderer {
bool alive;
size_t bufsize_request, rate_request, samplesize_request;
struct gl_data* gl;
} renderer;
struct renderer* rd_new(int x, int y, int w, int h, const char* shader_path);
void rd_update(struct renderer*, float* lb, float* rb, size_t bsz, bool modified);
void rd_destroy(struct renderer*);

82
shaders/graph/1.frag Normal file
View File

@@ -0,0 +1,82 @@
layout(pixel_center_integer) in vec4 gl_FragCoord;
#request uniform "screen" screen
uniform ivec2 screen; /* screen dimensions */
#request uniform "audio_sz" audio_sz
uniform int audio_sz;
#request setavgframes 4
#request setavgwindow true
// #request interpolate true
#request uniform "audio_l" audio_l
#request transform audio_l "window"
#request transform audio_l "fft"
#request transform audio_l "avg"
uniform sampler1D audio_l;
#request uniform "audio_r" audio_r
#request transform audio_r "window"
#request transform audio_r "fft"
#request transform audio_r "avg"
uniform sampler1D audio_r;
out vec4 fragment;
#include "settings.glsl"
#define CUT 0.5
#define SAMPLE_RANGE 0.2
#define SAMPLE_AMT 22
#define WSCALE 11
#define VSCALE 6000
#define DIRECTION 1
#define RCOL_OFF (gl_FragCoord.x / 3000)
#define LCOL_OFF ((screen.x - gl_FragCoord.x) / 3000)
#define COLOR vec4(vec3(0.3 + RCOL_OFF, 0.3, 0.3 + LCOL_OFF) + (gl_FragCoord.y / 170), 1)
#if DIRECTION < 0
#define LEFT_IDX (gl_FragCoord.x)
#define RIGHT_IDX (-gl_FragCoord.x + screen.x)
#else
#define LEFT_IDX (half_w - gl_FragCoord.x)
#define RIGHT_IDX (gl_FragCoord.x - half_w)
#endif
void main() {
float half_w = (screen.x / 2);
if (gl_FragCoord.x < half_w) {
float s = 0, r;
int t;
for (t = -SAMPLE_AMT; t <= SAMPLE_AMT; ++t) {
s += (texture(audio_l, log((LEFT_IDX + (t * SAMPLE_RANGE)) / (half_w)) / WSCALE).r);
}
s /= float(SAMPLE_AMT * 2) + 1;
s *= VSCALE;
if (gl_FragCoord.y + 1.5 <= s) {
fragment = COLOR;
} else {
fragment = vec4(0, 0, 0, 0);
}
} else {
float s = 0, r;
int t;
for (t = -SAMPLE_AMT; t <= SAMPLE_AMT; ++t) {
s += (texture(audio_r, log((RIGHT_IDX + (t * SAMPLE_RANGE)) / (half_w)) / WSCALE).r);
}
s /= float(SAMPLE_AMT * 2) + 1;
s *= VSCALE;
if (gl_FragCoord.y + 1.5 <= s) {
fragment = COLOR;
} else {
fragment = vec4(0, 0, 0, 0);
}
}
}

28
shaders/graph/2.frag Normal file
View File

@@ -0,0 +1,28 @@
layout(pixel_center_integer) in vec4 gl_FragCoord;
#request uniform "prev" tex
uniform sampler2D tex; /* screen texture */
#request uniform "screen" screen
uniform ivec2 screen; /* screen dimensions */
out vec4 fragment; /* output */
void main() {
fragment = texture(tex, vec2(gl_FragCoord.x / screen.x, gl_FragCoord.y / screen.y));
float a0 = texture(tex, vec2((gl_FragCoord.x + 1) / screen.x, (gl_FragCoord.y + 0) / screen.y)).a;
float a1 = texture(tex, vec2((gl_FragCoord.x + 1) / screen.x, (gl_FragCoord.y + 1) / screen.y)).a;
float a2 = texture(tex, vec2((gl_FragCoord.x + 0) / screen.x, (gl_FragCoord.y + 1) / screen.y)).a;
float a3 = texture(tex, vec2((gl_FragCoord.x + 1) / screen.x, (gl_FragCoord.y + 0) / screen.y)).a;
float a4 = texture(tex, vec2((gl_FragCoord.x - 1) / screen.x, (gl_FragCoord.y - 0) / screen.y)).a;
float a5 = texture(tex, vec2((gl_FragCoord.x - 1) / screen.x, (gl_FragCoord.y - 1) / screen.y)).a;
float a6 = texture(tex, vec2((gl_FragCoord.x - 0) / screen.x, (gl_FragCoord.y - 1) / screen.y)).a;
float a7 = texture(tex, vec2((gl_FragCoord.x - 1) / screen.x, (gl_FragCoord.y - 0) / screen.y)).a;
float avg = (a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7) / 8.0;
if (gl_FragCoord.y > 1) {
fragment *= avg;
}
}

28
shaders/graph/3.frag.old Normal file
View File

@@ -0,0 +1,28 @@
layout(pixel_center_integer) in vec4 gl_FragCoord;
#request uniform "screen" screen
#request uniform "prev" tex
uniform sampler2D tex; /* screen texture */
uniform ivec2 screen; /* screen dimensions */
out vec4 fragment; /* output */
void main() {
fragment = texture(tex, vec2(gl_FragCoord.x / screen.x, gl_FragCoord.y / screen.y));
float a0 = texture(tex, vec2((gl_FragCoord.x + 1) / screen.x, (gl_FragCoord.y + 0) / screen.y)).a;
float a1 = texture(tex, vec2((gl_FragCoord.x + 1) / screen.x, (gl_FragCoord.y + 1) / screen.y)).a;
float a2 = texture(tex, vec2((gl_FragCoord.x + 0) / screen.x, (gl_FragCoord.y + 1) / screen.y)).a;
float a3 = texture(tex, vec2((gl_FragCoord.x + 1) / screen.x, (gl_FragCoord.y + 0) / screen.y)).a;
float a4 = texture(tex, vec2((gl_FragCoord.x - 1) / screen.x, (gl_FragCoord.y - 0) / screen.y)).a;
float a5 = texture(tex, vec2((gl_FragCoord.x - 1) / screen.x, (gl_FragCoord.y - 1) / screen.y)).a;
float a6 = texture(tex, vec2((gl_FragCoord.x - 0) / screen.x, (gl_FragCoord.y - 1) / screen.y)).a;
float a7 = texture(tex, vec2((gl_FragCoord.x - 1) / screen.x, (gl_FragCoord.y - 0) / screen.y)).a;
float avg = (a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7) / 8.0;
if (gl_FragCoord.y > 1) fragment *= avg;
}

View File

@@ -0,0 +1,4 @@
#define MIN_THICKNESS 1
#define MAX_THICKNESS 12
#define BASE_COLOR vec4(0.7, 0.2, 0.45, 1)
#define AMPLIFY 500

74
shaders/rc.glsl Normal file
View File

@@ -0,0 +1,74 @@
/* The module to use. A module is a set of shaders used to produce
the visualizer. The structure for a module is the following:
module_name [directory]
1.frag [file: fragment shader],
2.frag [file: fragment shader],
...
Shaders are loaded in numerical order, starting at '1.frag',
continuing indefinitely. The results of each shader (except
for the final pass) is given to the next shader in the list
as a 2D sampler.
See documentation for more details. */
#request mod graph
/* GLFW window hints */
#request setfloating true
#request setdecorated true
#request setfocused true
#request setmaximized false
/* GLFW window title */
#request settitle "GLava"
/* GLFW buffer swap interval (vsync), set to '0' to prevent
waiting for refresh, '1' (or more) to wait for the specified
amount of frames. */
#request setswap 1
/* Frame limiter, set to the frames per second (FPS) desired or
simple set to zero (or lower) to disable the frame limiter. */
#request setframerate 0
/* Enable/disable printing framerate every second */
#request setprintframes true
/* Audio buffer size to be used for processing and shaders.
Increasing this value can have the effect of adding 'gravity'
to FFT output, as the audio signal will remain in the buffer
longer.
This value has a _massive_ effect on FFT performance and
quality for some modules. */
#request setbufsize 4096
/* Scale down the audio buffer before any operations are
performed on the data. Higher values are faster.
This value can affect the output of various transformations,
since it applies (crude) averaging to the data when shrinking
the buffer. It is reccommended to use `setsamplerate` and
`setsamplesize` to improve performance or accuracy instead. */
#request setbufscale 1
/* PulseAudio sample rate. Lower values can add 'gravity' to
FFT output, but can also reduce accuracy (especially for
higher frequencies). Most hardware samples at 44100Hz.
Lower sample rates also can make output more choppy, when
not using smoothing algorithms. */
#request setsamplerate 22000
/* PulseAudio sample buffer size. Lower values result in more
frequent audio updates (also depends on sampling rate), but
will also require all transformations to be applied much
more frequently (slower) */
#request setsamplesize 1024
/* OpenGL context and GLSL shader versions, do not change unless
you _absolutely_ know what you are doing. */
#request setversion 3 3
#request setshaderversion 330

24
shaders/wave/1.frag Normal file
View File

@@ -0,0 +1,24 @@
layout(pixel_center_integer) in vec4 gl_FragCoord;
#request uniform "screen" screen
uniform ivec2 screen; /* screen dimensions */
#request uniform "audio_l" audio_l
#request transform audio_l "window"
#request transform audio_l "wrange"
uniform sampler1D audio_l;
out vec4 fragment;
#include "settings.glsl"
void main() {
float os = ((texture(audio_l, gl_FragCoord.x / screen.x).r - 0.5) * AMPLIFY) + 0.5f;
float s = (os + (screen.y * 0.5f) - 0.5f); /* center to screen coords */
if (abs(s - gl_FragCoord.y) < clamp(abs(s - (screen.y * 0.5)) * 6, MIN_THICKNESS, MAX_THICKNESS)) {
fragment = BASE_COLOR + (abs((screen.y * 0.5f) - s) * 0.02);
} else {
fragment = vec4(0, 0, 0, 0);
}
}

26
shaders/wave/2.frag Normal file
View File

@@ -0,0 +1,26 @@
layout(pixel_center_integer) in vec4 gl_FragCoord;
#request uniform "prev" tex
uniform sampler2D tex; /* screen texture */
#request uniform "screen" screen
uniform ivec2 screen; /* screen dimensions */
out vec4 fragment; /* output */
void main() {
fragment = texture(tex, vec2(gl_FragCoord.x / screen.x, gl_FragCoord.y / screen.y));
float a0 = texture(tex, vec2((gl_FragCoord.x + 1) / screen.x, (gl_FragCoord.y + 0) / screen.y)).a;
float a1 = texture(tex, vec2((gl_FragCoord.x + 1) / screen.x, (gl_FragCoord.y + 1) / screen.y)).a;
float a2 = texture(tex, vec2((gl_FragCoord.x + 0) / screen.x, (gl_FragCoord.y + 1) / screen.y)).a;
float a3 = texture(tex, vec2((gl_FragCoord.x + 1) / screen.x, (gl_FragCoord.y + 0) / screen.y)).a;
float a4 = texture(tex, vec2((gl_FragCoord.x - 1) / screen.x, (gl_FragCoord.y - 0) / screen.y)).a;
float a5 = texture(tex, vec2((gl_FragCoord.x - 1) / screen.x, (gl_FragCoord.y - 1) / screen.y)).a;
float a6 = texture(tex, vec2((gl_FragCoord.x - 0) / screen.x, (gl_FragCoord.y - 1) / screen.y)).a;
float a7 = texture(tex, vec2((gl_FragCoord.x - 1) / screen.x, (gl_FragCoord.y - 0) / screen.y)).a;
float avg = (a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7) / 8.0;
fragment *= avg;
}

View File

@@ -0,0 +1,4 @@
#define MIN_THICKNESS 1
#define MAX_THICKNESS 10
#define BASE_COLOR vec4(0.7, 0.2, 0.45, 1)
#define AMPLIFY 500

49
xwincheck.c Normal file
View File

@@ -0,0 +1,49 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <limits.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include "xwincheck.h"
bool xwin_should_render(void) {
bool ret = true;
Display* d = XOpenDisplay(0);
Atom prop = XInternAtom(d, "_NET_ACTIVE_WINDOW", true);
Atom fullscreen = XInternAtom(d, "_NET_WM_STATE_FULLSCREEN", true);
Atom actual_type;
int actual_format, t;
unsigned long nitems, bytes_after;
unsigned char* data;
int handler(Display* d, XErrorEvent* e) {}
XSetErrorHandler(handler); /* dummy error handler */
if (Success != XGetWindowProperty(d, RootWindow(d, 0), prop, 0, 1, false, AnyPropertyType,
&actual_type, &actual_format, &nitems, &bytes_after, &data)) {
goto close; /* if an error occurs here, the WM probably isn't EWMH compliant */
}
Window active = ((Window*) data)[0];
prop = XInternAtom(d, "_NET_WM_STATE", true);
if (Success != XGetWindowProperty(d, active, prop, 0, LONG_MAX, false, AnyPropertyType,
&actual_type, &actual_format, &nitems, &bytes_after, &data)) {
goto close; /* some WMs are a little slow on creating _NET_WM_STATE, so errors may occur here */
}
for (t = 0; t < nitems; ++t) {
if (fullscreen == ((Atom*) data)[t]) {
ret = false;
}
}
close:
XCloseDisplay(d);
return ret;
}

2
xwincheck.h Normal file
View File

@@ -0,0 +1,2 @@
bool xwin_should_render(void);