PcmFormat.cxx 10.6 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2014 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 "config.h"
Max Kellermann's avatar
Max Kellermann committed
21
#include "PcmFormat.hxx"
22
#include "PcmBuffer.hxx"
Max Kellermann's avatar
Max Kellermann committed
23
#include "PcmUtils.hxx"
24
#include "Traits.hxx"
25 26
#include "FloatConvert.hxx"
#include "ShiftConvert.hxx"
27
#include "util/ConstBuffer.hxx"
28

29 30
#include "PcmDither.cxx" // including the .cxx file to get inlined templates

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
/**
 * 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 {
	typedef typename C::SrcTraits SrcTraits;
	typedef typename C::DstTraits DstTraits;

	void Convert(typename DstTraits::pointer_type gcc_restrict out,
		     typename SrcTraits::const_pointer_type gcc_restrict in,
		     size_t n) const {
		for (size_t i = 0; i != n; ++i)
			out[i] = C::Convert(in[i]);
	}
};
47

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

52 53 54
struct Convert24To16 {
	typedef SampleTraits<SampleFormat::S24_P32> SrcTraits;
	typedef SampleTraits<SampleFormat::S16> DstTraits;
55

56 57 58 59 60 61 62 63 64 65 66 67
	PcmDither &dither;

	Convert24To16(PcmDither &_dither):dither(_dither) {}

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

struct Convert32To16 {
	typedef SampleTraits<SampleFormat::S32> SrcTraits;
	typedef SampleTraits<SampleFormat::S16> DstTraits;
68

69 70 71 72 73 74
	PcmDither &dither;

	Convert32To16(PcmDither &_dither):dither(_dither) {}

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

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

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
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:
	typedef typename Portable::SrcTraits SrcTraits;
	typedef typename Portable::DstTraits DstTraits;

	void Convert(typename DstTraits::pointer_type out,
		     typename SrcTraits::const_pointer_type in,
		     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

119
template<class C>
120
static ConstBuffer<typename C::DstTraits::value_type>
121 122 123 124 125 126
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 };
127 128
}

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

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

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

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

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

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

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

175
	case SampleFormat::S16:
176
		return ConstBuffer<int16_t>::FromVoid(src);
177

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

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

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

191
	return nullptr;
192 193
}

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

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

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

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

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

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

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

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

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

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

247
	case SampleFormat::S24_P32:
248
		return ConstBuffer<int32_t>::FromVoid(src);
249

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

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

259
	return nullptr;
260
}
261

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

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

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

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

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

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

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

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

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

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

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

319
	case SampleFormat::S32:
320
		return ConstBuffer<int32_t>::FromVoid(src);
321

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

327
	return nullptr;
328
}
329

330 331
struct Convert8ToFloat
	: PerSampleConvert<IntegerToFloatSampleConvert<SampleFormat::S8>> {};
332

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

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

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

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

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

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

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

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

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

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

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

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

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

395
	return nullptr;
396
}