Volume.cxx 6.73 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2019 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 <assert.h>
31 32 33
#include <stdint.h>
#include <string.h>

34 35 36 37 38 39 40 41 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 69 70 71 72 73 74 75 76
#if GCC_OLDER_THAN(8,0)
/* GCC 6.3 emits this bogus warning in PcmVolumeConvert() because it
   checks an unreachable branch */
#pragma GCC diagnostic ignored "-Wshift-count-overflow"
#endif

/**
 * Apply software volume, converting to a different sample type.
 */
template<SampleFormat SF, SampleFormat DF,
	 class STraits=SampleTraits<SF>,
	 class DTraits=SampleTraits<DF>>
#if !GCC_OLDER_THAN(8,0)
constexpr
#endif
static typename DTraits::value_type
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;
}

77 78
template<SampleFormat F, class Traits=SampleTraits<F>>
static inline typename Traits::value_type
79 80
pcm_volume_sample(PcmDither &dither,
		  typename Traits::value_type _sample,
Max Kellermann's avatar
Max Kellermann committed
81
		  int volume) noexcept
82 83 84
{
	typename Traits::long_type sample(_sample);

85 86 87
	return dither.DitherShift<typename Traits::long_type,
				  Traits::BITS + PCM_VOLUME_BITS,
				  Traits::BITS>(sample * volume);
88 89
}

90
template<SampleFormat F, class Traits=SampleTraits<F>>
91
static void
92 93
pcm_volume_change(PcmDither &dither,
		  typename Traits::pointer_type dest,
94
		  typename Traits::const_pointer_type src,
95
		  size_t n,
Max Kellermann's avatar
Max Kellermann committed
96
		  int volume) noexcept
97
{
98 99 100 101 102
	transform_n(src, n, dest,
		    [&dither, volume](auto x){
			    return pcm_volume_sample<F, Traits>(dither, x,
								volume);
		    });
103 104 105
}

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

static void
114
pcm_volume_change_16(PcmDither &dither,
115
		     int16_t *dest, const int16_t *src, size_t n,
Max Kellermann's avatar
Max Kellermann committed
116
		     int volume) noexcept
117
{
118
	pcm_volume_change<SampleFormat::S16>(dither, dest, src, n, volume);
119 120
}

121 122 123 124
static void
PcmVolumeChange16to32(int32_t *dest, const int16_t *src, size_t n,
		      int volume) noexcept
{
125 126 127 128 129 130
	transform_n(src, n, dest,
		    [volume](auto x){
			    return PcmVolumeConvert<SampleFormat::S16,
						    SampleFormat::S24_P32>(x,
									   volume);
		    });
131 132
}

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

142
static void
143
pcm_volume_change_32(PcmDither &dither,
144
		     int32_t *dest, const int32_t *src, size_t n,
Max Kellermann's avatar
Max Kellermann committed
145
		     int volume) noexcept
146
{
147
	pcm_volume_change<SampleFormat::S32>(dither, dest, src, n, volume);
148 149
}

150
static void
151
pcm_volume_change_float(float *dest, const float *src, size_t n,
Max Kellermann's avatar
Max Kellermann committed
152
			float volume) noexcept
153
{
154 155
	transform_n(src, n, dest,
		    [volume](float x){ return x * volume; });
156 157
}

158
SampleFormat
159
PcmVolume::Open(SampleFormat _format, bool allow_convert)
160
{
161
	assert(format == SampleFormat::UNDEFINED);
162

163 164
	convert = false;

165 166
	switch (_format) {
	case SampleFormat::UNDEFINED:
167 168
		throw FormatRuntimeError("Software volume for %s is not implemented",
					 sample_format_to_string(_format));
169 170

	case SampleFormat::S8:
171 172
		break;

173
	case SampleFormat::S16:
174 175 176 177 178 179 180 181 182 183
		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;

184 185 186 187
	case SampleFormat::S24_P32:
	case SampleFormat::S32:
	case SampleFormat::FLOAT:
		break;
188 189 190 191

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

194
	return format = _format;
195 196 197
}

ConstBuffer<void>
198
PcmVolume::Apply(ConstBuffer<void> src) noexcept
199
{
200
	if (volume == PCM_VOLUME_1 && !convert)
201 202
		return src;

203
	size_t dest_size = src.size;
204 205 206 207 208 209 210
	if (convert) {
		assert(format == SampleFormat::S16);

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

211
	void *data = buffer.Get(dest_size);
212 213 214

	if (volume == 0) {
		/* optimized special case: 0% volume = memset(0) */
215 216
		PcmSilence({data, dest_size}, format);
		return { data, dest_size };
217 218
	}

219
	switch (format) {
220
	case SampleFormat::UNDEFINED:
221 222
		assert(false);
		gcc_unreachable();
223

224
	case SampleFormat::S8:
225
		pcm_volume_change_8(dither, (int8_t *)data,
226
				    (const int8_t *)src.data,
227
				    src.size / sizeof(int8_t),
Max Kellermann's avatar
Max Kellermann committed
228
				    volume);
229
		break;
230

231
	case SampleFormat::S16:
232 233 234 235 236 237 238 239 240 241
		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);
242
		break;
243

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

251
	case SampleFormat::S32:
252
		pcm_volume_change_32(dither, (int32_t *)data,
253
				     (const int32_t *)src.data,
254
				     src.size / sizeof(int32_t),
Max Kellermann's avatar
Max Kellermann committed
255
				     volume);
256
		break;
257

258
	case SampleFormat::FLOAT:
259 260
		pcm_volume_change_float((float *)data,
					(const float *)src.data,
261
					src.size / sizeof(float),
262
					pcm_volume_to_float(volume));
263
		break;
264 265 266 267

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

270
	return { data, dest_size };
271
}