Add excessFileFrames at the beginning of the file dataRecenter the 4-sample splines from -1 to 2 (relative to the interpolated sample) rather than 0 to 3

This commit is contained in:
Paul Ferrand 2020-05-28 23:34:48 +02:00 committed by Jean Pierre Cimalando
parent c48e14ee2b
commit 590af85823
6 changed files with 44 additions and 19 deletions

View file

@ -27,6 +27,7 @@
#include "FileInstrument.h" #include "FileInstrument.h"
#include "Buffer.h" #include "Buffer.h"
#include "AudioBuffer.h" #include "AudioBuffer.h"
#include "AudioSpan.h"
#include "Config.h" #include "Config.h"
#include "Debug.h" #include "Debug.h"
#include "Oversampler.h" #include "Oversampler.h"
@ -42,7 +43,7 @@ template <class T>
void readBaseFile(SndfileHandle& sndFile, sfz::AudioBuffer<T>& output, uint32_t numFrames, bool reverse) void readBaseFile(SndfileHandle& sndFile, sfz::AudioBuffer<T>& output, uint32_t numFrames, bool reverse)
{ {
output.reset(); output.reset();
output.resize(numFrames + sfz::config::excessFileFrames); output.resize(numFrames + 2 * sfz::config::excessFileFrames);
if (reverse) if (reverse)
sndFile.seek(-static_cast<sf_count_t>(numFrames), SEEK_END); sndFile.seek(-static_cast<sf_count_t>(numFrames), SEEK_END);
@ -52,20 +53,23 @@ void readBaseFile(SndfileHandle& sndFile, sfz::AudioBuffer<T>& output, uint32_t
if (channels == 1) { if (channels == 1) {
output.addChannel(); output.addChannel();
output.clear(); output.clear();
sndFile.readf(output.channelWriter(0), numFrames); sndFile.readf(output.channelWriter(0) + sfz::config::excessFileFrames, numFrames);
} else if (channels == 2) { } else if (channels == 2) {
output.addChannel(); output.addChannel();
output.addChannel(); output.addChannel();
output.clear(); output.clear();
sfz::Buffer<T> tempReadBuffer { 2 * numFrames }; sfz::Buffer<T> tempReadBuffer { 2 * numFrames };
sndFile.readf(tempReadBuffer.data(), numFrames); sndFile.readf(tempReadBuffer.data(), numFrames);
sfz::readInterleaved<T>(tempReadBuffer, output.getSpan(0).first(numFrames), output.getSpan(1).first(numFrames)); sfz::readInterleaved<T>(tempReadBuffer,
output.getSpan(0).subspan(sfz::config::excessFileFrames, numFrames),
output.getSpan(1).subspan(sfz::config::excessFileFrames, numFrames)
);
} }
if (reverse) { if (reverse) {
for (unsigned c = 0; c < channels; ++c) { for (unsigned c = 0; c < channels; ++c) {
// TODO: consider optimizing with SIMD // TODO: consider optimizing with SIMD
absl::Span<float> channel = output.getSpan(c).first(numFrames); absl::Span<float> channel = output.getSpan(c).subspan(sfz::config::excessFileFrames, numFrames);
std::reverse(channel.begin(), channel.end()); std::reverse(channel.begin(), channel.end());
} }
} }
@ -80,10 +84,12 @@ std::unique_ptr<sfz::AudioBuffer<T>> readFromFile(SndfileHandle& sndFile, uint32
if (factor == sfz::Oversampling::x1) if (factor == sfz::Oversampling::x1)
return baseBuffer; return baseBuffer;
auto outputBuffer = absl::make_unique<sfz::AudioBuffer<T>>(sndFile.channels(), numFrames * static_cast<int>(factor) + sfz::config::excessFileFrames); auto outputBuffer = absl::make_unique<sfz::AudioBuffer<T>>(sndFile.channels(), numFrames * static_cast<int>(factor) + 2 * sfz::config::excessFileFrames);
outputBuffer->clear(); outputBuffer->clear();
auto baseSpan = sfz::AudioSpan<T>(*baseBuffer).subspan(sfz::config::excessFileFrames, numFrames);
auto outputSpan = sfz::AudioSpan<T>(*outputBuffer).subspan(sfz::config::excessFileFrames, numFrames * static_cast<int>(factor));
sfz::Oversampler oversampler { factor }; sfz::Oversampler oversampler { factor };
oversampler.stream(*baseBuffer, *outputBuffer); oversampler.stream(baseSpan, outputSpan);
return outputBuffer; return outputBuffer;
} }
@ -100,10 +106,12 @@ void streamFromFile(SndfileHandle& sndFile, uint32_t numFrames, sfz::Oversamplin
auto baseBuffer = readFromFile<T>(sndFile, numFrames, sfz::Oversampling::x1, reverse); auto baseBuffer = readFromFile<T>(sndFile, numFrames, sfz::Oversampling::x1, reverse);
output.reset(); output.reset();
output.addChannels(baseBuffer->getNumChannels()); output.addChannels(baseBuffer->getNumChannels());
output.resize(numFrames * static_cast<int>(factor) + sfz::config::excessFileFrames); output.resize(numFrames * static_cast<int>(factor) + 2 * sfz::config::excessFileFrames);
output.clear(); output.clear();
sfz::Oversampler oversampler { factor }; sfz::Oversampler oversampler { factor };
oversampler.stream(*baseBuffer, output, filledFrames); auto baseSpan = sfz::AudioSpan<T>(*baseBuffer).subspan(sfz::config::excessFileFrames, numFrames);
auto outputSpan = sfz::AudioSpan<T>(output).subspan(sfz::config::excessFileFrames, numFrames * static_cast<int>(factor));
oversampler.stream(baseSpan, outputSpan, filledFrames);
} }
sfz::FilePool::FilePool(sfz::Logger& logger) sfz::FilePool::FilePool(sfz::Logger& logger)

View file

@ -66,11 +66,14 @@ struct FilePromise
{ {
if (dataStatus == DataStatus::Ready) if (dataStatus == DataStatus::Ready)
return AudioSpan<const float>(fileData) return AudioSpan<const float>(fileData)
.first(fileData.getNumFrames() - sfz::config::excessFileFrames); .subspan(sfz::config::excessFileFrames,
fileData.getNumFrames() - 2 * sfz::config::excessFileFrames);
else if (availableFrames > preloadedData->getNumFrames()) else if (availableFrames > preloadedData->getNumFrames())
return AudioSpan<const float>(fileData).first(availableFrames); return AudioSpan<const float>(fileData).subspan(sfz::config::excessFileFrames, availableFrames);
else else
return AudioSpan<const float>(*preloadedData); return AudioSpan<const float>(*preloadedData)
.subspan(sfz::config::excessFileFrames,
preloadedData->getNumFrames() - sfz::config::excessFileFrames);
} }
void reset() void reset()

View file

@ -17,6 +17,19 @@ enum InterpolatorModel : int {
kInterpolatorBspline3, kInterpolatorBspline3,
}; };
/**
* @brief Interpolate from a vector of values
*
* @tparam M the interpolator model
* @tparam R the sample type
* @param values Pointer to a value in a larger vector of values.
* Depending on the interpolator the algorithm may
* read samples before and after. Usually you need
* to ensure that you have order - 1 samples available
* before and after the pointer, padding if necessary.
* @param coeff the interpolation coefficient
* @return R
*/
template <InterpolatorModel M, class R> template <InterpolatorModel M, class R>
R interpolate(const R* values, R coeff); R interpolate(const R* values, R coeff);

