Volume.cxx 6.5 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2021 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13
 * http://www.musicpd.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
14 15 16 17
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 19
 */

20
#include "Volume.hxx"
21
#include "Silence.hxx"
22
#include "Traits.hxx"
23
#include "util/ConstBuffer.hxx"
24
#include "util/WritableBuffer.hxx"
25
#include "util/RuntimeError.hxx"
26
#include "util/TransformN.hxx"
27

28
#include "Dither.cxx" // including the .cxx file to get inlined templates
29

30
#include <cassert>
31
#include <cstdint>
32

33 34
#include <string.h>

35 36 37 38 39 40
/**
 * Apply software volume, converting to a different sample type.
 */
template<SampleFormat SF, SampleFormat DF,
	 class STraits=SampleTraits<SF>,
	 class DTraits=SampleTraits<DF>>
41
static constexpr typename DTraits::value_type
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
PcmVolumeConvert(typename STraits::value_type _sample, int volume) noexcept
{
	typename STraits::long_type sample(_sample);
	sample *= volume;

	static_assert(DTraits::BITS > STraits::BITS,
		      "Destination sample must be larger than source sample");

	/* after multiplying with the volume value, the "sample"
	   variable contains this number of precision bits: source
	   bits plus the volume bits */
	constexpr unsigned BITS = STraits::BITS + PCM_VOLUME_BITS;

	/* .. and now we need to scale to the requested destination
	   bits */

	typename DTraits::value_type result;
	if (BITS > DTraits::BITS)
		result = sample >> (BITS - DTraits::BITS);
	else if (BITS < DTraits::BITS)
		result = sample << (DTraits::BITS - BITS);
	else
		result = sample;

	return result;
}

69 70
template<SampleFormat F, class Traits=SampleTraits<F>>
static inline typename Traits::value_type
71 72
pcm_volume_sample(PcmDither &dither,
		  typename Traits::value_type _sample,
Max Kellermann's avatar
Max Kellermann committed
73
		  int volume) noexcept
74 75 76
{
	typename Traits::long_type sample(_sample);

77 78 79
	return dither.DitherShift<typename Traits::long_type,
				  Traits::BITS + PCM_VOLUME_BITS,
				  Traits::BITS>(sample * volume);
80 81
}

82
template<SampleFormat F, class Traits=SampleTraits<F>>
83
static void
84
pcm_volume_change(PcmDither &dither,
85 86
		  typename Traits::pointer dest,
		  typename Traits::const_pointer src,
87
		  size_t n,
Max Kellermann's avatar
Max Kellermann committed
88
		  int volume) noexcept
89
{
90 91 92 93 94
	transform_n(src, n, dest,
		    [&dither, volume](auto x){
			    return pcm_volume_sample<F, Traits>(dither, x,
								volume);
		    });
95 96 97
}

static void
98
pcm_volume_change_8(PcmDither &dither,
99
		    int8_t *dest, const int8_t *src, size_t n,
Max Kellermann's avatar
Max Kellermann committed
100
		    int volume) noexcept
101
{
102
	pcm_volume_change<SampleFormat::S8>(dither, dest, src, n, volume);
103 104 105
}

static void
106
pcm_volume_change_16(PcmDither &dither,
107
		     int16_t *dest, const int16_t *src, size_t n,
Max Kellermann's avatar
Max Kellermann committed
108
		     int volume) noexcept
109
{
110
	pcm_volume_change<SampleFormat::S16>(dither, dest, src, n, volume);
111 112
}

113 114 115 116
static void
PcmVolumeChange16to32(int32_t *dest, const int16_t *src, size_t n,
		      int volume) noexcept
{
117 118 119 120 121 122
	transform_n(src, n, dest,
		    [volume](auto x){
			    return PcmVolumeConvert<SampleFormat::S16,
						    SampleFormat::S24_P32>(x,
									   volume);
		    });
123 124
}

125
static void
126
pcm_volume_change_24(PcmDither &dither,
127
		     int32_t *dest, const int32_t *src, size_t n,
Max Kellermann's avatar
Max Kellermann committed
128
		     int volume) noexcept
129
{
130
	pcm_volume_change<SampleFormat::S24_P32>(dither, dest, src, n,
131
						 volume);
132 133
}

