|
36 | 36 | namespace dsp
|
37 | 37 | {
|
38 | 38 |
|
| 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 | + |
39 | 146 | /*!
|
40 | 147 | * \brief Compute closest resample up and down factors given a real valued resample factor.
|
41 | 148 | * \param[in] requiredResampleFactor - The resample factor we ideally want to use, should be != 1.
|
|
0 commit comments