diff options
author | Aylur <[email protected]> | 2024-10-29 15:09:22 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2024-10-29 15:09:22 +0100 |
commit | 8705ab494f448c508c43c4272b501a3f3bdbac70 (patch) | |
tree | 97f3282502524adb11ed75e173f09a8fe20da059 /lib | |
parent | 7e484188e7492ac7945c854bcc3f26cec1863c91 (diff) | |
parent | 078dc047d9fdf6e676134cf92a05e56b868f1ab3 (diff) |
Merge pull request #31 from Aylur/feat/cava
new lib: cava
Diffstat (limited to 'lib')
-rw-r--r-- | lib/cava/.gitignore | 1 | ||||
-rw-r--r-- | lib/cava/astal-cava.h | 70 | ||||
-rw-r--r-- | lib/cava/cava.c | 659 | ||||
-rw-r--r-- | lib/cava/meson.build | 80 | ||||
-rw-r--r-- | lib/cava/meson_options.txt | 13 | ||||
-rw-r--r-- | lib/cava/subprojects/cava.wrap | 7 | ||||
-rw-r--r-- | lib/cava/version | 1 |
7 files changed, 831 insertions, 0 deletions
diff --git a/lib/cava/.gitignore b/lib/cava/.gitignore new file mode 100644 index 0000000..2c7a6aa --- /dev/null +++ b/lib/cava/.gitignore @@ -0,0 +1 @@ +/subprojects/**/ diff --git a/lib/cava/astal-cava.h b/lib/cava/astal-cava.h new file mode 100644 index 0000000..343234a --- /dev/null +++ b/lib/cava/astal-cava.h @@ -0,0 +1,70 @@ +#ifndef ASTAL_CAVA_H +#define ASTAL_CAVA_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define ASTAL_CAVA_TYPE_INPUT (astal_cava_input_get_type()) + +typedef enum { + ASTAL_CAVA_INPUT_FIFO, + ASTAL_CAVA_INPUT_PORTAUDIO, + ASTAL_CAVA_INPUT_PIPEWIRE, + ASTAL_CAVA_INPUT_ALSA, + ASTAL_CAVA_INPUT_PULSE, + ASTAL_CAVA_INPUT_SNDIO, + ASTAL_CAVA_INPUT_OSS, + ASTAL_CAVA_INPUT_JACK, + ASTAL_CAVA_INPUT_SHMEM, + ASTAL_CAVA_INPUT_WINSCAP, +} AstalCavaInput; + +#define ASTAL_CAVA_TYPE_CAVA (astal_cava_cava_get_type()) + +G_DECLARE_FINAL_TYPE(AstalCavaCava, astal_cava_cava, ASTAL_CAVA, CAVA, GObject) + +AstalCavaCava* astal_cava_cava_get_default(); +AstalCavaCava* astal_cava_get_default(); + +gboolean astal_cava_cava_get_active(AstalCavaCava* self); +void astal_cava_cava_set_active(AstalCavaCava* self, gboolean active); + +GArray* astal_cava_cava_get_values(AstalCavaCava* self); + +gint astal_cava_cava_get_bars(AstalCavaCava* self); +void astal_cava_cava_set_bars(AstalCavaCava* self, gint bars); + +gboolean astal_cava_cava_get_autosens(AstalCavaCava* self); +void astal_cava_cava_set_autosens(AstalCavaCava* self, gboolean autosens); + +gboolean astal_cava_cava_get_stereo(AstalCavaCava* self); +void astal_cava_cava_set_stereo(AstalCavaCava* self, gboolean stereo); + +gdouble astal_cava_cava_get_noise_reduction(AstalCavaCava* self); +void astal_cava_cava_set_noise_reduction(AstalCavaCava* self, gdouble noise); + +gint astal_cava_cava_get_framerate(AstalCavaCava* self); +void astal_cava_cava_set_framerate(AstalCavaCava* self, gint framerate); + +AstalCavaInput astal_cava_cava_get_input(AstalCavaCava* self); +void astal_cava_cava_set_input(AstalCavaCava* self, AstalCavaInput input); + +gchar* astal_cava_cava_get_source(AstalCavaCava* self); +void astal_cava_cava_set_source(AstalCavaCava* self, const gchar* source); + +gint astal_cava_cava_get_channels(AstalCavaCava* self); +void astal_cava_cava_set_channels(AstalCavaCava* self, gint channels); + +gint astal_cava_cava_get_low_cutoff(AstalCavaCava* self); +void astal_cava_cava_set_low_cutoff(AstalCavaCava* self, gint low_cutoff); + +gint astal_cava_cava_get_high_cutoff(AstalCavaCava* self); +void astal_cava_cava_set_high_cutoff(AstalCavaCava* self, gint high_cutoff); + +gint astal_cava_cava_get_samplerate(AstalCavaCava* self); +void astal_cava_cava_set_samplerate(AstalCavaCava* self, gint samplerate); + +G_END_DECLS + +#endif // !ASTAL_CAVA_H diff --git a/lib/cava/cava.c b/lib/cava/cava.c new file mode 100644 index 0000000..1c5ef66 --- /dev/null +++ b/lib/cava/cava.c @@ -0,0 +1,659 @@ +#include <cava/common.h> +#include <gio/gio.h> + +#include "astal-cava.h" +#include "cava/config.h" +#include "glib-object.h" +#include "glib.h" +#include "glibconfig.h" + +struct _AstalCavaCava { + GObject parent_instance; + + gint bars; + gboolean autosens; + gboolean stereo; + gdouble noise_reduction; + gint framerate; + AstalCavaInput input; + gchar* audio_source; + gboolean active; + gint channels; + gint low_cutoff; + gint high_cutoff; + gint samplerate; + + GArray* values; +}; + +typedef struct { + struct cava_plan plan; + struct config_params cfg; + struct audio_data audio_data; + struct audio_raw audio_raw; + ptr input_src; + + gboolean constructed; + GThread* input_thread; + guint timer_id; + +} AstalCavaCavaPrivate; + +G_DEFINE_ENUM_TYPE(AstalCavaInput, astal_cava_input, + G_DEFINE_ENUM_VALUE(ASTAL_CAVA_INPUT_FIFO, "fifo"), + G_DEFINE_ENUM_VALUE(ASTAL_CAVA_INPUT_PORTAUDIO, "portaudio"), + G_DEFINE_ENUM_VALUE(ASTAL_CAVA_INPUT_PIPEWIRE, "pipewire"), + G_DEFINE_ENUM_VALUE(ASTAL_CAVA_INPUT_ALSA, "alsa"), + G_DEFINE_ENUM_VALUE(ASTAL_CAVA_INPUT_PULSE, "pulse"), + G_DEFINE_ENUM_VALUE(ASTAL_CAVA_INPUT_SNDIO, "sndio"), + G_DEFINE_ENUM_VALUE(ASTAL_CAVA_INPUT_SHMEM, "shmem"), + G_DEFINE_ENUM_VALUE(ASTAL_CAVA_INPUT_WINSCAP, "winscap")); + +G_DEFINE_TYPE_WITH_PRIVATE(AstalCavaCava, astal_cava_cava, G_TYPE_OBJECT) + +typedef enum { + ASTAL_CAVA_CAVA_PROP_VALUES = 1, + ASTAL_CAVA_CAVA_PROP_ACTIVE, + ASTAL_CAVA_CAVA_PROP_BARS, + ASTAL_CAVA_CAVA_PROP_AUTOSENS, + ASTAL_CAVA_CAVA_PROP_STEREO, + ASTAL_CAVA_CAVA_PROP_NOISE, + ASTAL_CAVA_CAVA_PROP_FRAMERATE, + ASTAL_CAVA_CAVA_PROP_INPUT, + ASTAL_CAVA_CAVA_PROP_SOURCE, + ASTAL_CAVA_CAVA_PROP_CHANNELS, + ASTAL_CAVA_CAVA_PROP_LOW_CUTOFF, + ASTAL_CAVA_CAVA_PROP_HIGH_CUTOFF, + ASTAL_CAVA_CAVA_PROP_SAMPLERATE, + ASTAL_CAVA_CAVA_N_PROPERTIES +} AstalCavaProperties; + +static GParamSpec* astal_cava_cava_properties[ASTAL_CAVA_CAVA_N_PROPERTIES] = { + NULL, +}; + +static gboolean exec_cava(AstalCavaCava* self) { + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + + pthread_mutex_lock(&priv->audio_data.lock); + cava_execute(priv->audio_data.cava_in, priv->audio_data.samples_counter, + priv->audio_raw.cava_out, &priv->plan); + if (priv->audio_data.samples_counter > 0) priv->audio_data.samples_counter = 0; + pthread_mutex_unlock(&priv->audio_data.lock); + + g_array_remove_range(self->values, 0, priv->audio_raw.number_of_bars); + g_array_insert_vals(self->values, 0, priv->audio_raw.cava_out, priv->audio_raw.number_of_bars); + + g_object_notify(G_OBJECT(self), "values"); + + return G_SOURCE_CONTINUE; +} + +static void astal_cava_cava_cleanup(AstalCavaCava* self) { + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + + g_source_remove(priv->timer_id); + pthread_mutex_lock(&priv->audio_data.lock); + priv->audio_data.terminate = 1; + pthread_mutex_unlock(&priv->audio_data.lock); + g_thread_join(priv->input_thread); + + cava_destroy(&priv->plan); + + g_free(priv->audio_data.cava_in); + g_free(priv->audio_data.source); + + g_free(priv->cfg.audio_source); + g_free(priv->cfg.raw_target); + g_free(priv->cfg.data_format); +} + +static void astal_cava_cava_start(AstalCavaCava* self) { + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + + if (self->framerate < 1) { + self->framerate = 1; + } + + if (self->low_cutoff < 1) { + self->low_cutoff = 1; + } + + if (self->high_cutoff < self->low_cutoff) { + self->high_cutoff = self->low_cutoff + 1; + } + + if (self->samplerate / 2 <= self->high_cutoff) { + self->samplerate = self->high_cutoff * 2 + 1; + } + + if (self->bars < 1) { + self->bars = 1; + } + + if (self->channels < 1 || self->channels > 2) { + self->channels = 2; + } + + priv->cfg = (struct config_params){ + .inAtty = 0, + .output = OUTPUT_RAW, + .raw_target = strdup("/dev/stdout"), + .data_format = strdup("binary"), + + .fixedbars = self->bars, + .autosens = self->autosens, + .stereo = self->stereo, + .noise_reduction = self->noise_reduction, + .framerate = self->framerate, + .input = (enum input_method)self->input, + .channels = self->channels, + .lower_cut_off = self->low_cutoff, + .upper_cut_off = self->high_cutoff, + .samplerate = self->samplerate, + + // not needed in this lib + .mono_opt = AVERAGE, + .waves = 0, + .userEQ = NULL, + .userEQ_keys = 0, + .userEQ_enabled = 0, + .samplebits = 16, + .waveform = 0, + .monstercat = 0, + .sens = 1, + .autoconnect = 2, + .reverse = 0, + .sleep_timer = 0, + .show_idle_bar_heads = 1, + .continuous_rendering = 0, + .sdl_width = 1000, + .sdl_height = 500, + .sdl_x = -1, + .sdl_y = -1, + .sdl_full_screen = 0, + .draw_and_quit = 0, + .zero_test = 0, + .non_zero_test = 0, + .sync_updates = 0, + .disable_blanking = 1, + .bar_height = 32, + .col = 0, + .bgcol = 0, + .autobars = 0, + .raw_format = FORMAT_BINARY, + .ascii_range = 1000, + .bit_format = 16, + .gradient = 0, + .gradient_count = 0, + .bar_width = 2, + .bar_spacing = 1, + .xaxis = NONE, + .orientation = ORIENT_BOTTOM, + .color = NULL, + .bcolor = NULL, + .gradient_colors = NULL, + .vertex_shader = NULL, + .fragment_shader = NULL, + .bar_delim = ';', + .frame_delim = '\n', + }; + + if (g_strcmp0(self->audio_source, "auto") == 0) { + switch (priv->cfg.input) { + case INPUT_ALSA: + priv->cfg.audio_source = g_strdup("hw:Loopback,1"); + break; + case INPUT_FIFO: + priv->cfg.audio_source = g_strdup("/tmp/mpd.fifo"); + break; + case INPUT_PULSE: + priv->cfg.audio_source = g_strdup("auto"); + break; + case INPUT_PIPEWIRE: + priv->cfg.audio_source = g_strdup("auto"); + break; + case INPUT_SNDIO: + priv->cfg.audio_source = g_strdup("default"); + break; + case INPUT_OSS: + priv->cfg.audio_source = g_strdup("/dev/dsp"); + break; + case INPUT_JACK: + priv->cfg.audio_source = g_strdup("default"); + break; + case INPUT_SHMEM: + priv->cfg.audio_source = g_strdup("/squeezelite-00:00:00:00:00:00"); + break; + case INPUT_PORTAUDIO: + priv->cfg.audio_source = g_strdup("auto"); + break; + default: + g_critical("unsupported audio source"); + } + } else { + priv->cfg.audio_source = g_strdup(self->audio_source); + } + + priv->audio_data = (struct audio_data){ + .cava_in = calloc(BUFFER_SIZE * priv->cfg.channels * 8, sizeof(gdouble)), + .input_buffer_size = BUFFER_SIZE * priv->cfg.channels, + .cava_buffer_size = BUFFER_SIZE * priv->cfg.channels * 8, + .format = -1, + .rate = 0, + .channels = priv->cfg.channels, + .source = g_strdup(priv->cfg.audio_source), + .terminate = 0, + .samples_counter = 0, + .IEEE_FLOAT = 0, + .suspendFlag = false, + }; + + priv->input_src = get_input(&priv->audio_data, &priv->cfg); + + audio_raw_init(&priv->audio_data, &priv->audio_raw, &priv->cfg, &priv->plan); + + priv->input_thread = g_thread_new("cava_input", priv->input_src, &priv->audio_data); + + priv->timer_id = g_timeout_add(1000 / priv->cfg.framerate, G_SOURCE_FUNC(exec_cava), self); +} + +static void astal_cava_cava_restart(AstalCavaCava* self) { + if (!self->active) return; + astal_cava_cava_cleanup(self); + astal_cava_cava_start(self); +} + +gboolean astal_cava_cava_get_active(AstalCavaCava* self) { return self->active; } + +void astal_cava_cava_set_active(AstalCavaCava* self, gboolean active) { + if (self->active == active) return; + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + + self->active = active; + + if (!priv->constructed) return; + if (!active) + astal_cava_cava_cleanup(self); + else + astal_cava_cava_start(self); +} + +/** + * astal_cava_cava_get_values + * @self: the AstalCavaCava object + * + * Returns: (transfer none) (element-type gdouble): a list of values + * + */ +GArray* astal_cava_cava_get_values(AstalCavaCava* self) { return self->values; } + +gint astal_cava_cava_get_bars(AstalCavaCava* self) { return self->bars; } + +void astal_cava_cava_set_bars(AstalCavaCava* self, gint bars) { + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + self->bars = bars; + if (priv->constructed) { + g_array_set_size(self->values, self->bars); + astal_cava_cava_restart(self); + } +} + +gboolean astal_cava_cava_get_autosens(AstalCavaCava* self) { return self->autosens; } + +void astal_cava_cava_set_autosens(AstalCavaCava* self, gboolean autosens) { + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + self->autosens = autosens; + if (priv->constructed) astal_cava_cava_restart(self); +} + +gboolean astal_cava_cava_get_stereo(AstalCavaCava* self) { return self->stereo; } + +void astal_cava_cava_set_stereo(AstalCavaCava* self, gboolean stereo) { + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + self->stereo = stereo; + if (priv->constructed) astal_cava_cava_restart(self); +} + +gdouble astal_cava_cava_get_noise_reduction(AstalCavaCava* self) { return self->noise_reduction; } + +void astal_cava_cava_set_noise_reduction(AstalCavaCava* self, gdouble noise) { + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + self->noise_reduction = noise; + if (priv->constructed) astal_cava_cava_restart(self); +} + +gint astal_cava_cava_get_framerate(AstalCavaCava* self) { return self->framerate; } + +void astal_cava_cava_set_framerate(AstalCavaCava* self, gint framerate) { + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + self->framerate = framerate; + if (priv->constructed) astal_cava_cava_restart(self); +} + +AstalCavaInput astal_cava_cava_get_input(AstalCavaCava* self) { return self->input; } + +void astal_cava_cava_set_input(AstalCavaCava* self, AstalCavaInput input) { + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + self->input = input; + if (priv->constructed) astal_cava_cava_restart(self); +} + +gchar* astal_cava_cava_get_source(AstalCavaCava* self) { return self->audio_source; } + +void astal_cava_cava_set_source(AstalCavaCava* self, const gchar* source) { + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + g_free(self->audio_source); + self->audio_source = g_strdup(source); + if (priv->constructed) astal_cava_cava_restart(self); +} + +gint astal_cava_cava_get_channels(AstalCavaCava* self) { return self->channels; } + +void astal_cava_cava_set_channels(AstalCavaCava* self, gint channels) { + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + self->channels = channels; + if (priv->constructed) astal_cava_cava_restart(self); +} + +gint astal_cava_cava_get_low_cutoff(AstalCavaCava* self) { return self->low_cutoff; } + +void astal_cava_cava_set_low_cutoff(AstalCavaCava* self, gint low_cutoff) { + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + self->low_cutoff = low_cutoff; + if (priv->constructed) astal_cava_cava_restart(self); +} + +gint astal_cava_cava_get_high_cutoff(AstalCavaCava* self) { return self->high_cutoff; } + +void astal_cava_cava_set_high_cutoff(AstalCavaCava* self, gint high_cutoff) { + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + self->high_cutoff = high_cutoff; + if (priv->constructed) astal_cava_cava_restart(self); +} + +gint astal_cava_cava_get_samplerate(AstalCavaCava* self) { return self->samplerate; } + +void astal_cava_cava_set_samplerate(AstalCavaCava* self, gint samplerate) { + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + self->samplerate = samplerate; + if (priv->constructed) astal_cava_cava_restart(self); +} + +static void astal_cava_cava_set_property(GObject* object, guint property_id, const GValue* value, + GParamSpec* pspec) { + AstalCavaCava* self = ASTAL_CAVA_CAVA(object); + + switch (property_id) { + case ASTAL_CAVA_CAVA_PROP_BARS: + astal_cava_cava_set_bars(self, g_value_get_int(value)); + break; + case ASTAL_CAVA_CAVA_PROP_ACTIVE: + astal_cava_cava_set_active(self, g_value_get_boolean(value)); + break; + case ASTAL_CAVA_CAVA_PROP_AUTOSENS: + astal_cava_cava_set_autosens(self, g_value_get_boolean(value)); + break; + case ASTAL_CAVA_CAVA_PROP_NOISE: + astal_cava_cava_set_noise_reduction(self, g_value_get_double(value)); + break; + case ASTAL_CAVA_CAVA_PROP_STEREO: + astal_cava_cava_set_stereo(self, g_value_get_boolean(value)); + break; + case ASTAL_CAVA_CAVA_PROP_FRAMERATE: + astal_cava_cava_set_framerate(self, g_value_get_int(value)); + break; + case ASTAL_CAVA_CAVA_PROP_INPUT: + astal_cava_cava_set_input(self, g_value_get_enum(value)); + break; + case ASTAL_CAVA_CAVA_PROP_SOURCE: + g_free(self->audio_source); + astal_cava_cava_set_source(self, g_value_get_string(value)); + break; + case ASTAL_CAVA_CAVA_PROP_CHANNELS: + astal_cava_cava_set_channels(self, g_value_get_int(value)); + break; + case ASTAL_CAVA_CAVA_PROP_LOW_CUTOFF: + astal_cava_cava_set_low_cutoff(self, g_value_get_int(value)); + break; + case ASTAL_CAVA_CAVA_PROP_HIGH_CUTOFF: + astal_cava_cava_set_high_cutoff(self, g_value_get_int(value)); + break; + case ASTAL_CAVA_CAVA_PROP_SAMPLERATE: + astal_cava_cava_set_samplerate(self, g_value_get_int(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void astal_cava_cava_get_property(GObject* object, guint property_id, GValue* value, + GParamSpec* pspec) { + AstalCavaCava* self = ASTAL_CAVA_CAVA(object); + + switch (property_id) { + case ASTAL_CAVA_CAVA_PROP_ACTIVE: + g_value_set_boolean(value, self->active); + break; + case ASTAL_CAVA_CAVA_PROP_BARS: + g_value_set_int(value, self->bars); + break; + case ASTAL_CAVA_CAVA_PROP_VALUES: + g_value_set_pointer(value, self->values); + break; + case ASTAL_CAVA_CAVA_PROP_AUTOSENS: + g_value_set_boolean(value, self->autosens); + break; + case ASTAL_CAVA_CAVA_PROP_NOISE: + g_value_set_double(value, self->noise_reduction); + break; + case ASTAL_CAVA_CAVA_PROP_STEREO: + g_value_set_boolean(value, self->stereo); + break; + case ASTAL_CAVA_CAVA_PROP_FRAMERATE: + g_value_set_int(value, self->framerate); + break; + case ASTAL_CAVA_CAVA_PROP_INPUT: + g_value_set_enum(value, self->input); + break; + case ASTAL_CAVA_CAVA_PROP_SOURCE: + g_value_set_string(value, self->audio_source); + break; + case ASTAL_CAVA_CAVA_PROP_CHANNELS: + g_value_set_int(value, self->channels); + break; + case ASTAL_CAVA_CAVA_PROP_LOW_CUTOFF: + g_value_set_int(value, self->low_cutoff); + break; + case ASTAL_CAVA_CAVA_PROP_HIGH_CUTOFF: + g_value_set_int(value, self->high_cutoff); + break; + case ASTAL_CAVA_CAVA_PROP_SAMPLERATE: + g_value_set_int(value, self->samplerate); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void astal_cava_cava_constructed(GObject* object) { + AstalCavaCava* self = ASTAL_CAVA_CAVA(object); + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + + gdouble* data = calloc(self->bars, sizeof(gdouble)); + memset(data, 0, self->bars * sizeof(gdouble)); + self->values = g_array_new_take(data, self->bars, TRUE, sizeof(gdouble)); + + priv->constructed = true; + + if (self->active) astal_cava_cava_start(self); +} + +static void astal_cava_cava_init(AstalCavaCava* self) { + AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self); + priv->constructed = false; + self->low_cutoff = 50; + self->high_cutoff = 10000; + self->samplerate = 44100; +} + +/** + * astal_cava_get_default + * + * gets the default Cava object. + * + * Returns: (nullable) (transfer none): + */ +AstalCavaCava* astal_cava_get_default() { return astal_cava_cava_get_default(); } + +/** + * astal_cava_cava_get_default + * + * gets the default Cava object. + * + * Returns: (nullable) (transfer none): + */ +AstalCavaCava* astal_cava_cava_get_default() { + static AstalCavaCava* self = NULL; + + if (self == NULL) self = g_object_new(ASTAL_CAVA_TYPE_CAVA, NULL); + + return self; +} + +static void astal_cava_cava_dispose(GObject* object) { + AstalCavaCava* self = ASTAL_CAVA_CAVA(object); + + if (self->active) astal_cava_cava_cleanup(self); + G_OBJECT_CLASS(astal_cava_cava_parent_class)->dispose(object); +} + +static void astal_cava_cava_finalize(GObject* object) { + AstalCavaCava* self = ASTAL_CAVA_CAVA(object); + + g_array_free(self->values, TRUE); + + G_OBJECT_CLASS(astal_cava_cava_parent_class)->finalize(object); +} + +static void astal_cava_cava_class_init(AstalCavaCavaClass* class) { + GObjectClass* object_class = G_OBJECT_CLASS(class); + object_class->get_property = astal_cava_cava_get_property; + object_class->set_property = astal_cava_cava_set_property; + object_class->constructed = astal_cava_cava_constructed; + object_class->dispose = astal_cava_cava_dispose; + object_class->finalize = astal_cava_cava_finalize; + + /** + * AstalCavaCava:values: (type GArray(gdouble)) + * + * A list of values, each represent the height of one bar. The values are generally between 0 + * and 1 but can overshoot occasionally, in which case the sensitivity will be decreased + * automatically if [[email protected]:autosens] is set. The array will have + * [[email protected]:bars] entries. If [[email protected]:stereo] is set, the first + * half of the array will represent the left channel and the second half the right channel, so + * there will be only bars/2 bars per channel. If the number of bars is odd, the last value will + * be 0. + */ + astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_VALUES] = + g_param_spec_pointer("values", "values", "a list of values", G_PARAM_READABLE); + /** + * AstalCavaCava:active: + * + * whether or not the audio capture and visualization is running. if false the values array will + * not be updated. + */ + astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_ACTIVE] = g_param_spec_boolean( + "active", "active", "active", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + /** + * AstalCavaCava:bars: + * + * the number of bars the visualizer should create. + */ + astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_BARS] = + g_param_spec_int("bars", "bars", "number of bars per channel", 1, G_MAXINT, 20, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + /** + * AstalCavaCava:autosens: + * + * When set, the sensitivity will automatically be adjusted. + */ + astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_AUTOSENS] = + g_param_spec_boolean("autosens", "autosens", "dynamically adjust sensitivity", TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + /** + * AstalCavaCava:cava: + * + * When set the output will contain visualization data for both channels. + */ + astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_STEREO] = g_param_spec_boolean( + "stereo", "stereo", "stereo", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + /** + * AstalCavaCava:noise-reduction: + * + * adjusts the noise-reduction filter. low values are fast and noisy, large values are slow and + * smooth. + */ + astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_NOISE] = + g_param_spec_double("noise_reduction", "noise_reduction", "noise reduction", 0, 1, 0.77, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + /** + * AstalCavaCava:framerate: + * + * how often the values should be updated + */ + astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_FRAMERATE] = + g_param_spec_int("framerate", "framerate", "framerate", 1, G_MAXINT, 60, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + /** + * AstalCavaCava:channels: + * + * how many input channels to consider + */ + astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_CHANNELS] = g_param_spec_int( + "channels", "channels", "channels", 1, 2, 2, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + /** + * AstalCavaCava:low-cutoff: + * + * cut off frequencies below this value + */ + astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_LOW_CUTOFF] = + g_param_spec_int("low-cutoff", "low-cutoff", "lower frequency cutoff", 1, G_MAXINT, 50, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + /** + * AstalCavaCava:high-cutoff: + * + * cut off frequencies above this value + */ + astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_HIGH_CUTOFF] = + g_param_spec_int("high-cutoff", "high-cutoff", "higher frequency cutoff", 1, G_MAXINT, + 10000, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + /** + * AstalCavaCava:samplerate: + * + * the samplerate of the input + */ + astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_SAMPLERATE] = + g_param_spec_int("samplerate", "samplerate", "samplerate", 1, G_MAXINT, 44100, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + /** + * AstalCavaCava:input: (type AstalCavaInput) + * + * specifies which audio server should be used. + */ + astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_INPUT] = + g_param_spec_enum("input", "input", "input", ASTAL_CAVA_TYPE_INPUT, + ASTAL_CAVA_INPUT_PIPEWIRE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + /** + * AstalCavaCava:source: + * + * specifies which audio source should be used. Refer to the cava docs on how to use this + * property. + */ + astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_SOURCE] = g_param_spec_string( + "source", "source", "source", "auto", G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + g_object_class_install_properties(object_class, ASTAL_CAVA_CAVA_N_PROPERTIES, + astal_cava_cava_properties); +} diff --git a/lib/cava/meson.build b/lib/cava/meson.build new file mode 100644 index 0000000..874eaf7 --- /dev/null +++ b/lib/cava/meson.build @@ -0,0 +1,80 @@ +project( + 'astal-cava', + 'c', + version: run_command('cat', join_paths(meson.project_source_root(), 'version')).stdout().strip(), + default_options: ['c_std=gnu11', 'warning_level=3', 'prefix=/usr'], +) + +add_project_arguments(['-Wno-pedantic', '-Wno-unused-parameter'], language: 'c') + +version_split = meson.project_version().split('.') +lib_so_version = version_split[0] + '.' + version_split[1] + +pkg_config = import('pkgconfig') +gnome = import('gnome') + +srcs = files( + 'astal-cava.h', + 'cava.c', +) + +install_headers('astal-cava.h') + +cava = dependency( + 'cava', + version: '>=0.10.3', + required: true, + fallback: ['cava', 'cava_dep'], +) + +deps = [ + dependency('gobject-2.0'), + dependency('gio-2.0'), + cava, +] + +astal_cava_lib = library( + 'astal-cava', + sources: srcs, + dependencies: deps, + version: meson.project_version(), + install: true, +) + +libastal_cava = declare_dependency(link_with: astal_cava_lib) + +pkg_config_name = 'astal-cava-' + lib_so_version + +if get_option('introspection') + gir = gnome.generate_gir( + astal_cava_lib, + sources: srcs, + nsversion: '0.1', + namespace: 'AstalCava', + symbol_prefix: 'astal_cava', + identifier_prefix: 'AstalCava', + includes: ['GObject-2.0', 'Gio-2.0'], + header: 'astal-cava.h', + export_packages: pkg_config_name, + install: true, + ) + + if get_option('vapi') + gnome.generate_vapi( + pkg_config_name, + sources: [gir[0]], + packages: ['gobject-2.0', 'gio-2.0'], + install: true, + ) + endif +endif + +pkg_config.generate( + name: 'astal-cava', + version: meson.project_version(), + libraries: [astal_cava_lib], + filebase: pkg_config_name, + subdirs: 'astal', + description: 'audio analyzing service using cava', + url: 'https://github.com/Aylur/astal', +) diff --git a/lib/cava/meson_options.txt b/lib/cava/meson_options.txt new file mode 100644 index 0000000..97aa4e7 --- /dev/null +++ b/lib/cava/meson_options.txt @@ -0,0 +1,13 @@ +option( + 'introspection', + type: 'boolean', + value: true, + description: 'Build gobject-introspection data', +) + +option( + 'vapi', + type: 'boolean', + value: true, + description: 'Generate vapi data (needs vapigen & introspection option)', +) diff --git a/lib/cava/subprojects/cava.wrap b/lib/cava/subprojects/cava.wrap new file mode 100644 index 0000000..f0309bf --- /dev/null +++ b/lib/cava/subprojects/cava.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = cava-0.10.3 +source_url = https://github.com/LukashonakV/cava/archive/0.10.3.tar.gz +source_filename = cava-0.10.3.tar.gz +source_hash = aab0a4ed3f999e8461ad9de63ef8a77f28b6b2011f7dd0c69ba81819d442f6f9 +[provide] +cava = cava_dep diff --git a/lib/cava/version b/lib/cava/version new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/lib/cava/version @@ -0,0 +1 @@ +0.1.0 |