134
static void
135
pcm_volume_change_32(PcmDither &dither,
136
		     int32_t *dest, const int32_t *src, size_t n,
Max Kellermann's avatar
Max Kellermann committed
137
		     int volume) noexcept
138
{
139
	pcm_volume_change<SampleFormat::S32>(dither, dest, src, n, volume);
140 141
}

142
static void
143
pcm_volume_change_float(float *dest, const float *src, size_t n,
Max Kellermann's avatar
Max Kellermann committed
144
			float volume) noexcept
145
{
146 147
	transform_n(src, n, dest,
		    [volume](float x){ return x * volume; });
148 149
}

150
SampleFormat
151
PcmVolume::Open(SampleFormat _format, bool allow_convert)
152
{
153
	assert(format == SampleFormat::UNDEFINED);
154

155 156
	convert = false;

157 158
	switch (_format) {
	case SampleFormat::UNDEFINED:
159 160
		throw FormatRuntimeError("Software volume for %s is not implemented",
					 sample_format_to_string(_format));
161 162

	case SampleFormat::S8:
163 164
		break;

165
	case SampleFormat::S16:
166 167 168 169 170 171 172 173 174 175
		if (allow_convert) {
			/* convert S16 to S24 to avoid discarding too
			   many bits of precision in this stage */
			format = _format;
			convert = true;
			return SampleFormat::S24_P32;
		}

		break;

176 177 178 179
	case SampleFormat::S24_P32:
	case SampleFormat::S32:
	case SampleFormat::FLOAT:
		break;
180 181 182 183

	case SampleFormat::DSD:
		// TODO: implement this; currently, it's a no-op
		break;
184 185
	}

186
	return format = _format;
187 188 189
}

ConstBuffer<void>
190
PcmVolume::Apply(ConstBuffer<void> src) noexcept
191
{
192
	if (volume == PCM_VOLUME_1 && !convert)
193 194
		return src;

195
	size_t dest_size = src.size;
196 197 198 199 200 201 202
	if (convert) {
		assert(format == SampleFormat::S16);

		/* converting to S24_P32 */
		dest_size *= 2;
	}

203
	void *data = buffer.Get(dest_size);
204 205 206

	if (volume == 0) {
		/* optimized special case: 0% volume = memset(0) */
207 208
		PcmSilence({data, dest_size}, format);
		return { data, dest_size };
209 210
	}

211
	switch (format) {
212
	case SampleFormat::UNDEFINED:
213 214
		assert(false);
		gcc_unreachable();
215

216
	case SampleFormat::S8:
217
		pcm_volume_change_8(dither, (int8_t *)data,
218
				    (const int8_t *)src.data,
219
				    src.size / sizeof(int8_t),
Max Kellermann's avatar
Max Kellermann committed
220
				    volume);
221
		break;
222

223
	case SampleFormat::S16:
224 225 226 227 228 229 230 231 232 233
		if (convert)
			PcmVolumeChange16to32((int32_t *)data,
					      (const int16_t *)src.data,
					      src.size / sizeof(int16_t),
					      volume);
		else
			pcm_volume_change_16(dither, (int16_t *)data,
					     (const int16_t *)src.data,
					     src.size / sizeof(int16_t),
					     volume);
234
		break;
235

236
	case SampleFormat::S24_P32:
237
		pcm_volume_change_24(dither, (int32_t *)data,
238
				     (const int32_t *)src.data,
239
				     src.size / sizeof(int32_t),
Max Kellermann's avatar
Max Kellermann committed
240
				     volume);
241
		break;
242

243
	case SampleFormat::S32:
244
		pcm_volume_change_32(dither, (int32_t *)data,
245
				     (const int32_t *)src.data,
246
				     src.size / sizeof(int32_t),
Max Kellermann's avatar
Max Kellermann committed
247
				     volume);
248
		break;
249

250
	case SampleFormat::FLOAT:
251 252
		pcm_volume_change_float((float *)data,
					(const float *)src.data,
253
					src.size / sizeof(float),
254
					pcm_volume_to_float(volume));
255
		break;
256 257 258 259

	case SampleFormat::DSD:
		// TODO: implement this; currently, it's a no-op
		return src;
260
	}
261

262
	return { data, dest_size };
263
}