diff options
Diffstat (limited to 'src/pcm')
-rw-r--r-- | src/pcm/Volume.cxx | 87 | ||||
-rw-r--r-- | src/pcm/Volume.hxx | 79 |
2 files changed, 126 insertions, 40 deletions
diff --git a/src/pcm/Volume.cxx b/src/pcm/Volume.cxx index 410df7695..539ef1238 100644 --- a/src/pcm/Volume.cxx +++ b/src/pcm/Volume.cxx @@ -19,9 +19,11 @@ #include "config.h" #include "Volume.hxx" +#include "Domain.hxx" #include "PcmUtils.hxx" #include "Traits.hxx" -#include "AudioFormat.hxx" +#include "util/ConstBuffer.hxx" +#include "util/Error.hxx" #include <stdint.h> #include <string.h> @@ -143,61 +145,88 @@ pcm_volume_change_float(float *dest, const float *src, const float *end, } bool -pcm_volume(void *buffer, size_t length, - SampleFormat format, - int volume) +PcmVolume::Open(SampleFormat _format, Error &error) { - if (volume == PCM_VOLUME_1S) - return true; + assert(format == SampleFormat::UNDEFINED); - if (volume <= 0) { - memset(buffer, 0, length); - return true; + switch (_format) { + case SampleFormat::UNDEFINED: + case SampleFormat::DSD: + error.Format(pcm_domain, + "Software volume for %s is not implemented", + sample_format_to_string(_format)); + return false; + + case SampleFormat::S8: + case SampleFormat::S16: + case SampleFormat::S24_P32: + case SampleFormat::S32: + case SampleFormat::FLOAT: + break; } - const void *end = pcm_end_pointer(buffer, length); + format = _format; + return true; +} + +ConstBuffer<void> +PcmVolume::Apply(ConstBuffer<void> src) +{ + if (volume == PCM_VOLUME_1) + return src; + + void *data = buffer.Get(src.size); + + if (volume == 0) { + /* optimized special case: 0% volume = memset(0) */ + /* TODO: is this valid for all sample formats? What + about floating point? */ + memset(data, 0, src.size); + return { data, src.size }; + } + + const void *end = pcm_end_pointer(src.data, src.size); switch (format) { case SampleFormat::UNDEFINED: case SampleFormat::DSD: - /* not implemented */ - return false; + assert(false); + gcc_unreachable(); case SampleFormat::S8: - pcm_volume_change_8((int8_t *)buffer, - (const int8_t *)buffer, + pcm_volume_change_8((int8_t *)data, + (const int8_t *)src.data, (const int8_t *)end, volume); - return true; + break; case SampleFormat::S16: - pcm_volume_change_16((int16_t *)buffer, - (const int16_t *)buffer, + pcm_volume_change_16((int16_t *)data, + (const int16_t *)src.data, (const int16_t *)end, volume); - return true; + break; case SampleFormat::S24_P32: - pcm_volume_change_24((int32_t *)buffer, - (const int32_t *)buffer, + pcm_volume_change_24((int32_t *)data, + (const int32_t *)src.data, (const int32_t *)end, volume); - return true; + break; case SampleFormat::S32: - pcm_volume_change_32((int32_t *)buffer, - (const int32_t *)buffer, + pcm_volume_change_32((int32_t *)data, + (const int32_t *)src.data, (const int32_t *)end, volume); - return true; + break; case SampleFormat::FLOAT: - pcm_volume_change_float((float *)buffer, - (const float *)buffer, + pcm_volume_change_float((float *)data, + (const float *)src.data, (const float *)end, pcm_volume_to_float(volume)); - return true; + break; } - assert(false); - gcc_unreachable(); + return { data, src.size }; } diff --git a/src/pcm/Volume.hxx b/src/pcm/Volume.hxx index 52102a294..c31aafb6e 100644 --- a/src/pcm/Volume.hxx +++ b/src/pcm/Volume.hxx @@ -22,10 +22,18 @@ #include "PcmPrng.hxx" #include "AudioFormat.hxx" +#include "PcmBuffer.hxx" #include <stdint.h> #include <stddef.h> +#ifndef NDEBUG +#include <assert.h> +#endif + +class Error; +template<typename T> struct ConstBuffer; + /** * Number of fractional bits for a fixed-point volume value. */ @@ -71,17 +79,66 @@ pcm_volume_dither(void) } /** - * Adjust the volume of the specified PCM buffer. - * - * @param buffer the PCM buffer - * @param length the length of the PCM buffer - * @param format the sample format of the PCM buffer - * @param volume the volume between 0 and #PCM_VOLUME_1 - * @return true on success, false if the audio format is not supported + * A class that converts samples from one format to another. */ -bool -pcm_volume(void *buffer, size_t length, - SampleFormat format, - int volume); +class PcmVolume { + SampleFormat format; + + unsigned volume; + + PcmBuffer buffer; + +public: + PcmVolume() + :volume(PCM_VOLUME_1) { +#ifndef NDEBUG + format = SampleFormat::UNDEFINED; +#endif + } + +#ifndef NDEBUG + ~PcmVolume() { + assert(format == SampleFormat::UNDEFINED); + } +#endif + + unsigned GetVolume() const { + return volume; + } + + /** + * @param _volume the volume level in the range + * [0..#PCM_VOLUME_1]; may be bigger than #PCM_VOLUME_1, but + * then it will most likely clip a lot + */ + void SetVolume(unsigned _volume) { + volume = _volume; + } + + /** + * Opens the object, prepare for Apply(). + * + * @param format the sample format + * @param error location to store the error + * @return true on success + */ + bool Open(SampleFormat format, Error &error); + + /** + * Closes the object. After that, you may call Open() again. + */ + void Close() { +#ifndef NDEBUG + assert(format != SampleFormat::UNDEFINED); + format = SampleFormat::UNDEFINED; +#endif + } + + /** + * Apply the volume level. + */ + gcc_pure + ConstBuffer<void> Apply(ConstBuffer<void> src); +}; #endif |