воскресенье, 21 сентября 2014 г.

Disable PulseAudio mute control

If you have PulseAudio installed together with ALSA mixers, there can be issues with channel muting. If you unmute Master channel, Speaker and Headphones still remain muted.

That's because PulseAudio tries to manage volumes itself. That behavior can be disabled by editing /usr/share/pulseaudio/alsa-mixer/paths/analog-output.conf.common file. Make sure its sections have switch and volume parameters set to ignore:

[Element PCM]
switch = ignore
volume = ignore

[Element Speaker]
switch = ignore
volume = ignore

[Element Headphone]
switch = ignore
volume = ignore



понедельник, 19 мая 2014 г.

Timed event queue

Sometimes one need a worker thread which executes tasks not just after they were added, but after some delay. Tasks can be pushed out of order. The code below uses GAsyncQueue to both passing tasks to worker thread and waiting for next event (new task or timer expire). Internal GQueue is used to keep tasks sorted by time they should be run at.



#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
 
struct task_s {
    struct timespec when;
    int number_to_print;
};
 
 
gint
compare_func(gconstpointer a, gconstpointer b, gpointer user_data)
{
    const struct task_s *task_a = a;
    const struct task_s *task_b = b;
 
    if (task_a->when.tv_sec < task_b->when.tv_sec)
        return -1;
    else if (task_a->when.tv_sec > task_b->when.tv_sec)
        return 1;
    else if (task_a->when.tv_nsec < task_b->when.tv_nsec)
        return -1;
    else if (task_a->when.tv_nsec > task_b->when.tv_nsec)
        return 1;
    else
        return 0;
}
 
gpointer
event_handler_thread(gpointer data)
{
    struct timespec now;
    GAsyncQueue *async_q = data;
    GQueue *int_q = g_queue_new();
 
    while (1) {
        struct task_s *task = g_queue_peek_head(int_q);
        gint64 timeout;
        if (task) {
            clock_gettime(CLOCK_REALTIME, &now);
            timeout = (task->when.tv_sec - now.tv_sec) * 1000 * 1000 +
                      (task->when.tv_nsec - now.tv_nsec) / 1000;
            if (timeout <= 0) {
                // remove task from queue
                g_queue_pop_head(int_q);
                // run task
                printf("now = %d.%03d, number = %d\n", (int)now.tv_sec,
                       (int)(now.tv_nsec/(1000*1000)), task->number_to_print);
                if (task->number_to_print == 999) {
                    free(task);
                    break;
                }
                free(task);
                // go to start
                continue;
            }
        }
 
        task = g_async_queue_timeout_pop(async_q, timeout);
 
        if (task)
            g_queue_insert_sorted(int_q, task, compare_func, NULL);
    }
 
    g_queue_free(int_q);
    return NULL;
}
 
void
push_work(GAsyncQueue *aq, int delay_ms, int number_to_print)
{
    delay_ms = delay_ms < 0 ? 0 : delay_ms;
 
    struct timespec now = { 0 };
    clock_gettime(CLOCK_REALTIME, &now);
 
    struct timespec then = now;
    then.tv_sec += delay_ms / 1000;
    then.tv_nsec += (delay_ms % 1000) * 1000 * 1000;
    while (then.tv_nsec >= 1000 * 1000 * 1000) {
        then.tv_sec += 1;
        then.tv_nsec -= 1000 * 1000 * 1000;
    }
 
    struct task_s *task = malloc(sizeof(*task));
    task->when = then;
    task->number_to_print = number_to_print;
 
    g_async_queue_push(aq, task);
}
 
int
main(void)
{
    GAsyncQueue *aq = g_async_queue_new();
    GThread *thread = g_thread_new("event_handler_thread", event_handler_thread, aq);
 
    push_work(aq, 2500, 999);
 
    push_work(aq, 0, 1);
    push_work(aq, 1000, 2);
    push_work(aq, -10, 3);
    push_work(aq, 500, 4);
    push_work(aq, 300, 5);
    push_work(aq, 600, 6);
 
    g_thread_join(thread);
    g_async_queue_unref(aq);
 
    return 0;
}