|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- /*
- * Carla Native Plugins
- * Copyright (C) 2013-2018 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
- * 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 "CarlaThread.hpp"
- #include "CarlaMathUtils.hpp"
-
- extern "C" {
- #include "audio_decoder/ad.h"
- }
-
- typedef struct adinfo ADInfo;
-
- struct AudioFilePool {
- float* buffer[2];
- uint64_t startFrame;
- uint32_t sampleRate;
- uint32_t size;
-
- #ifdef CARLA_PROPER_CPP11_SUPPORT
- AudioFilePool()
- : buffer{nullptr},
- startFrame(0),
- sampleRate(0),
- size(0) {}
- #else
- AudioFilePool()
- : startFrame(0),
- sampleRate(0),
- size(0)
- {
- buffer[0] = buffer[1] = nullptr;
- }
- #endif
-
- ~AudioFilePool()
- {
- CARLA_ASSERT(buffer[0] == nullptr);
- CARLA_ASSERT(buffer[1] == nullptr);
- CARLA_ASSERT(startFrame == 0);
- CARLA_ASSERT(size == 0);
- }
-
- void create(const uint32_t srate)
- {
- CARLA_ASSERT(buffer[0] == nullptr);
- CARLA_ASSERT(buffer[1] == nullptr);
- CARLA_ASSERT(startFrame == 0);
- CARLA_ASSERT(size == 0);
-
- size = srate * 60; // buffer of 60 secs
- sampleRate = srate;
-
- buffer[0] = new float[size];
- buffer[1] = new float[size];
-
- reset();
- }
-
- void destroy()
- {
- CARLA_ASSERT(buffer[0] != nullptr);
- CARLA_ASSERT(buffer[1] != nullptr);
- CARLA_ASSERT(size != 0);
-
- if (buffer[0] != nullptr)
- {
- delete[] buffer[0];
- buffer[0] = nullptr;
- }
-
- if (buffer[1] != nullptr)
- {
- delete[] buffer[1];
- buffer[1] = nullptr;
- }
-
- startFrame = 0;
- size = 0;
- }
-
- void reset()
- {
- startFrame = 0;
- CARLA_SAFE_ASSERT_RETURN(size != 0,);
-
- carla_zeroFloats(buffer[0], size);
- carla_zeroFloats(buffer[1], size);
- }
-
- CARLA_DECLARE_NON_COPY_STRUCT(AudioFilePool)
- };
-
- class AbstractAudioPlayer
- {
- public:
- virtual ~AbstractAudioPlayer() {}
- virtual uint64_t getLastFrame() const = 0;
- };
-
- class AudioFileThread : public CarlaThread
- {
- public:
- AudioFileThread(AbstractAudioPlayer* const player, const uint32_t sampleRate)
- : CarlaThread("AudioFileThread"),
- kPlayer(player),
- fLoopingMode(true),
- fNeedsRead(false),
- fQuitNow(true),
- fFilePtr(nullptr),
- fFileNfo(),
- fMaxPlayerFrame(0),
- fPollTempData(nullptr),
- fPollTempSize(0),
- fPool(),
- fMutex()
- {
- CARLA_ASSERT(kPlayer != nullptr);
-
- static bool adInitiated = false;
-
- if (! adInitiated)
- {
- ad_init();
- adInitiated = true;
- }
-
- ad_clear_nfo(&fFileNfo);
-
- fPool.create(sampleRate);
- }
-
- ~AudioFileThread() override
- {
- CARLA_ASSERT(fQuitNow);
- CARLA_ASSERT(! isThreadRunning());
-
- if (fFilePtr != nullptr)
- {
- ad_close(fFilePtr);
- fFilePtr = nullptr;
- }
-
- if (fPollTempData != nullptr)
- {
- delete[] fPollTempData;
- fPollTempData = nullptr;
- fPollTempSize = 0;
- }
-
- fPool.destroy();
- }
-
- void startNow()
- {
- fNeedsRead = true;
- fQuitNow = false;
- startThread();
- }
-
- void stopNow()
- {
- fNeedsRead = false;
- fQuitNow = true;
-
- stopThread(1000);
-
- const CarlaMutexLocker cml(fMutex);
- fPool.reset();
- }
-
- uint32_t getMaxFrame() const noexcept
- {
- return fMaxPlayerFrame;
- }
-
- void setLoopingMode(const bool on) noexcept
- {
- fLoopingMode = on;
- }
-
- void setNeedsRead() noexcept
- {
- fNeedsRead = true;
- }
-
- bool loadFilename(const char* const filename)
- {
- CARLA_SAFE_ASSERT_RETURN(! isThreadRunning(), false);
- CARLA_SAFE_ASSERT_RETURN(filename != nullptr && *filename != '\0', false);
-
- fPool.startFrame = 0;
-
- // clear old data
- if (fFilePtr != nullptr)
- {
- ad_close(fFilePtr);
- fFilePtr = nullptr;
- }
- if (fPollTempData != nullptr)
- {
- delete[] fPollTempData;
- fPollTempData = nullptr;
- fPollTempSize = 0;
- fMaxPlayerFrame = 0;
- }
-
- ad_clear_nfo(&fFileNfo);
-
- // open new
- fFilePtr = ad_open(filename, &fFileNfo);
-
- if (fFilePtr == nullptr)
- return false;
-
- ad_dump_nfo(99, &fFileNfo);
-
- // Fix for misinformation using libsndfile
- if (fFileNfo.frames % fFileNfo.channels)
- --fFileNfo.frames;
-
- if (fFileNfo.frames <= 0)
- carla_stderr("L: filename \"%s\" has 0 frames", filename);
-
- if ((fFileNfo.channels == 1 || fFileNfo.channels == 2) && fFileNfo.frames > 0)
- {
- // valid
- const size_t pollTempSize = std::min(static_cast<uint>(fFileNfo.frames),
- fPool.size * fFileNfo.channels);
-
- try {
- fPollTempData = new float[pollTempSize];
- } catch (...) {
- ad_close(fFilePtr);
- fFilePtr = nullptr;
- return false;
- }
-
- fMaxPlayerFrame = static_cast<uint32_t>(fFileNfo.frames/fFileNfo.channels);
- fPollTempSize = pollTempSize;
-
- readPoll();
- return true;
- }
- else
- {
- // invalid
- ad_clear_nfo(&fFileNfo);
- ad_close(fFilePtr);
- fFilePtr = nullptr;
- return false;
- }
- }
-
- bool tryPutData(AudioFilePool& pool, const uint32_t framePos, const uint32_t frames)
- {
- CARLA_SAFE_ASSERT_RETURN(pool.size == fPool.size, false);
-
- if (! fMutex.tryLock())
- return false;
-
- //if (pool.startFrame != fPool.startFrame || pool.buffer[0] != fPool.buffer[0] || pool.buffer[1] != fPool.buffer[1])
- {
- pool.startFrame = fPool.startFrame;
-
- if (frames == 0)
- {
- carla_copyFloats(pool.buffer[0], fPool.buffer[0], fPool.size);
- carla_copyFloats(pool.buffer[1], fPool.buffer[1], fPool.size);
- }
- else
- {
- CARLA_SAFE_ASSERT_UINT2_RETURN(framePos + frames < fPool.size, framePos, fPool.size, false);
-
- carla_copyFloats(pool.buffer[0] + framePos, fPool.buffer[0] + framePos, frames);
- carla_copyFloats(pool.buffer[1] + framePos, fPool.buffer[1] + framePos, frames);
- }
- }
-
- fMutex.unlock();
- return true;
- }
-
- void readPoll()
- {
- if (fFileNfo.frames <= 0 || fFilePtr == nullptr)
- {
- carla_stderr("R: no song loaded");
- fNeedsRead = false;
- return;
- }
-
- const uint64_t lastFrame = kPlayer->getLastFrame();
- int32_t readFrameCheck;
-
- if (lastFrame >= static_cast<uint64_t>(fFileNfo.frames))
- {
- if (fLoopingMode)
- {
- const uint64_t readFrameCheckLoop = lastFrame % fMaxPlayerFrame;
- CARLA_SAFE_ASSERT_RETURN(readFrameCheckLoop < INT32_MAX,);
-
- carla_debug("R: transport out of bounds for loop");
- readFrameCheck = static_cast<int32_t>(readFrameCheckLoop);
- }
- else
- {
- carla_stderr("R: transport out of bounds");
- fNeedsRead = false;
- return;
- }
- }
- else
- {
- CARLA_SAFE_ASSERT_RETURN(lastFrame < INT32_MAX,);
- readFrameCheck = static_cast<int32_t>(lastFrame);
- }
-
- const int32_t readFrame = readFrameCheck;
-
- // temp data buffer
- carla_zeroFloats(fPollTempData, fPollTempSize);
-
- {
- carla_debug("R: poll data - reading at %li:%02li", readFrame/fPool.sampleRate/60, (readFrame/fPool.sampleRate) % 60);
-
- ad_seek(fFilePtr, readFrame);
- size_t i = 0;
- ssize_t j = 0;
- ssize_t rv = ad_read(fFilePtr, fPollTempData, fPollTempSize);
-
- if (rv < 0)
- {
- carla_stderr("R: ad_read failed");
- fNeedsRead = false;
- return;
- }
-
- const size_t urv = static_cast<size_t>(rv);
-
- // see if we can read more
- if (readFrame + rv >= static_cast<ssize_t>(fFileNfo.frames) && urv < fPollTempSize)
- {
- carla_debug("R: from start");
- ad_seek(fFilePtr, 0);
- rv += ad_read(fFilePtr, fPollTempData+urv, fPollTempSize-urv);
- }
-
- // lock, and put data asap
- const CarlaMutexLocker cml(fMutex);
-
- do {
- for (; i < fPool.size && j < rv; ++j)
- {
- if (fFileNfo.channels == 1)
- {
- fPool.buffer[0][i] = fPollTempData[j];
- fPool.buffer[1][i] = fPollTempData[j];
- i++;
- }
- else
- {
- if (j % 2 == 0)
- {
- fPool.buffer[0][i] = fPollTempData[j];
- }
- else
- {
- fPool.buffer[1][i] = fPollTempData[j];
- i++;
- }
- }
- }
-
- if (i >= fPool.size)
- break;
-
- if (rv == fFileNfo.frames)
- {
- // full file read
- j = 0;
- carla_debug("R: full file was read, filling buffers again");
- }
- else
- {
- carla_debug("read break, not enough space");
-
- carla_zeroFloats(fPool.buffer[0] + i, fPool.size - i);
- carla_zeroFloats(fPool.buffer[1] + i, fPool.size - i);
- break;
- }
-
- } while (i < fPool.size);
-
- fPool.startFrame = lastFrame;
- }
-
- fNeedsRead = false;
- }
-
- protected:
- void run() override
- {
- while (! fQuitNow)
- {
- const uint64_t lastFrame = kPlayer->getLastFrame();
- const uint64_t loopedFrame = fLoopingMode ? lastFrame % fMaxPlayerFrame : lastFrame;
-
- if (fNeedsRead || lastFrame < fPool.startFrame || (lastFrame - fPool.startFrame >= fPool.size*3/4 && loopedFrame < fMaxPlayerFrame))
- readPoll();
-
- carla_msleep(50);
- }
- }
-
- private:
- AbstractAudioPlayer* const kPlayer;
-
- bool fLoopingMode;
- bool fNeedsRead;
- bool fQuitNow;
-
- void* fFilePtr;
- ADInfo fFileNfo;
-
- uint32_t fMaxPlayerFrame;
-
- float* fPollTempData;
- size_t fPollTempSize;
-
- AudioFilePool fPool;
- CarlaMutex fMutex;
-
- CARLA_DECLARE_NON_COPY_STRUCT(AudioFileThread)
- };
-
- #endif // AUDIO_BASE_HPP_INCLUDED
|