From b4b265088801c04745d2814a000df7cbe35c604e Mon Sep 17 00:00:00 2001 From: saker Date: Sat, 10 Feb 2024 18:00:23 -0500 Subject: [PATCH 01/31] Remove Qt includes --- src/core/Sample.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index cdb12851fa7..9973b844860 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -24,9 +24,6 @@ #include "Sample.h" -#include -#include - namespace lmms { Sample::Sample(const QString& audioFile) From d9ff3abc3561774c6c3c9a6c4ab3c5226eebf9a8 Mon Sep 17 00:00:00 2001 From: saker Date: Sat, 10 Feb 2024 19:02:07 -0500 Subject: [PATCH 02/31] Simplify copyBufferForward and copyBufferBackward functions --- include/Sample.h | 4 ++-- src/core/Sample.cpp | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/include/Sample.h b/include/Sample.h index 102aaf2d515..0d669c1199f 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -122,8 +122,8 @@ class LMMS_EXPORT Sample 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 copyBufferForward(sampleFrame* dst, size_t initialPosition, size_t advanceAmount) const; + void copyBufferBackward(sampleFrame* dst, size_t initialPosition, size_t advanceAmount) const; private: std::shared_ptr m_buffer = SampleBuffer::emptyBuffer(); diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 9973b844860..75439880aa0 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -201,18 +201,16 @@ void Sample::playSampleRange(PlaybackState* state, sampleFrame* dst, size_t numF if (framesToCopy < numFrames) { std::fill_n(dst + framesToCopy, numFrames - framesToCopy, sampleFrame{0, 0}); } } -void Sample::copyBufferForward(sampleFrame* dst, int initialPosition, int advanceAmount) const +void Sample::copyBufferForward(sampleFrame* dst, size_t initialPosition, size_t advanceAmount) const { - reversed() ? std::copy_n(m_buffer->rbegin() + initialPosition, advanceAmount, dst) + m_reversed ? std::copy_n(m_buffer->rbegin() + initialPosition, advanceAmount, dst) : std::copy_n(m_buffer->begin() + initialPosition, advanceAmount, dst); } -void Sample::copyBufferBackward(sampleFrame* dst, int initialPosition, int advanceAmount) const +void Sample::copyBufferBackward(sampleFrame* dst, size_t initialPosition, size_t 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); + m_reversed ? std::copy_n(m_buffer->begin() + initialPosition, advanceAmount, dst) + : std::copy_n(m_buffer->rbegin() + initialPosition, advanceAmount, dst); } void Sample::amplifySampleRange(sampleFrame* src, int numFrames) const From 045a632c540eb9383cc11bf95a1273a13e98c936 Mon Sep 17 00:00:00 2001 From: saker Date: Sat, 10 Feb 2024 19:09:49 -0500 Subject: [PATCH 03/31] Remove Qt forward declarations --- include/Sample.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/Sample.h b/include/Sample.h index 0d669c1199f..2f82d0796ec 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 { From 7ce82c698ea357fec7215f10535a8e741a38594c Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 00:41:38 -0500 Subject: [PATCH 04/31] Rework sample playing Ping pong has issues, will fix in a later commit --- include/Sample.h | 3 +- src/core/Sample.cpp | 145 ++++++++++++++++++++++++++++++-------------- 2 files changed, 101 insertions(+), 47 deletions(-) diff --git a/include/Sample.h b/include/Sample.h index 2f82d0796ec..d98de39dc83 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -117,7 +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 advance(PlaybackState* state, size_t advanceAmount, Loop loopMode); + void playSampleRange(sampleFrame* dst, size_t numFrames, int index, bool backwards) const; void amplifySampleRange(sampleFrame* src, int numFrames) const; void copyBufferForward(sampleFrame* dst, size_t initialPosition, size_t advanceAmount) const; void copyBufferBackward(sampleFrame* dst, size_t initialPosition, size_t advanceAmount) const; diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 75439880aa0..e3c1fa0004b 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -116,55 +116,68 @@ auto Sample::operator=(Sample&& other) -> Sample& bool Sample::play(sampleFrame* dst, PlaybackState* state, int numFrames, float desiredFrequency, Loop loopMode) { - if (numFrames <= 0 || desiredFrequency <= 0) { return false; } + if (loopMode == Loop::Off && state->m_frameIndex >= m_endFrame) { return false; } - auto resampleRatio = static_cast(Engine::audioEngine()->processingSampleRate()) / m_buffer->sampleRate(); - resampleRatio *= frequency() / desiredFrequency; - - auto playBuffer = std::vector(numFrames / resampleRatio); - if (!typeInfo::isEqual(resampleRatio, 1.0f)) - { - playBuffer.resize(playBuffer.size() + s_interpolationMargins[state->resampler().interpolationMode()]); - } - - const auto start = startFrame(); - const auto end = endFrame(); - const auto loopStart = loopStartFrame(); - const auto loopEnd = loopEndFrame(); + 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()]; + auto playBuffer = std::vector(numFrames / resampleRatio + marginSize); switch (loopMode) { - case Loop::Off: - state->m_frameIndex = std::clamp(state->m_frameIndex, start, end); - if (state->m_frameIndex == end) { return false; } + case Loop::Off: { + const auto framesToPlay = state->m_backwards + ? std::min(playBuffer.size(), state->m_frameIndex) + : std::min(playBuffer.size(), m_buffer->size() - state->m_frameIndex); + playSampleRange(playBuffer.data(), framesToPlay, state->m_frameIndex, state->m_backwards); 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) + } + case Loop::On: { + auto loopIndex = state->m_frameIndex; + for (auto& frame : playBuffer) { - state->m_frameIndex = loopEnd - 1; - state->m_backwards = true; + if (loopIndex < m_loopStartFrame && state->m_backwards) { loopIndex = m_loopEndFrame - 1; } + else if (loopIndex >= m_loopEndFrame) { loopIndex = m_loopStartFrame; } + playSampleRange(&frame, 1, state->m_backwards ? loopIndex-- : loopIndex++, state->m_backwards); } - else if (state->m_frameIndex <= loopStart && state->m_backwards) + break; + } + case Loop::PingPong: { + auto loopIndex = state->m_frameIndex; + auto backwards = state->m_backwards; + for (auto& frame : playBuffer) { - state->m_frameIndex = loopStart; - state->m_backwards = false; + if (loopIndex < m_loopStartFrame && backwards) + { + loopIndex = m_loopStartFrame; + backwards = false; + } + else if (loopIndex >= m_loopEndFrame) + { + loopIndex = m_loopEndFrame - 1; + backwards = true; + } + playSampleRange(&frame, 1, backwards ? loopIndex-- : loopIndex++, backwards); } break; } + } - playSampleRange(state, playBuffer.data(), playBuffer.size()); - - const auto result + const auto resampleResult = state->resampler().resample(&playBuffer[0][0], playBuffer.size(), &dst[0][0], numFrames, resampleRatio); - if (result.error != 0) { return false; } + advance(state, resampleResult.inputFramesUsed, loopMode); - state->m_frameIndex += (state->m_backwards ? -1 : 1) * result.inputFramesUsed; - amplifySampleRange(dst, result.outputFramesGenerated); + if (resampleResult.outputFramesGenerated < numFrames) + { + std::fill(dst + resampleResult.outputFramesGenerated, dst + resampleResult.outputFramesGenerated + numFrames, + sampleFrame{0, 0}); + } + + if (!typeInfo::isEqual(m_amplification, 1.0f)) + { + amplifySampleRange(dst, resampleResult.outputFramesGenerated); + } return true; } @@ -184,21 +197,61 @@ void Sample::setAllPointFrames(int startFrame, int endFrame, int loopStartFrame, setLoopEndFrame(loopEndFrame); } -void Sample::playSampleRange(PlaybackState* state, sampleFrame* dst, size_t numFrames) const +void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) { - auto framesToCopy = 0; - if (state->m_backwards) - { - framesToCopy = std::min(state->m_frameIndex - startFrame(), numFrames); - copyBufferBackward(dst, state->m_frameIndex, framesToCopy); - } - else + switch (loopMode) { - framesToCopy = std::min(endFrame() - state->m_frameIndex, numFrames); - copyBufferForward(dst, state->m_frameIndex, framesToCopy); + case Loop::Off: + state->m_frameIndex += (state->m_backwards ? -1 : 1) * advanceAmount; + break; + case Loop::On: + if (state->m_backwards) + { + state->m_frameIndex -= advanceAmount; + if (state->m_frameIndex < m_loopStartFrame) + { + state->m_frameIndex + = m_loopEndFrame - 1 - std::abs(state->m_frameIndex) % (m_loopEndFrame - m_loopStartFrame); + } + } + else + { + state->m_frameIndex += advanceAmount; + if (state->m_frameIndex >= m_loopEndFrame) + { + state->m_frameIndex + = m_loopStartFrame + std::abs(state->m_frameIndex) % (m_loopEndFrame - m_loopStartFrame); + } + } + break; + case Loop::PingPong: + if (state->m_backwards) + { + state->m_frameIndex -= advanceAmount; + if (state->m_frameIndex < m_loopStartFrame) + { + state->m_frameIndex + = m_loopStartFrame + std::abs(state->m_frameIndex) % (m_loopEndFrame - m_loopStartFrame); + state->m_backwards = false; + } + } + else + { + state->m_frameIndex += advanceAmount; + if (state->m_frameIndex >= m_loopEndFrame) + { + state->m_frameIndex + = m_loopEndFrame - 1 - state->m_frameIndex % (m_loopEndFrame - m_loopStartFrame); + state->m_backwards = true; + } + } + break; } +} - if (framesToCopy < numFrames) { std::fill_n(dst + framesToCopy, numFrames - framesToCopy, sampleFrame{0, 0}); } +void Sample::playSampleRange(sampleFrame* dst, size_t numFrames, int index, bool backwards) const +{ + backwards ? copyBufferBackward(dst, index, numFrames) : copyBufferForward(dst, index, numFrames); } void Sample::copyBufferForward(sampleFrame* dst, size_t initialPosition, size_t advanceAmount) const From 079f2c4ca3af39cc32c3d974524dd4a5ddade889 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 09:18:44 -0500 Subject: [PATCH 05/31] Fix position of index when copying backwards Since we are moving backwards, we need to act as if playback started from the end of the buffer. --- src/core/Sample.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index e3c1fa0004b..149e97cba6f 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -262,8 +262,8 @@ void Sample::copyBufferForward(sampleFrame* dst, size_t initialPosition, size_t void Sample::copyBufferBackward(sampleFrame* dst, size_t initialPosition, size_t advanceAmount) const { - m_reversed ? std::copy_n(m_buffer->begin() + initialPosition, advanceAmount, dst) - : std::copy_n(m_buffer->rbegin() + initialPosition, advanceAmount, dst); + m_reversed ? std::copy_n(m_buffer->begin() + m_buffer->size() - initialPosition, advanceAmount, dst) + : std::copy_n(m_buffer->rbegin() + m_buffer->size() - initialPosition, advanceAmount, dst); } void Sample::amplifySampleRange(sampleFrame* src, int numFrames) const From f30e87e43f0d4101f094948a6ad66b69ce611370 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 09:28:28 -0500 Subject: [PATCH 06/31] Add playRaw function --- include/Sample.h | 1 + src/core/Sample.cpp | 86 ++++++++++++++++++++++++--------------------- 2 files changed, 46 insertions(+), 41 deletions(-) diff --git a/include/Sample.h b/include/Sample.h index d98de39dc83..08fee2039b8 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -117,6 +117,7 @@ class LMMS_EXPORT Sample void setReversed(bool reversed) { m_reversed.store(reversed, std::memory_order_relaxed); } private: + void playRaw(sampleFrame* dst, size_t numFrames, PlaybackState* state, Loop loopMode); void advance(PlaybackState* state, size_t advanceAmount, Loop loopMode); void playSampleRange(sampleFrame* dst, size_t numFrames, int index, bool backwards) const; void amplifySampleRange(sampleFrame* src, int numFrames) const; diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 149e97cba6f..1e74ba69b96 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -124,45 +124,7 @@ bool Sample::play(sampleFrame* dst, PlaybackState* state, int numFrames, float d const auto marginSize = s_interpolationMargins[state->resampler().interpolationMode()]; auto playBuffer = std::vector(numFrames / resampleRatio + marginSize); - switch (loopMode) - { - case Loop::Off: { - const auto framesToPlay = state->m_backwards - ? std::min(playBuffer.size(), state->m_frameIndex) - : std::min(playBuffer.size(), m_buffer->size() - state->m_frameIndex); - playSampleRange(playBuffer.data(), framesToPlay, state->m_frameIndex, state->m_backwards); - break; - } - case Loop::On: { - auto loopIndex = state->m_frameIndex; - for (auto& frame : playBuffer) - { - if (loopIndex < m_loopStartFrame && state->m_backwards) { loopIndex = m_loopEndFrame - 1; } - else if (loopIndex >= m_loopEndFrame) { loopIndex = m_loopStartFrame; } - playSampleRange(&frame, 1, state->m_backwards ? loopIndex-- : loopIndex++, state->m_backwards); - } - break; - } - case Loop::PingPong: { - auto loopIndex = state->m_frameIndex; - auto backwards = state->m_backwards; - for (auto& frame : playBuffer) - { - if (loopIndex < m_loopStartFrame && backwards) - { - loopIndex = m_loopStartFrame; - backwards = false; - } - else if (loopIndex >= m_loopEndFrame) - { - loopIndex = m_loopEndFrame - 1; - backwards = true; - } - playSampleRange(&frame, 1, backwards ? loopIndex-- : loopIndex++, backwards); - } - break; - } - } + playRaw(playBuffer.data(), playBuffer.size(), state, loopMode); const auto resampleResult = state->resampler().resample(&playBuffer[0][0], playBuffer.size(), &dst[0][0], numFrames, resampleRatio); @@ -197,6 +159,49 @@ void Sample::setAllPointFrames(int startFrame, int endFrame, int loopStartFrame, setLoopEndFrame(loopEndFrame); } +void Sample::playRaw(sampleFrame* dst, size_t numFrames, PlaybackState* state, Loop loopMode) +{ + switch (loopMode) + { + case Loop::Off: { + const auto framesToPlay = state->m_backwards + ? std::min(numFrames, state->m_frameIndex) + : std::min(numFrames, m_buffer->size() - state->m_frameIndex); + playSampleRange(dst, framesToPlay, state->m_frameIndex, state->m_backwards); + break; + } + case Loop::On: { + auto loopIndex = state->m_frameIndex; + for (size_t i = 0; i < numFrames; ++i) + { + if (loopIndex < m_loopStartFrame && state->m_backwards) { loopIndex = m_loopEndFrame - 1; } + else if (loopIndex >= m_loopEndFrame) { loopIndex = m_loopStartFrame; } + playSampleRange(dst + i, 1, state->m_backwards ? loopIndex-- : loopIndex++, state->m_backwards); + } + break; + } + case Loop::PingPong: { + auto loopIndex = state->m_frameIndex; + auto backwards = state->m_backwards; + for (size_t i = 0; i < numFrames; ++i) + { + if (loopIndex < m_loopStartFrame && backwards) + { + loopIndex = m_loopStartFrame; + backwards = false; + } + else if (loopIndex >= m_loopEndFrame) + { + loopIndex = m_loopEndFrame - 1; + backwards = true; + } + playSampleRange(dst + i, 1, backwards ? loopIndex-- : loopIndex++, backwards); + } + break; + } + } +} + void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) { switch (loopMode) @@ -240,8 +245,7 @@ void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) state->m_frameIndex += advanceAmount; if (state->m_frameIndex >= m_loopEndFrame) { - state->m_frameIndex - = m_loopEndFrame - 1 - state->m_frameIndex % (m_loopEndFrame - m_loopStartFrame); + state->m_frameIndex = m_loopEndFrame - 1 - state->m_frameIndex % (m_loopEndFrame - m_loopStartFrame); state->m_backwards = true; } } From 5f5e9937b5bb2cb8656eb8c4c5e624a86c42c345 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 09:33:18 -0500 Subject: [PATCH 07/31] Combine forward and backward copying of the buffer --- include/Sample.h | 4 +--- src/core/Sample.cpp | 24 +++++++----------------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/include/Sample.h b/include/Sample.h index 08fee2039b8..0df7fa5da99 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -119,10 +119,8 @@ class LMMS_EXPORT Sample private: void playRaw(sampleFrame* dst, size_t numFrames, PlaybackState* state, Loop loopMode); void advance(PlaybackState* state, size_t advanceAmount, Loop loopMode); - void playSampleRange(sampleFrame* dst, size_t numFrames, int index, bool backwards) const; + void copyBuffer(sampleFrame* dst, size_t initialPosition, size_t advanceAmount, bool backwards) const; void amplifySampleRange(sampleFrame* src, int numFrames) const; - void copyBufferForward(sampleFrame* dst, size_t initialPosition, size_t advanceAmount) const; - void copyBufferBackward(sampleFrame* dst, size_t initialPosition, size_t advanceAmount) const; private: std::shared_ptr m_buffer = SampleBuffer::emptyBuffer(); diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 1e74ba69b96..f37ca670983 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -167,7 +167,7 @@ void Sample::playRaw(sampleFrame* dst, size_t numFrames, PlaybackState* state, L const auto framesToPlay = state->m_backwards ? std::min(numFrames, state->m_frameIndex) : std::min(numFrames, m_buffer->size() - state->m_frameIndex); - playSampleRange(dst, framesToPlay, state->m_frameIndex, state->m_backwards); + copyBuffer(dst, framesToPlay, state->m_frameIndex, state->m_backwards); break; } case Loop::On: { @@ -176,7 +176,7 @@ void Sample::playRaw(sampleFrame* dst, size_t numFrames, PlaybackState* state, L { if (loopIndex < m_loopStartFrame && state->m_backwards) { loopIndex = m_loopEndFrame - 1; } else if (loopIndex >= m_loopEndFrame) { loopIndex = m_loopStartFrame; } - playSampleRange(dst + i, 1, state->m_backwards ? loopIndex-- : loopIndex++, state->m_backwards); + copyBuffer(dst + i, 1, state->m_backwards ? loopIndex-- : loopIndex++, state->m_backwards); } break; } @@ -195,7 +195,7 @@ void Sample::playRaw(sampleFrame* dst, size_t numFrames, PlaybackState* state, L loopIndex = m_loopEndFrame - 1; backwards = true; } - playSampleRange(dst + i, 1, backwards ? loopIndex-- : loopIndex++, backwards); + copyBuffer(dst + i, 1, backwards ? loopIndex-- : loopIndex++, backwards); } break; } @@ -253,21 +253,11 @@ void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) } } -void Sample::playSampleRange(sampleFrame* dst, size_t numFrames, int index, bool backwards) const +void Sample::copyBuffer(sampleFrame* dst, size_t initialPosition, size_t advanceAmount, bool backwards) const { - backwards ? copyBufferBackward(dst, index, numFrames) : copyBufferForward(dst, index, numFrames); -} - -void Sample::copyBufferForward(sampleFrame* dst, size_t initialPosition, size_t advanceAmount) const -{ - m_reversed ? std::copy_n(m_buffer->rbegin() + initialPosition, advanceAmount, dst) - : std::copy_n(m_buffer->begin() + initialPosition, advanceAmount, dst); -} - -void Sample::copyBufferBackward(sampleFrame* dst, size_t initialPosition, size_t advanceAmount) const -{ - m_reversed ? std::copy_n(m_buffer->begin() + m_buffer->size() - initialPosition, advanceAmount, dst) - : std::copy_n(m_buffer->rbegin() + m_buffer->size() - initialPosition, advanceAmount, dst); + const auto position = backwards ? m_buffer->size() - initialPosition : initialPosition; + m_reversed ? std::copy_n(m_buffer->rbegin() + position, advanceAmount, dst) + : std::copy_n(m_buffer->begin() + position, advanceAmount, dst); } void Sample::amplifySampleRange(sampleFrame* src, int numFrames) const From 923f6a9c8caa3608a747019aaa6673164ae5a1d6 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 10:04:52 -0500 Subject: [PATCH 08/31] Fix/simplify playRaw function and remove copyBuffer --- include/Sample.h | 3 +-- src/core/Sample.cpp | 60 +++++++++++++++++---------------------------- 2 files changed, 24 insertions(+), 39 deletions(-) diff --git a/include/Sample.h b/include/Sample.h index 0df7fa5da99..d34a813fa00 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -117,9 +117,8 @@ class LMMS_EXPORT Sample void setReversed(bool reversed) { m_reversed.store(reversed, std::memory_order_relaxed); } private: - void playRaw(sampleFrame* dst, size_t numFrames, PlaybackState* state, Loop loopMode); + void playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* state, Loop loopMode) const; void advance(PlaybackState* state, size_t advanceAmount, Loop loopMode); - void copyBuffer(sampleFrame* dst, size_t initialPosition, size_t advanceAmount, bool backwards) const; void amplifySampleRange(sampleFrame* src, int numFrames) const; private: diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index f37ca670983..9188da6fae5 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -159,46 +159,39 @@ void Sample::setAllPointFrames(int startFrame, int endFrame, int loopStartFrame, setLoopEndFrame(loopEndFrame); } -void Sample::playRaw(sampleFrame* dst, size_t numFrames, PlaybackState* state, Loop loopMode) +void Sample::playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* state, Loop loopMode) const { - switch (loopMode) + auto index = state->m_frameIndex; + auto backwards = state->m_backwards; + + for (size_t i = 0; i < numFrames; ++i) { - case Loop::Off: { - const auto framesToPlay = state->m_backwards - ? std::min(numFrames, state->m_frameIndex) - : std::min(numFrames, m_buffer->size() - state->m_frameIndex); - copyBuffer(dst, framesToPlay, state->m_frameIndex, state->m_backwards); - break; - } - case Loop::On: { - auto loopIndex = state->m_frameIndex; - for (size_t i = 0; i < numFrames; ++i) - { - if (loopIndex < m_loopStartFrame && state->m_backwards) { loopIndex = m_loopEndFrame - 1; } - else if (loopIndex >= m_loopEndFrame) { loopIndex = m_loopStartFrame; } - copyBuffer(dst + i, 1, state->m_backwards ? loopIndex-- : loopIndex++, state->m_backwards); - } - break; - } - case Loop::PingPong: { - auto loopIndex = state->m_frameIndex; - auto backwards = state->m_backwards; - for (size_t i = 0; i < numFrames; ++i) + switch (loopMode) { - if (loopIndex < m_loopStartFrame && backwards) + case Loop::Off: + if (index > m_endFrame || (index < 0 && state->m_backwards)) { 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) { - loopIndex = m_loopStartFrame; + index = m_loopStartFrame; backwards = false; } - else if (loopIndex >= m_loopEndFrame) + else if (index >= m_loopEndFrame) { - loopIndex = m_loopEndFrame - 1; + index = m_loopEndFrame - 1; backwards = true; } - copyBuffer(dst + i, 1, backwards ? loopIndex-- : loopIndex++, backwards); + break; + default: + break; } - break; - } + + dst[i] = m_buffer->data()[backwards ? index-- : index++]; } } @@ -253,13 +246,6 @@ void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) } } -void Sample::copyBuffer(sampleFrame* dst, size_t initialPosition, size_t advanceAmount, bool backwards) const -{ - const auto position = backwards ? m_buffer->size() - initialPosition : initialPosition; - m_reversed ? std::copy_n(m_buffer->rbegin() + position, advanceAmount, dst) - : std::copy_n(m_buffer->begin() + position, advanceAmount, dst); -} - void Sample::amplifySampleRange(sampleFrame* src, int numFrames) const { const auto amplification = m_amplification.load(std::memory_order_relaxed); From aa6d221415bc506a91978c7371c226ca87165318 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 10:11:24 -0500 Subject: [PATCH 09/31] Handle backward non-looping case when playing beyond buffer --- src/core/Sample.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 9188da6fae5..5aa9a403039 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -116,7 +116,10 @@ auto Sample::operator=(Sample&& other) -> Sample& bool Sample::play(sampleFrame* dst, PlaybackState* state, int numFrames, float desiredFrequency, Loop loopMode) { - if (loopMode == Loop::Off && state->m_frameIndex >= m_endFrame) { return false; } + if (loopMode == Loop::Off && (state->m_frameIndex >= m_endFrame || (state->m_frameIndex < 0 && state->m_backwards))) + { + return false; + } const auto outputSampleRate = Engine::audioEngine()->processingSampleRate() * m_frequency / desiredFrequency; const auto inputSampleRate = m_buffer->sampleRate(); From 62fd3f75c4509a59fd319b03d2a62e2b3128fa17 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 10:14:27 -0500 Subject: [PATCH 10/31] Only use std::abs when potentially looping in the negatives --- src/core/Sample.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 5aa9a403039..07da2be15a9 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -221,7 +221,7 @@ void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) if (state->m_frameIndex >= m_loopEndFrame) { state->m_frameIndex - = m_loopStartFrame + std::abs(state->m_frameIndex) % (m_loopEndFrame - m_loopStartFrame); + = m_loopStartFrame + state->m_frameIndex % (m_loopEndFrame - m_loopStartFrame); } } break; From d5e13deaec5cd85321e86e95d944328788a28cbf Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 10:30:56 -0500 Subject: [PATCH 11/31] Simplify advance function --- src/core/Sample.cpp | 47 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 07da2be15a9..363e0d64c38 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -200,52 +200,35 @@ void Sample::playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* st void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) { + state->m_frameIndex += (state->m_backwards ? -1 : 1) * advanceAmount; + + const auto absIndex = std::abs(state->m_frameIndex); switch (loopMode) { - case Loop::Off: - state->m_frameIndex += (state->m_backwards ? -1 : 1) * advanceAmount; - break; case Loop::On: - if (state->m_backwards) + if (state->m_frameIndex < m_loopStartFrame && state->m_backwards) { - state->m_frameIndex -= advanceAmount; - if (state->m_frameIndex < m_loopStartFrame) - { - state->m_frameIndex - = m_loopEndFrame - 1 - std::abs(state->m_frameIndex) % (m_loopEndFrame - m_loopStartFrame); - } + state->m_frameIndex = m_loopEndFrame - 1 - absIndex % (m_loopEndFrame - m_loopStartFrame); } - else + else if (state->m_frameIndex >= m_loopEndFrame) { - state->m_frameIndex += advanceAmount; - if (state->m_frameIndex >= m_loopEndFrame) - { - state->m_frameIndex - = m_loopStartFrame + state->m_frameIndex % (m_loopEndFrame - m_loopStartFrame); - } + state->m_frameIndex = m_loopStartFrame + absIndex % (m_loopEndFrame - m_loopStartFrame); } break; case Loop::PingPong: - if (state->m_backwards) + if (state->m_frameIndex < m_loopStartFrame && state->m_backwards) { - state->m_frameIndex -= advanceAmount; - if (state->m_frameIndex < m_loopStartFrame) - { - state->m_frameIndex - = m_loopStartFrame + std::abs(state->m_frameIndex) % (m_loopEndFrame - m_loopStartFrame); - state->m_backwards = false; - } + state->m_frameIndex = m_loopStartFrame + absIndex % (m_loopEndFrame - m_loopStartFrame); + state->m_backwards = false; } - else + else if (state->m_frameIndex >= m_loopEndFrame) { - state->m_frameIndex += advanceAmount; - if (state->m_frameIndex >= m_loopEndFrame) - { - state->m_frameIndex = m_loopEndFrame - 1 - state->m_frameIndex % (m_loopEndFrame - m_loopStartFrame); - state->m_backwards = true; - } + state->m_frameIndex = m_loopEndFrame - 1 - absIndex % (m_loopEndFrame - m_loopStartFrame); + state->m_backwards = true; } break; + default: + break; } } From 0f0e45cabdbdd8f9e497129f309b8ee4f08595a5 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 10:36:37 -0500 Subject: [PATCH 12/31] Consider reversed variable in playRaw --- src/core/Sample.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 363e0d64c38..2efc933fef5 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -194,7 +194,9 @@ void Sample::playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* st break; } - dst[i] = m_buffer->data()[backwards ? index-- : index++]; + auto playIndex = backwards ? index-- : index++; + if (m_reversed) { playIndex = m_buffer->size() - playIndex; } + dst[i] = m_buffer->data()[playIndex]; } } From 2f107ad3703398077bae26d75d81db33119434ce Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 10:46:30 -0500 Subject: [PATCH 13/31] Merge amplifySampleRange into play function --- include/Sample.h | 1 - src/core/Sample.cpp | 15 +++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/include/Sample.h b/include/Sample.h index d34a813fa00..51ff4a9e879 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -119,7 +119,6 @@ class LMMS_EXPORT Sample private: void playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* state, Loop loopMode) const; void advance(PlaybackState* state, size_t advanceAmount, Loop loopMode); - void amplifySampleRange(sampleFrame* src, int numFrames) const; private: std::shared_ptr m_buffer = SampleBuffer::emptyBuffer(); diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 2efc933fef5..c172ffc90d0 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -141,7 +141,11 @@ bool Sample::play(sampleFrame* dst, PlaybackState* state, int numFrames, float d if (!typeInfo::isEqual(m_amplification, 1.0f)) { - amplifySampleRange(dst, resampleResult.outputFramesGenerated); + for (int i = 0; i < numFrames; ++i) + { + dst[i][0] *= m_amplification; + dst[i][1] *= m_amplification; + } } return true; @@ -234,13 +238,4 @@ void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) } } -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) - { - src[i][0] *= amplification; - src[i][1] *= amplification; - } -} } // namespace lmms From 3bc21ea04f565b72ab41c28cea8e7ffd6d7ff76b Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 10:53:04 -0500 Subject: [PATCH 14/31] Make advance method const --- include/Sample.h | 2 +- src/core/Sample.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/Sample.h b/include/Sample.h index 51ff4a9e879..ac995741db5 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -118,7 +118,7 @@ class LMMS_EXPORT Sample private: void playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* state, Loop loopMode) const; - void advance(PlaybackState* state, size_t advanceAmount, Loop loopMode); + void advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) const; private: std::shared_ptr m_buffer = SampleBuffer::emptyBuffer(); diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index c172ffc90d0..5dc38f3fac3 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -204,7 +204,7 @@ void Sample::playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* st } } -void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) +void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) const { state->m_frameIndex += (state->m_backwards ? -1 : 1) * advanceAmount; From bf48d2eaf199a691d1ebf127d8a4cfbe4c4b9617 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 10:59:13 -0500 Subject: [PATCH 15/31] Use type size_t for numFrames --- include/Sample.h | 4 ++-- include/SampleBuffer.h | 2 +- src/core/Sample.cpp | 4 ++-- src/core/SampleBuffer.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/Sample.h b/include/Sample.h index ac995741db5..193f73bbfee 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -78,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); @@ -87,7 +87,7 @@ class LMMS_EXPORT Sample auto operator=(const Sample&) -> Sample&; auto operator=(Sample&&) -> Sample&; - auto play(sampleFrame* dst, PlaybackState* state, int numFrames, float desiredFrequency = DefaultBaseFreq, + auto play(sampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency = DefaultBaseFreq, Loop loopMode = Loop::Off) -> bool; auto sampleDuration() const -> std::chrono::milliseconds; 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 5dc38f3fac3..0cece4cd666 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -44,7 +44,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()) @@ -114,7 +114,7 @@ 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) { if (loopMode == Loop::Off && (state->m_frameIndex >= m_endFrame || (state->m_frameIndex < 0 && state->m_backwards))) { 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) { From f5fe4a104775d302cacd557e4346698ea125e21c Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 11:46:13 -0500 Subject: [PATCH 16/31] Fix advance function Should be considering the distance after the loop points when doing the modulo operation. --- src/core/Sample.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 0cece4cd666..dd0246276b9 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -206,36 +206,38 @@ void Sample::playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* st void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) const { - state->m_frameIndex += (state->m_backwards ? -1 : 1) * advanceAmount; + const auto distanceAfterLoopEnd = std::abs(state->m_frameIndex - m_loopEndFrame); + const auto distanceAfterLoopStart = std::abs(state->m_frameIndex - m_loopStartFrame); - const auto absIndex = std::abs(state->m_frameIndex); switch (loopMode) { case Loop::On: if (state->m_frameIndex < m_loopStartFrame && state->m_backwards) { - state->m_frameIndex = m_loopEndFrame - 1 - absIndex % (m_loopEndFrame - m_loopStartFrame); + state->m_frameIndex = m_loopEndFrame - 1 - distanceAfterLoopStart % (m_loopEndFrame - m_loopStartFrame); } else if (state->m_frameIndex >= m_loopEndFrame) { - state->m_frameIndex = m_loopStartFrame + absIndex % (m_loopEndFrame - m_loopStartFrame); + state->m_frameIndex = m_loopStartFrame + distanceAfterLoopEnd % (m_loopEndFrame - m_loopStartFrame); } break; case Loop::PingPong: if (state->m_frameIndex < m_loopStartFrame && state->m_backwards) { - state->m_frameIndex = m_loopStartFrame + absIndex % (m_loopEndFrame - m_loopStartFrame); + state->m_frameIndex = m_loopStartFrame + distanceAfterLoopStart % (m_loopEndFrame - m_loopStartFrame); state->m_backwards = false; } else if (state->m_frameIndex >= m_loopEndFrame) { - state->m_frameIndex = m_loopEndFrame - 1 - absIndex % (m_loopEndFrame - m_loopStartFrame); + state->m_frameIndex = m_loopEndFrame - 1 - distanceAfterLoopEnd % (m_loopEndFrame - m_loopStartFrame); state->m_backwards = true; } break; default: break; } + + state->m_frameIndex += (state->m_backwards ? -1 : 1) * advanceAmount; } } // namespace lmms From 968635d56882902ba23518cc3f13d788354513f6 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 11:47:41 -0500 Subject: [PATCH 17/31] Make play function const --- include/Sample.h | 2 +- src/core/Sample.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/Sample.h b/include/Sample.h index 193f73bbfee..db22f2cab15 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -88,7 +88,7 @@ class LMMS_EXPORT Sample auto operator=(Sample&&) -> Sample&; auto play(sampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency = DefaultBaseFreq, - Loop loopMode = Loop::Off) -> bool; + Loop loopMode = Loop::Off) const -> bool; auto sampleDuration() const -> std::chrono::milliseconds; auto sampleFile() const -> const QString& { return m_buffer->audioFile(); } diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index dd0246276b9..c84ea45b689 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -114,7 +114,7 @@ auto Sample::operator=(Sample&& other) -> Sample& return *this; } -bool Sample::play(sampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency, Loop loopMode) +bool Sample::play(sampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency, Loop loopMode) const { if (loopMode == Loop::Off && (state->m_frameIndex >= m_endFrame || (state->m_frameIndex < 0 && state->m_backwards))) { From 78134b9e6984ca4944dc87eb84f7de38fd16b904 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 12:47:48 -0500 Subject: [PATCH 18/31] Use int instead of f_cnt_t --- include/Sample.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/Sample.h b/include/Sample.h index db22f2cab15..86fba1ddc03 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -60,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; From e3ee48f505e507140e57f0c96fc7923d23049186 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 12:49:20 -0500 Subject: [PATCH 19/31] More simplification and style changes --- src/core/Sample.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index c84ea45b689..b13fafa4ca6 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -198,46 +198,46 @@ void Sample::playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* st break; } - auto playIndex = backwards ? index-- : index++; - if (m_reversed) { playIndex = m_buffer->size() - playIndex; } - dst[i] = m_buffer->data()[playIndex]; + dst[i] = m_buffer->data()[m_reversed ? m_buffer->size() - index : index]; + backwards ? --index : ++index; } } void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) const { - const auto distanceAfterLoopEnd = std::abs(state->m_frameIndex - m_loopEndFrame); - const auto distanceAfterLoopStart = std::abs(state->m_frameIndex - m_loopStartFrame); + state->m_frameIndex += (state->m_backwards ? -1 : 1) * advanceAmount; + + const auto distanceFromLoopStart = std::abs(state->m_frameIndex - m_loopStartFrame); + const auto distanceFromLoopEnd = std::abs(state->m_frameIndex - m_loopEndFrame); switch (loopMode) { case Loop::On: if (state->m_frameIndex < m_loopStartFrame && state->m_backwards) { - state->m_frameIndex = m_loopEndFrame - 1 - distanceAfterLoopStart % (m_loopEndFrame - m_loopStartFrame); + state->m_frameIndex = m_loopEndFrame - 1 - distanceFromLoopStart % (m_loopEndFrame - m_loopStartFrame); } else if (state->m_frameIndex >= m_loopEndFrame) { - state->m_frameIndex = m_loopStartFrame + distanceAfterLoopEnd % (m_loopEndFrame - m_loopStartFrame); + state->m_frameIndex + = m_loopStartFrame + distanceFromLoopEnd % (m_loopEndFrame - m_loopStartFrame); } break; case Loop::PingPong: if (state->m_frameIndex < m_loopStartFrame && state->m_backwards) { - state->m_frameIndex = m_loopStartFrame + distanceAfterLoopStart % (m_loopEndFrame - m_loopStartFrame); + state->m_frameIndex = m_loopStartFrame + distanceFromLoopStart % (m_loopEndFrame - m_loopStartFrame); state->m_backwards = false; } else if (state->m_frameIndex >= m_loopEndFrame) { - state->m_frameIndex = m_loopEndFrame - 1 - distanceAfterLoopEnd % (m_loopEndFrame - m_loopStartFrame); + state->m_frameIndex = m_loopEndFrame - 1 - distanceFromLoopEnd % (m_loopEndFrame - m_loopStartFrame); state->m_backwards = true; } break; default: break; } - - state->m_frameIndex += (state->m_backwards ? -1 : 1) * advanceAmount; } } // namespace lmms From 706423b8f57adb1b43c685836c7691473fd2dba0 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 13:19:01 -0500 Subject: [PATCH 20/31] Style --- src/core/Sample.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index b13fafa4ca6..cf051b7b988 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -219,8 +219,7 @@ void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) } else if (state->m_frameIndex >= m_loopEndFrame) { - state->m_frameIndex - = m_loopStartFrame + distanceFromLoopEnd % (m_loopEndFrame - m_loopStartFrame); + state->m_frameIndex = m_loopStartFrame + distanceFromLoopEnd % (m_loopEndFrame - m_loopStartFrame); } break; case Loop::PingPong: From e2aaa075b1a1c6f91b853882b84cf04663929347 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 13:28:47 -0500 Subject: [PATCH 21/31] Handle potential off by one access --- src/core/Sample.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index cf051b7b988..94f459adf80 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -198,7 +198,7 @@ void Sample::playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* st break; } - dst[i] = m_buffer->data()[m_reversed ? m_buffer->size() - index : index]; + dst[i] = m_buffer->data()[m_reversed ? m_buffer->size() - index - 1 : index]; backwards ? --index : ++index; } } From 387757c1b1539d24582aa7ef41a682ad2569bcdc Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 13:40:16 -0500 Subject: [PATCH 22/31] Update src/core/Sample.cpp Co-authored-by: Kevin Zander --- src/core/Sample.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 94f459adf80..de0c727d040 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -135,7 +135,8 @@ bool Sample::play(sampleFrame* dst, PlaybackState* state, size_t numFrames, floa if (resampleResult.outputFramesGenerated < numFrames) { - std::fill(dst + resampleResult.outputFramesGenerated, dst + resampleResult.outputFramesGenerated + numFrames, + std::fill(dst + resampleResult.outputFramesGenerated, + dst + resampleResult.outputFramesGenerated + numFrames, sampleFrame{0, 0}); } From 94475d808802ebff1245d5f4f2c1e0e8465ed9e2 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 13:43:22 -0500 Subject: [PATCH 23/31] Reduce nesting within if guard --- src/core/Sample.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index de0c727d040..2bf5b4d05aa 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -116,10 +116,8 @@ auto Sample::operator=(Sample&& other) -> Sample& bool Sample::play(sampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency, Loop loopMode) const { - if (loopMode == Loop::Off && (state->m_frameIndex >= m_endFrame || (state->m_frameIndex < 0 && state->m_backwards))) - { - return false; - } + const auto pastBounds = state->m_frameIndex >= m_endFrame || (state->m_frameIndex < 0 && state->m_backwards); + if (loopMode == Loop::Off && pastBounds) { return false; } const auto outputSampleRate = Engine::audioEngine()->processingSampleRate() * m_frequency / desiredFrequency; const auto inputSampleRate = m_buffer->sampleRate(); From 267b5c11b7faf3ab360b00a18d73f0a3d97fdb81 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 14:59:15 -0500 Subject: [PATCH 24/31] Use std::fill_n --- src/core/Sample.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 2bf5b4d05aa..e680f17cfb4 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -133,9 +133,7 @@ bool Sample::play(sampleFrame* dst, PlaybackState* state, size_t numFrames, floa if (resampleResult.outputFramesGenerated < numFrames) { - std::fill(dst + resampleResult.outputFramesGenerated, - dst + resampleResult.outputFramesGenerated + numFrames, - sampleFrame{0, 0}); + std::fill_n(dst + resampleResult.outputFramesGenerated, numFrames, sampleFrame{}); } if (!typeInfo::isEqual(m_amplification, 1.0f)) From 8ac3aac0285b8bfb373043f885a88ea4a81ce13a Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 15:00:38 -0500 Subject: [PATCH 25/31] Use the correct size with std::fill_n --- src/core/Sample.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index e680f17cfb4..2d7bf7e0c74 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -133,7 +133,8 @@ bool Sample::play(sampleFrame* dst, PlaybackState* state, size_t numFrames, floa if (resampleResult.outputFramesGenerated < numFrames) { - std::fill_n(dst + resampleResult.outputFramesGenerated, numFrames, sampleFrame{}); + std::fill_n(dst + resampleResult.outputFramesGenerated, numFrames - resampleResult.outputFramesGenerated, + sampleFrame{}); } if (!typeInfo::isEqual(m_amplification, 1.0f)) From 7c79569528d492b07bfbc4b4dd66558e94860166 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 15:02:45 -0500 Subject: [PATCH 26/31] Move resampleResult.outputFramesGenerated into separate variable --- src/core/Sample.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 2d7bf7e0c74..24449d09c2e 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -131,11 +131,8 @@ bool Sample::play(sampleFrame* dst, PlaybackState* state, size_t numFrames, floa = state->resampler().resample(&playBuffer[0][0], playBuffer.size(), &dst[0][0], numFrames, resampleRatio); advance(state, resampleResult.inputFramesUsed, loopMode); - if (resampleResult.outputFramesGenerated < numFrames) - { - std::fill_n(dst + resampleResult.outputFramesGenerated, numFrames - resampleResult.outputFramesGenerated, - sampleFrame{}); - } + const auto outputFrames = resampleResult.outputFramesGenerated; + if (outputFrames < numFrames) { std::fill_n(dst + outputFrames, numFrames - outputFrames, sampleFrame{}); } if (!typeInfo::isEqual(m_amplification, 1.0f)) { From 907f613b64a99b94ab49a488cd41e05b15c8276d Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 11 Feb 2024 15:52:52 -0500 Subject: [PATCH 27/31] Add asserts in play function --- src/core/Sample.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 24449d09c2e..78976a84332 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -116,6 +116,9 @@ auto Sample::operator=(Sample&& other) -> Sample& bool Sample::play(sampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency, Loop loopMode) const { + assert(numFrames > 0); + assert(desiredFrequency > 0); + const auto pastBounds = state->m_frameIndex >= m_endFrame || (state->m_frameIndex < 0 && state->m_backwards); if (loopMode == Loop::Off && pastBounds) { return false; } From 29a0b3d52a434f591bf5e725d5cfdf5f1b8efd25 Mon Sep 17 00:00:00 2001 From: saker Date: Mon, 12 Feb 2024 18:11:23 -0500 Subject: [PATCH 28/31] Enforce start frame as lower bound for frame index --- src/core/Sample.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 78976a84332..13a9bf8b662 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -127,6 +127,8 @@ bool Sample::play(sampleFrame* dst, PlaybackState* state, size_t numFrames, floa const auto resampleRatio = outputSampleRate / inputSampleRate; const auto marginSize = s_interpolationMargins[state->resampler().interpolationMode()]; + state->m_frameIndex = std::max(m_startFrame, state->m_frameIndex); + auto playBuffer = std::vector(numFrames / resampleRatio + marginSize); playRaw(playBuffer.data(), playBuffer.size(), state, loopMode); From 0490ab1691aee54ac6badfb2a8df8909736149c8 Mon Sep 17 00:00:00 2001 From: saker Date: Wed, 14 Feb 2024 21:26:56 -0500 Subject: [PATCH 29/31] Handle arithmetic exception when the loop distance is 0 --- src/core/Sample.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 13a9bf8b662..dbbe3c78f02 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -206,31 +206,34 @@ void Sample::playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* st void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) const { state->m_frameIndex += (state->m_backwards ? -1 : 1) * advanceAmount; + if (loopMode == Loop::Off) { return; } 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; } switch (loopMode) { case Loop::On: if (state->m_frameIndex < m_loopStartFrame && state->m_backwards) { - state->m_frameIndex = m_loopEndFrame - 1 - distanceFromLoopStart % (m_loopEndFrame - m_loopStartFrame); + state->m_frameIndex = m_loopEndFrame - 1 - distanceFromLoopStart % loopSize; } else if (state->m_frameIndex >= m_loopEndFrame) { - state->m_frameIndex = m_loopStartFrame + distanceFromLoopEnd % (m_loopEndFrame - m_loopStartFrame); + 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 % (m_loopEndFrame - m_loopStartFrame); + 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 % (m_loopEndFrame - m_loopStartFrame); + state->m_frameIndex = m_loopEndFrame - 1 - distanceFromLoopEnd % loopSize; state->m_backwards = true; } break; From 57b9e756119e79d31458fa05254a7cf12fc945ab Mon Sep 17 00:00:00 2001 From: saker Date: Wed, 14 Feb 2024 21:40:04 -0500 Subject: [PATCH 30/31] Do not check if backwards is true --- src/core/Sample.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index dbbe3c78f02..27815c8ec52 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -176,7 +176,7 @@ void Sample::playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* st switch (loopMode) { case Loop::Off: - if (index > m_endFrame || (index < 0 && state->m_backwards)) { return; } + if (index < 0 || index > m_endFrame) { return; } break; case Loop::On: if (index < m_loopStartFrame && backwards) { index = m_loopEndFrame - 1; } From a34ef79c2ed142f90ee731bbde671559b6571b4a Mon Sep 17 00:00:00 2001 From: saker Date: Fri, 16 Feb 2024 14:20:22 -0500 Subject: [PATCH 31/31] Include cassert header --- src/core/Sample.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 27815c8ec52..333ee624d4d 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -24,6 +24,8 @@ #include "Sample.h" +#include + namespace lmms { Sample::Sample(const QString& audioFile)