PcmFormat.cxx 10.6 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2020 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
 */

Max Kellermann's avatar
Max Kellermann committed
20
#include "PcmFormat.hxx"
21
#include "Buffer.hxx"
22
#include "Traits.hxx"
23 24
#include "FloatConvert.hxx"
#include "ShiftConvert.hxx"
25
#include "util/ConstBuffer.hxx"
26
#include "util/TransformN.hxx"
27

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

30 31 32 33 34 35
/**
 * Wrapper for a class that converts one sample at a time into one
 * that converts a buffer at a time.
 */
template<typename C>
struct PerSampleConvert : C {
36 37
	using SrcTraits = typename C::SrcTraits;
	using DstTraits = typename C::DstTraits;
38

39 40
	void Convert(typename DstTraits::pointer gcc_restrict out,
		     typename SrcTraits::const_pointer gcc_restrict in,
41
		     size_t n) const {
42
		transform_n(in, n, out, C::Convert);
43 44
	}
};
45

46 47 48
struct Convert8To16
	: PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S8,
						  SampleFormat::S16>> {};
49

50
struct Convert24To16 {
51 52
	using SrcTraits = SampleTraits<SampleFormat::S24_P32>;
	using DstTraits = SampleTraits<SampleFormat::S16>;
53

54 55
	PcmDither &dither;

Max Kellermann's avatar
Max Kellermann committed
56
	explicit Convert24To16(PcmDither &_dither):dither(_dither) {}
57 58 59 60 61 62 63

	void Convert(int16_t *out, const int32_t *in, size_t n) {
		dither.Dither24To16(out, in, in + n);
	}
};

struct Convert32To16 {
64 65
	using SrcTraits = SampleTraits<SampleFormat::S32>;
	using DstTraits = SampleTraits<SampleFormat::S16>;
66

67 68
	PcmDither &dither;

Max Kellermann's avatar
Max Kellermann committed
69
	explicit Convert32To16(PcmDither &_dither):dither(_dither) {}
70 71 72

	void Convert(int16_t *out, const int32_t *in, size_t n) {
		dither.Dither32To16(out, in, in + n);
73
	}
74 75 76
};

template<SampleFormat F, class Traits=SampleTraits<F>>
77
struct PortableFloatToInteger
78 79
	: PerSampleConvert<FloatToIntegerSampleConvert<F, Traits>> {};

80 81 82 83 84 85 86 87 88 89 90
template<SampleFormat F, class Traits=SampleTraits<F>>
struct FloatToInteger : PortableFloatToInteger<F, Traits> {};

/**
 * A template class that attempts to use the "optimized" algorithm for
 * large portions of the buffer, and calls the "portable" algorithm"
 * for the rest when the last block is not full.
 */
template<typename Optimized, typename Portable>
class GlueOptimizedConvert : Optimized, Portable {
public:
91 92
	using SrcTraits = typename Portable::SrcTraits;
	using DstTraits = typename Portable::DstTraits;
93

94 95
	void Convert(typename DstTraits::pointer out,
		     typename SrcTraits::const_pointer in,
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
		     size_t n) const {
		Optimized::Convert(out, in, n);

		/* use the "portable" algorithm for the trailing
		   samples */
		size_t remaining = n % Optimized::BLOCK_SIZE;
		size_t done = n - remaining;
		Portable::Convert(out + done, in + done, remaining);
	}
};

#ifdef __ARM_NEON__
#include "Neon.hxx"

template<>
struct FloatToInteger<SampleFormat::S16, SampleTraits<SampleFormat::S16>>
	: GlueOptimizedConvert<NeonFloatTo16,
			       PortableFloatToInteger<SampleFormat::S16>> {};

#endif

117
template<class C>
118
static ConstBuffer<typename C::DstTraits::value_type>
119 120 121 122 123 124
AllocateConvert(PcmBuffer &buffer, C convert,
		ConstBuffer<typename C::SrcTraits::value_type> src)
{
	auto dest = buffer.GetT<typename C::DstTraits::value_type>(src.size);
	convert.Convert(dest, src.data, src.size);
	return { dest, src.size };
125 126
}

127
template<SampleFormat F, class Traits=SampleTraits<F>>
128
static ConstBuffer<typename Traits::value_type>
129
AllocateFromFloat(PcmBuffer &buffer, ConstBuffer<float> src)
130
{
131
	return AllocateConvert(buffer, FloatToInteger<F, Traits>(), src);
132 133
}

134 135 136
static ConstBuffer<int16_t>
pcm_allocate_8_to_16(PcmBuffer &buffer, ConstBuffer<int8_t> src)
{
137
	return AllocateConvert(buffer, Convert8To16(), src);
138 139 140
}

static ConstBuffer<int16_t>
141
pcm_allocate_24p32_to_16(PcmBuffer &buffer, PcmDither &dither,
142 143
			 ConstBuffer<int32_t> src)
{
144
	return AllocateConvert(buffer, Convert24To16(dither), src);
145 146
}

