Skip to content

Commit

Permalink
Merge pull request #11988 from abique/alex/reduce-sample-buffer-memor…
Browse files Browse the repository at this point in the history
…y-usage

Alex/reduce sample buffer memory usage
  • Loading branch information
daschuer authored Oct 1, 2023
2 parents 0119d85 + ba1ac45 commit ab8d11f
Show file tree
Hide file tree
Showing 34 changed files with 59,610 additions and 634,045 deletions.
2 changes: 1 addition & 1 deletion src/effects/backends/builtin/balanceeffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ EffectManifestPointer BalanceEffect::getManifest() {

BalanceGroupState::BalanceGroupState(const mixxx::EngineParameters& engineParameters)
: EffectState(engineParameters),
m_pHighBuf(MAX_BUFFER_LEN),
m_pHighBuf(engineParameters.samplesPerBuffer()),
m_oldSampleRate(engineParameters.sampleRate()),
m_freq(kMinCornerHz),
m_oldBalance(0),
Expand Down
6 changes: 3 additions & 3 deletions src/effects/backends/builtin/linkwitzriley8eqeffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ LinkwitzRiley8EQEffectGroupState::LinkwitzRiley8EQEffectGroupState(
m_oldSampleRate(engineParameters.sampleRate()),
m_loFreq(kStartupLoFreq),
m_hiFreq(kStartupHiFreq) {
m_pLowBuf = SampleUtil::alloc(MAX_BUFFER_LEN);
m_pMidBuf = SampleUtil::alloc(MAX_BUFFER_LEN);
m_pHighBuf = SampleUtil::alloc(MAX_BUFFER_LEN);
m_pLowBuf = SampleUtil::alloc(kMaxEngineSamples);
m_pMidBuf = SampleUtil::alloc(kMaxEngineSamples);
m_pHighBuf = SampleUtil::alloc(kMaxEngineSamples);

m_low1 = new EngineFilterLinkwitzRiley8Low(engineParameters.sampleRate(), kStartupLoFreq);
m_high1 = new EngineFilterLinkwitzRiley8High(engineParameters.sampleRate(), kStartupLoFreq);
Expand Down
23 changes: 13 additions & 10 deletions src/effects/backends/builtin/pitchshifteffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,13 @@ PitchShiftGroupState::PitchShiftGroupState(
}

PitchShiftGroupState::~PitchShiftGroupState() {
SampleUtil::free(m_retrieveBuffer[0]);
SampleUtil::free(m_retrieveBuffer[1]);
}

void PitchShiftGroupState::initializeBuffer(
const mixxx::EngineParameters& engineParameters) {
m_retrieveBuffer[0] = SampleUtil::alloc(
m_retrieveBuffer[0] = mixxx::SampleBuffer(
engineParameters.framesPerBuffer());
m_retrieveBuffer[1] = SampleUtil::alloc(
m_retrieveBuffer[1] = mixxx::SampleBuffer(
engineParameters.framesPerBuffer());
}

Expand Down Expand Up @@ -132,6 +130,8 @@ void PitchShiftEffect::processChannel(
Q_UNUSED(groupFeatures);
Q_UNUSED(enableState);

DEBUG_ASSERT(engineParameters.framesPerBuffer() <= pState->m_retrieveBuffer[0].size());

if (const bool formantPreserving = m_pFormantPreservingParameter->toBool();
m_currentFormant != formantPreserving) {
m_currentFormant = formantPreserving;
Expand Down Expand Up @@ -219,12 +219,15 @@ void PitchShiftEffect::processChannel(
pState->m_pRubberBand->setPitchScale(pitch);

SampleUtil::deinterleaveBuffer(
pState->m_retrieveBuffer[0],
pState->m_retrieveBuffer[1],
pState->m_retrieveBuffer[0].data(),
pState->m_retrieveBuffer[1].data(),
pInput,
engineParameters.framesPerBuffer());

CSAMPLE* retrieveBuffers[2]{pState->m_retrieveBuffer[0].data(),
pState->m_retrieveBuffer[1].data()};
pState->m_pRubberBand->process(
pState->m_retrieveBuffer,
retrieveBuffers,
engineParameters.framesPerBuffer(),
false);

Expand All @@ -234,11 +237,11 @@ void PitchShiftEffect::processChannel(
engineParameters.framesPerBuffer());

SINT receivedFrames = pState->m_pRubberBand->retrieve(
pState->m_retrieveBuffer,
retrieveBuffers,
framesToRead);

SampleUtil::interleaveBuffer(pOutput,
pState->m_retrieveBuffer[0],
pState->m_retrieveBuffer[1],
pState->m_retrieveBuffer[0].data(),
pState->m_retrieveBuffer[1].data(),
receivedFrames);
}
3 changes: 2 additions & 1 deletion src/effects/backends/builtin/pitchshifteffect.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "util/defs.h"
#include "util/math.h"
#include "util/sample.h"
#include "util/samplebuffer.h"
#include "util/types.h"

namespace RubberBand {
Expand All @@ -26,7 +27,7 @@ class PitchShiftGroupState : public EffectState {
void audioParametersChanged(const mixxx::EngineParameters& engineParameters);

std::unique_ptr<RubberBand::RubberBandStretcher> m_pRubberBand;
CSAMPLE* m_retrieveBuffer[2];
mixxx::SampleBuffer m_retrieveBuffer[2];
};

class PitchShiftEffect final : public EffectProcessorImpl<PitchShiftGroupState> {
Expand Down
8 changes: 4 additions & 4 deletions src/effects/backends/lv2/lv2effectprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ LV2EffectProcessor::LV2EffectProcessor(LV2EffectManifestPointer pManifest)
m_pPlugin(pManifest->getPlugin()),
m_audioPortIndices(pManifest->getAudioPortIndices()),
m_controlPortIndices(pManifest->getControlPortIndices()) {
m_inputL = new float[MAX_BUFFER_LEN];
m_inputR = new float[MAX_BUFFER_LEN];
m_outputL = new float[MAX_BUFFER_LEN];
m_outputR = new float[MAX_BUFFER_LEN];
m_inputL = new float[kMaxEngineSamples];
m_inputR = new float[kMaxEngineSamples];
m_outputL = new float[kMaxEngineSamples];
m_outputR = new float[kMaxEngineSamples];
}

void LV2EffectProcessor::loadEngineEffectParameters(
Expand Down
4 changes: 2 additions & 2 deletions src/engine/bufferscalers/enginebufferscalerubberband.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ SINT EngineBufferScaleRubberBand::retrieveAndDeinterleave(

DEBUG_ASSERT(received_frames <= frames);
SampleUtil::interleaveBuffer(pBuffer,
m_buffers[0].data() + frame_offset,
m_buffers[1].data() + frame_offset,
m_buffers[0].data(frame_offset),
m_buffers[1].data(frame_offset),
received_frames);

return received_frames;
Expand Down
13 changes: 0 additions & 13 deletions src/engine/cachingreader/cachingreaderchunk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,6 @@ constexpr SINT kInvalidChunkIndex = -1;

} // anonymous namespace

// One chunk should contain 1/2 - 1/4th of a second of audio.
// 8192 frames contain about 170 ms of audio at 48 kHz, which
// is well above (hopefully) the latencies people are seeing.
// At 10 ms latency one chunk is enough for 17 callbacks.
// Additionally the chunk size should be a power of 2 for
// easier memory alignment.
// TODO(XXX): The optimum value of the "constant" kFrames depends
// on the properties of the AudioSource as the remarks above suggest!
const mixxx::audio::ChannelCount CachingReaderChunk::kChannels = mixxx::kEngineChannelCount;
const SINT CachingReaderChunk::kFrames = 8192; // ~ 170 ms at 48 kHz
const SINT CachingReaderChunk::kSamples =
CachingReaderChunk::frames2samples(CachingReaderChunk::kFrames);

CachingReaderChunk::CachingReaderChunk(
mixxx::SampleBuffer::WritableSlice sampleBuffer)
: m_index(kInvalidChunkIndex),
Expand Down
46 changes: 27 additions & 19 deletions src/engine/cachingreader/cachingreaderchunk.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,25 @@
// and the worker.
class CachingReaderChunk {
public:
static const mixxx::audio::ChannelCount kChannels;
static const SINT kFrames;
static const SINT kSamples;

// Converts frames to samples
static SINT frames2samples(SINT frames) {
return frames * kChannels;
}
static double dFrames2samples(SINT frames) {
return static_cast<double>(frames) * kChannels;
}
// One chunk should contain 1/2 - 1/4th of a second of audio.
// 8192 frames contain about 170 ms of audio at 48 kHz, which
// is well above (hopefully) the latencies people are seeing.
// At 10 ms latency one chunk is enough for 17 callbacks.
// Additionally the chunk size should be a power of 2 for
// easier memory alignment.
// TODO(XXX): The optimum value of the "constant" kFrames depends
// on the properties of the AudioSource as the remarks above suggest!
static constexpr mixxx::audio::ChannelCount kChannels = mixxx::kEngineChannelCount;
static constexpr SINT kFrames = 8192; // ~ 170 ms at 48 kHz
static constexpr SINT kSamples = kFrames * kChannels;

// Converts frames to samples
static constexpr SINT frames2samples(SINT frames) noexcept {
return frames * kChannels;
}
static constexpr double dFrames2samples(SINT frames) noexcept {
return static_cast<double>(frames) * kChannels;
}
// Converts samples to frames
static SINT samples2frames(SINT samples) {
DEBUG_ASSERT(0 == (samples % kChannels));
Expand All @@ -36,7 +44,7 @@ class CachingReaderChunk {
static SINT indexForFrame(
/*const mixxx::AudioSourcePointer& pAudioSource,*/
SINT frameIndex) {
//DEBUG_ASSERT(pAudioSource->frameIndexRange().contains(frameIndex));
// DEBUG_ASSERT(pAudioSource->frameIndexRange().contains(frameIndex));
const SINT frameIndexOffset = frameIndex /*- pAudioSource->frameIndexMin()*/;
return frameIndexOffset / kFrames;
}
Expand All @@ -45,7 +53,7 @@ class CachingReaderChunk {
CachingReaderChunk(const CachingReaderChunk&) = delete;
CachingReaderChunk(CachingReaderChunk&&) = delete;

SINT getIndex() const {
SINT getIndex() const noexcept {
return m_index;
}

Expand Down Expand Up @@ -74,9 +82,9 @@ class CachingReaderChunk {
void init(SINT index);

private:
SINT frameIndexOffset() const {
SINT frameIndexOffset() const noexcept {
return m_index * kFrames;
}
}

SINT m_index;

Expand Down Expand Up @@ -104,7 +112,7 @@ class CachingReaderChunkForOwner: public CachingReaderChunk {
READ_PENDING
};

State getState() const {
State getState() const noexcept {
return m_state;
}

Expand Down Expand Up @@ -139,8 +147,8 @@ class CachingReaderChunkForOwner: public CachingReaderChunk {
CachingReaderChunkForOwner** ppTail);

private:
State m_state;
State m_state;

CachingReaderChunkForOwner* m_pPrev; // previous item in double-linked list
CachingReaderChunkForOwner* m_pNext; // next item in double-linked list
CachingReaderChunkForOwner* m_pPrev; // previous item in double-linked list
CachingReaderChunkForOwner* m_pNext; // next item in double-linked list
};
6 changes: 3 additions & 3 deletions src/engine/channelmixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ void ChannelMixer::applyEffectsAndMixChannels(const EngineMixer::GainCalculator&
gainCache.m_gain = newGain;
pEngineEffectsManager->processPostFaderAndMix(pChannelInfo->m_handle,
outputHandle,
pChannelInfo->m_pBuffer,
pChannelInfo->m_pBuffer.data(),
pOutput,
iBufferSize,
sampleRate,
Expand Down Expand Up @@ -85,13 +85,13 @@ void ChannelMixer::applyEffectsInPlaceAndMixChannels(
gainCache.m_gain = newGain;
pEngineEffectsManager->processPostFaderInPlace(pChannelInfo->m_handle,
outputHandle,
pChannelInfo->m_pBuffer,
pChannelInfo->m_pBuffer.data(),
iBufferSize,
sampleRate,
pChannelInfo->m_features,
oldGain,
newGain,
fadeout);
SampleUtil::add(pOutput, pChannelInfo->m_pBuffer, iBufferSize);
SampleUtil::add(pOutput, pChannelInfo->m_pBuffer.data(), iBufferSize);
}
}
4 changes: 2 additions & 2 deletions src/engine/effects/engineeffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ EngineEffect::EngineEffect(EffectManifestPointer pManifest,
// At this point the SoundDevice is not set up so we use the kInitalSampleRate.
const mixxx::EngineParameters engineParameters(
kInitalSampleRate,
MAX_BUFFER_LEN / mixxx::kEngineChannelCount);
kMaxEngineFrames);
m_pProcessor->initialize(activeInputChannels, registeredOutputChannels, engineParameters);
m_effectRampsFromDry = pManifest->effectRampsFromDry();
}
Expand All @@ -62,7 +62,7 @@ void EngineEffect::initalizeInputChannel(ChannelHandle inputChannel) {
// At this point the SoundDevice is not set up so we use the kInitalSampleRate.
const mixxx::EngineParameters engineParameters(
kInitalSampleRate,
MAX_BUFFER_LEN / mixxx::kEngineChannelCount);
kMaxEngineFrames);
m_pProcessor->initializeInputChannel(inputChannel, engineParameters);
}

Expand Down
6 changes: 4 additions & 2 deletions src/engine/effects/engineeffectchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ EngineEffectChain::EngineEffectChain(const QString& group,
m_enableState(EffectEnableState::Enabled),
m_mixMode(EffectChainMixMode::DrySlashWet),
m_dMix(0),
m_buffer1(MAX_BUFFER_LEN),
m_buffer2(MAX_BUFFER_LEN) {
m_buffer1(kMaxEngineSamples),
m_buffer2(kMaxEngineSamples) {
// Try to prevent memory allocation.
m_effects.reserve(256);

Expand Down Expand Up @@ -179,6 +179,8 @@ bool EngineEffectChain::process(const ChannelHandle& inputHandle,
const mixxx::audio::SampleRate sampleRate,
const GroupFeatureState& groupFeatures,
bool fadeout) {
DEBUG_ASSERT(numSamples <= kMaxEngineSamples);

// Compute the effective enable state from the channel input routing switch and
// the chain's enable state. When either of these are turned on/off, send the
// effects the intermediate enabling/disabling signal.
Expand Down
4 changes: 2 additions & 2 deletions src/engine/effects/engineeffectsmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

EngineEffectsManager::EngineEffectsManager(std::unique_ptr<EffectsResponsePipe> pResponsePipe)
: m_pResponsePipe(std::move(pResponsePipe)),
m_buffer1(MAX_BUFFER_LEN),
m_buffer2(MAX_BUFFER_LEN) {
m_buffer1(kMaxEngineSamples),
m_buffer2(kMaxEngineSamples) {
// Try to prevent memory allocation.
m_effects.reserve(256);
}
Expand Down
3 changes: 2 additions & 1 deletion src/engine/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ static constexpr audio::ChannelCount kEngineChannelCount =
audio::ChannelCount::stereo();

// Contains the information needed to process a buffer of audio
class EngineParameters {
class EngineParameters final {
public:
SINT framesPerBuffer() const {
return m_framesPerBuffer;
Expand All @@ -34,6 +34,7 @@ class EngineParameters {
sampleRate),
m_framesPerBuffer(framesPerBuffer) {
DEBUG_ASSERT(framesPerBuffer > 0);
DEBUG_ASSERT(sampleRate > 0);
}

private:
Expand Down
4 changes: 2 additions & 2 deletions src/engine/enginebuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ EngineBuffer::EngineBuffer(const QString& group,
m_iEnableSyncQueued(SYNC_REQUEST_NONE),
m_iSyncModeQueued(static_cast<int>(SyncMode::Invalid)),
m_bPlayAfterLoading(false),
m_pCrossfadeBuffer(SampleUtil::alloc(MAX_BUFFER_LEN)),
m_pCrossfadeBuffer(SampleUtil::alloc(kMaxEngineSamples)),
m_bCrossfadeReady(false),
m_iLastBufferSize(0) {
// This should be a static assertion, but isValid() is not constexpr.
Expand All @@ -110,7 +110,7 @@ EngineBuffer::EngineBuffer(const QString& group,
m_queuedSeek.setValue(kNoQueuedSeek);

// zero out crossfade buffer
SampleUtil::clear(m_pCrossfadeBuffer, MAX_BUFFER_LEN);
SampleUtil::clear(m_pCrossfadeBuffer, kMaxEngineSamples);

m_pReader = new CachingReader(group, pConfig);
connect(m_pReader, &CachingReader::trackLoading,
Expand Down
Loading

0 comments on commit ab8d11f

Please sign in to comment.