You are not logged in.
While watching the changes in alsamixer, it occured to me that there needs to be a way to un-mute channels in AlsaTune. If a channel is muted, the slider won't do anything and there's no way to know that without opening alsamixer. (or did I miss something?)
You did. Right-click any one of the sliders themselves in AlsaTune.
You know it's muted if the slider turns red/black striped. Check the screenie on page 3 towards the bottom.
I mentioned this in my last post, in the 2.1 version muting/unmuting is enabled via right-click on the sliders themselves.
But I never made it clear in the gui itself that you could do that, my bad, so I added a tooltip just for the sliders that have a mute function in version 3 that i'm working on, that let's you know you can right-click to mute.
Really appreciate you testing!!! It's hard with just a couple machines and those all being old,low spec, and basically all the same lol. I'm sure there's more tweaks to be done in the future. But after a couple long days working the new version i'm really happy with the progress.
Got it all essentially done, I added one more feature after adding the new button/script, I added a tooltip that only appears when you hover over volume sliders that have a mute function, to let you know that you can right-click to mute/unmute, it's a small thing but there was no clear instruction about that in the main gui.
So for the new script, it would be so amazing if some of you smart folks could take a minute and test function. It's a complete standalone script so you don't need AlsaTune to run it, just compile it and run. I'm especially interested in any new toggles it might miss, and whether it gives a proper description on common toggles or falls back to the default because of a new name.
You will need gcc of course and all that it pulls in, and you will also need the libgtk3-dev and libasound2-dev packages.
Compile:
gcc alsatoggle.c -o alsatoggle $(pkg-config --cflags --libs gtk+-3.0) -lasoundalsatoggle.c
#include <gtk/gtk.h>
#include <alsa/asoundlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
typedef struct {
snd_mixer_elem_t *elem;
} ToggleChannel;
typedef struct {
snd_mixer_t *mixer;
ToggleChannel *channels;
int num_channels;
GtkListStore *store;
} ToggleData;
// Normalize name for matching (lowercase, spaces/dashes removed)
static void normalize_name(char *dest, const char *src, size_t dest_size) {
size_t i = 0;
while (*src && i < dest_size - 1) {
char c = tolower(*src);
if (c != ' ' && c != '-') {
dest[i++] = c;
}
src++;
}
dest[i] = '\0';
}
// Get user-friendly description, this section will need fine-tuning and additions
static const char* get_description(const char *alsa_name) {
char normalized[128];
normalize_name(normalized, alsa_name, sizeof(normalized));
if (strstr(normalized, "automute") ||
strstr(normalized, "automutemode") ||
strstr(normalized, "automode")) {
return "Automatically mutes internal speakers when headphones are plugged in.";
}
else if (strstr(normalized, "loopback")) {
return "Routes audio output back to input (for recording what you hear).";
}
else if (strstr(normalized, "independenthp") ||
strstr(normalized, "independentheadphone")) {
return "Allows independent audio streams for headphones and speakers (no auto-muting of speakers).";
}
else if (strstr(normalized, "beep")) {
return "Controls the PC speaker beep tone (system alerts). Usually best left disabled to avoid noise.";
}
else if (strstr(normalized, "phantom") ||
strstr(normalized, "48v")) {
return "Provides 48V phantom power for condenser microphones.";
}
return "Toggles this audio control on or off.";
}
// Callback when a toggle is flipped
static void on_toggle_changed(GtkCellRendererToggle *renderer, gchar *path_str, ToggleData *data) {
GtkTreeIter iter;
if (!gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(data->store), &iter, path_str))
return;
gboolean active;
gtk_tree_model_get(GTK_TREE_MODEL(data->store), &iter, 0, &active, -1);
active = !active; // Flip it
int row = atoi(path_str);
snd_mixer_elem_t *elem = data->channels[row].elem;
// Apply to ALSA
if (snd_mixer_selem_has_playback_switch(elem)) {
snd_mixer_selem_set_playback_switch_all(elem, active);
}
if (snd_mixer_selem_has_capture_switch(elem)) {
snd_mixer_selem_set_capture_switch_all(elem, active);
}
if (snd_mixer_selem_is_enumerated(elem)) {
unsigned int item = active ? 1 : 0; // Most are 0=Disabled, 1=Enabled
snd_mixer_selem_set_enum_item(elem, 0, item);
}
// Update the store/UI
gtk_list_store_set(data->store, &iter, 0, active, -1);
}
static gboolean on_row_click(GtkWidget *treeview, GdkEventButton *event, ToggleData *data) {
if (event->button == 3) { // Right-click toggle
GtkTreePath *path;
if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), event->x, event->y, &path, NULL, NULL, NULL)) {
GtkTreeIter iter;
if (gtk_tree_model_get_iter(GTK_TREE_MODEL(data->store), &iter, path)) {
gboolean active;
gtk_tree_model_get(GTK_TREE_MODEL(data->store), &iter, 0, &active, -1);
active = !active;
gtk_list_store_set(data->store, &iter, 0, active, -1);
int row = gtk_tree_path_get_indices(path)[0];
snd_mixer_elem_t *elem = data->channels[row].elem;
// Same apply code as on_toggle_changed
if (snd_mixer_selem_has_playback_switch(elem)) {
snd_mixer_selem_set_playback_switch_all(elem, active);
}
if (snd_mixer_selem_has_capture_switch(elem)) {
snd_mixer_selem_set_capture_switch_all(elem, active);
}
if (snd_mixer_selem_is_enumerated(elem)) {
unsigned int item = active ? 1 : 0;
snd_mixer_selem_set_enum_item(elem, 0, item);
}
}
gtk_tree_path_free(path);
return TRUE; // Handled
}
}
return FALSE; // Pass left-click through
}
// Initialize ALSA and find toggles
static void init_alsa_toggles(ToggleData *data) {
data->mixer = NULL;
data->channels = NULL;
data->num_channels = 0;
if (snd_mixer_open(&data->mixer, 0) < 0) return;
if (snd_mixer_attach(data->mixer, "default") < 0 ||
snd_mixer_selem_register(data->mixer, NULL, NULL) < 0 ||
snd_mixer_load(data->mixer) < 0) {
snd_mixer_close(data->mixer);
return;
}
ToggleChannel temp[64];
int count = 0;
snd_mixer_elem_t *elem;
for (elem = snd_mixer_first_elem(data->mixer); elem; elem = snd_mixer_elem_next(elem)) {
if (!snd_mixer_selem_is_active(elem)) continue;
gboolean has_volume = snd_mixer_selem_has_playback_volume(elem) ||
snd_mixer_selem_has_capture_volume(elem);
gboolean has_switch = snd_mixer_selem_has_playback_switch(elem) ||
snd_mixer_selem_has_capture_switch(elem);
gboolean is_enum = snd_mixer_selem_is_enumerated(elem);
// Pure toggle: either a switch without volume, or an enumerated control
if ((has_switch && !has_volume) || is_enum) {
if (count >= 64) break;
temp[count].elem = elem;
count++;
}
}
if (count > 0) {
data->num_channels = count;
data->channels = g_new(ToggleChannel, count);
memcpy(data->channels, temp, count * sizeof(ToggleChannel));
}
}
static void cleanup_alsa(ToggleData *data) {
if (data->mixer) snd_mixer_close(data->mixer);
g_free(data->channels);
}
int main(int argc, char *argv[]) {
gtk_init(&argc, &argv);
gtk_window_set_default_icon_name("audio-speakers");
ToggleData data = {0};
init_alsa_toggles(&data);
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "AlsaTune - Toggle Functions");
gtk_window_set_default_size(GTK_WINDOW(window), 660, 400);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
// Some styling to match main app
GtkCssProvider *css = gtk_css_provider_new();
gtk_css_provider_load_from_data(css,
"box { border: 2px solid #333333; border-radius: 4px; }"
"button { border: 1px solid #333333; border-radius: 4px; }",
-1, NULL);
gtk_style_context_add_provider_for_screen(gdk_screen_get_default(),
GTK_STYLE_PROVIDER(css),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
gtk_container_add(GTK_CONTAINER(window), vbox);
GtkWidget *scrolled = gtk_scrolled_window_new(NULL, NULL);
gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 0);
if (data.num_channels == 0) {
GtkWidget *label = gtk_label_new("No toggle controls detected on this system.");
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
gtk_widget_set_margin_top(label, 40);
gtk_container_add(GTK_CONTAINER(scrolled), label);
} else {
GtkWidget *treeview = gtk_tree_view_new();
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
gtk_tree_selection_set_mode(selection, GTK_SELECTION_NONE);
gtk_container_add(GTK_CONTAINER(scrolled), treeview);
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
// Column 1: Toggle switch
GtkCellRenderer *rend_toggle = gtk_cell_renderer_toggle_new();
GtkTreeViewColumn *col_toggle = gtk_tree_view_column_new_with_attributes("", rend_toggle, "active", 0, NULL);
gtk_tree_view_column_set_fixed_width(col_toggle, 80);
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col_toggle);
// Column 2: Name
GtkCellRenderer *rend_name = gtk_cell_renderer_text_new();
g_object_set(rend_name, "weight", PANGO_WEIGHT_BOLD, "xpad", 10, NULL);
GtkTreeViewColumn *col_name = gtk_tree_view_column_new_with_attributes("Control", rend_name, "text", 1, NULL);
gtk_tree_view_column_set_min_width(col_name, 200);
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col_name);
// Column 3: Description (wraps and expands)
GtkCellRenderer *rend_desc = gtk_cell_renderer_text_new();
g_object_set(rend_desc, "wrap-mode", PANGO_WRAP_WORD, "wrap-width", 330, NULL);
GtkTreeViewColumn *col_desc = gtk_tree_view_column_new_with_attributes("Description", rend_desc, "text", 2, NULL);
gtk_tree_view_column_set_expand(col_desc, TRUE);
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col_desc);
// ListStore: active (bool), name (string), description (string)
data.store = gtk_list_store_new(3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING);
gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(data.store));
for (int i = 0; i < data.num_channels; i++) {
snd_mixer_elem_t *elem = data.channels[i].elem;
const char *name = snd_mixer_selem_get_name(elem);
const char *desc = get_description(name);
// Determine current state
gboolean active = FALSE;
if (snd_mixer_selem_has_playback_switch(elem)) {
int val;
snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &val);
active = val;
} else if (snd_mixer_selem_is_enumerated(elem)) {
unsigned int item;
snd_mixer_selem_get_enum_item(elem, 0, &item);
active = (item == 1); // Adjust if any are reversed
}
GtkTreeIter iter;
gtk_list_store_append(data.store, &iter);
gtk_list_store_set(data.store, &iter,
0, active,
1, name,
2, desc,
-1);
}
g_signal_connect(rend_toggle, "toggled", G_CALLBACK(on_toggle_changed), &data);
g_signal_connect(treeview, "button-press-event", G_CALLBACK(on_row_click), &data);
}
// Big goofy close button at bottom
GtkWidget *close_btn = gtk_button_new_with_label("To enable a function, check the box beside it or hover over its row and right-click."
"\nUncheck or click again to disable. Left-click here to exit this window when done.");
gtk_widget_set_tooltip_text(close_btn,
"Click this button (or the window's X) to close when you're done. "
"All changes are applied instantly and saved on the backend, no further action needed.");
gtk_widget_set_halign(close_btn, GTK_ALIGN_CENTER);
gtk_widget_set_margin_top(close_btn, 10);
gtk_widget_set_margin_bottom(close_btn, 10);
g_signal_connect_swapped(close_btn, "clicked", G_CALLBACK(gtk_widget_destroy), window);
gtk_box_pack_start(GTK_BOX(vbox), close_btn, FALSE, FALSE, 0);
gtk_widget_show_all(window);
gtk_main();
cleanup_alsa(&data);
return 0;
}Getting there....should have a version 3 of AlsaTune soon!