147
static ConstBuffer<int16_t>
148
pcm_allocate_32_to_16(PcmBuffer &buffer, PcmDither &dither,
149 150
		      ConstBuffer<int32_t> src)
{
151
	return AllocateConvert(buffer, Convert32To16(dither), src);
152 153
}

154 155
static ConstBuffer<int16_t>
pcm_allocate_float_to_16(PcmBuffer &buffer, ConstBuffer<float> src)
156
{
157
	return AllocateFromFloat<SampleFormat::S16>(buffer, src);
158 159
}

160
ConstBuffer<int16_t>
161
pcm_convert_to_16(PcmBuffer &buffer, PcmDither &dither,
162
		  SampleFormat src_format, ConstBuffer<void> src) noexcept
163
{
164
	switch (src_format) {
165 166
	case SampleFormat::UNDEFINED:
	case SampleFormat::DSD:
167 168
		break;

169
	case SampleFormat::S8:
170
		return pcm_allocate_8_to_16(buffer,
171
					    ConstBuffer<int8_t>::FromVoid(src));
172

173
	case SampleFormat::S16:
174
		return ConstBuffer<int16_t>::FromVoid(src);
175

176
	case SampleFormat::S24_P32:
Max Kellermann's avatar
Max Kellermann committed
177
		return pcm_allocate_24p32_to_16(buffer, dither,
178
						ConstBuffer<int32_t>::FromVoid(src));
179

180
	case SampleFormat::S32:
Max Kellermann's avatar
Max Kellermann committed
181
		return pcm_allocate_32_to_16(buffer, dither,
182
					     ConstBuffer<int32_t>::FromVoid(src));
183

184
	case SampleFormat::FLOAT:
Max Kellermann's avatar
Max Kellermann committed
185
		return pcm_allocate_float_to_16(buffer,
186
						ConstBuffer<float>::FromVoid(src));
187 188
	}

189
	return nullptr;
190 191
}

192 193 194
struct Convert8To24
	: PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S8,
						  SampleFormat::S24_P32>> {};
195

196 197 198
struct Convert16To24
	: PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S16,
						  SampleFormat::S24_P32>> {};
199

200 201
static ConstBuffer<int32_t>
pcm_allocate_8_to_24(PcmBuffer &buffer, ConstBuffer<int8_t> src)
202
{
203
	return AllocateConvert(buffer, Convert8To24(), src);
204 205
}

206 207
static ConstBuffer<int32_t>
pcm_allocate_16_to_24(PcmBuffer &buffer, ConstBuffer<int16_t> src)
208
{
209
	return AllocateConvert(buffer, Convert16To24(), src);
210 211
}

212 213 214 215
struct Convert32To24
	: PerSampleConvert<RightShiftSampleConvert<SampleFormat::S32,
						   SampleFormat::S24_P32>> {};

216 217
static ConstBuffer<int32_t>
pcm_allocate_32_to_24(PcmBuffer &buffer, ConstBuffer<int32_t> src)
218
{
219
	return AllocateConvert(buffer, Convert32To24(), src);
220 221
}

222
static ConstBuffer<int32_t>
223
pcm_allocate_float_to_24(PcmBuffer &buffer, ConstBuffer<float> src)
224
{
225
	return AllocateFromFloat<SampleFormat::S24_P32>(buffer, src);
226 227
}

228
ConstBuffer<int32_t>
229
pcm_convert_to_24(PcmBuffer &buffer,
230
		  SampleFormat src_format, ConstBuffer<void> src) noexcept
231
{
232
	switch (src_format) {
233 234
	case SampleFormat::UNDEFINED:
	case SampleFormat::DSD:
235 236
		break;

237
	case SampleFormat::S8:
238
		return pcm_allocate_8_to_24(buffer,
239
					    ConstBuffer<int8_t>::FromVoid(src));
240

241
	case SampleFormat::S16:
242
		return pcm_allocate_16_to_24(buffer,
243
					     ConstBuffer<int16_t>::FromVoid(src));
244

245
	case SampleFormat::S24_P32:
246
		return ConstBuffer<int32_t>::FromVoid(src);
247

248
	case SampleFormat::S32:
Max Kellermann's avatar
Max Kellermann committed
249
		return pcm_allocate_32_to_24(buffer,
250
					     ConstBuffer<int32_t>::FromVoid(src));
251

252
	case SampleFormat::FLOAT:
253 254
		return pcm_allocate_float_to_24(buffer,
						ConstBuffer<float>::FromVoid(src));
255 256
	}

257
	return nullptr;
258
}
259

260 261 262
struct Convert8To32
	: PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S8,
						  SampleFormat::S32>> {};
263

264 265 266
struct Convert16To32
	: PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S16,
						  SampleFormat::S32>> {};
267

268 269 270
struct Convert24To32
	: PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S24_P32,
						  SampleFormat::S32>> {};
271

272 273
static ConstBuffer<int32_t>
pcm_allocate_8_to_32(PcmBuffer &buffer, ConstBuffer<int8_t> src)
274
{
275
	return AllocateConvert(buffer, Convert8To32(), src);
276 277
}

