Commit 08e6cf3d authored by Max Kellermann's avatar Max Kellermann

pcm/PcmFormat: eliminate more duplicate code with templates

Refactor the conversion functions to classes and pass an instance to the new function AllocateConvert().
parent 044134eb
...@@ -450,6 +450,8 @@ libpcm_a_SOURCES = \ ...@@ -450,6 +450,8 @@ libpcm_a_SOURCES = \
src/pcm/PcmChannels.cxx src/pcm/PcmChannels.hxx \ src/pcm/PcmChannels.cxx src/pcm/PcmChannels.hxx \
src/pcm/PcmPack.cxx src/pcm/PcmPack.hxx \ src/pcm/PcmPack.cxx src/pcm/PcmPack.hxx \
src/pcm/PcmFormat.cxx src/pcm/PcmFormat.hxx \ src/pcm/PcmFormat.cxx src/pcm/PcmFormat.hxx \
src/pcm/FloatConvert.hxx \
src/pcm/ShiftConvert.hxx \
src/pcm/FormatConverter.cxx src/pcm/FormatConverter.hxx \ src/pcm/FormatConverter.cxx src/pcm/FormatConverter.hxx \
src/pcm/ChannelsConverter.cxx src/pcm/ChannelsConverter.hxx \ src/pcm/ChannelsConverter.cxx src/pcm/ChannelsConverter.hxx \
src/pcm/Resampler.hxx \ src/pcm/Resampler.hxx \
......
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* 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.
*
* 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.
*/
#ifndef MPD_PCM_FLOAT_CONVERT_HXX
#define MPD_PCM_FLOAT_CONVERT_HXX
#include "Traits.hxx"
/**
* Convert from float to an integer sample format.
*/
template<SampleFormat F, class Traits=SampleTraits<F>>
struct FloatToIntegerSampleConvert {
typedef SampleTraits<SampleFormat::FLOAT> SrcTraits;
typedef Traits DstTraits;
typedef typename SrcTraits::value_type SV;
typedef typename SrcTraits::long_type SL;
typedef typename DstTraits::value_type DV;
static constexpr SV factor = 1 << (DstTraits::BITS - 1);
gcc_const
static DV Convert(SV src) {
return PcmClamp<F, Traits>(SL(src * factor));
}
};
/**
* Convert from an integer sample format to float.
*/
template<SampleFormat F, class Traits=SampleTraits<F>>
struct IntegerToFloatSampleConvert {
typedef SampleTraits<SampleFormat::FLOAT> DstTraits;
typedef Traits SrcTraits;
typedef typename SrcTraits::value_type SV;
typedef typename DstTraits::value_type DV;
static constexpr DV factor = 0.5 / (1 << (SrcTraits::BITS - 2));
gcc_const
static DV Convert(SV src) {
return DV(src) * factor;
}
};
#endif
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include "PcmBuffer.hxx" #include "PcmBuffer.hxx"
#include "PcmUtils.hxx" #include "PcmUtils.hxx"
#include "Traits.hxx" #include "Traits.hxx"
#include "FloatConvert.hxx"
#include "ShiftConvert.hxx"
#include "util/ConstBuffer.hxx" #include "util/ConstBuffer.hxx"
#include "util/WritableBuffer.hxx" #include "util/WritableBuffer.hxx"
...@@ -34,73 +36,92 @@ ToConst(WritableBuffer<T> b) ...@@ -34,73 +36,92 @@ ToConst(WritableBuffer<T> b)
return { b.data, b.size }; return { b.data, b.size };
} }
static void /**
pcm_convert_8_to_16(int16_t *out, const int8_t *in, size_t n) * Wrapper for a class that converts one sample at a time into one
{ * that converts a buffer at a time.
for (size_t i = 0; i != n; ++i) */
out[i] = in[i] << 8; 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]);
}
};
static void struct Convert8To16
pcm_convert_24_to_16(PcmDither &dither, : PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S8,
int16_t *out, const int32_t *in, const int32_t *in_end) SampleFormat::S16>> {};
{
dither.Dither24To16(out, in, in_end);
}
static void struct Convert24To16 {
pcm_convert_32_to_16(PcmDither &dither, typedef SampleTraits<SampleFormat::S24_P32> SrcTraits;
int16_t *out, const int32_t *in, const int32_t *in_end) typedef SampleTraits<SampleFormat::S16> DstTraits;
{
dither.Dither32To16(out, in, in_end);
}
template<SampleFormat F, class Traits=SampleTraits<F>> PcmDither &dither;
static void
ConvertFromFloat(typename Traits::pointer_type dest, Convert24To16(PcmDither &_dither):dither(_dither) {}
const float *src, size_t n)
{ void Convert(int16_t *out, const int32_t *in, size_t n) {
const float factor = 1 << (Traits::BITS - 1); dither.Dither24To16(out, in, in + n);
}
};
struct Convert32To16 {
typedef SampleTraits<SampleFormat::S32> SrcTraits;
typedef SampleTraits<SampleFormat::S16> DstTraits;
for (size_t i = 0; i != n; ++i) { PcmDither &dither;
typename Traits::long_type sample(src[i] * factor);
dest[i] = PcmClamp<F, Traits>(sample); Convert32To16(PcmDither &_dither):dither(_dither) {}
void Convert(int16_t *out, const int32_t *in, size_t n) {
dither.Dither32To16(out, in, in + n);
} }
};
template<SampleFormat F, class Traits=SampleTraits<F>>
struct FloatToInteger
: PerSampleConvert<FloatToIntegerSampleConvert<F, Traits>> {};
template<class C>
static WritableBuffer<typename C::DstTraits::value_type>
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 };
} }
template<SampleFormat F, class Traits=SampleTraits<F>> template<SampleFormat F, class Traits=SampleTraits<F>>
static WritableBuffer<typename Traits::value_type> static WritableBuffer<typename Traits::value_type>
AllocateFromFloat(PcmBuffer &buffer, ConstBuffer<float> src) AllocateFromFloat(PcmBuffer &buffer, ConstBuffer<float> src)
{ {
auto dest = buffer.GetT<typename Traits::value_type>(src.size); return AllocateConvert(buffer, FloatToInteger<F, Traits>(), src);
ConvertFromFloat<F, Traits>(dest, src.data, src.size);
return { dest, src.size };
} }
static ConstBuffer<int16_t> static ConstBuffer<int16_t>
pcm_allocate_8_to_16(PcmBuffer &buffer, ConstBuffer<int8_t> src) pcm_allocate_8_to_16(PcmBuffer &buffer, ConstBuffer<int8_t> src)
{ {
auto dest = buffer.GetT<int16_t>(src.size); return ToConst(AllocateConvert(buffer, Convert8To16(), src));
pcm_convert_8_to_16(dest, src.data, src.size);
return { dest, src.size };
} }
static ConstBuffer<int16_t> static ConstBuffer<int16_t>
pcm_allocate_24p32_to_16(PcmBuffer &buffer, PcmDither &dither, pcm_allocate_24p32_to_16(PcmBuffer &buffer, PcmDither &dither,
ConstBuffer<int32_t> src) ConstBuffer<int32_t> src)
{ {
auto dest = buffer.GetT<int16_t>(src.size); return ToConst(AllocateConvert(buffer, Convert24To16(dither), src));
pcm_convert_24_to_16(dither, dest, src.data, src.end());
return { dest, src.size };
} }
static ConstBuffer<int16_t> static ConstBuffer<int16_t>
pcm_allocate_32_to_16(PcmBuffer &buffer, PcmDither &dither, pcm_allocate_32_to_16(PcmBuffer &buffer, PcmDither &dither,
ConstBuffer<int32_t> src) ConstBuffer<int32_t> src)
{ {
auto dest = buffer.GetT<int16_t>(src.size); return ToConst(AllocateConvert(buffer, Convert32To16(dither), src));
pcm_convert_32_to_16(dither, dest, src.data, src.end());
return { dest, src.size };
} }
static ConstBuffer<int16_t> static ConstBuffer<int16_t>
...@@ -141,51 +162,34 @@ pcm_convert_to_16(PcmBuffer &buffer, PcmDither &dither, ...@@ -141,51 +162,34 @@ pcm_convert_to_16(PcmBuffer &buffer, PcmDither &dither,
return nullptr; return nullptr;
} }
static void struct Convert8To24
pcm_convert_8_to_24(int32_t *out, const int8_t *in, size_t n) : PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S8,
{ SampleFormat::S24_P32>> {};
for (size_t i = 0; i != n; ++i)
out[i] = in[i] << 16;
}
static void struct Convert16To24
pcm_convert_16_to_24(int32_t *out, const int16_t *in, size_t n) : PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S16,
{ SampleFormat::S24_P32>> {};
for (size_t i = 0; i != n; ++i)
out[i] = in[i] << 8;
}
static void
pcm_convert_32_to_24(int32_t *gcc_restrict out,
const int32_t *gcc_restrict in,
size_t n)
{
for (size_t i = 0; i != n; ++i)
out[i] = in[i] >> 8;
}
static ConstBuffer<int32_t> static ConstBuffer<int32_t>
pcm_allocate_8_to_24(PcmBuffer &buffer, ConstBuffer<int8_t> src) pcm_allocate_8_to_24(PcmBuffer &buffer, ConstBuffer<int8_t> src)
{ {
auto dest = buffer.GetT<int32_t>(src.size); return ToConst(AllocateConvert(buffer, Convert8To24(), src));
pcm_convert_8_to_24(dest, src.data, src.size);
return { dest, src.size };
} }
static ConstBuffer<int32_t> static ConstBuffer<int32_t>
pcm_allocate_16_to_24(PcmBuffer &buffer, ConstBuffer<int16_t> src) pcm_allocate_16_to_24(PcmBuffer &buffer, ConstBuffer<int16_t> src)
{ {
auto dest = buffer.GetT<int32_t>(src.size); return ToConst(AllocateConvert(buffer, Convert16To24(), src));
pcm_convert_16_to_24(dest, src.data, src.size);
return { dest, src.size };
} }
struct Convert32To24
: PerSampleConvert<RightShiftSampleConvert<SampleFormat::S32,
SampleFormat::S24_P32>> {};
static ConstBuffer<int32_t> static ConstBuffer<int32_t>
pcm_allocate_32_to_24(PcmBuffer &buffer, ConstBuffer<int32_t> src) pcm_allocate_32_to_24(PcmBuffer &buffer, ConstBuffer<int32_t> src)
{ {
auto dest = buffer.GetT<int32_t>(src.size); return ToConst(AllocateConvert(buffer, Convert32To24(), src));
pcm_convert_32_to_24(dest, src.data, src.size);
return { dest, src.size };
} }
static WritableBuffer<int32_t> static WritableBuffer<int32_t>
...@@ -226,51 +230,34 @@ pcm_convert_to_24(PcmBuffer &buffer, ...@@ -226,51 +230,34 @@ pcm_convert_to_24(PcmBuffer &buffer,
return nullptr; return nullptr;
} }
static void struct Convert8To32
pcm_convert_8_to_32(int32_t *out, const int8_t *in, size_t n) : PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S8,
{ SampleFormat::S32>> {};
for (size_t i = 0; i != n; ++i)
out[i] = in[i] << 24;
}
static void struct Convert16To32
pcm_convert_16_to_32(int32_t *out, const int16_t *in, size_t n) : PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S16,
{ SampleFormat::S32>> {};
for (size_t i = 0; i != n; ++i)
out[i] = in[i] << 16;
}
static void struct Convert24To32
pcm_convert_24_to_32(int32_t *gcc_restrict out, : PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S24_P32,
const int32_t *gcc_restrict in, SampleFormat::S32>> {};
size_t n)
{
for (size_t i = 0; i != n; ++i)
out[i] = in[i] << 8;
}
static ConstBuffer<int32_t> static ConstBuffer<int32_t>
pcm_allocate_8_to_32(PcmBuffer &buffer, ConstBuffer<int8_t> src) pcm_allocate_8_to_32(PcmBuffer &buffer, ConstBuffer<int8_t> src)
{ {
auto dest = buffer.GetT<int32_t>(src.size); return ToConst(AllocateConvert(buffer, Convert8To32(), src));
pcm_convert_8_to_32(dest, src.data, src.size);
return { dest, src.size };
} }
static ConstBuffer<int32_t> static ConstBuffer<int32_t>
pcm_allocate_16_to_32(PcmBuffer &buffer, ConstBuffer<int16_t> src) pcm_allocate_16_to_32(PcmBuffer &buffer, ConstBuffer<int16_t> src)
{ {
auto dest = buffer.GetT<int32_t>(src.size); return ToConst(AllocateConvert(buffer, Convert16To32(), src));
pcm_convert_16_to_32(dest, src.data, src.size);
return { dest, src.size };
} }
static ConstBuffer<int32_t> static ConstBuffer<int32_t>
pcm_allocate_24p32_to_32(PcmBuffer &buffer, ConstBuffer<int32_t> src) pcm_allocate_24p32_to_32(PcmBuffer &buffer, ConstBuffer<int32_t> src)
{ {
auto dest = buffer.GetT<int32_t>(src.size); return ToConst(AllocateConvert(buffer, Convert24To32(), src));
pcm_convert_24_to_32(dest, src.data, src.size);
return { dest, src.size };
} }
static ConstBuffer<int32_t> static ConstBuffer<int32_t>
...@@ -280,7 +267,7 @@ pcm_allocate_float_to_32(PcmBuffer &buffer, ConstBuffer<float> src) ...@@ -280,7 +267,7 @@ pcm_allocate_float_to_32(PcmBuffer &buffer, ConstBuffer<float> src)
auto dest = pcm_allocate_float_to_24(buffer, src); auto dest = pcm_allocate_float_to_24(buffer, src);
/* convert to 32 bit in-place */ /* convert to 32 bit in-place */
pcm_convert_24_to_32(dest.data, dest.data, src.size); Convert24To32().Convert(dest.data, dest.data, src.size);
return ToConst(dest); return ToConst(dest);
} }
...@@ -316,49 +303,40 @@ pcm_convert_to_32(PcmBuffer &buffer, ...@@ -316,49 +303,40 @@ pcm_convert_to_32(PcmBuffer &buffer,
return nullptr; return nullptr;
} }
template<SampleFormat F, class Traits=SampleTraits<F>> struct Convert8ToFloat
static void : PerSampleConvert<IntegerToFloatSampleConvert<SampleFormat::S8>> {};
ConvertToFloat(float *dest,
typename Traits::const_pointer_type src,
size_t n)
{
constexpr float factor = 0.5 / (1 << (Traits::BITS - 2));
for (size_t i = 0; i != n; ++i)
dest[i] = float(src[i]) * factor;
}
template<SampleFormat F, class Traits=SampleTraits<F>> struct Convert16ToFloat
static ConstBuffer<float> : PerSampleConvert<IntegerToFloatSampleConvert<SampleFormat::S16>> {};
AllocateToFloat(PcmBuffer &buffer,
ConstBuffer<typename Traits::value_type> src) struct Convert24ToFloat
{ : PerSampleConvert<IntegerToFloatSampleConvert<SampleFormat::S24_P32>> {};
float *dest = buffer.GetT<float>(src.size);
ConvertToFloat<F, Traits>(dest, src.data, src.size); struct Convert32ToFloat
return { dest, src.size }; : PerSampleConvert<IntegerToFloatSampleConvert<SampleFormat::S32>> {};
}
static ConstBuffer<float> static ConstBuffer<float>
pcm_allocate_8_to_float(PcmBuffer &buffer, ConstBuffer<int8_t> src) pcm_allocate_8_to_float(PcmBuffer &buffer, ConstBuffer<int8_t> src)
{ {
return AllocateToFloat<SampleFormat::S8>(buffer, src); return ToConst(AllocateConvert(buffer, Convert8ToFloat(), src));
} }
static ConstBuffer<float> static ConstBuffer<float>
pcm_allocate_16_to_float(PcmBuffer &buffer, ConstBuffer<int16_t> src) pcm_allocate_16_to_float(PcmBuffer &buffer, ConstBuffer<int16_t> src)
{ {
return AllocateToFloat<SampleFormat::S16>(buffer, src); return ToConst(AllocateConvert(buffer, Convert16ToFloat(), src));
} }
static ConstBuffer<float> static ConstBuffer<float>
pcm_allocate_24p32_to_float(PcmBuffer &buffer, ConstBuffer<int32_t> src) pcm_allocate_24p32_to_float(PcmBuffer &buffer, ConstBuffer<int32_t> src)
{ {
return AllocateToFloat<SampleFormat::S24_P32>(buffer, src); return ToConst(AllocateConvert(buffer, Convert24ToFloat(), src));
} }
static ConstBuffer<float> static ConstBuffer<float>
pcm_allocate_32_to_float(PcmBuffer &buffer, ConstBuffer<int32_t> src) pcm_allocate_32_to_float(PcmBuffer &buffer, ConstBuffer<int32_t> src)
{ {
return AllocateToFloat<SampleFormat::S32>(buffer, src); return ToConst(AllocateConvert(buffer, Convert32ToFloat(), src));
} }
ConstBuffer<float> ConstBuffer<float>
......
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* 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.
*
* 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.
*/
#ifndef MPD_PCM_SHIFT_CONVERT_HXX
#define MPD_PCM_SHIFT_CONVERT_HXX
#include "Traits.hxx"
/**
* Convert from one integer sample format to another by shifting bits
* to the left.
*/
template<SampleFormat SF, SampleFormat DF,
class ST=SampleTraits<SF>,
class DT=SampleTraits<DF>>
struct LeftShiftSampleConvert {
typedef ST SrcTraits;
typedef DT DstTraits;
typedef typename SrcTraits::value_type SV;
typedef typename DstTraits::value_type DV;
static_assert(SrcTraits::BITS < DstTraits::BITS,
"Source format must be smaller than destination format");
constexpr static DV Convert(SV src) {
return DV(src) << (DstTraits::BITS - SrcTraits::BITS);
}
};
/**
* Convert from one integer sample format to another by shifting bits
* to the right.
*/
template<SampleFormat SF, SampleFormat DF,
class ST=SampleTraits<SF>,
class DT=SampleTraits<DF>>
struct RightShiftSampleConvert {
typedef ST SrcTraits;
typedef DT DstTraits;
typedef typename SrcTraits::value_type SV;
typedef typename DstTraits::value_type DV;
static_assert(SrcTraits::BITS > DstTraits::BITS,
"Source format must be smaller than destination format");
constexpr static DV Convert(SV src) {
return src >> (SrcTraits::BITS - DstTraits::BITS);
}
};
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment