From ccc1203ec39643c15823accc003126b2dff8f573 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 13 Aug 2023 21:06:09 +0200 Subject: [PATCH] Continue audiofile rework, almost done Signed-off-by: falkTX --- source/backend/plugin/CarlaPluginLV2.cpp | 10 +- .../zita-resampler/.kdev_include_paths | 1 + source/native-plugins/.kdev_include_paths | 3 + source/native-plugins/_data.cpp | 9 +- source/native-plugins/audio-base.hpp | 1024 +++++++---------- source/native-plugins/audio-file.cpp | 223 +--- source/utils/.kdev_include_paths | 1 + source/utils/CarlaMemUtils.hpp | 45 + source/utils/CarlaRingBuffer.hpp | 40 +- source/utils/CarlaShmUtils.hpp | 2 + source/utils/Lv2AtomRingBuffer.hpp | 35 +- 11 files changed, 552 insertions(+), 841 deletions(-) create mode 100644 source/modules/zita-resampler/.kdev_include_paths create mode 100644 source/native-plugins/.kdev_include_paths create mode 100644 source/utils/.kdev_include_paths create mode 100644 source/utils/CarlaMemUtils.hpp diff --git a/source/backend/plugin/CarlaPluginLV2.cpp b/source/backend/plugin/CarlaPluginLV2.cpp index c4014dbea..e43ea03bc 100644 --- a/source/backend/plugin/CarlaPluginLV2.cpp +++ b/source/backend/plugin/CarlaPluginLV2.cpp @@ -3255,29 +3255,31 @@ public: if (fExt.worker != nullptr && fEventsIn.ctrl != nullptr) { - fAtomBufferWorkerIn.createBuffer(eventBufferSize); - fAtomBufferWorkerResp.createBuffer(eventBufferSize); + fAtomBufferWorkerIn.createBuffer(eventBufferSize, true); + fAtomBufferWorkerResp.createBuffer(eventBufferSize, true); fAtomBufferRealtimeSize = fAtomBufferWorkerIn.getSize(); // actual buffer size will be next power of 2 fAtomBufferRealtime = static_cast(std::malloc(fAtomBufferRealtimeSize)); fAtomBufferWorkerInTmpData = new uint8_t[fAtomBufferRealtimeSize]; + carla_mlock(fAtomBufferRealtime, fAtomBufferRealtimeSize); } if (fRdfDescriptor->ParameterCount > 0 || (fUI.type != UI::TYPE_NULL && fEventsIn.count > 0 && (fEventsIn.data[0].type & CARLA_EVENT_DATA_ATOM) != 0)) { - fAtomBufferEvIn.createBuffer(eventBufferSize); + fAtomBufferEvIn.createBuffer(eventBufferSize, true); if (fAtomBufferRealtimeSize == 0) { fAtomBufferRealtimeSize = fAtomBufferEvIn.getSize(); // actual buffer size will be next power of 2 fAtomBufferRealtime = static_cast(std::malloc(fAtomBufferRealtimeSize)); + carla_mlock(fAtomBufferRealtime, fAtomBufferRealtimeSize); } } if (hasPatchParameterOutputs || (fUI.type != UI::TYPE_NULL && fEventsOut.count > 0 && (fEventsOut.data[0].type & CARLA_EVENT_DATA_ATOM) != 0)) { - fAtomBufferUiOut.createBuffer(std::min(eventBufferSize*32, 1638400U)); + fAtomBufferUiOut.createBuffer(std::min(eventBufferSize*32, 1638400U), true); fAtomBufferUiOutTmpData = new uint8_t[fAtomBufferUiOut.getSize()]; } diff --git a/source/modules/zita-resampler/.kdev_include_paths b/source/modules/zita-resampler/.kdev_include_paths new file mode 100644 index 000000000..d14b87be4 --- /dev/null +++ b/source/modules/zita-resampler/.kdev_include_paths @@ -0,0 +1 @@ +../../includes/ diff --git a/source/native-plugins/.kdev_include_paths b/source/native-plugins/.kdev_include_paths new file mode 100644 index 000000000..05774aad9 --- /dev/null +++ b/source/native-plugins/.kdev_include_paths @@ -0,0 +1,3 @@ +../includes/ +../modules/ +../utils/ diff --git a/source/native-plugins/_data.cpp b/source/native-plugins/_data.cpp index 2faac62a4..f19c9e633 100644 --- a/source/native-plugins/_data.cpp +++ b/source/native-plugins/_data.cpp @@ -284,6 +284,7 @@ static const NativePluginDescriptor sNativePluginDescriptors[] = { |NATIVE_PLUGIN_HAS_UI |NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE |NATIVE_PLUGIN_REQUESTS_IDLE + |NATIVE_PLUGIN_USES_CONTROL_VOLTAGE |NATIVE_PLUGIN_USES_TIME), /* supports */ NATIVE_PLUGIN_SUPPORTS_NOTHING, /* audioIns */ 0, @@ -296,7 +297,13 @@ static const NativePluginDescriptor sNativePluginDescriptors[] = { /* label */ "audiofile", /* maker */ "falkTX", /* copyright */ "GNU GPL v2+", - DESCFUNCS_WITHOUTCV + DESCFUNCS_WITHCV, + /* cvIns */ 0, + /* cvOuts */ 1, + /* bufnamefn */ nullptr, + /* bufrangefn */ nullptr, + /* ui_width */ 0, + /* ui_height */ 0 }, // -------------------------------------------------------------------------------------------------------------------- diff --git a/source/native-plugins/audio-base.hpp b/source/native-plugins/audio-base.hpp index 4d67c80f8..002ea07cf 100644 --- a/source/native-plugins/audio-base.hpp +++ b/source/native-plugins/audio-base.hpp @@ -19,6 +19,8 @@ #define AUDIO_BASE_HPP_INCLUDED #include "CarlaMathUtils.hpp" +#include "CarlaMemUtils.hpp" +#include "CarlaRingBuffer.hpp" extern "C" { #include "audio_decoder/ad.h" @@ -38,40 +40,15 @@ extern "C" { # pragma GCC diagnostic pop #endif -#if defined(CARLA_OS_WIN) -# include -# define CARLA_MLOCK(ptr, size) VirtualLock((ptr), (size)) -#elif !defined(CARLA_OS_WASM) -# include -# define CARLA_MLOCK(ptr, size) mlock((ptr), (size)) -#else -# define CARLA_MLOCK(ptr, size) -#endif - #define DEBUG_FILE_OPS typedef struct adinfo ADInfo; -static inline -bool carla_mlock(void* const ptr, const size_t size) -{ - #if defined(CARLA_OS_WASM) - // unsupported - return false; - (void)ptr; (void)size; - #elif defined(CARLA_OS_WIN) - return ::VirtualLock(ptr, size) != FALSE; - #else - return ::mlock(ptr, size) == 0; - #endif -} +// -------------------------------------------------------------------------------------------------------------------- struct AudioMemoryPool { float* buffer[2] = {}; -// float* tmpbuf[2]; uint32_t numFrames = 0; -// uint32_t maxFrame; -// volatile uint64_t startFrame; CarlaMutex mutex; AudioMemoryPool() noexcept {} @@ -81,47 +58,26 @@ struct AudioMemoryPool { destroy(); } - void create(const uint32_t desiredNumFrames/*, const uint32_t fileNumFrames, const bool withTempBuffers*/) + void create(const uint32_t desiredNumFrames) { CARLA_ASSERT(buffer[0] == nullptr); CARLA_ASSERT(buffer[1] == nullptr); -// CARLA_ASSERT(tmpbuf[0] == nullptr); -// 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); - -// if (withTempBuffers) -// { -// 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); -// } -// -// const water::GenericScopedLock gsl(mutex); -// -// startFrame = 0; -// numFrames = desiredNumFrames; -// maxFrame = fileNumFrames; + carla_mlock(buffer[0], sizeof(float)*desiredNumFrames); + carla_mlock(buffer[1], sizeof(float)*desiredNumFrames); + + const CarlaMutexLocker cml(mutex); + numFrames = desiredNumFrames; } void destroy() noexcept { { const CarlaMutexLocker cml(mutex); -// startFrame = 0; numFrames = 0; -// maxFrame = 0; } if (buffer[0] != nullptr) @@ -135,109 +91,17 @@ struct AudioMemoryPool { delete[] buffer[1]; buffer[1] = nullptr; } - -// if (tmpbuf[0] != nullptr) -// { -// delete[] tmpbuf[0]; -// tmpbuf[0] = nullptr; -// } -// -// if (tmpbuf[1] != nullptr) -// { -// delete[] tmpbuf[1]; -// tmpbuf[1] = nullptr; -// } } -// // 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, -// uint64_t& needsReadFrame) -// { -// 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/4; -// -// if (framePos < startFrame) -// { -// if (startFrame + numFrames <= maxFrame) -// { -// needsRead = true; -// needsReadFrame = framePos; -// return false; -// } -// -// frameDiff = framePos + (maxFrame - startFrame); -// -// 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; -// -// 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) -// { -// needsRead = true; -// needsReadFrame = framePos + (isOffline ? 0 : frames); -// } -// -// return true; -// } - CARLA_DECLARE_NON_COPYABLE(AudioMemoryPool) }; +// -------------------------------------------------------------------------------------------------------------------- + class AudioFileReader { public: AudioFileReader() -// : fEntireFileLoaded(false), -// fLoopingMode(true), -// fNeedsFrame(0), -// fNeedsRead(false), -// fPollTempData(nullptr), -// fPollTempSize(0), -// fResampleTempData(nullptr), -// fResampleTempSize(0), -// fPool(), -// fPoolMutex(), -// fPoolReadyToSwap(false), -// fResampler(), -// fReaderMutex() { ad_clear_nfo(&fFileNfo); } @@ -252,46 +116,35 @@ public: const CarlaMutexLocker cml(fReaderMutex); cleanup(); - -// fPool.destroy(); -// fNeedsFrame = 0; -// fNeedsRead = false; } -// bool isEntireFileLoaded() const noexcept -// { -// return fEntireFileLoaded; -// } - int getCurrentBitRate() const noexcept { return fCurrentBitRate; } -// uint32_t getMaxFrame() const noexcept -// { -// return fPool.maxFrame; -// } + float getLastPlayPosition() const noexcept + { + return fLastPlayPosition; + } + + float getReadableBufferFill() const noexcept + { + if (fFileNfo.channels == 0) + return 0.f; + + if (fEntireFileLoaded) + return 1.f; + + return 1.f - (static_cast(fRingBufferR.getReadableDataSize() / sizeof(float)) + / static_cast(fRingBufferR.getSize() / sizeof(float))); + } ADInfo getFileInfo() const noexcept { return fFileNfo; } -// void setLoopingMode(const bool on) noexcept -// { -// fLoopingMode = on; -// } - -// void setNeedsRead(const uint64_t frame) noexcept -// { -// if (fEntireFileLoaded) -// return; -// -// fNeedsFrame = frame; -// fNeedsRead = true; -// } - bool loadFilename(const char* const filename, const uint32_t sampleRate, const uint32_t previewDataSize, float* previewData) { @@ -310,10 +163,6 @@ public: ad_dump_nfo(99, &fFileNfo); - // Fix misinformation from libsndfile - while (fFileNfo.frames % fFileNfo.channels) - --fFileNfo.frames; - // invalid if ((fFileNfo.channels != 1 && fFileNfo.channels != 2) || fFileNfo.frames <= 0) { @@ -329,9 +178,9 @@ public: return false; } - const uint32_t numFrames = static_cast(fFileNfo.frames); + const uint64_t numFileFrames = static_cast(fFileNfo.frames); const bool needsResample = fFileNfo.sample_rate != sampleRate; - uint32_t numResampledFrames; + uint64_t numResampledFrames; if (needsResample) { @@ -345,467 +194,444 @@ public: } fResampleRatio = static_cast(sampleRate) / static_cast(fFileNfo.sample_rate); - numResampledFrames = static_cast(static_cast(numFrames) * fResampleRatio + 0.5); + numResampledFrames = static_cast(static_cast(numFileFrames) * fResampleRatio + 0.5); } else { fResampler.clear(); - fResampleRatio = 0.0; - numResampledFrames = numFrames; + fResampleRatio = 1.0; + numResampledFrames = numFileFrames; } - // read the first 30s of the file if seekable - const uint32_t initialFrames = fFileNfo.can_seek == 0 - ? numFrames - : std::min(numFrames, fFileNfo.sample_rate * 600); - const uint32_t initialResampledFrames = fFileNfo.can_seek == 0 - ? numResampledFrames - : std::min(numResampledFrames, sampleRate * 600); - fInitialMemoryPool.create(initialResampledFrames); - readIntoInitialMemoryPool(initialFrames, initialResampledFrames); - -// if (initialResampledFrames == numResampledFrames) + if (fFileNfo.can_seek == 0 || numResampledFrames <= sampleRate * 30) { + // read the first 30s of the file if seekable + const uint64_t initialFrames = fFileNfo.can_seek == 0 + ? numFileFrames + : std::min(numFileFrames, fFileNfo.sample_rate * 30); + const uint64_t initialResampledFrames = fFileNfo.can_seek == 0 + ? numResampledFrames + : std::min(numResampledFrames, sampleRate * 30); + + fInitialMemoryPool.create(initialResampledFrames); + readIntoInitialMemoryPool(initialFrames, initialResampledFrames); + // file is no longer needed, we have it all in memory ad_close(fFilePtr); fFilePtr = nullptr; - const float resampledFramesF = static_cast(initialResampledFrames); + const float resampledFramesF = static_cast(numResampledFrames); const float previewDataSizeF = static_cast(previewDataSize); for (uint i=0; i(i)/previewDataSizeF * resampledFramesF; - const uint step = carla_fixedValue(0U, initialResampledFrames-1U, static_cast(stepF + 0.5f)); + const uint step = carla_fixedValue(0, numResampledFrames-1, static_cast(stepF + 0.5f)); previewData[i] = std::max(std::fabs(fInitialMemoryPool.buffer[0][step]), std::fabs(fInitialMemoryPool.buffer[1][step])); } - return true; + fEntireFileLoaded = true; + } + else + { + readFilePreview(previewDataSize, previewData); + + // read only the first 4s, let disk streaming handle the rest + const uint64_t initialFrames = std::min(numFileFrames, fFileNfo.sample_rate * 4); + const uint64_t initialResampledFrames = std::min(numResampledFrames, sampleRate * 4); + + fRingBufferL.createBuffer(sampleRate * 6 * sizeof(float), true); + fRingBufferR.createBuffer(sampleRate * 6 * sizeof(float), true); + + fInitialMemoryPool.create(initialResampledFrames); + readIntoInitialMemoryPool(initialFrames, initialResampledFrames); + + fRingBufferL.writeCustomData(fInitialMemoryPool.buffer[0], fInitialMemoryPool.numFrames * sizeof(float)); + fRingBufferR.writeCustomData(fInitialMemoryPool.buffer[1], fInitialMemoryPool.numFrames * sizeof(float)); + fRingBufferL.commitWrite(); + fRingBufferR.commitWrite(); + + fEntireFileLoaded = false; } - // file is too big, do disk streaming as needed -// const uint32_t poolNumFrames = sampleRate * 5; -// const uint pollTempSize = poolNumFrames * fFileNfo.channels; -// uint resampleTempSize = 0; -// -// readFilePreview(previewDataSize, previewData); -// -// fPool.create(poolNumFrames, maxFrame, true); -// -// try { -// fPollTempData = new float[pollTempSize]; -// } catch (...) { -// ad_clear_nfo(&fFileNfo); -// ad_close(fFilePtr); -// fFilePtr = nullptr; -// carla_stderr2("loadFilename error, out of memory"); -// return false; -// } -// -// CARLA_MLOCK(fPollTempData, sizeof(float)*pollTempSize); -// -// if (needsResample) -// { -// resampleTempSize = static_cast(static_cast(poolNumFrames) * fResampleRatio + 0.5); -// resampleTempSize *= fFileNfo.channels; -// -// try { -// fResampleTempData = new float[resampleTempSize]; -// } catch (...) { -// delete[] fPollTempData; -// fPollTempData = nullptr; -// ad_clear_nfo(&fFileNfo); -// ad_close(fFilePtr); -// fFilePtr = nullptr; -// carla_stderr2("loadFilename error, out of memory"); -// return false; -// } -// -// CARLA_MLOCK(fResampleTempData, sizeof(float)*resampleTempSize); -// } -// -// fPollTempSize = pollTempSize; -// fResampleTempSize = resampleTempSize; - -// fNeedsRead = true; + fTotalResampledFrames = numResampledFrames; + fSampleRate = sampleRate; + return true; } -// void createSwapablePool(AudioFilePool& pool) -// { -// pool.create(fPool.numFrames, fPool.maxFrame, false); -// } - -// void putAndSwapAllData(AudioFilePool& pool) -// { -// const water::GenericScopedLock gsl1(fPool.mutex); -// const water::GenericScopedLock gsl2(pool.mutex); -// CARLA_SAFE_ASSERT_RETURN(fPool.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; -// pool.numFrames = fPool.numFrames; -// pool.buffer[0] = fPool.buffer[0]; -// pool.buffer[1] = fPool.buffer[1]; -// -// fPool.startFrame = 0; -// fPool.numFrames = 0; -// fPool.buffer[0] = nullptr; -// fPool.buffer[1] = nullptr; -// } - -// bool tryPutData(AudioFilePool& pool, -// float* const out1, -// float* const out2, -// uint64_t framePos, -// const uint32_t frames, -// const bool loopMode, -// const bool isOffline, -// bool& needsIdleRequest) -// { -// _tryPoolSwap(pool); -// -// bool needsRead = false; -// uint64_t needsReadFrame; -// const bool ret = pool.tryPutData(out1, out2, framePos, frames, loopMode, isOffline, needsRead, needsReadFrame); -// -// if (needsRead) -// { -// needsIdleRequest = true; -// setNeedsRead(needsReadFrame); -// } -// -// #ifdef DEBUG_FILE_OPS -// if (! ret) { -// carla_stdout("tryPutData fail"); -// } -// #endif -// return ret; -// } - - float tickFrames(float* const buffers[], - const uint32_t bufferOffset, - const uint32_t frames, - uint64_t framePos, - const bool loopingMode, const bool isOffline) + bool tickFrames(float* const buffers[], + uint32_t bufferOffset, uint32_t frames, uint64_t framePos, + const bool loopingMode, const bool isOffline) { - float* const out1 = buffers[0] + bufferOffset; - float* const out2 = buffers[1] + bufferOffset; - float* const playCV = buffers[2] + bufferOffset; + float* outL = buffers[0] + bufferOffset; + float* outR = buffers[1] + bufferOffset; + float* playCV = buffers[2] + bufferOffset; + + if (loopingMode && framePos >= fTotalResampledFrames) + framePos %= fTotalResampledFrames; + + if (framePos >= fTotalResampledFrames) + { + carla_zeroFloats(outL, frames); + carla_zeroFloats(outR, frames); + carla_zeroFloats(playCV, frames); + fLastPlayPosition = 1.f; + return false; + } + + uint32_t numPoolFrames, usableFrames; - // entire file in memory, use memory pool directly - if (true) { - uint32_t framesAvailable, numPoolFrames; + const CarlaMutexTryLocker cmtl(fInitialMemoryPool.mutex, isOffline); + + numPoolFrames = fInitialMemoryPool.numFrames; + + if (numPoolFrames == 0 || ! cmtl.wasLocked()) + { + carla_zeroFloats(outL, frames); + carla_zeroFloats(outR, frames); + carla_zeroFloats(playCV, frames); + carla_stdout("tickFrames not loaded"); + return false; + } + if (framePos < numPoolFrames) { - const CarlaMutexTryLocker cmtl(fInitialMemoryPool.mutex, isOffline); + usableFrames = std::min(frames, numPoolFrames - static_cast(framePos)); + + carla_copyFloats(outL, fInitialMemoryPool.buffer[0] + framePos, usableFrames); + carla_copyFloats(outR, fInitialMemoryPool.buffer[1] + framePos, usableFrames); + carla_fillFloatsWithSingleValue(playCV, 10.f, usableFrames); + + outL += usableFrames; + outR += usableFrames; + playCV += usableFrames; + bufferOffset += usableFrames; + framePos += usableFrames; + frames -= usableFrames; + } + + if (fEntireFileLoaded && frames != 0) + return tickFrames(buffers, bufferOffset, frames, framePos, loopingMode, isOffline); + } + + fLastPlayPosition = static_cast(framePos / 64) / static_cast(fTotalResampledFrames / 64); + + if (fEntireFileLoaded) + return false; + + if (frames == 0) + { + // ring buffer is good, waiting for data reads + if (fRingBufferFramePos == numPoolFrames) + return false; + + // out of bounds, host likely has repositioned transport + if (fRingBufferFramePos > numPoolFrames) + { + fNextFileReadPos = 0; + carla_stdout("tickFrames read from start"); + return true; + } + + // within bounds, skip frames until we reach the end of the memory pool + if (fRingBufferR.getReadableDataSize() >= numPoolFrames * sizeof(float)) + { + fRingBufferL.skipRead(numPoolFrames * sizeof(float)); + fRingBufferR.skipRead(numPoolFrames * sizeof(float)); + fRingBufferFramePos = numPoolFrames; + carla_stdout("tickFrames adjusted frame pos from start"); + } + + return true; + } + + uint32_t totalFramesAvailable = fRingBufferR.getReadableDataSize() / sizeof(float); + + if (framePos != fRingBufferFramePos) + { + // unaligned position, see if we need to relocate too + if (framePos < fRingBufferFramePos || framePos >= fRingBufferFramePos + totalFramesAvailable - frames) + { + carla_zeroFloats(outL, frames); + carla_zeroFloats(outR, frames); + carla_zeroFloats(playCV, frames); + + // wait until there previous relocation is done + if (fNextFileReadPos == -1) + fNextFileReadPos = framePos; + + return true; + } + + // oh nice, we can skip a few frames and be in sync + const uint32_t diffFrames = framePos - fRingBufferFramePos; + fRingBufferL.skipRead(diffFrames * sizeof(float)); + fRingBufferR.skipRead(diffFrames * sizeof(float)); + totalFramesAvailable -= diffFrames; + fRingBufferFramePos = framePos; + } + + usableFrames = std::min(frames, totalFramesAvailable); + + if (usableFrames == 0) + { + carla_stdout("tickFrames no more usable frames %lu %lu %u", framePos, fRingBufferFramePos, totalFramesAvailable); + carla_zeroFloats(outL, frames); + carla_zeroFloats(outR, frames); + carla_zeroFloats(playCV, frames); + return framePos < fTotalResampledFrames; + } - numPoolFrames = fInitialMemoryPool.numFrames; + fRingBufferL.readCustomData(outL, sizeof(float) * usableFrames); + fRingBufferR.readCustomData(outR, sizeof(float) * usableFrames); + carla_fillFloatsWithSingleValue(playCV, 10.f, usableFrames); - if (numPoolFrames == 0 || ! cmtl.wasLocked()) + fRingBufferFramePos += usableFrames; + totalFramesAvailable -= usableFrames; + + if (frames != usableFrames) + { + if (loopingMode) + { + bufferOffset += usableFrames; + framePos += usableFrames; + frames -= usableFrames; + carla_stdout("tickFrames looping return"); + return tickFrames(buffers, bufferOffset, frames, framePos, loopingMode, isOffline); + } + + carla_stdout("tickFrames partial usable frames"); + carla_zeroFloats(outL + usableFrames, frames - usableFrames); + carla_zeroFloats(outR + usableFrames, frames - usableFrames); + carla_zeroFloats(playCV + usableFrames, frames - usableFrames); + } + + return totalFramesAvailable <= fSampleRate * 2; + } + + void readFilePreview(uint32_t previewDataSize, float* const previewData) + { + carla_zeroFloats(previewData, previewDataSize); + + if (fFileNfo.can_seek == 0) + return; + + const uint fileNumFrames = static_cast(fFileNfo.frames); + const float fileNumFramesF = static_cast(fileNumFrames); + const float previewDataSizeF = static_cast(previewDataSize); + const uint samplesPerRun = fFileNfo.channels; + const uint maxSampleToRead = fileNumFrames - samplesPerRun; + CARLA_SAFE_ASSERT_INT_RETURN(samplesPerRun == 1 || samplesPerRun == 2, samplesPerRun,); + float tmp[2] = { 0.0f, 0.0f }; + + if (samplesPerRun == 2) + previewDataSize -= 1; + + for (uint i=0; i(i)/previewDataSizeF * fileNumFramesF; + const uint pos = carla_fixedValue(0U, maxSampleToRead, static_cast(posF)); + + ad_seek(fFilePtr, pos); + ad_read(fFilePtr, tmp, samplesPerRun); + previewData[i] = std::max(std::fabs(tmp[0]), std::fabs(tmp[1])); + } + } + + void readPoll() + { + const CarlaMutexLocker cml(fReaderMutex); + + const uint channels = fFileNfo.channels; + + if (channels == 0 || fFilePtr == nullptr) + { + carla_debug("R: no song loaded"); + return; + } + + fCurrentBitRate = ad_get_bitrate(fFilePtr); + + const int64_t nextFileReadPos = fNextFileReadPos; + + if (nextFileReadPos != -1) + { + carla_stdout("readPoll new pos %lu", nextFileReadPos); + + fRingBufferL.flush(); + fRingBufferR.flush(); + + fRingBufferFramePos = nextFileReadPos; + ad_seek(fFilePtr, nextFileReadPos / fResampleRatio); + } + + bool written = false; + uint test = 0; + + if (carla_isNotEqual(fResampleRatio, 1.0)) + { + float buffer[8192]; + float rbuffer[8192]; + ssize_t r; + uint prev_inp_count = 0; + + while (fRingBufferR.getWritableDataSize() >= sizeof(rbuffer)) + { + if (prev_inp_count != 0) + std::memmove(buffer, + buffer + (sizeof(buffer) / sizeof(float) - prev_inp_count * channels), + sizeof(float) * prev_inp_count * channels); + + r = ad_read(fFilePtr, + buffer + (prev_inp_count * channels), + sizeof(buffer) / sizeof(float) - (prev_inp_count * channels)); + + if (r < 0) { - carla_zeroFloats(out1, frames); - carla_zeroFloats(out2, frames); - carla_zeroFloats(playCV, frames); - return 0.f; + carla_stderr("R: ad_read failed"); + break; } - if (loopingMode && framePos >= numPoolFrames) - framePos %= numPoolFrames; + if (r == 0) + break; + + fResampler.inp_count = prev_inp_count + r / channels; + fResampler.out_count = sizeof(rbuffer) / sizeof(float) / channels; + fResampler.inp_data = buffer; + fResampler.out_data = rbuffer; + fResampler.process(); - if (framePos >= numPoolFrames) + r = sizeof(rbuffer) / sizeof(float) - fResampler.out_count * channels; + + if (fResampleRatio > 1.0) + { + if (fResampler.out_count == 0) + { + CARLA_SAFE_ASSERT_UINT(fResampler.inp_count != 0, fResampler.inp_count); + prev_inp_count = fResampler.inp_count; + } + else + { + CARLA_SAFE_ASSERT_UINT(fResampler.inp_count == 0, fResampler.inp_count); + } + } + else { - carla_zeroFloats(out1, frames); - carla_zeroFloats(out2, frames); - carla_zeroFloats(playCV, frames); - return 0.f; + CARLA_SAFE_ASSERT(fResampler.inp_count == 0); } - framesAvailable = std::min(frames, numPoolFrames - static_cast(framePos)); + if (r == 0) + break; + + test += r; - carla_copyFloats(out1, fInitialMemoryPool.buffer[0] + framePos, framesAvailable); - carla_copyFloats(out2, fInitialMemoryPool.buffer[1] + framePos, framesAvailable); - carla_fillFloatsWithSingleValue(playCV, 10.f, framesAvailable); + if (channels == 1) + { + written = true; + fRingBufferL.writeCustomData(rbuffer, r * sizeof(float)); + fRingBufferR.writeCustomData(rbuffer, r * sizeof(float)); + } + else + { + written = true; + for (ssize_t i=0; i < r;) + { + fRingBufferL.writeCustomData(&rbuffer[i++], sizeof(float)); + fRingBufferR.writeCustomData(&rbuffer[i++], sizeof(float)); + } + } } + } + else + { + float buffer[1024]; + ssize_t r; - if (frames != framesAvailable) + while (fRingBufferR.getWritableDataSize() >= sizeof(buffer)) { - tickFrames(buffers, - bufferOffset + framesAvailable, - frames - framesAvailable, - framePos + framesAvailable, - loopingMode, isOffline); + r = ad_read(fFilePtr, buffer, sizeof(buffer)/sizeof(float)); + + if (r < 0) + { + carla_stderr("R: ad_read failed"); + break; + } + + if (r == 0) + break; + + test += r; + + if (channels == 1) + { + written = true; + fRingBufferL.writeCustomData(buffer, r * sizeof(float)); + fRingBufferR.writeCustomData(buffer, r * sizeof(float)); + } + else + { + written = true; + for (ssize_t i=0; i < r;) + { + fRingBufferL.writeCustomData(&buffer[i++], sizeof(float)); + fRingBufferR.writeCustomData(&buffer[i++], sizeof(float)); + } + } } + } - return static_cast(framePos) / static_cast(numPoolFrames); + carla_stdout("readPoll written %d", test); + if (written) + { + fRingBufferL.commitWrite(); + fRingBufferR.commitWrite(); } - return 0.f; + if (nextFileReadPos != -1) + fNextFileReadPos = -1; } -// void readFilePreview(uint32_t previewDataSize, float* previewData) -// { -// carla_zeroFloats(previewData, previewDataSize); -// -// const uint fileNumFrames = static_cast(fFileNfo.frames); -// const float fileNumFramesF = static_cast(fileNumFrames); -// const float previewDataSizeF = static_cast(previewDataSize); -// const uint samplesPerRun = fFileNfo.channels; -// const uint maxSampleToRead = fileNumFrames - samplesPerRun; -// CARLA_SAFE_ASSERT_INT_RETURN(samplesPerRun == 1 || samplesPerRun == 2, samplesPerRun,); -// float tmp[2] = { 0.0f, 0.0f }; -// -// if (samplesPerRun == 2) -// previewDataSize -= 1; -// -// for (uint i=0; i(i)/previewDataSizeF * fileNumFramesF; -// const uint pos = carla_fixedValue(0U, maxSampleToRead, static_cast(posF)); -// -// ad_seek(fFilePtr, pos); -// ad_read(fFilePtr, tmp, samplesPerRun); -// previewData[i] = std::max(std::fabs(tmp[0]), std::fabs(tmp[1])); -// } -// } - -// void readPoll() -// { -// const CarlaMutexLocker cml(fReaderMutex); -// -// if (fFileNfo.channels == 0 || fFilePtr == nullptr) -// { -// carla_debug("R: no song loaded"); -// fNeedsFrame = 0; -// fNeedsRead = false; -// return; -// } -// if (fPollTempData == nullptr) -// { -// carla_debug("R: nothing to poll"); -// fNeedsFrame = 0; -// fNeedsRead = false; -// return; -// } -// -// const uint32_t maxFrame = fPool.maxFrame; -// uint64_t lastFrame = fNeedsFrame; -// int64_t readFrameCheck; -// -// if (lastFrame >= maxFrame) -// { -// if (fLoopingMode) -// { -// const uint64_t readFrameCheckLoop = lastFrame % maxFrame; -// CARLA_SAFE_ASSERT_RETURN(readFrameCheckLoop < INT32_MAX,); -// -// carla_debug("R: transport out of bounds for loop"); -// readFrameCheck = static_cast(readFrameCheckLoop); -// } -// else -// { -// carla_debug("R: transport out of bounds"); -// fNeedsFrame = 0; -// fNeedsRead = false; -// return; -// } -// } -// else -// { -// CARLA_SAFE_ASSERT_RETURN(lastFrame < INT32_MAX,); -// readFrameCheck = static_cast(lastFrame); -// } -// -// const int64_t readFrame = readFrameCheck; -// -// // temp data buffer -// carla_zeroFloats(fPollTempData, fPollTempSize); -// -// { -// #if 0 -// const int32_t sampleRate = 44100; -// carla_debug("R: poll data - reading at frame %li, time %li:%02li, lastFrame %li", -// readFrame, readFrame/sampleRate/60, (readFrame/sampleRate) % 60, lastFrame); -// #endif -// -// const int64_t readFrameReal = carla_isNotZero(fResampleRatio) -// ? static_cast(static_cast(readFrame) / fResampleRatio + 0.5) -// : readFrame; -// -// ad_seek(fFilePtr, readFrameReal); -// size_t i = 0; -// ssize_t j = 0; -// ssize_t rv = ad_read(fFilePtr, fPollTempData, fPollTempSize); -// -// if (rv < 0) -// { -// carla_stderr("R: ad_read1 failed"); -// fNeedsFrame = 0; -// fNeedsRead = false; -// return; -// } -// -// const size_t urv = static_cast(rv); -// -// // see if we can read more -// if (readFrameReal + rv >= static_cast(fFileNfo.frames) && urv < fPollTempSize) -// { -// #ifdef DEBUG_FILE_OPS -// carla_stdout("R: from start"); -// #endif -// ad_seek(fFilePtr, 0); -// j = ad_read(fFilePtr, fPollTempData+urv, fPollTempSize-urv); -// -// if (j < 0) -// { -// carla_stderr("R: ad_read2 failed"); -// fNeedsFrame = 0; -// fNeedsRead = false; -// return; -// } -// -// rv += j; -// } -// -// #ifdef DEBUG_FILE_OPS -// carla_stdout("R: reading %li frames at frame %lu", rv, readFrameCheck); -// #endif -// fCurrentBitRate = ad_get_bitrate(fFilePtr); -// -// // local copy -// const uint32_t poolNumFrames = fPool.numFrames; -// float* const pbuffer0 = fPool.tmpbuf[0]; -// float* const pbuffer1 = fPool.tmpbuf[1]; -// const float* tmpbuf = fPollTempData; -// -// // resample as needed -// if (fResampleTempSize != 0) -// { -// tmpbuf = fResampleTempData; -// fResampler.inp_count = static_cast(rv / fFileNfo.channels); -// fResampler.out_count = fResampleTempSize / fFileNfo.channels; -// fResampler.inp_data = fPollTempData; -// fResampler.out_data = fResampleTempData; -// fResampler.process(); -// CARLA_ASSERT_INT(fResampler.inp_count <= 1, fResampler.inp_count); -// } -// -// j = 0; -// do { -// if (fFileNfo.channels == 1) -// { -// for (; i < poolNumFrames && j < rv; ++i, ++j) -// pbuffer0[i] = pbuffer1[i] = tmpbuf[j]; -// } -// else -// { -// for (; i < poolNumFrames && j < rv; ++j) -// { -// if (j % 2 == 0) -// { -// pbuffer0[i] = tmpbuf[j]; -// } -// else -// { -// pbuffer1[i] = tmpbuf[j]; -// ++i; -// } -// } -// } -// -// if (i >= poolNumFrames) -// break; -// -// if (rv == fFileNfo.frames) -// { -// // full file read -// j = 0; -// #ifdef DEBUG_FILE_OPS -// carla_stdout("R: full file was read, filling buffers again"); -// #endif -// } -// else -// { -// #ifdef DEBUG_FILE_OPS -// carla_stdout("read break, not enough space"); -// #endif -// carla_zeroFloats(pbuffer0, poolNumFrames - i); -// carla_zeroFloats(pbuffer1, poolNumFrames - i); -// break; -// } -// -// } while (i < poolNumFrames); -// -// // lock, and put data asap -// const CarlaMutexLocker cmlp(fPoolMutex); -// const water::GenericScopedLock 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(readFrame); -// fPoolReadyToSwap = true; -// #ifdef DEBUG_FILE_OPS -// carla_stdout("Reading done and internal pool is now full"); -// #endif -// } -// -// fNeedsRead = false; -// } - private: -// bool fEntireFileLoaded; -// bool fLoopingMode; + bool fEntireFileLoaded = false; int fCurrentBitRate = 0; -// volatile uint64_t fNeedsFrame; -// volatile bool fNeedsRead; + float fLastPlayPosition = 0.f; + int64_t fNextFileReadPos = -1; + uint64_t fTotalResampledFrames = 0; void* fFilePtr = nullptr; ADInfo fFileNfo = {}; -// float* fPollTempData; -// uint fPollTempSize; - - double fResampleRatio = 0.0; -// float* fResampleTempData; -// uint fResampleTempSize; + uint32_t fSampleRate = 0; + double fResampleRatio = 1.0; AudioMemoryPool fInitialMemoryPool; -// CarlaMutex fPoolMutex; -// bool fPoolReadyToSwap; Resampler fResampler; CarlaMutex fReaderMutex; + CarlaHeapRingBuffer fRingBufferL, fRingBufferR; + uint64_t fRingBufferFramePos = 0; + // assumes reader lock is active void cleanup() { + fEntireFileLoaded = false; fCurrentBitRate = 0; -// fEntireFileLoaded = false; + fLastPlayPosition = 0.f; + fNextFileReadPos = -1; + fTotalResampledFrames = 0; + fSampleRate = 0; + fRingBufferFramePos = 0; fInitialMemoryPool.destroy(); + fRingBufferL.deleteBuffer(); + fRingBufferR.deleteBuffer(); if (fFilePtr != nullptr) { ad_close(fFilePtr); fFilePtr = nullptr; } - -// if (fPollTempData != nullptr) -// { -// delete[] fPollTempData; -// fPollTempData = nullptr; -// fPollTempSize = 0; -// } -// -// if (fResampleTempData != nullptr) -// { -// delete[] fResampleTempData; -// fResampleTempData = nullptr; -// fResampleTempSize = 0; -// } } void readIntoInitialMemoryPool(const uint numFrames, const uint numResampledFrames) @@ -828,18 +654,17 @@ private: if (numFrames != numResampledFrames) { - const uint resampledBufferSize = numResampledFrames * channels; - resampledBuffer = (float*)std::malloc(resampledBufferSize * sizeof(float)); + resampledBuffer = (float*)std::malloc(numResampledFrames * channels * sizeof(float)); CARLA_SAFE_ASSERT_RETURN(resampledBuffer != nullptr, std::free(fileBuffer);); fResampler.inp_count = numFrames; - fResampler.out_count = resampledBufferSize; + fResampler.out_count = numResampledFrames; fResampler.inp_data = fileBuffer; fResampler.out_data = resampledBuffer; fResampler.process(); - CARLA_SAFE_ASSERT_UINT(fResampler.out_count == 0, fResampler.out_count); - rv = static_cast(resampledBufferSize - fResampler.out_count); + fInitialMemoryPool.numFrames = numResampledFrames - fResampler.out_count; + rv = fInitialMemoryPool.numFrames * channels; } else { @@ -863,60 +688,17 @@ private: fInitialMemoryPool.buffer[1][j] = resampledBuffer[i++]; } } - - fInitialMemoryPool.numFrames = numResampledFrames; } if (resampledBuffer != fileBuffer) std::free(resampledBuffer); std::free(fileBuffer); - -// fEntireFileLoaded = true; } - // 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_u32; -// uint64_t tmp_u64; -// float* tmp_fp; -// -// const CarlaMutexTryLocker cmtl(fPoolMutex); -// -// if (! cmtl.wasLocked()) -// return; -// -// const water::GenericScopedLock gsl(fPool.mutex); -// -// if (! fPoolReadyToSwap) -// return; -// -// tmp_u64 = pool.startFrame; -// pool.startFrame = fPool.startFrame; -// fPool.startFrame = tmp_u64; -// -// tmp_u32 = pool.numFrames; -// pool.numFrames = fPool.numFrames; -// fPool.numFrames = tmp_u32; -// -// 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_COPYABLE(AudioFileReader) }; +// -------------------------------------------------------------------------------------------------------------------- + #endif // AUDIO_BASE_HPP_INCLUDED diff --git a/source/native-plugins/audio-file.cpp b/source/native-plugins/audio-file.cpp index c4dd6ceab..d6871fdf0 100644 --- a/source/native-plugins/audio-file.cpp +++ b/source/native-plugins/audio-file.cpp @@ -78,7 +78,7 @@ public: // fNeedsFileRead(false), // fEntireFileLoaded(false), // fMaxFrame(0), -// fLastPoolFill(0.0f), +// fReadableBufferFill(0.0f), // fPool(), // fReader(), // fFilename(), @@ -253,8 +253,8 @@ protected: return fVolume * 100.f; case kParameterInfoPosition: return fLastPosition; -// case kParameterInfoPoolFill: -// return fLastPoolFill; + case kParameterInfoPoolFill: + return fReadableBufferFill; case kParameterInfoBitRate: return static_cast(fReader.getCurrentBitRate()); } @@ -290,10 +290,7 @@ protected: { case kParameterLooping: if (fLoopMode != b) - { fLoopMode = b; - // fReader.setLoopingMode(b); - } break; case kParameterHostSync: if (fHostSync != b) @@ -350,19 +347,17 @@ protected: float* const out2 = outBuffer[1]; float* const playCV = outBuffer[2]; -// const water::GenericScopedLock gsl(fPool.mutex); - if (! fDoProcess) { // carla_stderr("P: no process"); carla_zeroFloats(out1, frames); carla_zeroFloats(out2, frames); carla_zeroFloats(playCV, frames); - fLastPosition = 0.0f; + fLastPosition = 0.f; + fReadableBufferFill = 0.f; return; } -// const bool loopMode = fLoopMode; bool needsIdleRequest = false; bool playing; uint64_t framePos; @@ -385,152 +380,20 @@ protected: // not playing if (! playing) { -// // carla_stderr("P: not playing"); -// if (framePos == 0 && fWasPlayingBefore) -// fReader.setNeedsRead(framePos); - carla_zeroFloats(out1, frames); carla_zeroFloats(out2, frames); carla_zeroFloats(playCV, frames); -// fWasPlayingBefore = false; return; } -// else -// { -// fWasPlayingBefore = true; -// } - - fLastPosition = fReader.tickFrames(outBuffer, 0, frames, framePos, fLoopMode, isOffline()) * 100.f; - -// // out of reach -// if ((frame < fPool.startFrame || frame >= fMaxFrame) && !loopMode) -// { -// if (frame < fPool.startFrame) -// { -// needsIdleRequest = true; -// fNeedsFileRead = true; -// fReader.setNeedsRead(frame); -// } -// -// carla_zeroFloats(out1, frames); -// carla_zeroFloats(out2, frames); -// carla_zeroFloats(playCV, frames); -// -// #ifndef __MOD_DEVICES__ -// if (fInlineDisplay.writtenValues < 32) -// { -// fInlineDisplay.lastValuesL[fInlineDisplay.writtenValues] = 0.0f; -// fInlineDisplay.lastValuesR[fInlineDisplay.writtenValues] = 0.0f; -// ++fInlineDisplay.writtenValues; -// } -// if (fInlineDisplay.pending == InlineDisplayNotPending) -// { -// needsIdleRequest = true; -// fInlineDisplay.pending = InlineDisplayNeedRequest; -// } -// #endif - -// if (needsIdleRequest) -// hostRequestIdle(); -// -// if (frame == 0) -// fLastPosition = 0.0f; -// else if (frame >= fMaxFrame) -// fLastPosition = 100.0f; -// else -// fLastPosition = static_cast(frame) / static_cast(fMaxFrame) * 100.0f; -// return; -// } - -// if (fEntireFileLoaded) -// { -// // NOTE: frame is always < fMaxFrame (or looping) -// uint32_t targetStartFrame = static_cast(loopMode ? frame % fMaxFrame : frame); -// -// for (uint32_t framesDone=0, framesToDo=frames, remainingFrames; framesDone < frames;) -// { -// if (targetStartFrame + framesToDo <= fMaxFrame) -// { -// // everything fits together -// carla_copyFloats(out1+framesDone, fPool.buffer[0]+targetStartFrame, framesToDo); -// carla_copyFloats(out2+framesDone, fPool.buffer[1]+targetStartFrame, framesToDo); -// carla_fillFloatsWithSingleValue(playCV+framesDone, 10.f, framesToDo); -// break; -// } -// -// remainingFrames = std::min(fMaxFrame - targetStartFrame, framesToDo); -// carla_copyFloats(out1+framesDone, fPool.buffer[0]+targetStartFrame, remainingFrames); -// carla_copyFloats(out2+framesDone, fPool.buffer[1]+targetStartFrame, remainingFrames); -// carla_fillFloatsWithSingleValue(playCV+framesDone, 10.f, remainingFrames); -// framesDone += remainingFrames; -// framesToDo -= remainingFrames; -// -// if (! loopMode) -// { -// // not looping, stop here -// if (framesToDo != 0) -// { -// carla_zeroFloats(out1+framesDone, framesToDo); -// carla_zeroFloats(out2+framesDone, framesToDo); -// carla_zeroFloats(playCV+framesDone, framesToDo); -// } -// break; -// } -// -// // reset for next loop -// targetStartFrame = 0; -// } - -// fLastPosition = static_cast(targetStartFrame) / static_cast(fMaxFrame) * 100.0f; -// } -// else -// { -// const bool offline = isOffline(); - -// if (fReader.tryPutData(fPool, out1, out2, frame, frames, loopMode, offline, needsIdleRequest)) -// { -// carla_fillFloatsWithSingleValue(playCV, 10.f, frames); -// } -// else -// { -// carla_zeroFloats(out1, frames); -// carla_zeroFloats(out2, frames); -// carla_zeroFloats(playCV, frames); -// } -// -// if (needsIdleRequest) -// { -// fNeedsFileRead = true; -// -// if (isOffline()) -// { -// needsIdleRequest = false; -// fReader.readPoll(); -// -// if (fReader.tryPutData(fPool, out1, out2, frame, frames, loopMode, offline, needsIdleRequest)) -// { -// carla_fillFloatsWithSingleValue(playCV, 10.f, frames); -// } -// else -// { -// carla_zeroFloats(out1, frames); -// carla_zeroFloats(out2, frames); -// carla_zeroFloats(playCV, frames); -// } -// -// if (needsIdleRequest) -// fNeedsFileRead = true; -// } -// } -// -// const uint32_t modframe = static_cast(frame % fMaxFrame); -// fLastPosition = static_cast(modframe) / static_cast(fMaxFrame) * 100.0f; -// -// if (modframe > fPool.startFrame) -// fLastPoolFill = static_cast(modframe - fPool.startFrame) / static_cast(fPool.numFrames) * 100.0f; -// else -// fLastPoolFill = 100.0f; -// } + + if (fReader.tickFrames(outBuffer, 0, frames, framePos, fLoopMode, isOffline()) && ! fPendingFileRead) + { + fPendingFileRead = true; + needsIdleRequest = true; + } + + fLastPosition = fReader.getLastPlayPosition() * 100.f; + fReadableBufferFill = fReader.getReadableBufferFill() * 100.f; const float volume = fVolume; if (carla_isNotEqual(volume, 1.0f)) @@ -586,11 +449,11 @@ protected: } #endif -// if (fNeedsFileRead) -// { -// fReader.readPoll(); -// fNeedsFileRead = false; -// } + if (fPendingFileRead) + { + fPendingFileRead = false; + fReader.readPoll(); + } } void sampleRateChanged(double) override @@ -725,17 +588,13 @@ private: #endif bool fEnabled = true; bool fDoProcess = false; -// bool fWasPlayingBefore; -// volatile bool fNeedsFileRead; -// -// bool fEntireFileLoaded; -// uint32_t fMaxFrame; + bool fPendingFileRead = false; + uint32_t fInternalTransportFrame = 0; float fLastPosition = 0.f; -// float fLastPoolFill; + float fReadableBufferFill = 0.f; float fVolume = 1.f; -// AudioFilePool fPool; AudioFileReader fReader; CarlaString fFilename; @@ -773,55 +632,41 @@ private: carla_stdout("AudioFilePlugin::loadFilename(\"%s\")", filename); fDoProcess = false; -// fLastPoolFill = 0.0f; -// fPool.destroy(); fReader.destroy(); fFilename.clear(); if (filename == nullptr || *filename == '\0') - { -// fMaxFrame = 0; return; - } constexpr uint32_t kPreviewDataLen = sizeof(fPreviewData)/sizeof(float); if (fReader.loadFilename(filename, static_cast(getSampleRate()), kPreviewDataLen, fPreviewData)) { -// fEntireFileLoaded = fReader.isEntireFileLoaded(); -// fMaxFrame = fReader.getMaxFrame(); -// -// if (fEntireFileLoaded) -// { -// fReader.putAndSwapAllData(fPool); -// fLastPoolFill = 100.0f; -// } -// else -// { -// fReader.createSwapablePool(fPool); -// fReader.readPoll(); -// } - fInternalTransportFrame = 0; fDoProcess = true; fFilename = filename; hostSendPreviewBufferData('f', kPreviewDataLen, fPreviewData); } - else - { -// fEntireFileLoaded = false; -// fMaxFrame = 0; - } } PluginClassEND(AudioFilePlugin) static const char* _get_buffer_port_name(NativePluginHandle, const uint32_t index, const bool isOutput) { - if (!isOutput || index != 2) + if (!isOutput) return nullptr; - return "Play status"; + switch (index) + { + case 0: + return "output_1"; + case 1: + return "output_2"; + case 2: + return "Play status"; + } + + return nullptr; } static const NativePortRange* _get_buffer_port_range(NativePluginHandle, const uint32_t index, const bool isOutput) diff --git a/source/utils/.kdev_include_paths b/source/utils/.kdev_include_paths new file mode 100644 index 000000000..536b5b330 --- /dev/null +++ b/source/utils/.kdev_include_paths @@ -0,0 +1 @@ +../includes/ diff --git a/source/utils/CarlaMemUtils.hpp b/source/utils/CarlaMemUtils.hpp new file mode 100644 index 000000000..3c51be23e --- /dev/null +++ b/source/utils/CarlaMemUtils.hpp @@ -0,0 +1,45 @@ +/* + * Carla shared memory utils + * Copyright (C) 2023 Filipe Coelho + * + * 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 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. + * + * For a full copy of the GNU General Public License see the doc/GPL.txt file. + */ + +#ifndef CARLA_MEM_UTILS_HPP_INCLUDED +#define CARLA_MEM_UTILS_HPP_INCLUDED + +#include "CarlaUtils.hpp" + +#if !defined(CARLA_OS_WASM) && !defined(CARLA_OS_WIN) +# include +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +static inline +bool carla_mlock(void* const ptr, const size_t size) +{ + #if defined(CARLA_OS_WASM) + // unsupported + return false; + (void)ptr; (void)size; + #elif defined(CARLA_OS_WIN) + return ::VirtualLock(ptr, size) != FALSE; + #else + return ::mlock(ptr, size) == 0; + #endif +} + +// -------------------------------------------------------------------------------------------------------------------- + +#endif // CARLA_MEM_UTILS_HPP_INCLUDED diff --git a/source/utils/CarlaRingBuffer.hpp b/source/utils/CarlaRingBuffer.hpp index 09969bca0..f864b1270 100644 --- a/source/utils/CarlaRingBuffer.hpp +++ b/source/utils/CarlaRingBuffer.hpp @@ -19,6 +19,7 @@ #define CARLA_RING_BUFFER_HPP_INCLUDED #include "CarlaMathUtils.hpp" +#include "CarlaMemUtils.hpp" // -------------------------------------------------------------------------------------------------------------------- // Buffer structs @@ -102,12 +103,22 @@ public: { CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,); - fBuffer->head = 0; - fBuffer->tail = 0; - fBuffer->wrtn = 0; + fBuffer->head = fBuffer->tail = fBuffer->wrtn = 0; fBuffer->invalidateCommit = false; carla_zeroBytes(fBuffer->buf, fBuffer->size); + + fErrorReading = fErrorWriting = false; + } + + void flush() noexcept + { + CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,); + + fBuffer->head = fBuffer->tail = fBuffer->wrtn = 0; + fBuffer->invalidateCommit = false; + + fErrorWriting = false; } // ---------------------------------------------------------------------------------------------------------------- @@ -141,6 +152,11 @@ public: return fBuffer->buf == nullptr || fBuffer->head == fBuffer->tail; } + uint32_t getSize() const noexcept + { + return fBuffer != nullptr ? fBuffer->size : 0; + } + uint32_t getReadableDataSize() const noexcept { CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, 0); @@ -154,7 +170,7 @@ public: { CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, 0); - const uint32_t wrap = (fBuffer->tail >= fBuffer->wrtn) ? 0 : fBuffer->size; + const uint32_t wrap = fBuffer->tail > fBuffer->wrtn ? 0 : fBuffer->size; return wrap + fBuffer->tail - fBuffer->wrtn; } @@ -502,10 +518,7 @@ class CarlaHeapRingBuffer : public CarlaRingBufferControl { public: CarlaHeapRingBuffer() noexcept - : fHeapBuffer{0, 0, 0, 0, false, nullptr} - { - carla_zeroStruct(fHeapBuffer); - } + : fHeapBuffer{0, 0, 0, 0, false, nullptr} {} ~CarlaHeapRingBuffer() noexcept override { @@ -516,7 +529,7 @@ public: fHeapBuffer.buf = nullptr; } - void createBuffer(const uint32_t size) noexcept + void createBuffer(const uint32_t size, const bool mlock) noexcept { CARLA_SAFE_ASSERT_RETURN(fHeapBuffer.buf == nullptr,); CARLA_SAFE_ASSERT_RETURN(size > 0,); @@ -529,11 +542,18 @@ public: fHeapBuffer.size = p2size; setRingBuffer(&fHeapBuffer, true); + + if (mlock) + { + carla_mlock(&fHeapBuffer, sizeof(fHeapBuffer)); + carla_mlock(fHeapBuffer.buf, p2size); + } } void deleteBuffer() noexcept { - CARLA_SAFE_ASSERT_RETURN(fHeapBuffer.buf != nullptr,); + if (fHeapBuffer.buf == nullptr) + return; setRingBuffer(nullptr, false); diff --git a/source/utils/CarlaShmUtils.hpp b/source/utils/CarlaShmUtils.hpp index bd59ac6e7..8f11b260a 100644 --- a/source/utils/CarlaShmUtils.hpp +++ b/source/utils/CarlaShmUtils.hpp @@ -208,6 +208,8 @@ void* carla_shm_map(carla_shm_t& shm, const std::size_t size) noexcept return nullptr; } + ::VirtualLock(ptr, size); + shm.map = map; return ptr; #else diff --git a/source/utils/Lv2AtomRingBuffer.hpp b/source/utils/Lv2AtomRingBuffer.hpp index 65f2f736b..c45790cab 100644 --- a/source/utils/Lv2AtomRingBuffer.hpp +++ b/source/utils/Lv2AtomRingBuffer.hpp @@ -1,6 +1,6 @@ /* * LV2 Atom Ring Buffer - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2023 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,26 +23,23 @@ #include "lv2/atom.h" -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- class Lv2AtomRingBuffer : public CarlaRingBufferControl { public: Lv2AtomRingBuffer() noexcept : fMutex(), - fHeapBuffer(HeapBuffer_INIT), + fHeapBuffer{0, 0, 0, 0, false, nullptr}, fNeedsDataDelete(true) { - carla_zeroStruct(fHeapBuffer); } Lv2AtomRingBuffer(Lv2AtomRingBuffer& ringBuf, uint8_t buf[]) noexcept : fMutex(), - fHeapBuffer(HeapBuffer_INIT), + fHeapBuffer{0, 0, 0, 0, false, nullptr}, fNeedsDataDelete(false) { - carla_zeroStruct(fHeapBuffer); - fHeapBuffer.buf = buf; fHeapBuffer.size = ringBuf.fHeapBuffer.size; @@ -64,15 +61,15 @@ public: fHeapBuffer.buf = nullptr; } - // ------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------------- - void createBuffer(const uint32_t size) noexcept + void createBuffer(const uint32_t size, const bool mlock) noexcept { CARLA_SAFE_ASSERT_RETURN(fHeapBuffer.buf == nullptr,); CARLA_SAFE_ASSERT_RETURN(fNeedsDataDelete,); CARLA_SAFE_ASSERT_RETURN(size > 0,); - const uint32_t p2size(carla_nextPowerOf2(size)); + const uint32_t p2size = carla_nextPowerOf2(size); try { fHeapBuffer.buf = new uint8_t[p2size]; @@ -80,6 +77,12 @@ public: fHeapBuffer.size = p2size; setRingBuffer(&fHeapBuffer, true); + + if (mlock) + { + carla_mlock(&fHeapBuffer, sizeof(fHeapBuffer)); + carla_mlock(fHeapBuffer.buf, p2size); + } } void deleteBuffer() noexcept @@ -99,7 +102,7 @@ public: return fHeapBuffer.size; } - // ------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------------- bool tryLock() const noexcept { @@ -111,7 +114,7 @@ public: fMutex.unlock(); } - // ------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------------- // NOTE: must have been locked before bool get(uint32_t& portIndex, LV2_Atom* const retAtom) noexcept @@ -145,7 +148,7 @@ public: } protected: - // ------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------------- bool readAtom(uint32_t& portIndex, LV2_Atom* const retAtom) noexcept { @@ -175,7 +178,7 @@ protected: return true; } - // ------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------------- bool writeAtom(const LV2_Atom* const atom, const int32_t portIndex) noexcept { @@ -191,7 +194,7 @@ protected: return commitWrite(); } - // ------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------------- private: CarlaMutex fMutex; @@ -204,6 +207,6 @@ private: CARLA_DECLARE_NON_COPYABLE(Lv2AtomRingBuffer) }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- #endif // LV2_ATOM_RING_BUFFER_HPP_INCLUDED