Skip to content

Instantly share code, notes, and snippets.

@sound-logic
Forked from jasonwhite/pulsetest.cc
Created February 22, 2024 20:03
Show Gist options
  • Select an option

  • Save sound-logic/00cf28f83993a2f7199538d281f831ad to your computer and use it in GitHub Desktop.

Select an option

Save sound-logic/00cf28f83993a2f7199538d281f831ad to your computer and use it in GitHub Desktop.

Revisions

  1. @jasonwhite jasonwhite revised this gist May 11, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion pulsetest.cc
    Original file line number Diff line number Diff line change
    @@ -196,7 +196,7 @@ class PulseAudio
    switch (facility)
    {
    case PA_SUBSCRIPTION_EVENT_SINK:
    pa_context_get_sink_info_by_index(c, idx, sink_info_callback, userdata);
    op = pa_context_get_sink_info_by_index(c, idx, sink_info_callback, userdata);
    break;

    default:
  2. @jasonwhite jasonwhite revised this gist Apr 27, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion pulsetest.cc
    Original file line number Diff line number Diff line change
    @@ -72,7 +72,7 @@ class PulseAudio
    return false;
    }

    pa_context_set_state_callback(_context, context_state_callback, _mainloop_api);
    pa_context_set_state_callback(_context, context_state_callback, this);

    return true;
    }
  3. @jasonwhite jasonwhite created this gist Jan 7, 2015.
    246 changes: 246 additions & 0 deletions pulsetest.cc
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,246 @@
    /**
    * Author: Jason White
    * License: Public Domain
    *
    * Description:
    * This is a simple test program to hook into PulseAudio volume change
    * notifications. It was created for the possibility of having an automatically
    * updating volume widget in a tiling window manager status bar.
    *
    * Compiling:
    *
    * g++ $(shell pkg-config libpulse --cflags --libs) pulsetest.c -o pulsetest
    */
    #include <stdio.h>
    #include <assert.h>
    #include <signal.h>
    #include <pulse/pulseaudio.h>


    class PulseAudio
    {
    private:
    pa_mainloop* _mainloop;
    pa_mainloop_api* _mainloop_api;
    pa_context* _context;
    pa_signal_event* _signal;

    public:
    PulseAudio()
    : _mainloop(NULL), _mainloop_api(NULL), _context(NULL), _signal(NULL)
    {
    }

    /**
    * Initializes state and connects to the PulseAudio server.
    */
    bool initialize()
    {
    _mainloop = pa_mainloop_new();
    if (!_mainloop)
    {
    fprintf(stderr, "pa_mainloop_new() failed.\n");
    return false;
    }

    _mainloop_api = pa_mainloop_get_api(_mainloop);

    if (pa_signal_init(_mainloop_api) != 0)
    {
    fprintf(stderr, "pa_signal_init() failed\n");
    return false;
    }

    _signal = pa_signal_new(SIGINT, exit_signal_callback, this);
    if (!_signal)
    {
    fprintf(stderr, "pa_signal_new() failed\n");
    return false;
    }
    signal(SIGPIPE, SIG_IGN);

    _context = pa_context_new(_mainloop_api, "PulseAudio Test");
    if (!_context)
    {
    fprintf(stderr, "pa_context_new() failed\n");
    return false;
    }

    if (pa_context_connect(_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0)
    {
    fprintf(stderr, "pa_context_connect() failed: %s\n", pa_strerror(pa_context_errno(_context)));
    return false;
    }

    pa_context_set_state_callback(_context, context_state_callback, _mainloop_api);

    return true;
    }

    /**
    * Runs the main PulseAudio event loop. Calling quit will cause the event
    * loop to exit.
    */
    int run()
    {
    int ret = 1;
    if (pa_mainloop_run(_mainloop, &ret) < 0)
    {
    fprintf(stderr, "pa_mainloop_run() failed.\n");
    return ret;
    }

    return ret;
    }

    /**
    * Exits the main loop with the specified return code.
    */
    void quit(int ret = 0)
    {
    _mainloop_api->quit(_mainloop_api, ret);
    }

    /**
    * Called when the PulseAudio system is to be destroyed.
    */
    void destroy()
    {
    if (_context)
    {
    pa_context_unref(_context);
    _context = NULL;
    }

    if (_signal)
    {
    pa_signal_free(_signal);
    pa_signal_done();
    _signal = NULL;
    }

    if (_mainloop)
    {
    pa_mainloop_free(_mainloop);
    _mainloop = NULL;
    _mainloop_api = NULL;
    }
    }

    ~PulseAudio()
    {
    destroy();
    }

    private:

    /*
    * Called on SIGINT.
    */
    static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata)
    {
    PulseAudio* pa = (PulseAudio*)userdata;
    if (pa) pa->quit();
    }

    /*
    * Called whenever the context status changes.
    */
    static void context_state_callback(pa_context *c, void *userdata)
    {
    assert(c && userdata);

    PulseAudio* pa = (PulseAudio*)userdata;

    switch (pa_context_get_state(c))
    {
    case PA_CONTEXT_CONNECTING:
    case PA_CONTEXT_AUTHORIZING:
    case PA_CONTEXT_SETTING_NAME:
    break;

    case PA_CONTEXT_READY:
    fprintf(stderr, "PulseAudio connection established.\n");
    pa_context_get_server_info(c, server_info_callback, userdata);

    // Subscribe to sink events from the server. This is how we get
    // volume change notifications from the server.
    pa_context_set_subscribe_callback(c, subscribe_callback, userdata);
    pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK, NULL, NULL);
    break;

    case PA_CONTEXT_TERMINATED:
    pa->quit(0);
    fprintf(stderr, "PulseAudio connection terminated.\n");
    break;

    case PA_CONTEXT_FAILED:
    default:
    fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
    pa->quit(1);
    break;
    }
    }

    /*
    * Called when an event we subscribed to occurs.
    */
    static void subscribe_callback(pa_context *c,
    pa_subscription_event_type_t type, uint32_t idx, void *userdata)
    {
    unsigned facility = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
    //type &= PA_SUBSCRIPTION_EVENT_TYPE_MASK;

    pa_operation *op = NULL;

    switch (facility)
    {
    case PA_SUBSCRIPTION_EVENT_SINK:
    pa_context_get_sink_info_by_index(c, idx, sink_info_callback, userdata);
    break;

    default:
    assert(0); // Got event we aren't expecting.
    break;
    }

    if (op)
    pa_operation_unref(op);
    }

    /*
    * Called when the requested sink information is ready.
    */
    static void sink_info_callback(pa_context *c, const pa_sink_info *i,
    int eol, void *userdata)
    {
    if (i)
    {
    float volume = (float)pa_cvolume_avg(&(i->volume)) / (float)PA_VOLUME_NORM;
    printf("percent volume = %.0f%%%s\n", volume * 100.0f, i->mute ? " (muted)" : "");
    }
    }

    /*
    * Called when the requested information on the server is ready. This is
    * used to find the default PulseAudio sink.
    */
    static void server_info_callback(pa_context *c, const pa_server_info *i,
    void *userdata)
    {
    printf("default sink name = %s\n", i->default_sink_name);
    pa_context_get_sink_info_by_name(c, i->default_sink_name, sink_info_callback, userdata);
    }
    };


    int main(int argc, char *argv[])
    {
    PulseAudio pa = PulseAudio();
    if (!pa.initialize())
    return 0;

    int ret = pa.run();

    return ret;
    }