summaryrefslogtreecommitdiff
path: root/lib/cava/cava.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/cava/cava.c')
-rw-r--r--lib/cava/cava.c213
1 files changed, 213 insertions, 0 deletions
diff --git a/lib/cava/cava.c b/lib/cava/cava.c
new file mode 100644
index 0000000..46931f3
--- /dev/null
+++ b/lib/cava/cava.c
@@ -0,0 +1,213 @@
+#include "astal-cava.h"
+#include "cava/common.h"
+#include "cava/config.h"
+#include "glib-object.h"
+#include "glib.h"
+
+#include <gio/gio.h>
+#include <cava/common.h>
+#include <linux/limits.h>
+
+struct _AstalCavaCava {
+ GObject parent_instance;
+
+ gint bars;
+ gchar* config_path;
+
+ 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;
+
+ GThread* input_thread;
+ guint timer_id;
+
+} AstalCavaCavaPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE(AstalCavaCava, astal_cava_cava, G_TYPE_OBJECT)
+
+typedef enum {
+ ASTAL_CAVA_CAVA_PROP_VALUES = 1,
+ ASTAL_CAVA_CAVA_PROP_BARS,
+ ASTAL_CAVA_CAVA_PROP_CONFIG_PATH,
+ ASTAL_CAVA_CAVA_N_PROPERTIES
+} AstalCavaProperties;
+
+static GParamSpec* astal_cava_cava_properties[ASTAL_CAVA_CAVA_N_PROPERTIES] = {
+ NULL,
+};
+
+/**
+ * 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; }
+
+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:
+ self->bars = g_value_get_int(value);
+ break;
+ case ASTAL_CAVA_CAVA_PROP_CONFIG_PATH:
+ self->config_path = g_strdup(g_value_get_string(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_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_CONFIG_PATH:
+ g_value_set_string(value, self->config_path);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ break;
+ }
+}
+
+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_constructed(GObject* object) {
+ AstalCavaCava* self = ASTAL_CAVA_CAVA(object);
+ AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self);
+
+ struct error_s error = {};
+
+ if (!load_config(self->config_path, &priv->cfg, false, &error)) {
+ g_critical("Error loading config. %s", error.message);
+ return;
+ }
+
+ priv->cfg.inAtty = 0;
+ priv->cfg.output = OUTPUT_RAW;
+ priv->cfg.raw_target = strdup("/dev/stdout");
+ priv->cfg.data_format = strdup("binary");
+
+ if(self->bars > 0) priv->cfg.fixedbars = self->bars;
+ else self->bars = priv->cfg.fixedbars;
+
+ 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);
+
+ self->values = g_array_sized_new(TRUE, TRUE, sizeof(gdouble), priv->audio_raw.number_of_bars);
+ g_array_set_size(self->values, priv->audio_raw.number_of_bars);
+
+ 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_init(AstalCavaCava* self) {
+ AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self);
+}
+
+/**
+ * astal_cava_cava_get_default
+ *
+ * Returns: (nullable) (transfer none): gets the default Cava object.
+ */
+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);
+ 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);
+}
+
+static void astal_cava_cava_finalize(GObject* object) {
+ AstalCavaCava* self = ASTAL_CAVA_CAVA(object);
+ AstalCavaCavaPrivate* priv = astal_cava_cava_get_instance_private(self);
+
+ cava_destroy(&priv->plan);
+ 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;
+
+ /**
+ * AstalCava:values: (type GList(gdouble))
+ *
+ * A list of values
+ */
+ astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_VALUES] =
+ g_param_spec_pointer("values", "values", "a list of values", G_PARAM_READABLE);
+ astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_BARS] =
+ g_param_spec_int("bars", "bars", "number of bars per channel", 0, G_MAXINT, 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_CONFIG_PATH] =
+ g_param_spec_string("config-path", "config-path", "config-path", "", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_properties(object_class, ASTAL_CAVA_CAVA_N_PROPERTIES,
+ astal_cava_cava_properties);
+}