diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index e0028e36..6143ebef 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -27,6 +27,7 @@ #include "FileInstrument.h" #include "Buffer.h" #include "AudioBuffer.h" +#include "AudioSpan.h" #include "Config.h" #include "Debug.h" #include "Oversampler.h" @@ -42,7 +43,7 @@ template void readBaseFile(SndfileHandle& sndFile, sfz::AudioBuffer& output, uint32_t numFrames, bool reverse) { output.reset(); - output.resize(numFrames + sfz::config::excessFileFrames); + output.resize(numFrames + 2 * sfz::config::excessFileFrames); if (reverse) sndFile.seek(-static_cast(numFrames), SEEK_END); @@ -52,20 +53,23 @@ void readBaseFile(SndfileHandle& sndFile, sfz::AudioBuffer& output, uint32_t if (channels == 1) { output.addChannel(); output.clear(); - sndFile.readf(output.channelWriter(0), numFrames); + sndFile.readf(output.channelWriter(0) + sfz::config::excessFileFrames, numFrames); } else if (channels == 2) { output.addChannel(); output.addChannel(); output.clear(); sfz::Buffer tempReadBuffer { 2 * numFrames }; sndFile.readf(tempReadBuffer.data(), numFrames); - sfz::readInterleaved(tempReadBuffer, output.getSpan(0).first(numFrames), output.getSpan(1).first(numFrames)); + sfz::readInterleaved(tempReadBuffer, + output.getSpan(0).subspan(sfz::config::excessFileFrames, numFrames), + output.getSpan(1).subspan(sfz::config::excessFileFrames, numFrames) + ); } if (reverse) { for (unsigned c = 0; c < channels; ++c) { // TODO: consider optimizing with SIMD - absl::Span channel = output.getSpan(c).first(numFrames); + absl::Span channel = output.getSpan(c).subspan(sfz::config::excessFileFrames, numFrames); std::reverse(channel.begin(), channel.end()); } } @@ -80,10 +84,12 @@ std::unique_ptr> readFromFile(SndfileHandle& sndFile, uint32 if (factor == sfz::Oversampling::x1) return baseBuffer; - auto outputBuffer = absl::make_unique>(sndFile.channels(), numFrames * static_cast(factor) + sfz::config::excessFileFrames); + auto outputBuffer = absl::make_unique>(sndFile.channels(), numFrames * static_cast(factor) + 2 * sfz::config::excessFileFrames); outputBuffer->clear(); + auto baseSpan = sfz::AudioSpan(*baseBuffer).subspan(sfz::config::excessFileFrames, numFrames); + auto outputSpan = sfz::AudioSpan(*outputBuffer).subspan(sfz::config::excessFileFrames, numFrames * static_cast(factor)); sfz::Oversampler oversampler { factor }; - oversampler.stream(*baseBuffer, *outputBuffer); + oversampler.stream(baseSpan, outputSpan); return outputBuffer; } @@ -100,10 +106,12 @@ void streamFromFile(SndfileHandle& sndFile, uint32_t numFrames, sfz::Oversamplin auto baseBuffer = readFromFile(sndFile, numFrames, sfz::Oversampling::x1, reverse); output.reset(); output.addChannels(baseBuffer->getNumChannels()); - output.resize(numFrames * static_cast(factor) + sfz::config::excessFileFrames); + output.resize(numFrames * static_cast(factor) + 2 * sfz::config::excessFileFrames); output.clear(); sfz::Oversampler oversampler { factor }; - oversampler.stream(*baseBuffer, output, filledFrames); + auto baseSpan = sfz::AudioSpan(*baseBuffer).subspan(sfz::config::excessFileFrames, numFrames); + auto outputSpan = sfz::AudioSpan(output).subspan(sfz::config::excessFileFrames, numFrames * static_cast(factor)); + oversampler.stream(baseSpan, outputSpan, filledFrames); } sfz::FilePool::FilePool(sfz::Logger& logger) diff --git a/src/sfizz/FilePool.h b/src/sfizz/FilePool.h index b60e5cc2..94e9fa1d 100644 --- a/src/sfizz/FilePool.h +++ b/src/sfizz/FilePool.h @@ -66,11 +66,14 @@ struct FilePromise { if (dataStatus == DataStatus::Ready) return AudioSpan(fileData) - .first(fileData.getNumFrames() - sfz::config::excessFileFrames); + .subspan(sfz::config::excessFileFrames, + fileData.getNumFrames() - 2 * sfz::config::excessFileFrames); else if (availableFrames > preloadedData->getNumFrames()) - return AudioSpan(fileData).first(availableFrames); + return AudioSpan(fileData).subspan(sfz::config::excessFileFrames, availableFrames); else - return AudioSpan(*preloadedData); + return AudioSpan(*preloadedData) + .subspan(sfz::config::excessFileFrames, + preloadedData->getNumFrames() - sfz::config::excessFileFrames); } void reset() diff --git a/src/sfizz/Interpolators.h b/src/sfizz/Interpolators.h index 834113bf..f784eefd 100644 --- a/src/sfizz/Interpolators.h +++ b/src/sfizz/Interpolators.h @@ -17,6 +17,19 @@ enum InterpolatorModel : int { 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 R interpolate(const R* values, R coeff); diff --git a/src/sfizz/Interpolators.hpp b/src/sfizz/Interpolators.hpp index ef7e0324..1f462578 100644 --- a/src/sfizz/Interpolators.hpp +++ b/src/sfizz/Interpolators.hpp @@ -44,7 +44,7 @@ public: { __m128 x = _mm_sub_ps(_mm_setr_ps(-1, 0, 1, 2), _mm_set1_ps(coeff)); __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 __m128 xmm0 = y; __m128 xmm1 = _mm_shuffle_ps(xmm0, xmm0, 0xe5); @@ -68,8 +68,8 @@ public: static inline R process(const R* values, R coeff) { R y = 0; - for (int i = 0; i < 4; ++i) { - R h = hermite3(i - 1 - coeff); + for (int i = -1; i < 3; ++i) { + R h = hermite3(i - coeff); y += h * values[i]; } return y; @@ -88,7 +88,7 @@ public: { __m128 x = _mm_sub_ps(_mm_setr_ps(-1, 0, 1, 2), _mm_set1_ps(coeff)); __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 __m128 xmm0 = y; __m128 xmm1 = _mm_shuffle_ps(xmm0, xmm0, 0xe5); @@ -112,8 +112,8 @@ public: static inline R process(const R* values, R coeff) { R y = 0; - for (int i = 0; i < 4; ++i) { - R h = bspline3(i - 1 - coeff); + for (int i = -1; i < 3; ++i) { + R h = bspline3(i - coeff); y += h * values[i]; } return y; diff --git a/src/sfizz/Oversampler.cpp b/src/sfizz/Oversampler.cpp index f3313b88..1027b8ba 100644 --- a/src/sfizz/Oversampler.cpp +++ b/src/sfizz/Oversampler.cpp @@ -61,7 +61,7 @@ sfz::Oversampler::Oversampler(sfz::Oversampling factor, size_t chunkSize) } -void sfz::Oversampler::stream(const sfz::AudioBuffer& input, sfz::AudioBuffer& output, std::atomic* framesReady) +void sfz::Oversampler::stream(AudioSpan input, AudioSpan output, std::atomic* framesReady) { ASSERT(output.getNumFrames() >= input.getNumFrames() * static_cast(factor)); ASSERT(output.getNumChannels() == input.getNumChannels()); diff --git a/src/sfizz/Oversampler.h b/src/sfizz/Oversampler.h index d6daa4a9..25e74eaf 100644 --- a/src/sfizz/Oversampler.h +++ b/src/sfizz/Oversampler.h @@ -11,6 +11,7 @@ #include "Debug.h" #include "Buffer.h" #include "AudioBuffer.h" +#include "AudioSpan.h" #include "Config.h" namespace sfz { @@ -39,7 +40,7 @@ public: * @param output * @param framesReady an atomic counter for the ready frames. If null no signaling is done. */ - void stream(const AudioBuffer& input, AudioBuffer& output, std::atomic* framesReady = nullptr); + void stream(AudioSpan input, AudioSpan output, std::atomic* framesReady = nullptr); Oversampler() = delete; Oversampler(const Oversampler&) = delete;