View file

@ -44,7 +44,7 @@ public:
{ {
__m128 x = _mm_sub_ps(_mm_setr_ps(-1, 0, 1, 2), _mm_set1_ps(coeff)); __m128 x = _mm_sub_ps(_mm_setr_ps(-1, 0, 1, 2), _mm_set1_ps(coeff));
__m128 h = hermite3x4(x); __m128 h = hermite3x4(x);
__m128 y = _mm_mul_ps(h, _mm_loadu_ps(values)); __m128 y = _mm_mul_ps(h, _mm_loadu_ps(values - 1));
// sum 4 to 1 // sum 4 to 1
__m128 xmm0 = y; __m128 xmm0 = y;
__m128 xmm1 = _mm_shuffle_ps(xmm0, xmm0, 0xe5); __m128 xmm1 = _mm_shuffle_ps(xmm0, xmm0, 0xe5);
@ -68,8 +68,8 @@ public:
static inline R process(const R* values, R coeff) static inline R process(const R* values, R coeff)
{ {
R y = 0; R y = 0;
for (int i = 0; i < 4; ++i) { for (int i = -1; i < 3; ++i) {
R h = hermite3<R>(i - 1 - coeff); R h = hermite3<R>(i - coeff);
y += h * values[i]; y += h * values[i];
} }
return y; return y;
@ -88,7 +88,7 @@ public:
{ {
__m128 x = _mm_sub_ps(_mm_setr_ps(-1, 0, 1, 2), _mm_set1_ps(coeff)); __m128 x = _mm_sub_ps(_mm_setr_ps(-1, 0, 1, 2), _mm_set1_ps(coeff));
__m128 h = bspline3x4(x); __m128 h = bspline3x4(x);
__m128 y = _mm_mul_ps(h, _mm_loadu_ps(values)); __m128 y = _mm_mul_ps(h, _mm_loadu_ps(values - 1));
// sum 4 to 1 // sum 4 to 1
__m128 xmm0 = y; __m128 xmm0 = y;
__m128 xmm1 = _mm_shuffle_ps(xmm0, xmm0, 0xe5); __m128 xmm1 = _mm_shuffle_ps(xmm0, xmm0, 0xe5);
@ -112,8 +112,8 @@ public:
static inline R process(const R* values, R coeff) static inline R process(const R* values, R coeff)
{ {
R y = 0; R y = 0;
for (int i = 0; i < 4; ++i) { for (int i = -1; i < 3; ++i) {
R h = bspline3<R>(i - 1 - coeff); R h = bspline3<R>(i - coeff);
y += h * values[i]; y += h * values[i];
} }
return y; return y;

View file

@ -61,7 +61,7 @@ sfz::Oversampler::Oversampler(sfz::Oversampling factor, size_t chunkSize)
} }
void sfz::Oversampler::stream(const sfz::AudioBuffer<float>& input, sfz::AudioBuffer<float>& output, std::atomic<size_t>* framesReady) void sfz::Oversampler::stream(AudioSpan<float> input, AudioSpan<float> output, std::atomic<size_t>* framesReady)
{ {
ASSERT(output.getNumFrames() >= input.getNumFrames() * static_cast<int>(factor)); ASSERT(output.getNumFrames() >= input.getNumFrames() * static_cast<int>(factor));
ASSERT(output.getNumChannels() == input.getNumChannels()); ASSERT(output.getNumChannels() == input.getNumChannels());

View file

@ -11,6 +11,7 @@
#include "Debug.h" #include "Debug.h"
#include "Buffer.h" #include "Buffer.h"
#include "AudioBuffer.h" #include "AudioBuffer.h"
#include "AudioSpan.h"
#include "Config.h" #include "Config.h"
namespace sfz { namespace sfz {
@ -39,7 +40,7 @@ public:
* @param output * @param output
* @param framesReady an atomic counter for the ready frames. If null no signaling is done. * @param framesReady an atomic counter for the ready frames. If null no signaling is done.
*/ */
void stream(const AudioBuffer<float>& input, AudioBuffer<float>& output, std::atomic<size_t>* framesReady = nullptr); void stream(AudioSpan<float> input, AudioSpan<float> output, std::atomic<size_t>* framesReady = nullptr);
Oversampler() = delete; Oversampler() = delete;
Oversampler(const Oversampler&) = delete; Oversampler(const Oversampler&) = delete;