This is a separate script that will be called from a button on the regular AlsaTune gui and pops up it's own window. There just wasn't space on the main gui to do everything and this works a lot better, more modular so it's easier to work on and very simple. It works basically the same way as AlsaTune, polls the backend for functions that are pure on/off toggles, checkbox to indicate status, name, description. Easy and simple even for a novice to work with. All changes are instantaneously applied in real-time, you can open the alsamixer backend and watch it change while you click.
Im curious about this, ive always used below code to disable DPMS and prevent screen from blanking, what is yours doing differently?
Overkill, two of the commands are the same as yours, the first command in mine takes the extra step of turning all the values to nil before turning it off completely. (values = blank,suspend,shutdown). In certain systems/situations xset can be persnickety, this insures that if somehow the -dpms command is reversed, it will still not blank etc. because all the values are turned off.
Good analogy is a volume control like in alsamixer, it has a slider for volume but also a mute function that cuts off sound completely.
-dpms is the mute switch, and 0 0 0 is the volume sliders that we're turning all the way down.
Mintstick is what you seek my friend, it's in the repo, it's an awesome utility. Refracta2usb if you want to do more complex things like multi-boot and persistence.
That's awesome! Thanks so much for giving that a try, working on the next upgrade all day yesterday and still not done, but i've figured out how I want to deal with the toggles on the backend, and I have a working prototype now, very simple to use, very hard to make, lol.
Just an observation from what i've learned so far:
I like C, only been messing with it since earlier this year, but it's fairly easy to get the hang of, i'm enjoying it more and more. C is not really what makes things take so long, it's GTK and CSS and doing 50+ cycles of compile/test/mod/repeat just to get one f***ing element to be in the right place and look right.
me: Okay gcc, let's rock this thing, go!
gcc: Sorry bro, you used a deprecated command that's been replaced by a command that does the exact same thing but has another name.
me:
gcc:
me: **** you
gcc: **** you noob, RTFM.
Cool beans! I just now uploaded the new version 2.1-0 to Sourceforge it has the new script and some updated info:
https://sourceforge.net/projects/vuu-do … /AlsaTune/
Give it some of that Refracta magic! ![]()
Crap, lost my build-depends file, been messing with this for months, so if I remember correctly:
libasound2-dev
libgtk-3-dev
libglib2.0-dev
Compile:
gcc -o mxeq mxeq.c $(pkg-config --cflags --libs gtk+-3.0 alsa glib-2.0) -WallAny chance this could be made as an architecture-independent package? I just tried to install it on i386 system and it refused.
Well I tagged it in the control file as amd64 because that's what I was building on and I don't have have a 32 bit machine or system installed to test it, but i'm pretty sure you'd have to re-compile the main script using the 32 bit libraries. Different versions should be fairly easy, but making it arch-agnostic would be tough, especially for me.
But it's worth a try to just re-compile the binary on your machine using 32 libs, i'd sure be curious, the rest of it is just a dash script and yad so that should work fine.
Let me know if you want to try and i'll send or post the lastest source code for the binary, it's way improved over the last version so you should definitely try it.
Devuan Linux could very well use what #!++ and BunsenLabs have as default: a customised Openbox WM.
To do that would probably mean much less work for the devs at Devuan.
Have to disagree with that a bit, BunsenLabs is actually quite complex and takes a lot of work for them to produce, and for the upcoming release it's mostly XFCE now though the WM is still Openbox.
It takes a lot of work in my experience to craft a 'box system that is user-friendly to garden-variety users. There's a lot of dots to connect to make it comfy.
I still think Mate would be a better choice for Devuan and be easier for the devs to deal with.
Looks way more polished then the big red rectangle, good job. Did you have any luck with the auto detect?
Thanks!! Yeah I did finally work out the auto-detect, first round I way over thought and added a mountain of code but it kinda worked, woke up next day and threw all that out, went for stupid simple and that worked pretty good but it wasn't really the right way to do it. Finally I figured it out and it was so simple and elegant and right under my nose that i'm again kinda embarrassed but I did finally get it...just wish my first instinct in these things was the correct one a little more often, lol.
I'm going to package the new version today and get it up then take a small breather before working on the next phase.
Mo bettah I think, what do y'all think? I like the barber pole thing, not near as obnoxious as previous version, nice and clean and simple but still easy to identify what channels are muted. This took a LOT of "stepwise refinement" today, it's embarassing really that it takes so long, I really don't like CSS.

