/* * Carla Native Plugins * Copyright (C) 2013-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 GPL.txt file */ #ifndef AUDIO_BASE_HPP_INCLUDED #define AUDIO_BASE_HPP_INCLUDED #include "CarlaMathUtils.hpp" extern "C" { #include "audio_decoder/ad.h" } // #include "water/threads/ScopedLock.h" // #include "water/threads/SpinLock.h" #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Weffc++" #endif #include "zita-resampler/resampler.h" #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) # 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 {} ~AudioMemoryPool() noexcept { destroy(); } void create(const uint32_t desiredNumFrames/*, const uint32_t fileNumFrames, const bool withTempBuffers*/) { 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; } void destroy() noexcept { { const CarlaMutexLocker cml(mutex); // startFrame = 0; numFrames = 0; // maxFrame = 0; } if (buffer[0] != nullptr) { delete[] buffer[0]; buffer[0] = nullptr; } if (buffer[1] != nullptr) { 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); } ~AudioFileReader() { destroy(); } void destroy() { 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; // } 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) { CARLA_SAFE_ASSERT_RETURN(filename != nullptr && *filename != '\0', false); const CarlaMutexLocker cml(fReaderMutex); cleanup(); ad_clear_nfo(&fFileNfo); // open new fFilePtr = ad_open(filename, &fFileNfo); if (fFilePtr == nullptr) return false; 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) { if (fFileNfo.channels != 1 && fFileNfo.channels != 2) carla_stderr("loadFilename(\"%s\", ...) has not 1 or 2 channels", filename); if (fFileNfo.frames <= 0) carla_stderr("loadFilename(\"%s\", ...) has 0 frames", filename); ad_clear_nfo(&fFileNfo); ad_close(fFilePtr); fFilePtr = nullptr; return false; } const uint32_t numFrames = static_cast(fFileNfo.frames); const bool needsResample = fFileNfo.sample_rate != sampleRate; uint32_t numResampledFrames; if (needsResample) { if (! fResampler.setup(fFileNfo.sample_rate, sampleRate, fFileNfo.channels, 32)) { ad_clear_nfo(&fFileNfo); ad_close(fFilePtr); fFilePtr = nullptr; carla_stderr2("loadFilename(\"%s\", ...) error, resampler setup failed"); return false; } fResampleRatio = static_cast(sampleRate) / static_cast(fFileNfo.sample_rate); numResampledFrames = static_cast(static_cast(numFrames) * fResampleRatio + 0.5); } else { fResampler.clear(); fResampleRatio = 0.0; numResampledFrames = numFrames; } // 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) { // file is no longer needed, we have it all in memory ad_close(fFilePtr); fFilePtr = nullptr; const float resampledFramesF = static_cast(initialResampledFrames); 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)); previewData[i] = std::max(std::fabs(fInitialMemoryPool.buffer[0][step]), std::fabs(fInitialMemoryPool.buffer[1][step])); } return true; } // 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; 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) { float* const out1 = buffers[0] + bufferOffset; float* const out2 = buffers[1] + bufferOffset; float* const playCV = buffers[2] + bufferOffset; // 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(out1, frames); carla_zeroFloats(out2, frames); carla_zeroFloats(playCV, frames); return 0.f; } if (loopingMode && framePos >= numPoolFrames) framePos %= numPoolFrames; if (framePos >= numPoolFrames) { carla_zeroFloats(out1, frames); carla_zeroFloats(out2, frames); carla_zeroFloats(playCV, frames); return 0.f; } framesAvailable = std::min(frames, numPoolFrames - static_cast(framePos)); carla_copyFloats(out1, fInitialMemoryPool.buffer[0] + framePos, framesAvailable); carla_copyFloats(out2, fInitialMemoryPool.buffer[1] + framePos, framesAvailable); carla_fillFloatsWithSingleValue(playCV, 10.f, framesAvailable); } if (frames != framesAvailable) { tickFrames(buffers, bufferOffset + framesAvailable, frames - framesAvailable, framePos + framesAvailable, loopingMode, isOffline); } return static_cast(framePos) / static_cast(numPoolFrames); } return 0.f; } // 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; int fCurrentBitRate = 0; // volatile uint64_t fNeedsFrame; // volatile bool fNeedsRead; void* fFilePtr = nullptr; ADInfo fFileNfo = {}; // float* fPollTempData; // uint fPollTempSize; double fResampleRatio = 0.0; // float* fResampleTempData; // uint fResampleTempSize; AudioMemoryPool fInitialMemoryPool; // CarlaMutex fPoolMutex; // bool fPoolReadyToSwap; Resampler fResampler; CarlaMutex fReaderMutex; // assumes reader lock is active void cleanup() { fCurrentBitRate = 0; // fEntireFileLoaded = false; fInitialMemoryPool.destroy(); 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) { const uint channels = fFileNfo.channels; const uint fileBufferSize = numFrames * channels; float* const fileBuffer = (float*)std::malloc(fileBufferSize * sizeof(float)); CARLA_SAFE_ASSERT_RETURN(fileBuffer != nullptr,); ad_seek(fFilePtr, 0); ssize_t rv = ad_read(fFilePtr, fileBuffer, fileBufferSize); CARLA_SAFE_ASSERT_INT2_RETURN(rv == static_cast(fileBufferSize), rv, fileBufferSize, std::free(fileBuffer)); fCurrentBitRate = ad_get_bitrate(fFilePtr); float* resampledBuffer; if (numFrames != numResampledFrames) { const uint resampledBufferSize = numResampledFrames * channels; resampledBuffer = (float*)std::malloc(resampledBufferSize * sizeof(float)); CARLA_SAFE_ASSERT_RETURN(resampledBuffer != nullptr, std::free(fileBuffer);); fResampler.inp_count = numFrames; fResampler.out_count = resampledBufferSize; 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); } else { resampledBuffer = fileBuffer; } { // lock, and put data asap const CarlaMutexLocker cml(fInitialMemoryPool.mutex); if (channels == 1) { for (ssize_t i=0; i < rv; ++i) fInitialMemoryPool.buffer[0][i] = fInitialMemoryPool.buffer[1][i] = resampledBuffer[i]; } else { for (ssize_t i=0, j=0; i < rv; ++j) { fInitialMemoryPool.buffer[0][j] = resampledBuffer[i++]; 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