diff --git a/src/apulse-stream.c b/src/apulse-stream.c index 135df3e..0cfa904 100644 --- a/src/apulse-stream.c +++ b/src/apulse-stream.c @@ -134,6 +134,8 @@ data_available_for_stream(pa_mainloop_api *a, pa_io_event *ioe, int fd, pa_io_ev size_t bytecnt = MIN(buf_size, frame_count * frame_size); bytecnt = ringbuffer_read(s->rb, buf, bytecnt); + pa_apply_volume_multiplier(buf, bytecnt, s->c->sink_volume, &s->ss); + if (bytecnt == 0) { // application is not ready yet, play silence bytecnt = MIN(buf_size, frame_count * frame_size); @@ -163,6 +165,7 @@ data_available_for_stream(pa_mainloop_api *a, pa_io_event *ioe, int fd, pa_io_ev if (bytecnt > 0) { snd_pcm_readi(s->ph, buf, bytecnt / frame_size); + pa_apply_volume_multiplier(buf, bytecnt, s->c->source_volume, &s->ss); ringbuffer_write(s->rb, buf, bytecnt); } diff --git a/src/util.c b/src/util.c index f6f770e..e348ea3 100644 --- a/src/util.c +++ b/src/util.c @@ -23,6 +23,7 @@ */ #include "util.h" +#include "trace.h" int pa_format_to_alsa(pa_sample_format_t format) @@ -87,3 +88,73 @@ pa_find_multiple_of(size_t number, size_t multiple_of) { return number - (number % multiple_of); } + +void +pa_apply_volume_multiplier(void *buf, size_t sz, const pa_volume_t volume[PA_CHANNELS_MAX], + const pa_sample_spec *ss) +{ + char *p = buf; + char *last = p + sz; + float fvol[PA_CHANNELS_MAX]; + uint32_t channels = MIN(ss->channels, PA_CHANNELS_MAX); + + if (channels == 0) { + // No channels — nothing to scale. + return; + } + + int all_normal = 1; + for (uint32_t k = 0; k < channels; k++) + all_normal = all_normal && (volume[k] == PA_VOLUME_NORM); + + if (all_normal) { + // No scaling required. + return; + } + + for (uint32_t k = 0; k < channels; k++) + fvol[k] = pa_sw_volume_to_linear(volume[k]); + + switch (ss->format) { + case PA_SAMPLE_FLOAT32NE: + while (p < last) { + for (uint32_t k = 0; k < channels && p < last; k++) { + float sample; + memcpy(&sample, p, sizeof(sample)); + sample *= fvol[k]; + memcpy(p, &sample, sizeof(sample)); + p += sizeof(sample); + } + } + break; + + case PA_SAMPLE_S16NE: + while (p < last) { + for (uint32_t k = 0; k < channels && p < last; k++) { + uint16_t sample; + memcpy(&sample, p, sizeof(sample)); + float sample_scaled = sample * fvol[k]; + sample = CLAMP(sample_scaled, 0.0, 65535.0); + memcpy(p, &sample, sizeof(sample)); + p += sizeof(sample); + } + } + break; + + case PA_SAMPLE_U8: + case PA_SAMPLE_ALAW: + case PA_SAMPLE_ULAW: + case PA_SAMPLE_S16RE: + case PA_SAMPLE_FLOAT32RE: + case PA_SAMPLE_S32NE: + case PA_SAMPLE_S32RE: + case PA_SAMPLE_S24NE: + case PA_SAMPLE_S24RE: + case PA_SAMPLE_S24_32NE: + case PA_SAMPLE_S24_32RE: + default: + trace_error("format %s is not implemented in %s\n", pa_sample_format_to_string(ss->format), + __func__); + break; + } +} diff --git a/src/util.h b/src/util.h index ab8e4e7..be394c5 100644 --- a/src/util.h +++ b/src/util.h @@ -39,4 +39,8 @@ pa_sample_format_from_string(const char *str); size_t pa_find_multiple_of(size_t number, size_t multiple_of); +void +pa_apply_volume_multiplier(void *buf, size_t sz, const pa_volume_t volume[PA_CHANNELS_MAX], + const pa_sample_spec *ss); + #endif // APULSE__UTIL_H