diff --git a/include/Sample.h b/include/Sample.h index 102aaf2d515..86fba1ddc03 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -33,9 +33,6 @@ #include "SampleBuffer.h" #include "lmms_export.h" -class QPainter; -class QRect; - namespace lmms { class LMMS_EXPORT Sample { @@ -63,17 +60,17 @@ class LMMS_EXPORT Sample } auto resampler() -> AudioResampler& { return m_resampler; } - auto frameIndex() const -> f_cnt_t { return m_frameIndex; } + auto frameIndex() const -> int { return m_frameIndex; } auto varyingPitch() const -> bool { return m_varyingPitch; } auto backwards() const -> bool { return m_backwards; } - void setFrameIndex(f_cnt_t frameIndex) { m_frameIndex = frameIndex; } + void setFrameIndex(int frameIndex) { m_frameIndex = frameIndex; } void setVaryingPitch(bool varyingPitch) { m_varyingPitch = varyingPitch; } void setBackwards(bool backwards) { m_backwards = backwards; } private: AudioResampler m_resampler; - f_cnt_t m_frameIndex = 0; + int m_frameIndex = 0; bool m_varyingPitch = false; bool m_backwards = false; friend class Sample; @@ -81,7 +78,7 @@ class LMMS_EXPORT Sample Sample() = default; Sample(const QByteArray& base64, int sampleRate = Engine::audioEngine()->processingSampleRate()); - Sample(const sampleFrame* data, int numFrames, int sampleRate = Engine::audioEngine()->processingSampleRate()); + Sample(const sampleFrame* data, size_t numFrames, int sampleRate = Engine::audioEngine()->processingSampleRate()); Sample(const Sample& other); Sample(Sample&& other); explicit Sample(const QString& audioFile); @@ -90,8 +87,8 @@ class LMMS_EXPORT Sample auto operator=(const Sample&) -> Sample&; auto operator=(Sample&&) -> Sample&; - auto play(sampleFrame* dst, PlaybackState* state, int numFrames, float desiredFrequency = DefaultBaseFreq, - Loop loopMode = Loop::Off) -> bool; + auto play(sampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency = DefaultBaseFreq, + Loop loopMode = Loop::Off) const -> bool; auto sampleDuration() const -> std::chrono::milliseconds; auto sampleFile() const -> const QString& { return m_buffer->audioFile(); } @@ -120,10 +117,8 @@ class LMMS_EXPORT Sample void setReversed(bool reversed) { m_reversed.store(reversed, std::memory_order_relaxed); } private: - void playSampleRange(PlaybackState* state, sampleFrame* dst, size_t numFrames) const; - void amplifySampleRange(sampleFrame* src, int numFrames) const; - void copyBufferForward(sampleFrame* dst, int initialPosition, int advanceAmount) const; - void copyBufferBackward(sampleFrame* dst, int initialPosition, int advanceAmount) const; + void playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* state, Loop loopMode) const; + void advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) const; private: std::shared_ptr m_buffer = SampleBuffer::emptyBuffer(); diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index 0db8aa4d35c..4089eb44627 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -56,7 +56,7 @@ class LMMS_EXPORT SampleBuffer SampleBuffer(const QString& base64, int sampleRate); SampleBuffer(std::vector data, int sampleRate); SampleBuffer( - const sampleFrame* data, int numFrames, int sampleRate = Engine::audioEngine()->processingSampleRate()); + const sampleFrame* data, size_t numFrames, int sampleRate = Engine::audioEngine()->processingSampleRate()); friend void swap(SampleBuffer& first, SampleBuffer& second) noexcept; auto toBase64() const -> QString; diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index cdb12851fa7..333ee624d4d 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -24,8 +24,7 @@ #include "Sample.h" -#include -#include +#include namespace lmms { @@ -47,7 +46,7 @@ Sample::Sample(const QByteArray& base64, int sampleRate) { } -Sample::Sample(const sampleFrame* data, int numFrames, int sampleRate) +Sample::Sample(const sampleFrame* data, size_t numFrames, int sampleRate) : m_buffer(std::make_shared(data, numFrames, sampleRate)) , m_startFrame(0) , m_endFrame(m_buffer->size()) @@ -117,58 +116,40 @@ auto Sample::operator=(Sample&& other) -> Sample& return *this; } -bool Sample::play(sampleFrame* dst, PlaybackState* state, int numFrames, float desiredFrequency, Loop loopMode) +bool Sample::play(sampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency, Loop loopMode) const { - if (numFrames <= 0 || desiredFrequency <= 0) { return false; } + assert(numFrames > 0); + assert(desiredFrequency > 0); - auto resampleRatio = static_cast(Engine::audioEngine()->processingSampleRate()) / m_buffer->sampleRate(); - resampleRatio *= frequency() / desiredFrequency; + const auto pastBounds = state->m_frameIndex >= m_endFrame || (state->m_frameIndex < 0 && state->m_backwards); + if (loopMode == Loop::Off && pastBounds) { return false; } - auto playBuffer = std::vector(numFrames / resampleRatio); - if (!typeInfo::isEqual(resampleRatio, 1.0f)) - { - playBuffer.resize(playBuffer.size() + s_interpolationMargins[state->resampler().interpolationMode()]); - } + const auto outputSampleRate = Engine::audioEngine()->processingSampleRate() * m_frequency / desiredFrequency; + const auto inputSampleRate = m_buffer->sampleRate(); + const auto resampleRatio = outputSampleRate / inputSampleRate; + const auto marginSize = s_interpolationMargins[state->resampler().interpolationMode()]; - const auto start = startFrame(); - const auto end = endFrame(); - const auto loopStart = loopStartFrame(); - const auto loopEnd = loopEndFrame(); + state->m_frameIndex = std::max(m_startFrame, state->m_frameIndex); - switch (loopMode) + auto playBuffer = std::vector(numFrames / resampleRatio + marginSize); + playRaw(playBuffer.data(), playBuffer.size(), state, loopMode); + + const auto resampleResult + = state->resampler().resample(&playBuffer[0][0], playBuffer.size(), &dst[0][0], numFrames, resampleRatio); + advance(state, resampleResult.inputFramesUsed, loopMode); + + const auto outputFrames = resampleResult.outputFramesGenerated; + if (outputFrames < numFrames) { std::fill_n(dst + outputFrames, numFrames - outputFrames, sampleFrame{}); } + + if (!typeInfo::isEqual(m_amplification, 1.0f)) { - case Loop::Off: - state->m_frameIndex = std::clamp(state->m_frameIndex, start, end); - if (state->m_frameIndex == end) { return false; } - break; - case Loop::On: - state->m_frameIndex = std::clamp(state->m_frameIndex, start, loopEnd); - if (state->m_frameIndex == loopEnd) { state->m_frameIndex = loopStart; } - break; - case Loop::PingPong: - state->m_frameIndex = std::clamp(state->m_frameIndex, start, loopEnd); - if (state->m_frameIndex == loopEnd) - { - state->m_frameIndex = loopEnd - 1; - state->m_backwards = true; - } - else if (state->m_frameIndex <= loopStart && state->m_backwards) + for (int i = 0; i < numFrames; ++i) { - state->m_frameIndex = loopStart; - state->m_backwards = false; + dst[i][0] *= m_amplification; + dst[i][1] *= m_amplification; } - break; } - playSampleRange(state, playBuffer.data(), playBuffer.size()); - - const auto result - = state->resampler().resample(&playBuffer[0][0], playBuffer.size(), &dst[0][0], numFrames, resampleRatio); - if (result.error != 0) { return false; } - - state->m_frameIndex += (state->m_backwards ? -1 : 1) * result.inputFramesUsed; - amplifySampleRange(dst, result.outputFramesGenerated); - return true; } @@ -187,44 +168,80 @@ void Sample::setAllPointFrames(int startFrame, int endFrame, int loopStartFrame, setLoopEndFrame(loopEndFrame); } -void Sample::playSampleRange(PlaybackState* state, sampleFrame* dst, size_t numFrames) const +void Sample::playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* state, Loop loopMode) const { - auto framesToCopy = 0; - if (state->m_backwards) - { - framesToCopy = std::min(state->m_frameIndex - startFrame(), numFrames); - copyBufferBackward(dst, state->m_frameIndex, framesToCopy); - } - else + auto index = state->m_frameIndex; + auto backwards = state->m_backwards; + + for (size_t i = 0; i < numFrames; ++i) { - framesToCopy = std::min(endFrame() - state->m_frameIndex, numFrames); - copyBufferForward(dst, state->m_frameIndex, framesToCopy); - } + switch (loopMode) + { + case Loop::Off: + if (index < 0 || index > m_endFrame) { return; } + break; + case Loop::On: + if (index < m_loopStartFrame && backwards) { index = m_loopEndFrame - 1; } + else if (index >= m_loopEndFrame) { index = m_loopStartFrame; } + break; + case Loop::PingPong: + if (index < m_loopStartFrame && backwards) + { + index = m_loopStartFrame; + backwards = false; + } + else if (index >= m_loopEndFrame) + { + index = m_loopEndFrame - 1; + backwards = true; + } + break; + default: + break; + } - if (framesToCopy < numFrames) { std::fill_n(dst + framesToCopy, numFrames - framesToCopy, sampleFrame{0, 0}); } + dst[i] = m_buffer->data()[m_reversed ? m_buffer->size() - index - 1 : index]; + backwards ? --index : ++index; + } } -void Sample::copyBufferForward(sampleFrame* dst, int initialPosition, int advanceAmount) const +void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) const { - reversed() ? std::copy_n(m_buffer->rbegin() + initialPosition, advanceAmount, dst) - : std::copy_n(m_buffer->begin() + initialPosition, advanceAmount, dst); -} + state->m_frameIndex += (state->m_backwards ? -1 : 1) * advanceAmount; + if (loopMode == Loop::Off) { return; } -void Sample::copyBufferBackward(sampleFrame* dst, int initialPosition, int advanceAmount) const -{ - reversed() ? std::reverse_copy( - m_buffer->rbegin() + initialPosition - advanceAmount, m_buffer->rbegin() + initialPosition, dst) - : std::reverse_copy( - m_buffer->begin() + initialPosition - advanceAmount, m_buffer->begin() + initialPosition, dst); -} + const auto distanceFromLoopStart = std::abs(state->m_frameIndex - m_loopStartFrame); + const auto distanceFromLoopEnd = std::abs(state->m_frameIndex - m_loopEndFrame); + const auto loopSize = m_loopEndFrame - m_loopStartFrame; + if (loopSize == 0) { return; } -void Sample::amplifySampleRange(sampleFrame* src, int numFrames) const -{ - const auto amplification = m_amplification.load(std::memory_order_relaxed); - for (int i = 0; i < numFrames; ++i) + switch (loopMode) { - src[i][0] *= amplification; - src[i][1] *= amplification; + case Loop::On: + if (state->m_frameIndex < m_loopStartFrame && state->m_backwards) + { + state->m_frameIndex = m_loopEndFrame - 1 - distanceFromLoopStart % loopSize; + } + else if (state->m_frameIndex >= m_loopEndFrame) + { + state->m_frameIndex = m_loopStartFrame + distanceFromLoopEnd % loopSize; + } + break; + case Loop::PingPong: + if (state->m_frameIndex < m_loopStartFrame && state->m_backwards) + { + state->m_frameIndex = m_loopStartFrame + distanceFromLoopStart % loopSize; + state->m_backwards = false; + } + else if (state->m_frameIndex >= m_loopEndFrame) + { + state->m_frameIndex = m_loopEndFrame - 1 - distanceFromLoopEnd % loopSize; + state->m_backwards = true; + } + break; + default: + break; } } + } // namespace lmms diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 550a9d7bc10..6483dd522e4 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -31,7 +31,7 @@ namespace lmms { -SampleBuffer::SampleBuffer(const sampleFrame* data, int numFrames, int sampleRate) +SampleBuffer::SampleBuffer(const sampleFrame* data, size_t numFrames, int sampleRate) : m_data(data, data + numFrames) , m_sampleRate(sampleRate) {