diff options
-rw-r--r-- | docs/default.nix | 7 | ||||
-rw-r--r-- | docs/guide/libraries/apps.md | 25 | ||||
-rw-r--r-- | docs/guide/libraries/cava.md | 91 | ||||
-rw-r--r-- | docs/vitepress.config.ts | 1 | ||||
-rw-r--r-- | flake.nix | 4 | ||||
-rw-r--r-- | lib/apps/application.vala | 52 | ||||
-rw-r--r-- | lib/apps/apps.vala | 58 | ||||
-rw-r--r-- | lib/astal/io/cli.vala | 4 | ||||
-rw-r--r-- | lib/auth/src/pam.c | 1 | ||||
-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 | ||||
-rw-r--r-- | lib/mpris/mpris.vala | 10 | ||||
-rw-r--r-- | lib/mpris/player.vala | 72 | ||||
-rw-r--r-- | lib/notifd/notification.vala | 12 | ||||
-rw-r--r-- | lib/notifd/proxy.vala | 4 | ||||
-rw-r--r-- | lib/notifd/signals.md | 2 | ||||
-rw-r--r-- | nix/libcava.nix | 60 |
22 files changed, 1118 insertions, 116 deletions
diff --git a/docs/default.nix b/docs/default.nix index 0d79ce1..1370fc6 100644 --- a/docs/default.nix +++ b/docs/default.nix @@ -154,6 +154,13 @@ in version = ../lib/bluetooth/version; }} ${genLib { + flakepkg = "cava"; + gir = "Cava"; + description = "Audio visualization library using cava"; + version = ../lib/cava/version; + authors = "kotontrion"; + }} + ${genLib { flakepkg = "hyprland"; gir = "Hyprland"; description = "IPC client for Hyprland"; diff --git a/docs/guide/libraries/apps.md b/docs/guide/libraries/apps.md index 7349951..1871d18 100644 --- a/docs/guide/libraries/apps.md +++ b/docs/guide/libraries/apps.md @@ -55,8 +55,9 @@ astal-apps --help import Apps from "gi://AstalApps" const apps = new Apps.Apps({ - includeEntry: true, - includeExecutable: true, + nameMultiplier: 2, + entryMultiplier: 0, + executableMultiplier: 2, }) for (const app of apps.fuzzy_query("spotify")) { @@ -68,8 +69,9 @@ for (const app of apps.fuzzy_query("spotify")) { from gi.repository import AstalApps as Apps apps = Apps.Apps( - include_entry=True, - include_executable=True, + name_multiplier=2, + entry_multiplier=0, + executable_multiplier=2, ) for app in apps.fuzzy_query("obsidian"): @@ -81,8 +83,9 @@ for app in apps.fuzzy_query("obsidian"): local Apps = require("lgi").require("AstalApps") local apps = Apps.Apps({ - include_entry = true, - include_executable = true, + name_multiplier = 2, + entry_multiplier = 0, + executable_multiplier = 2, }) for _, app in ipairs(apps:fuzzy_query("lutris")) do @@ -91,7 +94,15 @@ end ``` ```vala [<i class="devicon-vala-plain"></i> Vala] -// Not yet documented, contributions are appreciated +var apps = new AstalApps.Apps() { + name_multiplier = 2, + entry_multiplier = 0, + executable_multiplier = 2, +}; + +foreach (var app in apps.fuzzy_query("firefox")) { + print(app.name); +} ``` ::: diff --git a/docs/guide/libraries/cava.md b/docs/guide/libraries/cava.md new file mode 100644 index 0000000..e695e16 --- /dev/null +++ b/docs/guide/libraries/cava.md @@ -0,0 +1,91 @@ +# Cava + +Audio visualizer using [cava](https://github.com/karlstav/cava). + +## Installation + +1. install dependencies + +Note that it requires [libcava](https://github.com/LukashonakV/cava), a fork of cava, which provides cava as a shared library. + +:::code-group + +```sh [<i class="devicon-archlinux-plain"></i> Arch] +sudo pacman -Syu meson vala gobject-introspection +paru -S libcava +``` + +```sh [<i class="devicon-fedora-plain"></i> Fedora] +# Not yet documented +``` + +```sh [<i class="devicon-ubuntu-plain"></i> Ubuntu] +# Not yet documented +``` + +::: + +2. clone repo + +```sh +git clone https://github.com/aylur/astal.git +cd astal/lib/cava +``` + +3. install + +```sh +meson setup build +meson install -C build +``` + +:::tip +Most distros recommend manual installs in `/usr/local`, +which is what `meson` defaults to. If you want to install to `/usr` +instead which most package managers do, set the `prefix` option: + +```sh +meson setup --prefix /usr build +``` + +::: + +## Usage + +You can browse the [Cava reference](https://aylur.github.io/libastal/cava). + +### CLI + +There is no CLI for this library, use the one provided by cava. + +```sh +cava +``` + +### Library + +:::code-group + +```js [<i class="devicon-javascript-plain"></i> JavaScript] +import Cava from "gi://AstalCava" + +const cava = Cava.get_default() + +cava.connect("notify::values", () => { + print(cava.get_values()) +}) +``` + +```py [<i class="devicon-python-plain"></i> Python] +# Not yet documented +``` + +```lua [<i class="devicon-lua-plain"></i> Lua] +-- Not yet documented +``` + +```vala [<i class="devicon-vala-plain"></i> Vala] +// Not yet documented +``` + +::: diff --git a/docs/vitepress.config.ts b/docs/vitepress.config.ts index 2df3eea..f542a68 100644 --- a/docs/vitepress.config.ts +++ b/docs/vitepress.config.ts @@ -104,6 +104,7 @@ export default defineConfig({ { text: "Auth", link: "/guide/libraries/auth" }, { text: "Battery", link: "/guide/libraries/battery" }, { text: "Bluetooth", link: "/guide/libraries/bluetooth" }, + { text: "Cava", link: "/guide/libraries/cava" }, { text: "Hyprland", link: "/guide/libraries/hyprland" }, { text: "Mpris", link: "/guide/libraries/mpris" }, { text: "Network", link: "/guide/libraries/network" }, @@ -53,6 +53,7 @@ auth = mkPkg "astal-auth" ./lib/auth [pam]; battery = mkPkg "astal-battery" ./lib/battery [json-glib]; bluetooth = mkPkg "astal-bluetooth" ./lib/bluetooth []; + cava = mkPkg "astal-cava" ./lib/cava [(pkgs.callPackage ./nix/libcava.nix {})]; hyprland = mkPkg "astal-hyprland" ./lib/hyprland [json-glib]; mpris = mkPkg "astal-mpris" ./lib/mpris [gvfs json-glib]; network = mkPkg "astal-network" ./lib/network [networkmanager]; @@ -61,12 +62,11 @@ river = mkPkg "astal-river" ./lib/river [json-glib]; tray = mkPkg "astal-tray" ./lib/tray [gtk3 gdk-pixbuf libdbusmenu-gtk3 json-glib]; wireplumber = mkPkg "astal-wireplumber" ./lib/wireplumber [wireplumber]; - # polkit = mkPkg "astal-polkit" ./lib/polkit [polkit]; gjs = pkgs.stdenvNoCC.mkDerivation { src = ./lang/gjs; name = "astal-gjs"; - buildInputs = [ + nativeBuildInputs = [ meson ninja pkg-config diff --git a/lib/apps/application.vala b/lib/apps/application.vala index 0a2f73c..3a9900a 100644 --- a/lib/apps/application.vala +++ b/lib/apps/application.vala @@ -1,3 +1,6 @@ +/** + * Object representing an applications .desktop file. + */ public class AstalApps.Application : Object { /** * The underlying DesktopAppInfo. @@ -47,6 +50,20 @@ public class AstalApps.Application : Object { */ public string[] keywords { owned get { return app.get_keywords(); } } + /** + * `Categories` field from the desktop file. + */ + public string[] categories { + owned get { + if (app.get_categories() == null) + return {}; + + var categories = app.get_categories(); + var arr = categories.split(";"); + return categories.has_suffix(";") ? arr[0:arr.length-1] : arr; + } + } + internal Application(string id, int? frequency = 0) { Object(app: new DesktopAppInfo(id)); this.frequency = frequency; @@ -94,6 +111,12 @@ public class AstalApps.Application : Object { score.keywords = s; } } + foreach (var category in categories) { + var s = fuzzy_match_string(term, category); + if (s > score.categories) { + score.categories = s; + } + } return score; } @@ -117,12 +140,27 @@ public class AstalApps.Application : Object { score.keywords = keyword.down().contains(term.down()) ? 1 : 0; } } + foreach (var category in categories) { + if (score.categories == 0) { + score.categories = category.down().contains(term.down()) ? 1 : 0; + } + } return score; } internal Json.Node to_json() { - var builder = new Json.Builder() + var keyword_arr = new Json.Builder().begin_array(); + foreach (string keyword in keywords) { + keyword_arr.add_string_value(keyword); + } + + var category_arr = new Json.Builder().begin_array(); + foreach (string category in categories) { + category_arr.add_string_value(category); + } + + return new Json.Builder() .begin_object() .set_member_name("name").add_string_value(name) .set_member_name("entry").add_string_value(entry) @@ -130,15 +168,8 @@ public class AstalApps.Application : Object { .set_member_name("description").add_string_value(description) .set_member_name("icon_name").add_string_value(icon_name) .set_member_name("frequency").add_int_value(frequency) - .set_member_name("keywords") - .begin_array(); - - foreach (string keyword in keywords) { - builder.add_string_value(keyword); - } - - return builder - .end_array() + .set_member_name("keywords").add_value(keyword_arr.end_array().get_root()) + .set_member_name("categories").add_value(category_arr.end_array().get_root()) .end_object() .get_root(); } @@ -150,4 +181,5 @@ public struct AstalApps.Score { int executable; int description; int keywords; + int categories; } diff --git a/lib/apps/apps.vala b/lib/apps/apps.vala index dde7d44..999643c 100644 --- a/lib/apps/apps.vala +++ b/lib/apps/apps.vala @@ -1,3 +1,8 @@ +/** + * This object can be used to query applications. + * Multipliers can be set to customize [[email protected]] results + * from queries which then are summed and sorted accordingly. + */ public class AstalApps.Apps : Object { private string cache_directory; private string cache_file; @@ -27,21 +32,21 @@ public class AstalApps.Apps : Object { /** * Extra multiplier to apply when matching the entry of an application. - * Defaults to `1` + * Defaults to `0` */ - public double entry_multiplier { get; set; default = 1; } + public double entry_multiplier { get; set; default = 0; } /** * Extra multiplier to apply when matching the executable of an application. - * Defaults to `1` + * Defaults to `0.5` */ - public double executable_multiplier { get; set; default = 1; } + public double executable_multiplier { get; set; default = 0.5; } /** * Extra multiplier to apply when matching the description of an application. - * Defaults to `0.5` + * Defaults to `0` */ - public double description_multiplier { get; set; default = 0.5; } + public double description_multiplier { get; set; default = 0; } /** * Extra multiplier to apply when matching the keywords of an application. @@ -50,34 +55,10 @@ public class AstalApps.Apps : Object { public double keywords_multiplier { get; set; default = 0.5; } /** - * Consider the name of an application during queries. - * Defaults to `true` - */ - public bool include_name { get; set; default = true; } - - /** - * Consider the entry of an application during queries. - * Defaults to `false` - */ - public bool include_entry { get; set; default = false; } - - /** - * Consider the executable of an application during queries. - * Defaults to `false` - */ - public bool include_executable { get; set; default = false; } - - /** - * Consider the description of an application during queries. - * Defaults to `false` - */ - public bool include_description { get; set; default = false; } - - /** - * Consider the keywords of an application during queries. - * Defaults to `false` + * Extra multiplier to apply when matching the categories of an application. + * Defaults to `0` */ - public bool include_keywords { get; set; default = false; } + public double categories_multiplier { get; set; default = 0; } construct { cache_directory = Environment.get_user_cache_dir() + "/astal"; @@ -115,11 +96,12 @@ public class AstalApps.Apps : Object { if (alg == FUZZY) s = a.fuzzy_match(search); if (alg == EXACT) s = a.exact_match(search); - if (include_name) r += s.name * name_multiplier; - if (include_entry) r += s.entry * entry_multiplier; - if (include_executable) r += s.executable * executable_multiplier; - if (include_description) r += s.description * description_multiplier; - if (include_keywords) r += s.keywords * keywords_multiplier; + r += s.name * name_multiplier; + r += s.entry * entry_multiplier; + r += s.executable * executable_multiplier; + r += s.description * description_multiplier; + r += s.keywords * keywords_multiplier; + r += s.categories * categories_multiplier; return r; } diff --git a/lib/astal/io/cli.vala b/lib/astal/io/cli.vala index f69cf0b..9e47b53 100644 --- a/lib/astal/io/cli.vala +++ b/lib/astal/io/cli.vala @@ -81,7 +81,7 @@ int main(string[] argv) { return 0; } } catch (DBusError.SERVICE_UNKNOWN e) { - return err(@"there is no \"$instance_name\" instance runnning"); + return err(@"there is no \"$instance_name\" instance runnning\n"); } catch (Error e) { return err(e.message); } @@ -95,7 +95,7 @@ int main(string[] argv) { var reply = AstalIO.send_message(instance_name, request); print("%s\n", reply); } catch (IOError.NOT_FOUND e) { - return err(@"there is no \"$instance_name\" instance runnning"); + return err(@"there is no \"$instance_name\" instance runnning\n"); } catch (Error e) { return err(e.message); } diff --git a/lib/auth/src/pam.c b/lib/auth/src/pam.c index d0afec4..f50107e 100644 --- a/lib/auth/src/pam.c +++ b/lib/auth/src/pam.c @@ -58,7 +58,6 @@ static GParamSpec *astal_auth_pam_properties[ASTAL_AUTH_PAM_N_PROPERTIES] = { G_DEFINE_TYPE_WITH_PRIVATE(AstalAuthPam, astal_auth_pam, G_TYPE_OBJECT); /** - * * AstalAuthPam * * For simple authentication using only a password, using the [[email protected]] 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 diff --git a/lib/mpris/mpris.vala b/lib/mpris/mpris.vala index 8eaffa5..8039d39 100644 --- a/lib/mpris/mpris.vala +++ b/lib/mpris/mpris.vala @@ -73,12 +73,10 @@ public class AstalMpris.Mpris : Object { var p = new Player(busname); _players.set(busname, p); - p.notify["available"].connect(() => { - if (!p.available) { - player_closed(p); - _players.remove(busname); - notify_property("players"); - } + p.closed.connect(() => { + player_closed(p); + _players.remove(busname); + notify_property("players"); }); player_added(p); diff --git a/lib/mpris/player.vala b/lib/mpris/player.vala index b72c15e..2050f61 100644 --- a/lib/mpris/player.vala +++ b/lib/mpris/player.vala @@ -9,7 +9,12 @@ public class AstalMpris.Player : Object { private IPlayer proxy; private uint pollid; // periodically notify position - // identifiers + internal signal void appeared() { available = true; } + internal signal void closed() { available = false; } + + /** + * Full dbus namae of this player. + */ public string bus_name { owned get; private set; } /** @@ -37,7 +42,7 @@ public class AstalMpris.Player : Object { * The media player may refuse to allow clients to shut it down. * In this case, the [[email protected]:can_quit] property is false and this method does nothing. */ - public void quit() throws Error { + public void quit() { try { proxy.quit(); } catch (Error err) { critical(err.message); } } @@ -559,15 +564,33 @@ public class AstalMpris.Player : Object { } construct { - try { - try_proxy(); - sync(); - } catch (Error error) { - critical(error.message); - } + notify["bus-name"].connect(() => { + try { + setup_proxy(); + setup_position_poll(); + sync(); + } catch (Error error) { + critical(error.message); + } + }); } - private void try_proxy() throws Error { + private void setup_position_poll() { + var current_position = position; + + pollid = Timeout.add_seconds(1, () => { + if (!available) + return Source.CONTINUE; + + if (position >= 0 && current_position != position) { + current_position = position; + notify_property("position"); + } + return Source.CONTINUE; + }, Priority.DEFAULT); + } + + private void setup_proxy() throws Error { if (proxy != null) return; @@ -577,28 +600,19 @@ public class AstalMpris.Player : Object { "/org/mpris/MediaPlayer2" ); - if (proxy.g_name_owner != null) - available = false; + if (proxy.g_name_owner != null) { + appeared(); + } proxy.notify["g-name-owner"].connect(() => { if (proxy.g_name_owner != null) { - available = true; + appeared(); } else { - available = false; + closed(); } }); proxy.g_properties_changed.connect(sync); - - pollid = Timeout.add_seconds(1, () => { - if (!available) - return Source.CONTINUE; - - if (position >= 0) { - notify_property("position"); - } - return Source.CONTINUE; - }, Priority.DEFAULT); } ~Player() { @@ -622,18 +636,6 @@ public enum AstalMpris.PlaybackStatus { return STOPPED; } } - - internal string to_string() { - switch (this) { - case PLAYING: - return "Playing"; - case PAUSED: - return "Paused"; - case STOPPED: - default: - return "Stopped"; - } - } } public enum AstalMpris.Loop { diff --git a/lib/notifd/notification.vala b/lib/notifd/notification.vala index 527a352..29c6c56 100644 --- a/lib/notifd/notification.vala +++ b/lib/notifd/notification.vala @@ -158,12 +158,6 @@ public class AstalNotifd.Notification : Object { */ public signal void resolved(ClosedReason reason); - /** - * Emitted when the user dismisses this notification. - * - * @see dismiss - */ - public signal void dismissed(); /** * Emitted when an [[email protected]] of this notification is invoked. @@ -173,12 +167,10 @@ public class AstalNotifd.Notification : Object { public signal void invoked(string action_id); /** - * Dismiss this notification popup - * - * This method doesn't have any functionality on its own, but should be handled - * by frontend implementation to hide notification popups. + * Resolve this notification with [[email protected]_BY_USER]. */ public void dismiss() { dismissed(); } + internal signal void dismissed(); /** * Invoke an [[email protected]] of this notification. diff --git a/lib/notifd/proxy.vala b/lib/notifd/proxy.vala index bedb8b9..95e7105 100644 --- a/lib/notifd/proxy.vala +++ b/lib/notifd/proxy.vala @@ -32,10 +32,6 @@ internal class AstalNotifd.DaemonProxy : Object { set { proxy.dont_disturb = value; } } - public uint[] notification_ids() throws DBusError, IOError { - return proxy.notification_ids(); - } - public Notification get_notification(uint id) { return notifs.get(id); } diff --git a/lib/notifd/signals.md b/lib/notifd/signals.md index cdc6688..e13b92c 100644 --- a/lib/notifd/signals.md +++ b/lib/notifd/signals.md @@ -5,7 +5,7 @@ ignore this, I'm just dumb and can't follow where signals go or get emitted from ## Notification * resolved(reason) - by daemon/proxy -* dismissed() - by user with `.dismiss()` +* dismissed() - by user with `.dismiss()`, used to emit resolved from proxy/daemon * invoked(action) - by user with `.invoke()` ## Deamon diff --git a/nix/libcava.nix b/nix/libcava.nix new file mode 100644 index 0000000..866599d --- /dev/null +++ b/nix/libcava.nix @@ -0,0 +1,60 @@ +{ + stdenv, + fetchFromGitHub, + autoreconfHook, + autoconf-archive, + alsa-lib, + fftw, + iniparser, + libpulseaudio, + portaudio, + sndio, + SDL2, + libGL, + pipewire, + jack2, + ncurses, + pkgconf, + meson, + ninja, +}: +stdenv.mkDerivation rec { + pname = "cava"; + version = "0.10.3"; + + src = fetchFromGitHub { + owner = "LukashonakV"; + repo = "cava"; + rev = "0.10.3"; + hash = "sha256-ZDFbI69ECsUTjbhlw2kHRufZbQMu+FQSMmncCJ5pagg="; + }; + + buildInputs = [ + alsa-lib + libpulseaudio + ncurses + iniparser + sndio + SDL2 + libGL + portaudio + jack2 + pipewire + ]; + + propagatedBuildInputs = [ + fftw + ]; + + nativeBuildInputs = [ + autoreconfHook + autoconf-archive + pkgconf + meson + ninja + ]; + + preAutoreconf = '' + echo ${version} > version + ''; +} |