Skip to content

Commit 716c704

Browse files
committed
Tweaked resample code
Also added new ResampleRange function to do basic linear interpolated ressampling of one range to anotehr
1 parent a5e777e commit 716c704

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed

dsp_resample.hpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,113 @@
3636
namespace dsp
3737
{
3838

39+
/*!
40+
* \brief Round a floating type's value down to the integer value below, converting to an integer
41+
* type.
42+
* \param[in] value - Floating point value to round.
43+
* \return Integer value.
44+
*/
45+
template <typename FloatT, typename IntegerT> IntegerT FloatToInt(FloatT value)
46+
{
47+
static_assert(std::is_floating_point<FloatT>::value, "invalid floating point type");
48+
static_assert(std::is_integral<IntegerT>::value, "invalid integral point type");
49+
50+
return (value < FloatT(0)) ? IntegerT(std::ceil(value) - 0.5)
51+
: IntegerT(std::floor(value) + 0.5);
52+
}
53+
54+
/*!
55+
* \brief Resample the input range into the output range using linear interpolation.
56+
* \param[in] first - First item of source range.
57+
* \param[in] last - Last item of source range, same role as end iterator on an STL container.
58+
* \param[out] targetFirst - First item of target range.
59+
* \param[out] targetLast - Last item of target range, same role as end iterator on an STL
60+
* container.
61+
*
62+
* This function works out the resampling factor from the ratio of the size of the
63+
* source range to the target range.
64+
*/
65+
template <typename InIter, typename OutIter>
66+
void ResampleRange(InIter first, InIter last, OutIter targetFirst, OutIter targetLast)
67+
{
68+
// Do we have a valid source range?
69+
auto tmpSourceSize = std::distance(first, last);
70+
71+
DSP_ASSERT_THROW(tmpSourceSize > 0, "std::distance(first, last) <= 0");
72+
73+
auto tmpTargetSize = std::distance(targetFirst, targetLast);
74+
75+
DSP_ASSERT_THROW(tmpTargetSize > 0, "std::distance(targetFirst, targetLast) <= 0");
76+
77+
// Do we need to do any downsampling? If not copy
78+
// source range into target vector and return early.
79+
const auto sourceSize = static_cast<size_t>(tmpSourceSize);
80+
const auto targetSize = static_cast<size_t>(tmpTargetSize);
81+
82+
if (sourceSize == targetSize)
83+
{
84+
std::copy(first, last, targetFirst);
85+
return;
86+
}
87+
88+
// Work out the exact real-valued sample stride.
89+
// This means we can find the exact sample position
90+
// in the original data where our resampled value
91+
// should be taken from for the downsampled data.
92+
// The exact sample position is likely to lie between
93+
// 2 given samples of the source data.
94+
const auto sampleStride =
95+
static_cast<double>(sourceSize - 1) / static_cast<double>(targetSize - 1);
96+
97+
double exactSamplePos = 0.0;
98+
auto finalItem = std::next(first, sourceSize - 1);
99+
100+
// Tracking iterator and sample pos.
101+
auto itrBefore = first;
102+
size_t sampleBefore = 0;
103+
size_t pos = 0;
104+
using out_type_t = typename std::iterator_traits<OutIter>::value_type;
105+
106+
for (auto outIter = targetFirst; outIter != targetLast; std::advance(outIter, 1), ++pos)
107+
{
108+
out_type_t interpolatedSample;
109+
110+
// Keep endpoints as they are.
111+
if (0 == pos)
112+
{
113+
interpolatedSample = *first;
114+
}
115+
else if (pos == targetSize - 1)
116+
{
117+
interpolatedSample = *finalItem;
118+
}
119+
// Downsample into smaller buffer by using
120+
// linear interpolation to maintain more
121+
// accurate amplitude values.
122+
else
123+
{
124+
// Remember previous sample before last exact pos.
125+
const size_t prevSample = sampleBefore;
126+
// Find the sample in the source data just
127+
// before our exact sample position.
128+
sampleBefore = FloatToInt<double, size_t>(exactSamplePos);
129+
// Work our the distance of our exact sample position
130+
// from the sample before as a ratio.
131+
const double ratio = exactSamplePos - double(sampleBefore);
132+
std::advance(itrBefore, sampleBefore - prevSample);
133+
auto itrAfter = std::next(itrBefore);
134+
// Work out an interpolated correction factor to
135+
// add to the sample value of the sample before
136+
// our exact sample position.
137+
const double correction = (*itrAfter - *itrBefore) * ratio;
138+
interpolatedSample = *itrBefore + out_type_t(correction);
139+
}
140+
141+
*outIter = interpolatedSample;
142+
exactSamplePos += sampleStride;
143+
}
144+
}
145+
39146
/*!
40147
* \brief Compute closest resample up and down factors given a real valued resample factor.
41148
* \param[in] requiredResampleFactor - The resample factor we ideally want to use, should be != 1.

0 commit comments

Comments
 (0)