30 Commits

Author SHA1 Message Date
Jarcode
d07b93d54e Fix clickthrough for reparented windows, closes #80 2019-03-09 16:55:03 -08:00
Jarcode
f7170afb75 Fixed attached base frequencies in 'bars' 2019-03-09 15:27:35 -08:00
Jarcode
8ccbf22732 Merge branch 'master' of https://github.com/wacossusca34/glava 2019-03-09 14:52:58 -08:00
Jarcode
5fdf0545a8 Added GLSL sampling modes 2019-03-09 14:52:38 -08:00
Levi Webb
b77d1a7b3a Update build requirements, closes #102 2019-03-02 11:26:03 -08:00
Jarcode
f87c70af2b Disable second 'graph' pass if nessecary (& other formatting), see #97 2019-02-05 17:45:17 -08:00
Jarcode
7c30c4a78a Added support for disabling shader passes, see #97 2019-02-04 22:07:26 -08:00
Levi Webb
bc2db4a415 Merge pull request #97 from arch1t3cht30/graph_tweaks
Anti-aliasing and better joining for the graph module
2019-02-02 14:58:33 -08:00
Theo Müller
3ff1097941 Fix inverting in antialias pass 2019-02-01 22:40:46 +01:00
Theo Müller
041bfdfd55 Make anti-aliasing work with invert 2019-02-01 20:19:15 +01:00
Theo Müller
8cbf7bc579 Cubic interpolation for joining graph channels 2019-01-31 12:05:05 +01:00
Theo Müller
91530dfc49 Make joining optional 2019-01-30 20:26:35 +01:00
Theo Müller
a39a1324e1 Add anti-aliasing option for graph 2019-01-30 19:59:51 +01:00
Theo Müller
140d98b404 Join middle borders instead of clamping them down 2019-01-30 19:59:51 +01:00
Levi Webb
f235362f44 Merge pull request #94 from dan-santana/musl-build
Fix musl build
2019-01-04 21:45:19 -08:00
Levi Webb
32853b73d8 Merge pull request #93 from dan-santana/master
Use SONAMEs when loading GLX libs dinamically
2019-01-04 21:43:29 -08:00
Daniel Santana
4705699bb6 Fix musl build 2019-01-04 22:23:52 -02:00
Daniel Santana
3ef23f0db8 Use SONAMEs when loading GLX libs dinamically 2019-01-04 21:57:52 -02:00
Levi Webb
4d51ccbd54 Update README.md 2019-01-01 14:00:11 -08:00
Levi Webb
6c02d15c80 Force bash in Makefile, see #91 2019-01-01 12:47:57 -08:00
Levi Webb
40dac829cc Merge pull request #92 from Aaahh/patch-7
Check SHADERDIR exist
2019-01-01 12:46:06 -08:00
Aaahh Ahh
bde3101c42 Check SHADERDIR exist
Unity has incorrectly set XDG_CONFIG_DIRS to a path that does not exist
2018-12-30 16:49:40 -05:00
Levi Webb
f2857e5f21 Merge pull request #83 from jubalh/readme
Add install instructions
2018-12-17 13:09:01 -08:00
Levi Webb
66a9b09b10 Merge pull request #82 from jubalh/giti
Add gitignore
2018-11-30 18:15:16 -08:00
Michael Vetter
31a39b6ecd Add install instructions
Some distros have packages for glava. Let's mention them.
2018-11-30 14:49:13 +01:00
Michael Vetter
dd0f5bf0f9 Add gitignore
Add files that we don't want to have in version control so devs have it
easier.
2018-11-30 14:42:58 +01:00
Jarcode
1337253257 Added audio backends, fifo support, see #78 2018-11-17 21:03:02 -08:00
Jarcode
76325cb126 Fixed uninitialized GLSL output variable, closes #70 2018-11-13 17:04:12 -08:00
Jarcode
426f70f579 Added --request parameter, fixed reading directives at end of file, limited most output to --verbose option 2018-11-02 16:27:54 -07:00
Jarcode
1d2b014da7 Fix leak with 'xroot' transparency 2018-11-02 13:09:48 -07:00
22 changed files with 634 additions and 209 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*.o
build_state
glava

View File

@@ -1,3 +1,5 @@
SHELL := /bin/bash
src = $(wildcard *.c)
obj = $(src:.c=.o)
@@ -39,6 +41,9 @@ ifeq ($(INSTALL),unix)
ifndef SHADERDIR
ifdef XDG_CONFIG_DIRS
SHADERDIR = /$(firstword $(subst :, ,$(XDG_CONFIG_DIRS)))/glava/
ifeq ($(wildcard $(SHADERDIR)/..),)
SHADERDIR = /etc/xdg/glava/
endif
else
SHADERDIR = /etc/xdg/glava/
endif

View File

@@ -3,7 +3,7 @@
**GLava** is an OpenGL audio spectrum visualizer. Its primary use case is for desktop windows or backgrounds. Displayed to the left is the `radial` shader module, and [here is a demonstration video](https://streamable.com/dgpj8). Development is active, and reporting issues is encouranged.
**Compiling** (Or use the Arch Linux [`glava` package](https://www.archlinux.org/packages/community/x86_64/glava/), or the [`glava-git` AUR package](https://aur.archlinux.org/packages/glava-git/))**:**
**Compiling:**
```bash
$ git clone https://github.com/wacossusca34/glava
@@ -31,9 +31,16 @@ You can pass `BUILD=debug` to the makefile for debug builds of both glad and gla
**Ubuntu/Debian users:** the following command ensures you have all the needed packages and headers to compile GLava:
```bash
sudo apt-get install libpulse0 libpulse-dev libglfw3 libglfw3-dev libxext6 libxext-dev libxcomposite-dev python make gcc
sudo apt-get install libpulse0 libpulse-dev libxext6 libxext-dev libxrender-dev libxcomposite-dev make gcc
```
## Installation
Some distributions have a package for `glava`. If your distribution is not listed please use the compilation instructions above.
- Arch Linux [`glava` package](https://www.archlinux.org/packages/community/x86_64/glava/), or [`glava-git` AUR package](https://aur.archlinux.org/packages/glava-git/)
- NixOS [package](https://github.com/NixOS/nixpkgs/blob/release-18.09/pkgs/applications/misc/glava/default.nix)
- openSUSE [package](https://build.opensuse.org/package/show/X11:Utilities/glava)
## [Configuration](https://github.com/wacossusca34/glava/wiki)
GLava will start by looking for an entry point in the user configuration folder (`~/.config/glava/rc.glsl`\*), and will fall back to loading from the shader installation folder (`/etc/xdg/glava`\*). The entry point will specify a module to load and should set global configuration variables. Configuration for specific modules can be done in their respective `.glsl` files, which the module itself will include.
@@ -68,6 +75,21 @@ GLava aims to be compatible with _most_ EWMH compliant window managers. Below is
Note that some WMs listed without issues have specific overrides when using the `--desktop` flag. See `shaders/env_*.glsl` files for details.
## Reading from MPD's FIFO output
Add the following to your `~/.config/mpd.conf`:
```
audio_output {
type "fifo"
name "glava_fifo"
path "/tmp/mpd.fifo"
format "22050:16:2"
}
```
Note the `22050` sample rate -- this is the reccommended setting for GLava. Restart MPD (if nessecary) and start GLava with `glava --audio=fifo`.
## Licensing
GLava is licensed under the terms of the GPLv3, with the exemption of `khrplatform.h`, which is licensed under the terms in its header. GLava includes some (heavily modified) source code that originated from [cava](https://github.com/karlstav/cava), which was initially provided under the MIT license. The source files that originated from cava are the following:
@@ -86,7 +108,3 @@ The modified files are relicensed under the terms of the GPLv3. The MIT license
The below copyright applies for the modifications to the files listed above, and the remaining sources in the repository:
`Copyright (c) 2017 Levi Webb`
## Porting
GLava was built with GLFW, making the graphics frontend mostly compatible if it were to be ported to Windows, and I have taken most of the Xlib-specific code and placed it into `xwin.c` if anyone decides they wish to attempt at a port.

183
fifo.c
View File

@@ -7,74 +7,123 @@
#include <fcntl.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <poll.h>
#include "fifo.h"
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;
/* Implementation struct storage */
typeof(*audio_impls) audio_impls[sizeof(audio_impls) / sizeof(struct audio_impl*)] = {};
size_t audio_impls_idx = 0;
/* FIFO backend */
static void init(struct audio_data* audio) {
if (!audio->source) {
audio->source = strdup("/tmp/mpd.fifo");
}
}
static void* entry(void* data) {
struct audio_data* audio = (struct audio_data *) data;
float* bl = (float*) audio->audio_out_l;
float* br = (float*) audio->audio_out_r;
size_t fsz = audio->audio_buf_sz;
size_t ssz = audio->sample_sz;
int fd;
int16_t buf[ssz / 2];
size_t q;
int timeout = 50;
struct timespec tv_last = {}, tv;
bool measured = false;
if ((fd = open(audio->source, O_RDONLY)) == -1) {
fprintf(stderr, "failed to open FIFO audio source \"%s\": %s\n", audio->source, strerror(errno));
exit(EXIT_FAILURE);
}
struct pollfd pfd = {
.fd = fd,
.events = POLLIN
};
size_t buffer_offset = (fsz - (ssz / 4));
while (true) {
/* The poll timeout is set to accommodate an approximate UPS, but has little purpose except
for effectively setting the rate of empty samples in the event of the FIFO descriptor
blocking for long periods of time. */
switch (poll(&pfd, 1, timeout)) {
case -1:
fprintf(stderr, "FIFO backend: poll() failed (%s)\n", strerror(errno));
exit(EXIT_FAILURE);
case 0:
pthread_mutex_lock(&audio->mutex);
memmove(bl, &bl[ssz / 4], buffer_offset * sizeof(float));
memmove(br, &br[ssz / 4], buffer_offset * sizeof(float));
for (q = 0; q < (ssz / 4); ++q) bl[buffer_offset + q] = 0;
for (q = 0; q < (ssz / 4); ++q) br[buffer_offset + q] = 0;
audio->modified = true;
pthread_mutex_unlock(&audio->mutex);
break;
default: {
read(fd, buf, sizeof(buf));
clock_gettime(CLOCK_REALTIME, measured ? &tv : &tv_last);
if (measured) {
/* Set the timeout slightly higher than the delay between samples to prevent empty writes */
timeout = (((tv.tv_sec - tv_last.tv_sec) * 1000) + ((tv.tv_nsec - tv_last.tv_nsec) / 1000000)) + 1;
tv_last = tv;
} else measured = true;
pthread_mutex_lock(&audio->mutex);
memmove(bl, &bl[ssz / 4], buffer_offset * sizeof(float));
memmove(br, &br[ssz / 4], buffer_offset * sizeof(float));
for (size_t n = 0, q = 0; q < (ssz / 2); q += 2) {
size_t idx = (fsz - (ssz / 4)) + n;
if (audio->channels == 1) {
float sample = ((buf[q] + buf[q + 1]) / 2) / (float) 65535;
bl[idx] = sample;
br[idx] = sample;
}
if (audio->channels == 2) {
bl[idx] = buf[q] / (float) 65535;
br[idx] = buf[q + 1] / (float) 65535;
}
n++;
}
audio->modified = true;
pthread_mutex_unlock(&audio->mutex);
break;
}
}
if (audio->terminate == 1) {
close(fd);
break;
}
}
return 0;
}
AUDIO_ATTACH(fifo);

24
fifo.h
View File

@@ -14,4 +14,26 @@ struct audio_data {
pthread_mutex_t mutex;
};
void* input_fifo(void* data);
struct audio_impl {
const char* name;
void (*init)(struct audio_data* data);
void* (*entry)(void* data);
};
#define AUDIO_FUNC(F) \
.F = (typeof(((struct audio_impl*) NULL)->F)) &F
extern struct audio_impl* audio_impls[4];
extern size_t audio_impls_idx;
static inline void register_audio_impl(struct audio_impl* impl) { audio_impls[audio_impls_idx++] = impl; }
#define AUDIO_ATTACH(N) \
static struct audio_impl N##_var = { \
.name = #N, \
AUDIO_FUNC(init), \
AUDIO_FUNC(entry), \
}; \
void __attribute__((constructor)) _##N##_construct(void) { \
register_audio_impl(&N##_var); \
}

104
glava.c
View File

@@ -69,6 +69,10 @@
#error "Unsupported target system"
#endif
#ifndef ACCESSPERMS
#define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO) /* 0777 */
#endif
/* Copy installed shaders/configuration from the installed location
(usually /etc/xdg). Modules (folders) will be linked instead of
copied. */
@@ -166,9 +170,10 @@ static const char* help_str =
"-d, --desktop enables running glava as a desktop window by detecting the\n"
" desktop environment and setting the appropriate properties\n"
" automatically. Can override properties in \"rc.glsl\".\n"
"-r, --request=REQUEST evaluates the specified request after loading \"rc.glsl\".\n"
"-m, --force-mod=NAME forces the specified module to load instead, ignoring any\n"
" `#request mod` instances in the entry point.\n"
"-e, --entry=NAME specifies the name of the file to look for when loading shaders,\n"
"-e, --entry=FILE specifies the name of the file to look for when loading shaders,\n"
" by default this is \"rc.glsl\".\n"
"-C, --copy-config creates copies and symbolic links in the user configuration\n"
" directory for glava, copying any files in the root directory\n"
@@ -176,16 +181,30 @@ static const char* help_str =
"-b, --backend specifies a window creation backend to use. By default, the most\n"
" appropriate backend will be used for the underlying windowing\n"
" system.\n"
"-a, --audio=BACKEND specifies an audio input backend to use.\n"
"-V, --version print application version and exit\n"
"\n"
GLAVA_VERSION_STRING "\n"
" -- Copyright (C) 2017 Levi Webb\n";
"The REQUEST argument is evaluated identically to the \'#request\' preprocessor directive\n"
"in GLSL files.\n"
"\n"
"The DEFINE argument is appended to the associated file before it is processed. It is\n"
"evaluated identically to the \'#define' preprocessor directive.\n"
"\n"
"The FILE argument may be any file path. All specified file paths are relative to the\n"
"active configuration root (usually ~/.config/glava if present).\n"
"\n"
"The BACKEND argument may be any of the following strings (for this particular build):\n"
"%s"
"\n"
GLAVA_VERSION_STRING "\n";
static const char* opt_str = "dhvVe:Cm:b:";
static const char* opt_str = "dhvVe:Cm:b:r:a:";
static struct option p_opts[] = {
{"help", no_argument, 0, 'h'},
{"verbose", no_argument, 0, 'v'},
{"desktop", no_argument, 0, 'd'},
{"audio", required_argument, 0, 'a'},
{"request", required_argument, 0, 'r'},
{"entry", required_argument, 0, 'e'},
{"force-mod", required_argument, 0, 'm'},
{"copy-config", no_argument, 0, 'C'},
@@ -196,22 +215,33 @@ static struct option p_opts[] = {
static renderer* rd = NULL;
void handle_term(int signum) {
static void handle_term(int signum) {
if (rd->alive) {
puts("\nInterrupt recieved, closing...");
rd->alive = false;
}
}
static inline void append_buf(char** buf, size_t* sz_store, char* str) {
buf = realloc(buf, ++(*sz_store) * sizeof(char*));
buf[*sz_store - 1] = str;
}
int main(int argc, char** argv) {
/* Evaluate these macros only once, since they allocate */
const char* install_path = SHADER_INSTALL_PATH;
const char* user_path = SHADER_USER_PATH;
const char* entry = "rc.glsl";
const char* force = NULL;
const char* backend = NULL;
const char
* install_path = SHADER_INSTALL_PATH,
* user_path = SHADER_USER_PATH,
* entry = "rc.glsl",
* force = NULL,
* backend = NULL,
* audio_impl_name = "pulseaudio";
const char* system_shader_paths[] = { user_path, install_path, NULL };
char** requests = malloc(1);
size_t requests_sz = 0;
bool verbose = false, copy_mode = false, desktop = false;
int c, idx;
@@ -220,19 +250,27 @@ int main(int argc, char** argv) {
case 'v': verbose = true; break;
case 'C': copy_mode = true; break;
case 'd': desktop = true; break;
case 'e': entry = optarg; break;
case 'm': force = optarg; break;
case 'b': backend = optarg; break;
case 'r': append_buf(requests, &requests_sz, optarg); break;
case 'e': entry = optarg; break;
case 'm': force = optarg; break;
case 'b': backend = optarg; break;
case 'a': audio_impl_name = optarg; break;
case '?': exit(EXIT_FAILURE); break;
case 'V':
puts(GLAVA_VERSION_STRING);
exit(EXIT_SUCCESS);
break;
default:
case 'h':
printf(help_str, argc > 0 ? argv[0] : "glava");
case 'h': {
char buf[2048];
size_t bsz = 0;
for (size_t t = 0; t < audio_impls_idx; ++t)
bsz += snprintf(buf + bsz, sizeof(buf) - bsz, "\t\"%s\"%s\n", audio_impls[t]->name,
!strcmp(audio_impls[t]->name, audio_impl_name) ? " (default)" : "");
printf(help_str, argc > 0 ? argv[0] : "glava", buf);
exit(EXIT_SUCCESS);
break;
}
}
}
@@ -241,7 +279,19 @@ int main(int argc, char** argv) {
exit(EXIT_SUCCESS);
}
rd = rd_new(system_shader_paths, entry, force, backend, desktop);
/* Handle `--force` argument as a request override */
if (force) {
const size_t bsz = 5 + strlen(force);
char* force_req_buf = malloc(bsz);
snprintf(force_req_buf, bsz, "mod %s", force);
append_buf(requests, &requests_sz, force_req_buf);
}
/* Null terminate array arguments */
append_buf(requests, &requests_sz, NULL);
rd = rd_new(system_shader_paths, entry, (const char**) requests,
backend, desktop, verbose);
struct sigaction action = { .sa_handler = handle_term };
sigaction(SIGTERM, &action, NULL);
@@ -273,13 +323,27 @@ int main(int argc, char** argv) {
.sample_sz = rd->samplesize_request,
.modified = false
};
if (!audio.source) {
get_pulse_default_sink(&audio);
printf("Using default PulseAudio sink: %s\n", audio.source);
struct audio_impl* impl = NULL;
for (size_t t = 0; t < audio_impls_idx; ++t) {
if (!strcmp(audio_impls[t]->name, audio_impl_name)) {
impl = audio_impls[t];
break;
}
}
if (!impl) {
fprintf(stderr, "The specified audio backend (\"%s\") is not available.\n", audio_impl_name);
exit(EXIT_FAILURE);
}
impl->init(&audio);
if (verbose) printf("Using audio source: %s\n", audio.source);
pthread_t thread;
pthread_create(&thread, NULL, input_pulse, (void*) &audio);
pthread_create(&thread, NULL, impl->entry, (void*) &audio);
float lb[rd->bufsize_request], rb[rd->bufsize_request];
while (rd->alive) {

View File

@@ -494,7 +494,7 @@ void ext_process(struct glsl_ext* ext, const char* f) {
arg_start_idx = t + 1;
} else arg_start = false;
if (at == '\n' || state == DEFINE) {
if (at == '\n' || at == '\0' || state == DEFINE) {
/* end directive */
size_t a;
struct schar r = directive(ext, args, args_sz, state, line, f);

View File

@@ -193,8 +193,8 @@ static void init(void) {
maximized = false;
transparent = false;
void* hgl = dlopen("libGL.so", RTLD_LAZY);
void* hglx = dlopen("libGLX.so", RTLD_LAZY);
void* hgl = dlopen("libGL.so.1", RTLD_LAZY);
void* hglx = dlopen("libGLX.so.0", RTLD_LAZY);
if (!hgl && !hglx) {
fprintf(stderr, "Failed to load GLX functions (libGL and libGLX do not exist!)\n");
@@ -254,15 +254,37 @@ static void apply_decorations(Window w) {
}
}
static bool find_parent(Window w, Window* parent) {
Window root, *children = NULL;
unsigned int num_children;
if(!XQueryTree(display, w, &root, parent, &children, &num_children))
return false;
if (children)
XFree(children);
return *parent != None;
}
static void apply_clickthrough(struct glxwin* w) {
if (w->clickthrough) {
int ignored;
if (XShapeQueryExtension(display, &ignored, &ignored)) {
Region region;
if ((region = XCreateRegion())) {
XShapeCombineRegion(display, w->w, ShapeInput, 0, 0, region, ShapeSet);
XDestroyRegion(region);
Window root = DefaultRootWindow(display);
Window win = w->w;
while (win != None) {
Region region;
if ((region = XCreateRegion())) {
XShapeCombineRegion(display, w->w, ShapeInput, 0, 0, region, ShapeSet);
XDestroyRegion(region);
}
Window parent;
find_parent(win, &parent);
win = (parent == root ? None : parent);
}
} else {
fprintf(stderr, "Warning: XShape extension not available\n");
}
}
}
@@ -278,6 +300,10 @@ static void process_events(struct glxwin* w) {
w->should_close = true;
}
break;
case MapNotify:
apply_clickthrough(w);
XFlush(display);
break;
case VisibilityNotify:
switch (ev.xvisibility.state) {
case VisibilityFullyObscured:
@@ -514,7 +540,6 @@ static void set_geometry(struct glxwin* w, int x, int y, int d, int h) {
static void set_visible(struct glxwin* w, bool visible) {
if (visible) {
XMapWindow(display, w->w);
apply_clickthrough(w);
switch (w->override_state) {
case '+': XRaiseWindow(display, w->w); break;
case '-': XLowerWindow(display, w->w); break;

View File

@@ -54,7 +54,9 @@ static void pulseaudio_context_state_callback(pa_context* pulseaudio_context, vo
}
void get_pulse_default_sink(struct audio_data* audio) {
static void init(struct audio_data* audio) {
if (audio->source) return;
pa_mainloop_api* mainloop_api;
pa_context* pulseaudio_context;
@@ -106,7 +108,7 @@ void get_pulse_default_sink(struct audio_data* audio) {
#error "Unsupported float format (requires 32 bit IEEE (little or big endian) floating point support)"
#endif
void* input_pulse(void* data) {
static void* entry(void* data) {
struct audio_data* audio = (struct audio_data*) data;
int i, n;
size_t ssz = audio->sample_sz;
@@ -188,3 +190,5 @@ void* input_pulse(void* data) {
return 0;
}
AUDIO_ATTACH(pulseaudio);

218
render.c
View File

@@ -91,7 +91,7 @@ struct gl_bind {
struct gl_sfbo {
GLuint fbo, tex, shader;
bool valid, nativeonly;
bool indirect, nativeonly;
const char* name;
struct gl_bind* binds;
size_t binds_sz;
@@ -139,6 +139,7 @@ static GLuint shaderload(const char* rpath,
struct request_handler* handlers,
int shader_version,
bool raw,
bool* skipped,
struct gl_data* gl) {
size_t s_len = strlen(shader);
@@ -218,14 +219,33 @@ static GLuint shaderload(const char* rpath,
if (ret == GL_FALSE) {
glGetShaderiv(s, GL_INFO_LOG_LENGTH, &ilen);
if (ilen) {
GLchar ebuf[ilen];
GLchar* ebuf = malloc(sizeof(GLchar) * ilen);
glGetShaderInfoLog(s, ilen, NULL, ebuf);
/* check for `#error __disablestage` and flag `*skipped` accordingly */
if (skipped != NULL) {
bool last = false;
static const char* skip_keyword = "__disablestage";
size_t sksz = sizeof(skip_keyword);
for(size_t t = 0; t < (size_t) ilen; ++t) {
if (ebuf[t] == '_') {
if (last && !strncmp(ebuf + t - 1, skip_keyword, sksz)) {
*skipped = true;
goto free_ebuf;
} else last = true;
} else last = false;
}
}
fprintf(stderr, "Shader compilation failed for '%s':\n", path);
fwrite(ebuf, sizeof(GLchar), ilen - 1, stderr);
#ifdef GLAVA_DEBUG
fprintf(stderr, "Processed shader source for '%s':\n", path);
fwrite(buf, sizeof(GLchar), sl, stderr);
#endif
free_ebuf:
free(ebuf);
return 0;
} else {
fprintf(stderr, "Shader compilation failed for '%s', but no info was available\n", path);
@@ -282,14 +302,16 @@ static GLuint shaderlink_f(GLuint* arr) {
}
/* load shaders */
#define shaderbuild(gl, shader_path, c, d, r, v, ...) \
shaderbuild_f(gl, shader_path, c, d, r, v, (const char*[]) {__VA_ARGS__, 0})
#define shaderbuild(gl, shader_path, c, d, r, v, s, ...) \
shaderbuild_f(gl, shader_path, c, d, r, v, s, (const char*[]) {__VA_ARGS__, 0})
static GLuint shaderbuild_f(struct gl_data* gl,
const char* shader_path,
const char* config, const char* defaults,
struct request_handler* handlers,
int shader_version,
bool* skipped,
const char** arr) {
if (skipped) *skipped = false;
const char* str;
int i = 0, sz = 0, t;
while ((str = arr[i++]) != NULL) ++sz;
@@ -303,7 +325,7 @@ static GLuint shaderbuild_f(struct gl_data* gl,
if (!strcmp(path + t + 1, "frag") || !strcmp(path + t + 1, "glsl")) {
if (!(shaders[i] = shaderload(path, GL_FRAGMENT_SHADER,
shader_path, config, defaults, handlers,
shader_version, false, gl))) {
shader_version, false, skipped, gl))) {
return 0;
}
} else if (!strcmp(path + t + 1, "vert")) {
@@ -323,7 +345,7 @@ static GLuint shaderbuild_f(struct gl_data* gl,
}
}
/* load builtin vertex shader */
shaders[sz] = shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC, NULL, NULL, handlers, shader_version, true, gl);
shaders[sz] = shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC, NULL, NULL, handlers, shader_version, true, NULL, gl);
fflush(stdout);
return shaderlink_f(shaders);
}
@@ -358,9 +380,9 @@ static void update_1d_tex(GLuint tex, size_t w, float* data) {
/* setup screen framebuffer object and its texture */
static void setup_sfbo(struct gl_sfbo* s, int w, int h) {
GLuint tex = s->valid ? s->tex : ({ glGenTextures(1, &s->tex); s->tex; });
GLuint fbo = s->valid ? s->fbo : ({ glGenFramebuffers(1, &s->fbo); s->fbo; });
s->valid = true;
GLuint tex = s->indirect ? s->tex : ({ glGenTextures(1, &s->tex); s->tex; });
GLuint fbo = s->indirect ? s->fbo : ({ glGenFramebuffers(1, &s->fbo); s->fbo; });
s->indirect = true;
/* bind texture and setup space */
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@@ -617,48 +639,48 @@ void transform_fft(struct gl_data* d, void** _, void* in) {
float wtemp, wr, wpr, wpi, wi, theta;
float tempr, tempi;
// reverse-binary reindexing
n = nn<<1;
j=1;
for (i=1; i<n; i+=2) {
if (j>i) {
/* reverse-binary reindexing */
n = nn << 1;
j = 1;
for (i = 1; i < n; i += 2) {
if (j > i) {
swap(data[j-1], data[i-1]);
swap(data[j], data[i]);
}
m = nn;
while (m>=2 && j>m) {
while (m >= 2 && j > m) {
j -= m;
m >>= 1;
}
j += m;
};
// here begins the Danielson-Lanczos section
mmax=2;
while (n>mmax) {
istep = mmax<<1;
theta = -(2*M_PI/mmax);
wtemp = sin(0.5*theta);
wpr = -2.0*wtemp*wtemp;
/* here begins the Danielson-Lanczos section */
mmax = 2;
while (n > mmax) {
istep = mmax << 1;
theta = -(2 * M_PI / mmax);
wtemp = sin(0.5 * theta);
wpr = -2.0 * wtemp * wtemp;
wpi = sin(theta);
wr = 1.0;
wi = 0.0;
for (m=1; m < mmax; m += 2) {
for (i=m; i <= n; i += istep) {
j=i+mmax;
tempr = wr*data[j-1] - wi*data[j];
tempi = wr * data[j] + wi*data[j-1];
for (m = 1; m < mmax; m += 2) {
for (i = m; i <= n; i += istep) {
j= i + mmax;
tempr = wr * data[j-1] - wi * data[j];
tempi = wr * data[j] + wi * data[j-1];
data[j-1] = data[i-1] - tempr;
data[j] = data[i] - tempi;
data[j] = data[i] - tempi;
data[i-1] += tempr;
data[i] += tempi;
data[i] += tempi;
}
wtemp=wr;
wr += wr*wpr - wi*wpi;
wi += wi*wpr + wtemp*wpi;
wtemp = wr;
wr += wr * wpr - wi * wpi;
wi += wi * wpr + wtemp * wpi;
}
mmax=istep;
mmax = istep;
}
/* abs and log scale */
@@ -688,9 +710,9 @@ static struct gl_bind_src* lookup_bind_src(const char* str) {
return NULL;
}
struct renderer* rd_new(const char** paths, const char* entry,
const char* force_mod, const char* force_backend,
bool auto_desktop) {
struct renderer* rd_new(const char** paths, const char* entry,
const char** requests, const char* force_backend,
bool auto_desktop, bool verbose) {
xwin_wait_for_wm();
@@ -770,7 +792,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
exit(EXIT_FAILURE);
}
printf("Using backend: '%s'\n", backend);
if (verbose) printf("Using backend: '%s'\n", backend);
for (size_t t = 0; t < wcbs_idx; ++t) {
if (wcbs[t]->name && !strcmp(wcbs[t]->name, backend)) {
@@ -785,7 +807,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
}
#ifdef GLAD_DEBUG
printf("Assigning debug callback\n");
if (verbose) printf("Assigning debug callback\n");
static bool assigned_debug_cb = false;
if (!assigned_debug_cb) {
glad_set_post_callback(glad_debugcb);
@@ -798,7 +820,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
int shader_version = 330,
context_version_major = 3,
context_version_minor = 3;
const char* module = force_mod;
const char* module = NULL;
const char* wintitle_default = "GLava";
char* xwintype = NULL, * wintitle = (char*) wintitle_default;
char** xwinstates = malloc(1);
@@ -868,8 +890,8 @@ struct renderer* rd_new(const char** paths, const char* entry,
{
.name = "mod", .fmt = "s",
.handler = RHANDLER(name, args, {
if (loading_module && !force_mod) {
if (module != NULL && module != force_mod) free((char*) module);
if (loading_module) {
if (module != NULL) free((char*) module);
size_t len = strlen((char*) args[0]);
char* str = malloc(sizeof(char) * (len + 1));
strncpy(str, (char*) args[0], len + 1);
@@ -1113,14 +1135,17 @@ struct renderer* rd_new(const char** paths, const char* entry,
if (errno != ENOENT &&
errno != ENOTDIR &&
errno != ELOOP) {
fprintf(stderr, "Failed to load desktop environment specific presets at '%s': %s\n", se_buf, strerror(errno));
fprintf(stderr, "Failed to load desktop environment specific presets "
"at '%s': %s\n", se_buf, strerror(errno));
exit(EXIT_FAILURE);
} else {
printf("No presets for current desktop environment (\"%s\"), using default presets for embedding\n", env);
printf("No presets for current desktop environment (\"%s\"), "
"using default presets for embedding\n", env);
snprintf(se_buf, bsz, "%s/env_default.glsl", data);
fd = open(se_buf, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "Failed to load default presets at '%s': %s\n", se_buf, strerror(errno));
fprintf(stderr, "Failed to load default presets at '%s': %s\n",
se_buf, strerror(errno));
exit(EXIT_FAILURE);
}
}
@@ -1128,7 +1153,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
fstat(fd, &st);
map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
ext.source = map;
ext.source = map;
ext.source_len = st.st_size;
loading_presets = true;
@@ -1138,12 +1163,35 @@ struct renderer* rd_new(const char** paths, const char* entry,
munmap((void*) map, st.st_size);
} else {
fprintf(stderr, "Failed to detect the desktop environment! Is the window manager EWMH compliant?");
fprintf(stderr, "Failed to detect the desktop environment! "
"Is the window manager EWMH compliant?");
}
}
break;
}
{
struct glsl_ext ext = {
.cd = data,
.handlers = handlers
};
const char* req;
char fbuf[64];
int idx = 1;
for (const char** i = requests; (req = *i) != NULL; ++i) {
size_t rlen = strlen(req) + 16;
char* rbuf = malloc(rlen);
rlen = snprintf(rbuf, rlen, "#request %s", req);
snprintf(fbuf, sizeof(fbuf), "[request arg %d]", idx);
ext.source = rbuf;
ext.source_len = rlen;
ext_process(&ext, fbuf);
ext_free(&ext);
++idx;
}
}
if (!module) {
fprintf(stderr,
@@ -1182,9 +1230,9 @@ struct renderer* rd_new(const char** paths, const char* entry,
char shaders[bsz]; /* module pack path to use */
snprintf(shaders, bsz, "%s/%s", data, module);
printf("Loading module: '%s'\n", module);
if (verbose) printf("Loading module: '%s'\n", module);
if (!force_mod) free((void*) module);
free((void*) module);
loading_module = false;
/* Iterate through shader passes in the shader directory and build textures, framebuffers, and
@@ -1211,7 +1259,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
if (d->d_type == DT_REG || d->d_type == DT_UNKNOWN) {
snprintf(buf, sizeof(buf), "%d." SHADER_EXT_FRAG, (int) idx);
if (!strcmp(buf, d->d_name)) {
printf("found GLSL stage: '%s'\n", d->d_name);
if (verbose) printf("found GLSL stage: '%s'\n", d->d_name);
++count;
found = true;
}
@@ -1232,43 +1280,46 @@ struct renderer* rd_new(const char** paths, const char* entry,
if (d->d_type == DT_REG || d->d_type == DT_UNKNOWN) {
snprintf(buf, sizeof(buf), "%d." SHADER_EXT_FRAG, (int) idx);
if (!strcmp(buf, d->d_name)) {
printf("compiling: '%s'\n", d->d_name);
if (verbose) printf("compiling: '%s'\n", d->d_name);
struct gl_sfbo* s = &stages[idx - 1];
*s = (struct gl_sfbo) {
.name = strdup(d->d_name),
.shader = 0,
.valid = false,
.indirect = false,
.nativeonly = false,
.binds = malloc(1),
.binds_sz = 0
};
current = s;
GLuint id = shaderbuild(gl, shaders, data, dd, handlers, shader_version, d->d_name);
if (!id) {
bool skip;
GLuint id = shaderbuild(gl, shaders, data, dd, handlers, shader_version, &skip, d->d_name);
if (skip && verbose) printf("disabled: '%s'\n", d->d_name);
if (!id && !skip)
abort();
}
s->shader = id;
/* Only setup a framebuffer and texture if this isn't the final step,
as it can rendered directly */
if (idx != count) {
int w, h;
gl->wcb->get_fbsize(gl->w, &w, &h);
setup_sfbo(&stages[idx - 1], w, h);
}
if (id) {
/* Only setup a framebuffer and texture if this isn't the final step,
as it can rendered directly */
if (idx != count) {
int w, h;
gl->wcb->get_fbsize(gl->w, &w, &h);
setup_sfbo(&stages[idx - 1], w, h);
}
glUseProgram(id);
glUseProgram(id);
/* Setup uniform bindings */
size_t b;
for (b = 0; b < s->binds_sz; ++b) {
s->binds[b].uniform = glGetUniformLocation(id, s->binds[b].name);
/* Setup uniform bindings */
size_t b;
for (b = 0; b < s->binds_sz; ++b) {
s->binds[b].uniform = glGetUniformLocation(id, s->binds[b].name);
}
glBindFragDataLocation(id, 1, "fragment");
glUseProgram(0);
}
glBindFragDataLocation(id, 1, "fragment");
glUseProgram(0);
found = true;
}
@@ -1285,16 +1336,13 @@ struct renderer* rd_new(const char** paths, const char* entry,
{
struct gl_sfbo* final = NULL;
if (!gl->premultiply_alpha) {
for (size_t t = 0; t < gl->stages_sz; ++t) {
if (!gl->stages[t].nativeonly) {
final = &gl->stages[t];
}
for (size_t t = 0; t < gl->stages_sz; ++t) {
if (gl->stages[t].shader && (gl->premultiply_alpha || !gl->stages[t].nativeonly)) {
final = &gl->stages[t];
}
}
/* Invalidate framebuffer and use direct rendering if it was instantiated
due to a following `nativeonly` shader pass. */
if (final) final->valid = false;
/* Use dirct rendering on final pass */
if (final) final->indirect = false;
}
/* Compile smooth pass shader */
@@ -1306,7 +1354,7 @@ struct renderer* rd_new(const char** paths, const char* entry,
char util[usz]; /* module pack path to use */
snprintf(util, usz, "%s/%s", data, util_folder);
loading_smooth_pass = true;
if (!(gl->sm_prog = shaderbuild(gl, util, data, dd, handlers, shader_version, "smooth_pass.frag")))
if (!(gl->sm_prog = shaderbuild(gl, util, data, dd, handlers, shader_version, NULL, "smooth_pass.frag")))
abort();
loading_smooth_pass = false;
}
@@ -1428,7 +1476,7 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
/* Resize screen textures if needed */
if (ww != gl->lww || wh != gl->lwh) {
for (t = 0; t < gl->stages_sz; ++t) {
if (gl->stages[t].valid) {
if (gl->stages[t].indirect) {
setup_sfbo(&gl->stages[t], ww, wh);
}
}
@@ -1457,16 +1505,16 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
/* Current shader program */
struct gl_sfbo* current = &gl->stages[t];
if (current->nativeonly && !gl->premultiply_alpha)
if (!current->shader || (current->nativeonly && !gl->premultiply_alpha))
continue;
/* Bind framebuffer if this is not the final pass */
if (current->valid)
if (current->indirect)
glBindFramebuffer(GL_FRAMEBUFFER, current->fbo);
glClear(GL_COLOR_BUFFER_BIT);
if (!current->valid && gl->copy_desktop) {
if (!current->indirect && gl->copy_desktop) {
/* Self-contained code for drawing background image */
static GLuint bg_prog, bg_utex, bg_screen;
static bool setup = false;
@@ -1483,9 +1531,9 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
"}" "\n";
if (!setup) {
bg_prog = shaderlink(shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC,
NULL, NULL, NULL, 330, true, gl),
NULL, NULL, NULL, 330, true, NULL, gl),
shaderload(NULL, GL_FRAGMENT_SHADER, frag_shader,
NULL, NULL, NULL, 330, true, gl));
NULL, NULL, NULL, 330, true, NULL, gl));
bg_utex = glGetUniformLocation(bg_prog, "tex");
bg_screen = glGetUniformLocation(bg_prog, "screen");
glBindFragDataLocation(bg_prog, 1, "fragment");
@@ -1603,7 +1651,7 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
/* Return state */
glUseProgram(current->shader);
if (current->valid)
if (current->indirect)
glBindFramebuffer(GL_FRAMEBUFFER, current->fbo);
else
glBindFramebuffer(GL_FRAMEBUFFER, 0);
@@ -1642,7 +1690,7 @@ bool rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
drawoverlay(&gl->overlay); /* Fullscreen quad (actually just two triangles) */
/* Reset some state */
if (current->valid)
if (current->indirect)
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glUseProgram(0);

View File

@@ -11,9 +11,9 @@ typedef struct renderer {
struct gl_data* gl;
} renderer;
struct renderer* rd_new (const char** paths, const char* entry,
const char* force_mod, const char* force_backend,
bool auto_desktop);
struct renderer* rd_new (const char** paths, const char* entry,
const char** requests, const char* force_backend,
bool auto_desktop, bool verbose);
bool rd_update (struct renderer*, float* lb, float* rb,
size_t bsz, bool modified);
void rd_destroy (struct renderer*);

View File

@@ -49,13 +49,15 @@ void main() {
#else
float d = AREA_HEIGHT - AREA_Y;
#endif
float nbars = floor((AREA_WIDTH * 0.5F) / float(BAR_WIDTH + BAR_GAP)) * 2;
float section = BAR_WIDTH + BAR_GAP; /* size of section for each bar (including gap) */
float center = section / 2.0F; /* half section, distance to center */
float m = abs(mod(dx, section)); /* position in section */
float md = m - center; /* position in section from center line */
float nbars = floor((AREA_WIDTH * 0.5F) / section) * 2;
float p, s;
if (md < ceil(float(BAR_WIDTH) / 2) && md >= -floor(float(BAR_WIDTH) / 2)) { /* if not in gap */
float p = int(dx / section) / float(nbars / 2); /* position, (-1.0F, 1.0F)) */
s = dx / section;
p = (sign(s) == 1.0 ? ceil(s) : floor(s)) / float(nbars / 2); /* position, (-1.0F, 1.0F)) */
p += sign(p) * ((0.5F + center) / AREA_WIDTH); /* index center of bar position */
/* Apply smooth function and index texture */
#define smooth_f(tex, p) smooth_audio(tex, audio_sz, p)
@@ -66,7 +68,7 @@ void main() {
return;
}
/* handle user options and store result of indexing in 'v' */
if (p >= 0.0F) {
if (p > 0.0F) {
#if DIRECTION == 1
p = 1.0F - p;
#endif

View File

@@ -32,6 +32,8 @@ out vec4 fragment;
/* This shader is based on radial.glsl, refer to it for more commentary */
float apply_smooth(float theta) {
fragment = vec4(0, 0, 0, 0);
float idx = theta + ROTATE;
float dir = mod(abs(idx), TWOPI);

View File

@@ -13,8 +13,14 @@
#define DRAW_OUTLINE 0
/* 1 to draw edge highlight, 0 to disable */
#define DRAW_HIGHLIGHT 1
/* Whether to anti-alias the border of the graph, creating a smoother curve.
This may have a small impact on performance.
Note: requires `xroot` or `none` opacity to be set */
#define ANTI_ALIAS 0
/* outline color */
#define OUTLINE #262626
/* 1 to join the two channels together in the middle, 0 to clamp both down to zero */
#define JOIN_CHANNELS 0
/* 1 to invert (vertically), 0 otherwise */
#define INVERT 0

View File

@@ -81,16 +81,31 @@ out vec4 fragment;
#define TWOPI 6.28318530718
float half_w;
float middle;
highp float pixel = 1.0F / float(screen.x);
void render_side(in sampler1D tex, float idx) {
highp float pixel = 1.0F / float(screen.x);
float get_line_height(in sampler1D tex, float idx) {
float s = smooth_audio_adj(tex, audio_sz, idx / half_w, pixel);
/* scale the data upwards so we can see it */
s *= VSCALE;
/* clamp far ends of the screen down to make the ends of the graph smoother */
s *= clamp((abs((screen.x / 2) - gl_FragCoord.x) / screen.x) * 48, 0.0F, 1.0F);
float fact = clamp((abs((screen.x / 2) - gl_FragCoord.x) / screen.x) * 48, 0.0F, 1.0F);
#if JOIN_CHANNELS > 0
fact = -2 * pow(fact, 3) + 3 * pow(fact, 2); /* To avoid spikes */
s = fact * s + (1 - fact) * middle;
#else
s *= fact;
#endif
s *= clamp((min(gl_FragCoord.x, screen.x - gl_FragCoord.x) / screen.x) * 48, 0.0F, 1.0F);
return s;
}
void render_side(in sampler1D tex, float idx) {
float s = get_line_height(tex, idx);
/* and finally set fragment color if we are in range */
#if INVERT > 0
float pos = float(screen.y) - gl_FragCoord.y;
@@ -106,6 +121,9 @@ void render_side(in sampler1D tex, float idx) {
void main() {
half_w = (screen.x / 2);
middle = VSCALE * (smooth_audio_adj(audio_l, audio_sz, 1, pixel) + smooth_audio_adj(audio_r, audio_sz, 0, pixel)) / 2;
if (gl_FragCoord.x < half_w) {
render_side(audio_l, LEFT_IDX);
} else {

View File

@@ -9,6 +9,10 @@ out vec4 fragment; /* output */
#include "@graph.glsl"
#include ":graph.glsl"
#if DRAW_OUTLINE == 0 && DRAW_HIGHLIGHT == 0
#error __disablestage
#endif
void main() {
fragment = texelFetch(tex, ivec2(gl_FragCoord.x, gl_FragCoord.y), 0);

104
shaders/graph/3.frag Normal file
View File

@@ -0,0 +1,104 @@
in vec4 gl_FragCoord;
#request uniform "screen" screen
uniform ivec2 screen; /* screen dimensions */
#request uniform "prev" tex
uniform sampler2D tex; /* screen texture */
out vec4 fragment; /* output */
#include "@graph.glsl"
#include ":graph.glsl"
#if ANTI_ALIAS == 0
#error __disablestage
#endif
/* Moves toward the border of the graph, gives the
y coordinate of the last colored pixel */
float get_col_height_up(float x, float oy) {
float y = oy;
#if INVERT > 0
while (y >= 0) {
#else
while (y < screen.y) {
#endif
vec4 f = texelFetch(tex, ivec2(x, y), 0);
if (f.a <= 0) {
#if INVERT > 0
y += 1;
#else
y -= 1;
#endif
break;
}
#if INVERT > 0
y -= 1;
#else
y += 1;
#endif
}
return y;
}
/* Moves toward the base of the graph, gives the
y coordinate of the first colored pixel */
float get_col_height_down(float x, float oy) {
float y = oy;
#if INVERT > 0
while (y < screen.y) {
#else
while (y >= 0) {
#endif
vec4 f = texelFetch(tex, ivec2(x, y), 0);
if (f.a > 0) {
break;
}
#if INVERT > 0
y += 1;
#else
y -= 1;
#endif
}
return y;
}
void main() {
fragment = texelFetch(tex, ivec2(gl_FragCoord.x, gl_FragCoord.y), 0);
#if ANTI_ALIAS > 0
if (fragment.a <= 0) {
bool left_done = false;
float h2;
float a_fact = 0;
if (texelFetch(tex, ivec2(gl_FragCoord.x - 1, gl_FragCoord.y), 0).a > 0) {
float h1 = get_col_height_up(gl_FragCoord.x - 1, gl_FragCoord.y);
h2 = get_col_height_down(gl_FragCoord.x, gl_FragCoord.y);
fragment = texelFetch(tex, ivec2(gl_FragCoord.x, h2), 0);
a_fact = clamp(abs((h1 - gl_FragCoord.y) / (h2 - h1)), 0.0, 1.0);
left_done = true;
}
if (texelFetch(tex, ivec2(gl_FragCoord.x + 1, gl_FragCoord.y), 0).a > 0) {
if (!left_done) {
h2 = get_col_height_down(gl_FragCoord.x, gl_FragCoord.y);
fragment = texelFetch(tex, ivec2(gl_FragCoord.x, h2), 0);
}
float h3 = get_col_height_up(gl_FragCoord.x + 1, gl_FragCoord.y);
a_fact = max(a_fact, clamp(abs((h3 - gl_FragCoord.y) / (h2 - h3)), 0.0, 1.0));
}
fragment.a *= a_fact;
}
#endif
}

View File

@@ -32,7 +32,7 @@
/* Offset (X) of the visualization */
#define CENTER_OFFSET_X 0
/* Gravity step, overrude frin `smooth_parameters.glsl` */
/* Gravity step, override from `smooth_parameters.glsl` */
#request setgravitystep 5.0
/* Smoothing factor, override from `smooth_parameters.glsl` */

View File

@@ -98,9 +98,14 @@
default when GLava itself is a desktop window. */
#request setclickthrough false
/* PulseAudio source. Can be a number or a name of an audio
sink or device to record from. Set to "auto" to use the
default output device. */
/* Audio source
When the "pulseaudio" backend is set, this can be a number or
a name of an audio sink or device to record from. Set to "auto"
to use the default output device.
When the "fifo" backend is set, "auto" is interpreted as
"/tmp/mpd.fifo". Otherwise, a valid path should be provided. */
#request setsource "auto"
/* Buffer swap interval (vsync), set to '0' to prevent
@@ -189,7 +194,11 @@
Lower sample rates also can make output more choppy, when
not using interpolation. It's generally OK to leave this
value unless you have a strange PulseAudio configuration. */
value unless you have a strange PulseAudio configuration.
This option does nothing when using the "fifo" audio
backend. Instead, an ideal rate should be be configured
in the application generating the output. */
#request setsamplerate 22050
/* ** DEPRECATED **

View File

@@ -13,10 +13,25 @@
- circular heavily rounded points
- sinusoidal rounded at both low and high weighted values
like a sine wave
- linear not rounded at all, just use linear distance
- linear not rounded at all; linear distance
*/
#define ROUND_FORMULA sinusoidal
/* The sampling mode for processing raw FFT input:
- average averages all the inputs in the sample range for
a given point. Produces smooth output, but peaks
are not well represented
- maximum obtains the best value from the closest peak in
the sample range. Very accurate peaks, but
output is jagged and sporadic.
- hybrid uses the results from both `average` and `maximum`
with the weight provided in `SAMPLE_HYBRID_WEIGHT` */
#define SAMPLE_MODE average
/* Weight should be provided in the range (0, 1). Higher values favour
averaged results. `hybrid` mode only. */
#define SAMPLE_HYBRID_WEIGHT 0.65
/* Factor used to scale frequencies. Lower values allows lower
frequencies to occupy more space. */
#define SAMPLE_SCALE 8

View File

@@ -21,6 +21,10 @@
/* take value x that scales linearly between [0, 1) and return its circlar curve */
#define circular(x) sqrt(1 - (((x) - 1) * ((x) - 1)))
#define average 0
#define maximum 1
#define hybrid 2
float scale_audio(float idx) {
return -log((-(SAMPLE_RANGE) * idx) + 1) / (SAMPLE_SCALE);
}
@@ -32,20 +36,42 @@ float iscale_audio(float idx) {
/* Note: the SMOOTH_FACTOR macro is defined by GLava itself, from `#request setsmoothfactor`*/
float smooth_audio(in sampler1D tex, int tex_sz, highp float idx) {
#if PRE_SMOOTHED_AUDIO < 1
float
smin = scale_audio(clamp(idx - SMOOTH_FACTOR, 0, 1)) * tex_sz,
smax = scale_audio(clamp(idx + SMOOTH_FACTOR, 0, 1)) * tex_sz,
avg = 0, s, weight = 0;
float m = ((smax - smin) / 2.0F);
smin = scale_audio(clamp(idx - SMOOTH_FACTOR, 0, 1)) * tex_sz,
smax = scale_audio(clamp(idx + SMOOTH_FACTOR, 0, 1)) * tex_sz;
float m = ((smax - smin) / 2.0F), s, w;
float rm = smin + m; /* middle */
#if SAMPLE_MODE == average
float avg = 0, weight = 0;
for (s = smin; s <= smax; s += 1.0F) {
float w = ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1));
w = ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1));
weight += w;
avg += texelFetch(tex, int(round(s)), 0).r * w;
}
avg /= weight;
return avg;
#elif SAMPLE_MODE == hybrid
float vmax = 0, avg = 0, weight = 0, v;
for (s = smin; s < smax; s += 1.0F) {
w = ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1));
weight += w;
v = texelFetch(tex, int(round(s)), 0).r * w;
avg += v;
if (vmax < v)
vmax = v;
}
return (vmax * (1 - SAMPLE_HYBRID_WEIGHT)) + ((avg / weight) * SAMPLE_HYBRID_WEIGHT);
#elif SAMPLE_MODE == maximum
float vmax = 0, v;
for (s = smin; s < smax; s += 1.0F) {
w = texelFetch(tex, int(round(s)), 0).r * ROUND_FORMULA(clamp((m - abs(rm - s)) / m, 0, 1));
if (vmax < w)
vmax = w;
}
return vmax;
#endif
#else
return texelFetch(tex, int(round(idx * tex_sz)), 0).r;
#endif

1
xwin.c
View File

@@ -389,6 +389,7 @@ unsigned int xwin_copyglbg(struct renderer* rd, unsigned int tex) {
}
if (image) XDestroyImage(image);
XFree(info);
return texture;
}