I don't use a power manager in the mini's I make, nor in my own installs, I have a small script that runs once on startup that turns it all off.
#! /bin/sh
xset dpms 0 0 0
xset -dpms
xset s offgood luck with updates
If everything works, why update? ![]()
Man this stuff is hard for an old guy with no formal education. And it's kinda amazing, most of the time it's like "arrrgh...arrrgh...arrrgh" but then that one time it's like "OMFG BUAIDH MOFO" and that one time kinda makes up for all the "arrrghs"... does that make sense?
Started off today just wanting to tweak, that red color was obnoxious so I intended just to deal with that, ended up changing quite a bit and fixing a couple bugs along the way.
No more big list of channel names, finally figured out a simple way to detect that didn't drag in other depends, it's working great, pulling everything that's needed but also skipping simple toggles (i.e. auto-mute-mode, loopback etc., i'll add a function for those in a future update), soooo much less code. Fixed a bug where "Beep" was controlling the "Capture" slider. Added logic to remember what channels are muted as before the highlighting wouldn't survive re-starts.
Still only a 44.2k binary, tiny little powerhouse! Only going to get better, it's addicting I tell ya, lol. ![]()
If you REALLY want immutable, burn your favorite distro to a DVD and run it live. Maybe with a persistence partition on metal/ssd.
I used to run Puppy Linux that way.
dpkg-reconfigure tzdatawait...you guys are adding man pages to stuff?
Wow that took a long time, but version 2.0-1 is almost ready.
I tried various methods for detecting a given soundcard's controls and got it working mostly, but it literally doubled the size of the binary, 100% increase in size, not acceptable for just a small addition to a tiny utility. But had a thought yesterday while working at the library. The current app has slider names hardcoded, some 7 in all, and behavior is good in that if you don't have that particular slider name on your system, it simply won't add it to the GUI. So my low cost solution to that is that I added greatly to the number of potential slider names in the script, taken from my testing of various machines, suggestions made here (thanks!) and some internet searching, so now the app supports some 54 different controls and it's super easy to add more in the future, but as-is it ought to work for 90+% of most laptops and desktops. Had to add a scrollbar obviously. Only a 10% increase in code to do it that way, main binary is still under 50k.
I also added a mute/unmute function for the volume sliders that have a mute function, rather than take up extra space in the GUI with checkboxes or whatnot, I simply made it a right-click function, so now you can right-click on volume sliders and it will toggle mute/unmute and if the channel is muted now it has a big red box over it that shows very clearly that it's muted. Certainly there's a cleaner prettier way and at some point i'll work on the visual, but for now the function is up and running and that was the main point for this update.
Please feel free to post any channel names not on my list, here's the relevant code snippet so you can see what I have now:
*snip* deprecated
Here's a screenie of the working proto (re-sized to be smaller for the forum):
Nice! Welcome to the forum! ![]()
"Headset Mic," a "Headphone Mic,"
@stultumanto; wouldn't that be the same thing? Headphones with a mic is a headset unless i'm missing something here. Also:
I also get no controls of any kind with my UMC202HD USB interface, because it uses non-standard element names.
When you get a chance would you mind listing some?
Working on detection last 2 days, been way more of a pain than I thought it would be but I have a rough working prototype, but it added quite a bit of code the way I did it, trying to figure out a simpler solution.
Another small issue that rears it's head on some machines like my cheap laptop is that alsamixer controls on the backend are not infinitely adjustable, they go up in increments depending on your sound card, like main volume in the terminal goes from 0 to 7 then 15 then 23 when you adjust it, and I have two controls which are worse on mine, the beep and mic boost go up in increments of 33, so it goes 0-33-67-100 and if you set it say at 45 in AT it will revert to 33 automatically when you open it again.
Re: Having AlsaTune and the terminal version of alsamixer open at the same time:
Forehead slapper, I only tested one direction, if you have both open and make changes in AlsaTune they immediately happen in alsamixer in realtime, just not vice versa.
Amazing...I am up to my neck right now in my own projects, but very soon i'm gonna clear a partition and do a clean mini install, and set it up with jack-bridge so I can test. My wife plays bass and i'm a drummer and i'd love to check out the recording feature. Also really interested in device selection and routing and dealing with bluetooth issues.
Nice work! This is important stuff too, offering some new and worthwhile choices in audio that avoid the pulse/pipe, looking forward to what's next, please keep us updated!
https://www.wric.com/news/weird-news/fu … -bathroom/

Broke into the liquor store, trash-panda'ed the place, got drunk and passed out in the bathroom. ![]()
Like the title says, not a big deal but here it is.
Default color for the forum when not logged in is purpy. Yet the favicon shown on the browser tab is blue. And on the login page it also has another instance of the logo, this time in yellow.
Can we pick a color and sync these things up? ![]()
Lol, not really that complex.
DuckDuckGo search terms: does cloudflare use ai to write code?
The answer at the top from the Duck's "search assist" (more ai, lol) :
Yes, Cloudflare uses AI models, such as Claude, to assist in writing code, particularly for projects like their OAuth library, where the AI-generated code is thoroughly reviewed by engineers for security and compliance. This approach allows for efficient coding while maintaining high standards of quality and safety.
Thoroughly reviewed bro...totally no hallucinating this time I swear...just push it, what's the worst that could happen bro? ![]()
Why not just copy the image you want over to /usr/share/slim/themes/<theme_name>/? Then just delete the current image (or rename it to something else) and change the name of the new image to background.png/jpg.
Simplest way right there. But it absolutely helps a LOT if you have a couple of right-click context items, Open as Root and Edit as Root.
I haven't used slim in many years, can you not just specify a specific image in slim.conf?