From 2504f2699e5c2f973018c0602144d0591b5a9b55 Mon Sep 17 00:00:00 2001 Message-Id: <2504f2699e5c2f973018c0602144d0591b5a9b55.1715154601.git.mdw@distorted.org.uk> From: Mark Wooding Date: Fri, 20 Jul 2018 03:17:54 +0100 Subject: [PATCH] lib/uaudio-pulseaudio.c: Add volume control support. Organization: Straylight/Edgeware From: Mark Wooding Now that we have everything in place, implement PulseAudio volume control. --- CHANGES.html | 3 -- lib/uaudio-pulseaudio.c | 94 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 6 deletions(-) diff --git a/CHANGES.html b/CHANGES.html index f802ace..0ed1e72 100644 --- a/CHANGES.html +++ b/CHANGES.html @@ -26,9 +26,6 @@ href="README.upgrades.html">README.upgrades before upgrading.

build time. It is the default for client applications, reflecting its widespread use in desktop environments.

-

Volume setting is not currently supported when PulseAudio is in - use.

-

Changes To RTP Support

diff --git a/lib/uaudio-pulseaudio.c b/lib/uaudio-pulseaudio.c index 7908e6d..c7a823f 100644 --- a/lib/uaudio-pulseaudio.c +++ b/lib/uaudio-pulseaudio.c @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -39,6 +40,8 @@ static const char *const pulseaudio_options[] = { static pa_threaded_mainloop *loop; static pa_context *ctx; static pa_stream *str; +static pa_channel_map cmap; +static uint32_t strix; #define PAERRSTR pa_strerror(pa_context_errno(ctx)) @@ -76,6 +79,19 @@ static void cb_wakeup(attribute((unused)) pa_stream *xstr, attribute((unused)) void *p) { pa_threaded_mainloop_signal(loop, 0); } +struct simpleop { + const char *what; + int donep; +}; + +/** @brief Callback: wake up main loop when a simple operation completes. */ +static void cb_success(attribute((unused)) pa_context *xctx, + int winp, void *p) { + struct simpleop *sop = p; + if(!winp) disorder_fatal(0, "%s failed: %s", sop->what, PAERRSTR); + sop->donep = 1; pa_threaded_mainloop_signal(loop, 0); +} + /** @brief Open the PulseAudio sound device */ static void pulseaudio_open() { /* Much of the following is cribbed from the PulseAudio `simple' source. */ @@ -130,6 +146,7 @@ static void pulseaudio_open() { pa_threaded_mainloop_wait(loop); /* All done. */ + strix = pa_stream_get_index(str); pa_threaded_mainloop_unlock(loop); } @@ -177,18 +194,89 @@ static void pulseaudio_stop(void) { } static void pulseaudio_open_mixer(void) { - disorder_error(0, "no pulseaudio mixer support yet"); + if(!str) + disorder_fatal(0, "won't open pulseaudio mixer with no stream open"); + switch(uaudio_channels) { + case 1: pa_channel_map_init_mono(&cmap); break; + case 2: pa_channel_map_init_stereo(&cmap); break; + default: disorder_error(0, "no pulseaudio mixer support for %d channels", + uaudio_channels); + } } static void pulseaudio_close_mixer(void) { } +struct getvol { + pa_cvolume vol; + int donep; +}; + +/** @brief Callback: pick out our stream's volume. */ +static void cb_getvol(attribute((unused)) pa_context *xctx, + const pa_sink_input_info *info, int flag, void *p) { + struct getvol *gv = p; + + if(flag < 0) + disorder_fatal(0, "failed to read own pulseaudio sink-input volume: %s", + PAERRSTR); + else if(!flag) { + gv->vol = info->volume; + gv->donep = -1; + } else if (!gv->donep) + disorder_fatal(0, "no answer reading own pulseaudio sink-input volume"); + else { + gv->donep = 1; + pa_threaded_mainloop_signal(loop, 0); + } +} + static void pulseaudio_get_volume(int *left, int *right) { - *left = *right = 0; + pa_operation *op; + struct getvol gv; + double l, r; + + pa_threaded_mainloop_lock(loop); + gv.donep = 0; + op = pa_context_get_sink_input_info(ctx, strix, cb_getvol, &gv); + while(gv.donep <= 0) pa_threaded_mainloop_wait(loop); + pa_threaded_mainloop_unlock(loop); + pa_operation_unref(op); + + switch(uaudio_channels) { + case 1: l = r = gv.vol.values[0]; break; + case 2: l = gv.vol.values[0]; r = gv.vol.values[1]; break; + default: l = r = 0; break; + } + + *left = 100.0*l/PA_VOLUME_NORM + 0.5; + *right = 100.0*r/PA_VOLUME_NORM + 0.5; } static void pulseaudio_set_volume(int *left, int *right) { - *left = *right = 0; + pa_operation *op; + struct simpleop sop; + pa_cvolume vol; + double r, l; + + l = *left*PA_VOLUME_NORM/100.0 + 0.5; + r = *right*PA_VOLUME_NORM/100.0 + 0.5; + + pa_cvolume_init(&vol); vol.channels = uaudio_channels; + switch(uaudio_channels) { + case 1: if(r < l) r = l; vol.values[0] = vol.values[1] = r; break; + case 2: vol.values[0] = l; vol.values[1] = r; break; + default: return; + } + + pa_threaded_mainloop_lock(loop); + sop.what = "set pulseaudio volume"; sop.donep = 0; + op = pa_context_set_sink_input_volume(ctx, strix, &vol, cb_success, &sop); + while(!sop.donep) pa_threaded_mainloop_wait(loop); + pa_threaded_mainloop_unlock(loop); + pa_operation_unref(op); + + pulseaudio_get_volume(left, right); } static void pulseaudio_configure(void) { -- [mdw]