Revert "Switch to libsamplerate's callback API in Sample (#7361)" (#7410)

This reverts commit 2f5f12aaae.
This commit is contained in:
saker
2024-08-04 11:01:26 -04:00
committed by GitHub
parent 735e483d9f
commit 5b366cfe3c
9 changed files with 265 additions and 69 deletions

View File

@@ -0,0 +1,69 @@
/*
* AudioResampler.cpp - wrapper for libsamplerate
*
* Copyright (c) 2023 saker <sakertooth@gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#include "AudioResampler.h"
#include <samplerate.h>
#include <stdexcept>
#include <string>
namespace lmms {
AudioResampler::AudioResampler(int interpolationMode, int channels)
: m_interpolationMode(interpolationMode)
, m_channels(channels)
, m_state(src_new(interpolationMode, channels, &m_error))
{
if (!m_state)
{
const auto errorMessage = std::string{src_strerror(m_error)};
const auto fullMessage = std::string{"Failed to create an AudioResampler: "} + errorMessage;
throw std::runtime_error{fullMessage};
}
}
AudioResampler::~AudioResampler()
{
src_delete(m_state);
}
auto AudioResampler::resample(const float* in, long inputFrames, float* out, long outputFrames, double ratio)
-> ProcessResult
{
auto data = SRC_DATA{};
data.data_in = in;
data.input_frames = inputFrames;
data.data_out = out;
data.output_frames = outputFrames;
data.src_ratio = ratio;
data.end_of_input = 0;
return {src_process(m_state, &data), data.input_frames_used, data.output_frames_gen};
}
void AudioResampler::setRatio(double ratio)
{
src_set_ratio(m_state, ratio);
}
} // namespace lmms

View File

@@ -4,6 +4,7 @@ set(LMMS_SRCS
core/AudioEngine.cpp
core/AudioEngineProfiler.cpp
core/AudioEngineWorkerThread.cpp
core/AudioResampler.cpp
core/AutomatableModel.cpp
core/AutomationClip.cpp
core/AutomationNode.cpp

View File

@@ -26,8 +26,6 @@
#include <cassert>
#include "MixHelpers.h"
namespace lmms {
Sample::Sample(const QString& audioFile)
@@ -118,28 +116,43 @@ auto Sample::operator=(Sample&& other) -> Sample&
return *this;
}
bool Sample::play(SampleFrame* dst, PlaybackState* state, size_t numFrames, double frequency, Loop loopMode) const
bool Sample::play(SampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency, Loop loopMode) const
{
assert(numFrames > 0);
assert(frequency > 0);
if (m_buffer->empty()) { return false; }
assert(desiredFrequency > 0);
const auto outputSampleRate = Engine::audioEngine()->outputSampleRate() * m_frequency / frequency;
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()->outputSampleRate() * m_frequency / desiredFrequency;
const auto inputSampleRate = m_buffer->sampleRate();
const auto resampleRatio = outputSampleRate / inputSampleRate;
const auto marginSize = s_interpolationMargins[state->resampler().interpolationMode()];
state->frameIndex = std::max<int>(m_startFrame, state->frameIndex);
state->sample = this;
state->loop = &loopMode;
state->m_frameIndex = std::max<int>(m_startFrame, state->m_frameIndex);
src_set_ratio(state->resampleState, resampleRatio);
if (src_callback_read(state->resampleState, resampleRatio, numFrames, &dst[0][0]) != 0)
auto playBuffer = std::vector<SampleFrame>(numFrames / resampleRatio + marginSize);
playRaw(playBuffer.data(), playBuffer.size(), state, loopMode);
state->resampler().setRatio(resampleRatio);
const auto resampleResult
= state->resampler().resample(&playBuffer[0][0], playBuffer.size(), &dst[0][0], numFrames, resampleRatio);
advance(state, resampleResult.inputFramesUsed, loopMode);
const auto outputFrames = static_cast<f_cnt_t>(resampleResult.outputFramesGenerated);
if (outputFrames < numFrames) { std::fill_n(dst + outputFrames, numFrames - outputFrames, SampleFrame{}); }
if (!typeInfo<float>::isEqual(m_amplification, 1.0f))
{
MixHelpers::multiply(dst, m_amplification, numFrames);
return true;
for (auto i = std::size_t{0}; i < numFrames; ++i)
{
dst[i][0] *= m_amplification;
dst[i][1] *= m_amplification;
}
}
return false;
return true;
}
auto Sample::sampleDuration() const -> std::chrono::milliseconds
@@ -157,43 +170,82 @@ void Sample::setAllPointFrames(int startFrame, int endFrame, int loopStartFrame,
setLoopEndFrame(loopEndFrame);
}
long Sample::render(void* callbackData, float** data)
void Sample::playRaw(SampleFrame* dst, size_t numFrames, const PlaybackState* state, Loop loopMode) const
{
const auto state = static_cast<PlaybackState*>(callbackData);
const auto loop = *state->loop;
const auto sample = state->sample;
auto& index = state->frameIndex;
auto& backwards = state->backwards;
if (m_buffer->size() < 1) { return; }
switch (loop)
auto index = state->m_frameIndex;
auto backwards = state->m_backwards;
for (size_t i = 0; i < numFrames; ++i)
{
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;
}
dst[i] = m_buffer->data()[m_reversed ? m_buffer->size() - index - 1 : index];
backwards ? --index : ++index;
}
}
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::Off:
if (index < 0 || index >= sample->m_endFrame) { return 0; }
break;
case Loop::On:
if (index < sample->m_loopStartFrame && state->backwards) { index = sample->m_loopEndFrame - 1; }
else if (index >= sample->m_loopEndFrame) { index = sample->m_loopStartFrame; }
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 (index < sample->m_loopStartFrame && state->backwards)
if (state->m_frameIndex < m_loopStartFrame && state->m_backwards)
{
index = sample->m_loopStartFrame;
backwards = false;
state->m_frameIndex = m_loopStartFrame + distanceFromLoopStart % loopSize;
state->m_backwards = false;
}
else if (index >= sample->m_loopEndFrame)
else if (state->m_frameIndex >= m_loopEndFrame)
{
index = sample->m_loopEndFrame - 1;
backwards = true;
state->m_frameIndex = m_loopEndFrame - 1 - distanceFromLoopEnd % loopSize;
state->m_backwards = true;
}
break;
default:
break;
}
const auto srcIndex = sample->m_reversed ? sample->m_buffer->size() - index - 1 : index;
*data = const_cast<float*>(&sample->m_buffer->data()[srcIndex][0]);
backwards ? --index : ++index;
return 1;
}
} // namespace lmms