278 279
static ConstBuffer<int32_t>
pcm_allocate_16_to_32(PcmBuffer &buffer, ConstBuffer<int16_t> src)
280
{
281
	return AllocateConvert(buffer, Convert16To32(), src);
282 283
}

284 285
static ConstBuffer<int32_t>
pcm_allocate_24p32_to_32(PcmBuffer &buffer, ConstBuffer<int32_t> src)
286
{
287
	return AllocateConvert(buffer, Convert24To32(), src);
288 289
}

290 291
static ConstBuffer<int32_t>
pcm_allocate_float_to_32(PcmBuffer &buffer, ConstBuffer<float> src)
292
{
293
	return AllocateFromFloat<SampleFormat::S32>(buffer, src);
294 295
}

296
ConstBuffer<int32_t>
297
pcm_convert_to_32(PcmBuffer &buffer,
298
		  SampleFormat src_format, ConstBuffer<void> src) noexcept
299
{
300
	switch (src_format) {
301 302
	case SampleFormat::UNDEFINED:
	case SampleFormat::DSD:
303 304
		break;

305
	case SampleFormat::S8:
Max Kellermann's avatar
Max Kellermann committed
306
		return pcm_allocate_8_to_32(buffer,
307
					    ConstBuffer<int8_t>::FromVoid(src));
308

309
	case SampleFormat::S16:
Max Kellermann's avatar
Max Kellermann committed
310
		return pcm_allocate_16_to_32(buffer,
311
					     ConstBuffer<int16_t>::FromVoid(src));
312

313
	case SampleFormat::S24_P32:
Max Kellermann's avatar
Max Kellermann committed
314
		return pcm_allocate_24p32_to_32(buffer,
315
						ConstBuffer<int32_t>::FromVoid(src));
316

317
	case SampleFormat::S32:
318
		return ConstBuffer<int32_t>::FromVoid(src);
319

320
	case SampleFormat::FLOAT:
Max Kellermann's avatar
Max Kellermann committed
321
		return pcm_allocate_float_to_32(buffer,
322
						ConstBuffer<float>::FromVoid(src));
323 324
	}

325
	return nullptr;
326
}
327

328 329
struct Convert8ToFloat
	: PerSampleConvert<IntegerToFloatSampleConvert<SampleFormat::S8>> {};
330

331 332 333 334 335 336 337 338
struct Convert16ToFloat
	: PerSampleConvert<IntegerToFloatSampleConvert<SampleFormat::S16>> {};

struct Convert24ToFloat
	: PerSampleConvert<IntegerToFloatSampleConvert<SampleFormat::S24_P32>> {};

struct Convert32ToFloat
	: PerSampleConvert<IntegerToFloatSampleConvert<SampleFormat::S32>> {};
339

340 341
static ConstBuffer<float>
pcm_allocate_8_to_float(PcmBuffer &buffer, ConstBuffer<int8_t> src)
342
{
343
	return AllocateConvert(buffer, Convert8ToFloat(), src);
344 345
}

346 347
static ConstBuffer<float>
pcm_allocate_16_to_float(PcmBuffer &buffer, ConstBuffer<int16_t> src)
348
{
349
	return AllocateConvert(buffer, Convert16ToFloat(), src);
350 351
}

352 353
static ConstBuffer<float>
pcm_allocate_24p32_to_float(PcmBuffer &buffer, ConstBuffer<int32_t> src)
354
{
355
	return AllocateConvert(buffer, Convert24ToFloat(), src);
356 357
}

358 359
static ConstBuffer<float>
pcm_allocate_32_to_float(PcmBuffer &buffer, ConstBuffer<int32_t> src)
360
{
361
	return AllocateConvert(buffer, Convert32ToFloat(), src);
362 363
}

364
ConstBuffer<float>
365
pcm_convert_to_float(PcmBuffer &buffer,
366
		     SampleFormat src_format, ConstBuffer<void> src) noexcept
367 368
{
	switch (src_format) {
369 370
	case SampleFormat::UNDEFINED:
	case SampleFormat::DSD:
371 372
		break;

373
	case SampleFormat::S8:
374
		return pcm_allocate_8_to_float(buffer,
375
					       ConstBuffer<int8_t>::FromVoid(src));
376

377
	case SampleFormat::S16:
378
		return pcm_allocate_16_to_float(buffer,
379
					       ConstBuffer<int16_t>::FromVoid(src));
380

381
	case SampleFormat::S32:
382
		return pcm_allocate_32_to_float(buffer,
383 384 385 386 387
					       ConstBuffer<int32_t>::FromVoid(src));

	case SampleFormat::S24_P32:
		return pcm_allocate_24p32_to_float(buffer,
						   ConstBuffer<int32_t>::FromVoid(src));
388

389
	case SampleFormat::FLOAT:
390
		return ConstBuffer<float>::FromVoid(src);
391 392
	}

393
	return nullptr;
394
}