This reverts commit 2f5f12aaae.
This commit is contained in:
69
src/core/AudioResampler.cpp
Normal file
69
src/core/AudioResampler.cpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user