Browse Source

audiofile: better use of local vs internal pool

tags/v2.3.0-RC1
falkTX 4 years ago
parent
commit
e5ff619c8b
2 changed files with 193 additions and 110 deletions
  1. +176
    -100
      source/native-plugins/audio-base.hpp
  2. +17
    -10
      source/native-plugins/audio-file.cpp

+ 176
- 100
source/native-plugins/audio-base.hpp View File

@@ -1,6 +1,6 @@
/*
* Carla Native Plugins
* Copyright (C) 2013-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2013-2021 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -47,12 +47,15 @@ extern "C" {
# define CARLA_MLOCK(ptr, size) mlock((ptr), (size))
#endif

// #define DEBUG_FILE_OPS

typedef struct adinfo ADInfo;

struct AudioFilePool {
float* buffer[2];
float* tmpbuf[2];
uint32_t numFrames;
uint32_t maxFrame;
volatile uint64_t startFrame;
water::SpinLock mutex;

@@ -61,6 +64,7 @@ struct AudioFilePool {
: buffer{nullptr},
tmpbuf{nullptr},
numFrames(0),
maxFrame(0),
startFrame(0),
mutex() {}
#else
@@ -79,7 +83,7 @@ struct AudioFilePool {
destroy();
}

void create(const uint32_t desiredNumFrames, const bool withTempBuffers)
void create(const uint32_t desiredNumFrames, const uint32_t fileNumFrames, const bool withTempBuffers)
{
CARLA_ASSERT(buffer[0] == nullptr);
CARLA_ASSERT(buffer[1] == nullptr);
@@ -87,9 +91,12 @@ struct AudioFilePool {
CARLA_ASSERT(tmpbuf[1] == nullptr);
CARLA_ASSERT(startFrame == 0);
CARLA_ASSERT(numFrames == 0);
CARLA_ASSERT(maxFrame == 0);

buffer[0] = new float[desiredNumFrames];
buffer[1] = new float[desiredNumFrames];
carla_zeroFloats(buffer[0], desiredNumFrames);
carla_zeroFloats(buffer[1], desiredNumFrames);
CARLA_MLOCK(buffer[0], sizeof(float)*desiredNumFrames);
CARLA_MLOCK(buffer[1], sizeof(float)*desiredNumFrames);

@@ -97,11 +104,17 @@ struct AudioFilePool {
{
tmpbuf[0] = new float[desiredNumFrames];
tmpbuf[1] = new float[desiredNumFrames];
carla_zeroFloats(tmpbuf[0], desiredNumFrames);
carla_zeroFloats(tmpbuf[1], desiredNumFrames);
CARLA_MLOCK(tmpbuf[0], sizeof(float)*desiredNumFrames);
CARLA_MLOCK(tmpbuf[1], sizeof(float)*desiredNumFrames);
}

reset(desiredNumFrames);
const water::GenericScopedLock<water::SpinLock> gsl(mutex);

startFrame = 0;
numFrames = desiredNumFrames;
maxFrame = fileNumFrames;
}

void destroy() noexcept
@@ -137,25 +150,73 @@ struct AudioFilePool {
}
}

void reset(const uint32_t desiredNumFrames) noexcept
// NOTE it is assumed that mutex is locked
bool tryPutData(float* const out1,
float* const out2,
uint64_t framePos,
const uint32_t frames,
const bool loopingMode,
const bool isOffline,
bool& needsRead,
uint32_t& needsReadFrame)
{
if (desiredNumFrames != 0)
CARLA_SAFE_ASSERT_RETURN(numFrames != 0, false);
CARLA_SAFE_ASSERT_RETURN(maxFrame != 0, false);

if (framePos >= maxFrame)
{
if (loopingMode)
framePos %= maxFrame;
else
return false;
}

uint64_t frameDiff;
const uint32_t numFramesNearEnd = numFrames*3/5;

if (framePos < startFrame)
{
carla_zeroFloats(buffer[0], desiredNumFrames);
carla_zeroFloats(buffer[1], desiredNumFrames);
if (startFrame + numFrames <= maxFrame)
{
needsRead = true;
needsReadFrame = framePos;
return false;
}

frameDiff = framePos + (maxFrame - startFrame);

if (tmpbuf[0] != nullptr)
carla_zeroFloats(tmpbuf[0], desiredNumFrames);
if (tmpbuf[1] != nullptr)
carla_zeroFloats(tmpbuf[1], desiredNumFrames);
if (frameDiff + frames >= numFrames)
{
needsRead = true;
needsReadFrame = framePos;
return false;
}

carla_copyFloats(out1, buffer[0] + frameDiff, frames);
carla_copyFloats(out2, buffer[1] + frameDiff, frames);
}
else
{
frameDiff = framePos - startFrame;

const water::GenericScopedLock<water::SpinLock> gsl(mutex);
if (frameDiff + frames >= numFrames)
{
needsRead = true;
needsReadFrame = framePos;
return false;
}

carla_copyFloats(out1, buffer[0] + frameDiff, frames);
carla_copyFloats(out2, buffer[1] + frameDiff, frames);
}

if (frameDiff > numFramesNearEnd)
{
startFrame = 0;
numFrames = desiredNumFrames;
needsRead = true;
needsReadFrame = framePos + (isOffline ? 0 : frames);
}

return true;
}

CARLA_DECLARE_NON_COPY_STRUCT(AudioFilePool)
@@ -171,13 +232,14 @@ public:
fNeedsRead(false),
fFilePtr(nullptr),
fFileNfo(),
fMaxFrame(0),
fResampleRatio(0.0),
fPollTempData(nullptr),
fPollTempSize(0),
fResampleRatio(0.0),
fResampleTempData(nullptr),
fResampleTempSize(0),
fPool(),
fPoolMutex(),
fPoolReadyToSwap(false),
fResampler(),
fReaderMutex()
{
@@ -239,17 +301,7 @@ public:

uint32_t getMaxFrame() const noexcept
{
return fMaxFrame;
}

uint64_t getPoolStartFrame() const noexcept
{
return fPool.startFrame;
}

uint32_t getPoolNumFrames() const noexcept
{
return fPool.numFrames;
return fPool.maxFrame;
}

ADInfo getFileInfo() const noexcept
@@ -302,6 +354,7 @@ public:
const uint32_t fileNumFrames = static_cast<uint32_t>(fFileNfo.frames);
const uint32_t maxPoolNumFrames = sampleRate * 30;
const bool needsResample = fFileNfo.sample_rate != sampleRate;
uint32_t maxFrame;

if (needsResample)
{
@@ -315,18 +368,22 @@ public:
}

fResampleRatio = static_cast<double>(sampleRate) / static_cast<double>(fFileNfo.sample_rate);
maxFrame = static_cast<uint32_t>(static_cast<double>(fileNumFrames) * fResampleRatio + 0.5);
}
else
{
fResampler.clear();
fResampleRatio = 0.0;
maxFrame = fileNumFrames;
}

if (fileNumFrames <= maxPoolNumFrames)
{
// entire file fits in a small pool, lets read it now
fPool.create(needsResample ? static_cast<uint32_t>(static_cast<double>(fileNumFrames) * fResampleRatio + 0.5)
: fileNumFrames, false);
const uint32_t poolNumFrames = needsResample
? static_cast<uint32_t>(static_cast<double>(fileNumFrames) * fResampleRatio + 0.5)
: fileNumFrames;
fPool.create(poolNumFrames, maxFrame, false);
readEntireFileIntoPool(needsResample);
ad_close(fFilePtr);
fFilePtr = nullptr;
@@ -343,13 +400,13 @@ public:
else
{
// file is too big for our audio pool, we need an extra buffer
const uint32_t poolNumFrames = sampleRate * 1;
const uint32_t poolNumFrames = sampleRate * 5;
const uint pollTempSize = poolNumFrames * fFileNfo.channels;
uint resampleTempSize = 0;

readFilePreview(previewDataSize, previewData);

fPool.create(poolNumFrames, true);
fPool.create(poolNumFrames, maxFrame, true);

try {
fPollTempData = new float[pollTempSize];
@@ -387,10 +444,6 @@ public:
fResampleTempSize = resampleTempSize;
}

fMaxFrame = carla_isNotZero(fResampleRatio)
? static_cast<uint32_t>(static_cast<double>(fileNumFrames) * fResampleRatio + 0.5)
: fileNumFrames;

fNeedsRead = true;
return true;
}
@@ -404,13 +457,20 @@ public:
}
}

void createSwapablePool(AudioFilePool& pool)
{
pool.create(fPool.numFrames, fPool.maxFrame, false);
}

void putAndSwapAllData(AudioFilePool& pool)
{
const water::GenericScopedLock<water::SpinLock> gsl1(fPool.mutex);
const water::GenericScopedLock<water::SpinLock> gsl2(pool.mutex);
CARLA_SAFE_ASSERT_RETURN(fPool.numFrames != 0,);
CARLA_SAFE_ASSERT_RETURN(pool.numFrames == 0,);
CARLA_SAFE_ASSERT_RETURN(fPool.buffer[0] != nullptr,);
CARLA_SAFE_ASSERT_RETURN(fPool.tmpbuf[0] == nullptr,);
CARLA_SAFE_ASSERT_RETURN(pool.numFrames == 0,);
CARLA_SAFE_ASSERT_RETURN(pool.buffer[0] == nullptr,);
CARLA_SAFE_ASSERT_RETURN(pool.tmpbuf[0] == nullptr,);

pool.startFrame = fPool.startFrame;
@@ -424,70 +484,33 @@ public:
fPool.buffer[1] = nullptr;
}

bool tryPutData(float* const out1,
bool tryPutData(AudioFilePool& pool,
float* const out1,
float* const out2,
uint64_t framePos,
const uint32_t frames,
const bool loopMode,
const bool isOffline,
bool& needsRead)
bool& needsIdleRequest)
{
const water::GenericScopedLock<water::SpinLock> gsl(fPool.mutex);
CARLA_SAFE_ASSERT_RETURN(fPool.numFrames != 0, false);
_tryPoolSwap(pool);

if (framePos >= fMaxFrame)
{
if (fLoopingMode)
framePos %= fMaxFrame;
else
return false;
}

uint64_t frameDiff;
const uint64_t numFramesNearEnd = fPool.numFrames*3/5;

if (framePos < fPool.startFrame)
{
if (fPool.startFrame + fPool.numFrames <= fMaxFrame)
{
needsRead = true;
setNeedsRead(framePos);
return false;
}

frameDiff = framePos + (fMaxFrame - fPool.startFrame);

if (frameDiff + frames >= fPool.numFrames)
{
needsRead = true;
setNeedsRead(framePos);
return false;
}
bool needsRead = false;
uint32_t needsReadFrame;
const bool ret = pool.tryPutData(out1, out2, framePos, frames, loopMode, isOffline, needsRead, needsReadFrame);

carla_copyFloats(out1, fPool.buffer[0] + frameDiff, frames);
carla_copyFloats(out2, fPool.buffer[1] + frameDiff, frames);
}
else
if (needsRead)
{
frameDiff = framePos - fPool.startFrame;

if (frameDiff + frames >= fPool.numFrames)
{
needsRead = true;
setNeedsRead(framePos);
return false;
}

carla_copyFloats(out1, fPool.buffer[0] + frameDiff, frames);
carla_copyFloats(out2, fPool.buffer[1] + frameDiff, frames);
needsIdleRequest = true;
setNeedsRead(needsReadFrame);
}

if (frameDiff > numFramesNearEnd)
{
needsRead = true;
setNeedsRead(framePos + (isOffline ? 0 : frames));
#ifdef DEBUG_FILE_OPS
if (! ret) {
carla_stdout("tryPutData fail");
}
return true;
#endif
return ret;
}

void readFilePreview(const uint32_t previewDataSize, float* previewData)
@@ -586,7 +609,7 @@ public:
{
const CarlaMutexLocker cml(fReaderMutex);

if (fMaxFrame == 0 || fFileNfo.channels == 0 || fFilePtr == nullptr)
if (fFileNfo.channels == 0 || fFilePtr == nullptr)
{
carla_debug("R: no song loaded");
fNeedsFrame = 0;
@@ -602,13 +625,14 @@ public:
}

uint64_t lastFrame = fNeedsFrame;
uint32_t maxFrame = fPool.maxFrame;
int64_t readFrameCheck;

if (lastFrame >= fMaxFrame)
if (lastFrame >= maxFrame)
{
if (fLoopingMode)
{
const uint64_t readFrameCheckLoop = lastFrame % fMaxFrame;
const uint64_t readFrameCheckLoop = lastFrame % maxFrame;
CARLA_SAFE_ASSERT_RETURN(readFrameCheckLoop < INT32_MAX,);

carla_debug("R: transport out of bounds for loop");
@@ -662,7 +686,9 @@ public:
// see if we can read more
if (readFrameReal + rv >= static_cast<ssize_t>(fFileNfo.frames) && urv < fPollTempSize)
{
carla_debug("R: from start");
#ifdef DEBUG_FILE_OPS
carla_stdout("R: from start");
#endif
ad_seek(fFilePtr, 0);
j = ad_read(fFilePtr, fPollTempData+urv, fPollTempSize-urv);

@@ -677,7 +703,9 @@ public:
rv += j;
}

carla_debug("R: reading %li frames at frame %lu", rv, readFrameCheck);
#ifdef DEBUG_FILE_OPS
carla_stdout("R: reading %li frames at frame %lu", rv, readFrameCheck);
#endif

// local copy
const uint32_t poolNumFrames = fPool.numFrames;
@@ -727,12 +755,15 @@ public:
{
// full file read
j = 0;
carla_debug("R: full file was read, filling buffers again");
#ifdef DEBUG_FILE_OPS
carla_stdout("R: full file was read, filling buffers again");
#endif
}
else
{
carla_debug("read break, not enough space");

#ifdef DEBUG_FILE_OPS
carla_stdout("read break, not enough space");
#endif
carla_zeroFloats(pbuffer0, poolNumFrames - i);
carla_zeroFloats(pbuffer1, poolNumFrames - i);
break;
@@ -741,11 +772,16 @@ public:
} while (i < poolNumFrames);

// lock, and put data asap
const CarlaMutexLocker cml(fPoolMutex);
const water::GenericScopedLock<water::SpinLock> gsl(fPool.mutex);

std::memcpy(fPool.buffer[0], pbuffer0, sizeof(float)*poolNumFrames);
std::memcpy(fPool.buffer[1], pbuffer1, sizeof(float)*poolNumFrames);
fPool.startFrame = static_cast<uint64_t>(readFrame);
fPoolReadyToSwap = true;
#ifdef DEBUG_FILE_OPS
carla_stdout("Reading done and internal pool is now full");
#endif
}

fNeedsRead = false;
@@ -760,19 +796,59 @@ private:
void* fFilePtr;
ADInfo fFileNfo;

uint32_t fMaxFrame;
double fResampleRatio;

float* fPollTempData;
uint fPollTempSize;

double fResampleRatio;
float* fResampleTempData;
uint fResampleTempSize;

AudioFilePool fPool;
CarlaMutex fPoolMutex;
bool fPoolReadyToSwap;
Resampler fResampler;
CarlaMutex fReaderMutex;

// try a pool data swap if possible and relevant
// NOTE it is assumed that `pool` mutex is locked
void _tryPoolSwap(AudioFilePool& pool)
{
uint32_t tmp_u;
float* tmp_fp;

const CarlaMutexTryLocker cmtl(fPoolMutex);

if (! cmtl.wasLocked())
return;

const water::GenericScopedLock<water::SpinLock> gsl(fPool.mutex);

if (! fPoolReadyToSwap)
return;

tmp_u = pool.startFrame;
pool.startFrame = fPool.startFrame;
fPool.startFrame = tmp_u;

tmp_u = pool.numFrames;
pool.numFrames = fPool.numFrames;
fPool.numFrames = tmp_u;

tmp_fp = pool.buffer[0];
pool.buffer[0] = fPool.buffer[0];
fPool.buffer[0] = tmp_fp;

tmp_fp = pool.buffer[1];
pool.buffer[1] = fPool.buffer[1];
fPool.buffer[1] = tmp_fp;

fPoolReadyToSwap = false;

#ifdef DEBUG_FILE_OPS
carla_stdout("Pools have been swapped, internal one is now invalidated");
#endif
}

CARLA_DECLARE_NON_COPY_STRUCT(AudioFileReader)
};



+ 17
- 10
source/native-plugins/audio-file.cpp View File

@@ -1,6 +1,6 @@
/*
* Carla Native Plugins
* Copyright (C) 2013-2020 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2013-2021 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -73,6 +73,7 @@ public:
fDoProcess(false),
fWasPlayingBefore(false),
fNeedsFileRead(false),
fEntireFileLoaded(false),
fMaxFrame(0),
fLastPosition(0),
fPool(),
@@ -248,11 +249,14 @@ protected:
const NativeMidiEvent*, uint32_t) override
{
const NativeTimeInfo* const timePos(getTimeInfo());
const bool loopMode = fLoopMode;

float* out1 = outBuffer[0];
float* out2 = outBuffer[1];
bool needsIdleRequest = false;

const water::GenericScopedLock<water::SpinLock> gsl(fPool.mutex);

if (! fDoProcess)
{
// carla_stderr("P: no process");
@@ -280,7 +284,7 @@ protected:
}

// out of reach
if ((timePos->frame < fPool.startFrame || timePos->frame >= fMaxFrame) && !fLoopMode)
if ((timePos->frame < fPool.startFrame || timePos->frame >= fMaxFrame) && !loopMode)
{
if (timePos->frame < fPool.startFrame)
{
@@ -318,10 +322,10 @@ protected:
return;
}

if (fReader.isEntireFileLoaded())
if (fEntireFileLoaded)
{
// NOTE: timePos->frame is always < fMaxFrame (or looping)
uint32_t targetStartFrame = static_cast<uint32_t>(fLoopMode ? timePos->frame % fMaxFrame : timePos->frame);
uint32_t targetStartFrame = static_cast<uint32_t>(loopMode ? timePos->frame % fMaxFrame : timePos->frame);

for (uint32_t framesDone=0, framesToDo=frames, remainingFrames; framesDone < frames;)
{
@@ -339,7 +343,7 @@ protected:
framesDone += remainingFrames;
framesToDo -= remainingFrames;

if (! fLoopMode)
if (! loopMode)
{
// not looping, stop here
if (framesToDo != 0)
@@ -360,7 +364,7 @@ protected:
{
const bool offline = isOffline();

if (! fReader.tryPutData(out1, out2, timePos->frame, frames, offline, needsIdleRequest))
if (! fReader.tryPutData(fPool, out1, out2, timePos->frame, frames, loopMode, offline, needsIdleRequest))
{
carla_zeroFloats(out1, frames);
carla_zeroFloats(out2, frames);
@@ -375,7 +379,7 @@ protected:
needsIdleRequest = false;
fReader.readPoll();

if (! fReader.tryPutData(out1, out2, timePos->frame, frames, offline, needsIdleRequest))
if (! fReader.tryPutData(fPool, out1, out2, timePos->frame, frames, loopMode, offline, needsIdleRequest))
{
carla_zeroFloats(out1, frames);
carla_zeroFloats(out2, frames);
@@ -571,6 +575,7 @@ private:
bool fWasPlayingBefore;
volatile bool fNeedsFileRead;

bool fEntireFileLoaded;
uint32_t fMaxFrame;
float fLastPosition;

@@ -622,8 +627,8 @@ private:
carla_debug("AudioFilePlugin::loadFilename(\"%s\")", filename);

fDoProcess = false;
fReader.destroy();
fPool.destroy();
fReader.destroy();

if (filename == nullptr || *filename == '\0')
{
@@ -635,15 +640,16 @@ private:

if (fReader.loadFilename(filename, static_cast<uint32_t>(getSampleRate()), previewDataSize, fPreviewData))
{
fEntireFileLoaded = fReader.isEntireFileLoaded();
fMaxFrame = fReader.getMaxFrame();

if (fReader.isEntireFileLoaded())
if (fEntireFileLoaded)
{
fReader.putAndSwapAllData(fPool);
}
else
{
fPool.create(fReader.getPoolNumFrames(), false);
fReader.createSwapablePool(fPool);
fReader.readPoll();
}

@@ -652,6 +658,7 @@ private:
}
else
{
fEntireFileLoaded = false;
fMaxFrame = 0;
}
}


Loading…
Cancel
Save