@@ -232,9 +232,11 @@ protected: | |||
carla_zeroFloats(audioIns[1], bufferSize); | |||
carla_zeroStructs(pData->events.in, kMaxEngineEventInternalCount); | |||
int64_t oldTime, newTime; | |||
while (! shouldThreadExit()) | |||
{ | |||
const int64_t oldTime = getTimeInMicroseconds(); | |||
oldTime = getTimeInMicroseconds(); | |||
const PendingRtEventsRunner prt(this, bufferSize, true); | |||
@@ -244,7 +246,7 @@ protected: | |||
pData->graph.process(pData, audioIns, audioOuts, bufferSize); | |||
const int64_t newTime = getTimeInMicroseconds(); | |||
newTime = getTimeInMicroseconds(); | |||
CARLA_SAFE_ASSERT_CONTINUE(newTime >= oldTime); | |||
const int64_t remainingTime = cycleTime - (newTime - oldTime); | |||
@@ -267,7 +269,7 @@ protected: | |||
std::free(audioOuts[0]); | |||
std::free(audioOuts[1]); | |||
carla_stdout("CarlaEngineDummy audio thread finished"); | |||
carla_stdout("CarlaEngineDummy audio thread finished with %u Xruns", pData->xruns); | |||
} | |||
// ------------------------------------------------------------------- | |||
@@ -68,6 +68,7 @@ | |||
#include "jackbridge/JackBridge.hpp" | |||
#include "water/files/File.h" | |||
#include "water/misc/Time.h" | |||
using CarlaBackend::CarlaEngine; | |||
using CarlaBackend::EngineCallbackOpcode; | |||
@@ -281,6 +282,15 @@ public: | |||
gIsInitiated = true; | |||
const bool testing = std::getenv("CARLA_BRIDGE_TESTING") != nullptr; | |||
int64_t timeToEnd; | |||
if (testing) | |||
{ | |||
timeToEnd = water::Time::currentTimeMillis() + 10 * 1000; | |||
fEngine->transportPlay(); | |||
} | |||
#if defined(USING_JUCE) && (defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)) | |||
# ifndef CARLA_OS_WIN | |||
static const int argc = 0; | |||
@@ -298,6 +308,8 @@ public: | |||
# else | |||
carla_msleep(5); | |||
# endif | |||
if (testing && timeToEnd - water::Time::currentTimeMillis() < 0) | |||
break; | |||
} | |||
#endif | |||
@@ -332,7 +344,7 @@ protected: | |||
} | |||
private: | |||
const CarlaEngine* fEngine; | |||
CarlaEngine* fEngine; | |||
#ifdef USING_JUCE | |||
const juce::ScopedJuceInitialiser_GUI fJuceInitialiser; | |||
@@ -649,7 +661,14 @@ int main(int argc, char* argv[]) | |||
if (const CarlaPluginInfo* const pluginInfo = carla_get_plugin_info(gHostHandle, 0)) | |||
{ | |||
if (pluginInfo->hints & CarlaBackend::PLUGIN_HAS_CUSTOM_UI) | |||
if (itype == CarlaBackend::PLUGIN_INTERNAL && (std::strcmp(label, "audiofile") == 0 || std::strcmp(label, "midifile") == 0)) | |||
{ | |||
if (file.exists()) | |||
carla_set_custom_data(gHostHandle, 0, | |||
CarlaBackend::CUSTOM_DATA_TYPE_STRING, | |||
"file", file.getFullPathName().toRawUTF8()); | |||
} | |||
else if (pluginInfo->hints & CarlaBackend::PLUGIN_HAS_CUSTOM_UI) | |||
{ | |||
#ifdef HAVE_X11 | |||
if (std::getenv("DISPLAY") != nullptr) | |||
@@ -97,21 +97,14 @@ struct AudioFilePool { | |||
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) | |||
AudioFileThread() | |||
: CarlaThread("AudioFileThread"), | |||
kPlayer(player), | |||
fEntireFileLoaded(false), | |||
fLoopingMode(true), | |||
fNeedsFrame(0), | |||
fNeedsRead(false), | |||
fQuitNow(true), | |||
fFilePtr(nullptr), | |||
@@ -120,10 +113,9 @@ public: | |||
fPollTempData(nullptr), | |||
fPollTempSize(0), | |||
fPool(), | |||
fMutex() | |||
fMutex(), | |||
fSignal() | |||
{ | |||
CARLA_ASSERT(kPlayer != nullptr); | |||
static bool adInitiated = false; | |||
if (! adInitiated) | |||
@@ -168,16 +160,19 @@ public: | |||
if (fPollTempData == nullptr) | |||
return; | |||
fNeedsRead = true; | |||
fNeedsFrame = 0; | |||
fNeedsRead = false; | |||
fQuitNow = false; | |||
startThread(); | |||
} | |||
void stopNow() | |||
{ | |||
fNeedsFrame = 0; | |||
fNeedsRead = false; | |||
fQuitNow = true; | |||
fSignal.signal(); | |||
stopThread(1000); | |||
const CarlaMutexLocker cml(fMutex); | |||
@@ -209,9 +204,14 @@ public: | |||
fLoopingMode = on; | |||
} | |||
void setNeedsRead() noexcept | |||
void setNeedsRead(const uint64_t frame) noexcept | |||
{ | |||
if (fEntireFileLoaded) | |||
return; | |||
fNeedsFrame = frame; | |||
fNeedsRead = true; | |||
fSignal.signal(); | |||
} | |||
bool loadFilename(const char* const filename, const uint32_t sampleRate) | |||
@@ -295,24 +295,73 @@ public: | |||
carla_copyFloats(pool.buffer[1], fPool.buffer[1], fPool.numFrames); | |||
} | |||
bool tryPutData(AudioFilePool& pool, const uint64_t framePos, const uint32_t frames) | |||
bool tryPutData(float* const out1, float* const out2, uint64_t framePos, const uint32_t frames) | |||
{ | |||
CARLA_SAFE_ASSERT_RETURN(pool.numFrames == fPool.numFrames, false); | |||
CARLA_SAFE_ASSERT_RETURN(fPool.numFrames != 0, false); | |||
if (framePos >= fPool.numFrames) | |||
return false; | |||
if (framePos >= fNumFileFrames) | |||
{ | |||
if (fLoopingMode) | |||
framePos %= fNumFileFrames; | |||
else | |||
return false; | |||
} | |||
uint64_t frameDiff; | |||
const uint64_t numFramesNearEnd = fPool.numFrames*3/4; | |||
#if 1 | |||
const CarlaMutexLocker cml(fMutex); | |||
/* | |||
#else | |||
const CarlaMutexTryLocker cmtl(fMutex); | |||
if (! cmtl.wasLocked()) | |||
return false; | |||
*/ | |||
{ | |||
for (int i=0; i<5; ++i) | |||
{ | |||
pthread_yield(); | |||
if (cmtl.tryAgain()) | |||
break; | |||
if (i == 4) | |||
return false; | |||
} | |||
} | |||
#endif | |||
pool.startFrame = fPool.startFrame; | |||
if (framePos < fPool.startFrame) | |||
{ | |||
if (fPool.startFrame + fPool.numFrames <= fNumFileFrames) | |||
{ | |||
setNeedsRead(framePos); | |||
return false; | |||
} | |||
carla_copyFloats(pool.buffer[0] + framePos, fPool.buffer[0] + framePos, frames); | |||
carla_copyFloats(pool.buffer[1] + framePos, fPool.buffer[1] + framePos, frames); | |||
frameDiff = framePos + (fNumFileFrames - fPool.startFrame); | |||
if (frameDiff + frames >= fPool.numFrames) | |||
{ | |||
setNeedsRead(framePos); | |||
return false; | |||
} | |||
carla_copyFloats(out1, fPool.buffer[0] + frameDiff, frames); | |||
carla_copyFloats(out2, fPool.buffer[1] + frameDiff, frames); | |||
} | |||
else | |||
{ | |||
frameDiff = framePos - fPool.startFrame; | |||
if (frameDiff + frames >= fPool.numFrames) | |||
{ | |||
setNeedsRead(framePos); | |||
return false; | |||
} | |||
carla_copyFloats(out1, fPool.buffer[0] + frameDiff, frames); | |||
carla_copyFloats(out2, fPool.buffer[1] + frameDiff, frames); | |||
} | |||
if (frameDiff > numFramesNearEnd) | |||
setNeedsRead(framePos + frames); | |||
return true; | |||
} | |||
@@ -372,17 +421,19 @@ public: | |||
if (fNumFileFrames == 0 || 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; | |||
} | |||
uint64_t lastFrame = kPlayer->getLastFrame(); | |||
uint64_t lastFrame = fNeedsFrame; | |||
int64_t readFrameCheck; | |||
if (lastFrame >= fNumFileFrames) | |||
@@ -398,6 +449,7 @@ public: | |||
else | |||
{ | |||
carla_debug("R: transport out of bounds"); | |||
fNeedsFrame = 0; | |||
fNeedsRead = false; | |||
return; | |||
} | |||
@@ -428,6 +480,7 @@ public: | |||
if (rv < 0) | |||
{ | |||
carla_stderr("R: ad_read failed"); | |||
fNeedsFrame = 0; | |||
fNeedsRead = false; | |||
return; | |||
} | |||
@@ -442,36 +495,46 @@ public: | |||
rv += ad_read(fFilePtr, fPollTempData+urv, fPollTempSize-urv); | |||
} | |||
carla_debug("R: reading %li frames at frame %lu", rv, readFrameCheck); | |||
// local copy | |||
const uint32_t poolNumFrame = fPool.numFrames; | |||
const int64_t fileFrames = fFileNfo.frames; | |||
const bool isMonoFile = fFileNfo.channels == 1; | |||
float* const pbuffer0 = fPool.buffer[0]; | |||
float* const pbuffer1 = fPool.buffer[1]; | |||
const float* const tmpbuf = fPollTempData; | |||
// lock, and put data asap | |||
const CarlaMutexLocker cml(fMutex); | |||
do { | |||
for (; i < fPool.numFrames && j < rv; ++j) | |||
for (; i < poolNumFrame && j < rv; ++j) | |||
{ | |||
if (fFileNfo.channels == 1) | |||
if (isMonoFile) | |||
{ | |||
fPool.buffer[0][i] = fPollTempData[j]; | |||
fPool.buffer[1][i] = fPollTempData[j]; | |||
pbuffer0[i] = pbuffer1[i] = tmpbuf[j]; | |||
i++; | |||
} | |||
else | |||
{ | |||
if (j % 2 == 0) | |||
{ | |||
fPool.buffer[0][i] = fPollTempData[j]; | |||
pbuffer0[i] = tmpbuf[j]; | |||
} | |||
else | |||
{ | |||
fPool.buffer[1][i] = fPollTempData[j]; | |||
i++; | |||
pbuffer1[i] = tmpbuf[j]; | |||
++i; | |||
} | |||
} | |||
} | |||
if (i >= fPool.numFrames) | |||
if (i >= poolNumFrame) { | |||
break; | |||
} | |||
if (rv == fFileNfo.frames) | |||
if (rv == fileFrames) | |||
{ | |||
// full file read | |||
j = 0; | |||
@@ -481,14 +544,14 @@ public: | |||
{ | |||
carla_debug("read break, not enough space"); | |||
carla_zeroFloats(fPool.buffer[0] + i, fPool.numFrames - i); | |||
carla_zeroFloats(fPool.buffer[1] + i, fPool.numFrames - i); | |||
carla_zeroFloats(pbuffer0, poolNumFrame - i); | |||
carla_zeroFloats(pbuffer1, poolNumFrame - i); | |||
break; | |||
} | |||
} while (i < fPool.numFrames); | |||
} while (i < poolNumFrame); | |||
fPool.startFrame = lastFrame; | |||
fPool.startFrame = readFrame; | |||
} | |||
fNeedsRead = false; | |||
@@ -497,25 +560,22 @@ public: | |||
protected: | |||
void run() override | |||
{ | |||
const uint64_t numFramesNearEnd = fPool.numFrames*3/4; | |||
uint64_t lastFrame; | |||
while (! fQuitNow) | |||
{ | |||
lastFrame = kPlayer->getLastFrame(); | |||
if (fNeedsRead || lastFrame < fPool.startFrame || lastFrame - fPool.startFrame >= numFramesNearEnd) | |||
if (fNeedsRead) | |||
readPoll(); | |||
carla_msleep(50); | |||
if (fQuitNow) | |||
break; | |||
fSignal.wait(); | |||
} | |||
} | |||
private: | |||
AbstractAudioPlayer* const kPlayer; | |||
bool fEntireFileLoaded; | |||
bool fLoopingMode; | |||
volatile uint64_t fNeedsFrame; | |||
volatile bool fNeedsRead; | |||
volatile bool fQuitNow; | |||
@@ -529,6 +589,7 @@ private: | |||
AudioFilePool fPool; | |||
CarlaMutex fMutex; | |||
CarlaSignal fSignal; | |||
CARLA_DECLARE_NON_COPY_STRUCT(AudioFileThread) | |||
}; | |||
@@ -46,11 +46,9 @@ static const char* const audiofilesWildcard = | |||
// ----------------------------------------------------------------------- | |||
#ifdef HAVE_PYQT | |||
class AudioFilePlugin : public NativePluginWithMidiPrograms<FileAudio>, | |||
public AbstractAudioPlayer | |||
class AudioFilePlugin : public NativePluginWithMidiPrograms<FileAudio> | |||
#else | |||
class AudioFilePlugin : public NativePluginClass, | |||
public AbstractAudioPlayer | |||
class AudioFilePlugin : public NativePluginClass | |||
#endif | |||
{ | |||
public: | |||
@@ -60,13 +58,12 @@ public: | |||
#else | |||
: NativePluginClass(host), | |||
#endif | |||
AbstractAudioPlayer(), | |||
fLoopMode(true), | |||
fDoProcess(false), | |||
fLastFrame(0), | |||
fWasPlayingBefore(false), | |||
fMaxFrame(0), | |||
fPool(), | |||
fThread(this) | |||
fThread() | |||
#ifdef HAVE_PYQT | |||
, fPrograms(hostGetFilePath("audio"), audiofilesWildcard), | |||
fInlineDisplay() | |||
@@ -80,11 +77,6 @@ public: | |||
fPool.destroy(); | |||
} | |||
uint64_t getLastFrame() const override | |||
{ | |||
return fLastFrame; | |||
} | |||
protected: | |||
// ------------------------------------------------------------------- | |||
// Plugin parameter calls | |||
@@ -139,7 +131,6 @@ protected: | |||
fLoopMode = b; | |||
fThread.setLoopingMode(b); | |||
fThread.setNeedsRead(); | |||
} | |||
void setCustomData(const char* const key, const char* const value) override | |||
@@ -166,8 +157,7 @@ protected: | |||
if (! fDoProcess) | |||
{ | |||
//carla_stderr("P: no process"); | |||
fLastFrame = timePos->frame; | |||
// carla_stderr("P: no process"); | |||
carla_zeroFloats(out1, frames); | |||
carla_zeroFloats(out2, frames); | |||
return; | |||
@@ -176,23 +166,26 @@ protected: | |||
// not playing | |||
if (! timePos->playing) | |||
{ | |||
//carla_stderr("P: not playing"); | |||
if (timePos->frame == 0 && fLastFrame > 0) | |||
fThread.setNeedsRead(); | |||
// carla_stderr("P: not playing"); | |||
if (timePos->frame == 0 && fWasPlayingBefore) | |||
fThread.setNeedsRead(timePos->frame); | |||
fLastFrame = timePos->frame; | |||
carla_zeroFloats(out1, frames); | |||
carla_zeroFloats(out2, frames); | |||
fWasPlayingBefore = false; | |||
return; | |||
} | |||
else | |||
{ | |||
fWasPlayingBefore = true; | |||
} | |||
// out of reach | |||
if ((timePos->frame < fPool.startFrame || timePos->frame >= fMaxFrame) && !fLoopMode) | |||
{ | |||
if (timePos->frame < fPool.startFrame) | |||
fThread.setNeedsRead(); | |||
fThread.setNeedsRead(timePos->frame); | |||
fLastFrame = timePos->frame; | |||
carla_zeroFloats(out1, frames); | |||
carla_zeroFloats(out2, frames); | |||
@@ -251,15 +244,7 @@ protected: | |||
} | |||
else | |||
{ | |||
// NOTE: timePos->frame is always >= fPool.startFrame | |||
const uint64_t poolStartFrame = timePos->frame - fThread.getPoolStartFrame(); | |||
if (fThread.tryPutData(fPool, poolStartFrame, frames)) | |||
{ | |||
carla_copyFloats(out1, fPool.buffer[0]+poolStartFrame, frames); | |||
carla_copyFloats(out2, fPool.buffer[1]+poolStartFrame, frames); | |||
} | |||
else | |||
if (! fThread.tryPutData(out1, out2, timePos->frame, frames)) | |||
{ | |||
carla_zeroFloats(out1, frames); | |||
carla_zeroFloats(out2, frames); | |||
@@ -277,11 +262,10 @@ protected: | |||
if (! fInlineDisplay.pending) | |||
{ | |||
fInlineDisplay.pending = true; | |||
// FIXME this is not supposed to be here, but in some idle callback | |||
hostQueueDrawInlineDisplay(); | |||
} | |||
#endif | |||
fLastFrame = timePos->frame; | |||
} | |||
// ------------------------------------------------------------------- | |||
@@ -428,8 +412,8 @@ protected: | |||
private: | |||
bool fLoopMode; | |||
bool fDoProcess; | |||
bool fWasPlayingBefore; | |||
volatile uint64_t fLastFrame; | |||
uint32_t fMaxFrame; | |||
AudioFilePool fPool; | |||
@@ -205,35 +205,17 @@ private: | |||
CARLA_SAFE_ASSERT_CONTINUE(midiEventHolder != nullptr); | |||
const MidiMessage& midiMessage(midiEventHolder->message); | |||
//const double time(track->getEventTime(i)*sampleRate); | |||
const int dataSize(midiMessage.getRawDataSize()); | |||
const int dataSize = midiMessage.getRawDataSize(); | |||
if (dataSize <= 0 || dataSize > MAX_EVENT_DATA_SIZE) | |||
continue; | |||
if (midiMessage.isActiveSense()) | |||
continue; | |||
if (midiMessage.isMetaEvent()) | |||
continue; | |||
if (midiMessage.isMidiStart()) | |||
continue; | |||
if (midiMessage.isMidiContinue()) | |||
continue; | |||
if (midiMessage.isMidiStop()) | |||
continue; | |||
if (midiMessage.isMidiClock()) | |||
continue; | |||
if (midiMessage.isSongPositionPointer()) | |||
continue; | |||
if (midiMessage.isQuarterFrame()) | |||
continue; | |||
if (midiMessage.isFullFrame()) | |||
continue; | |||
if (midiMessage.isMidiMachineControlMessage()) | |||
continue; | |||
if (midiMessage.isSysEx()) | |||
const uint8_t* const data = midiMessage.getRawData(); | |||
if (! MIDI_IS_CHANNEL_MESSAGE(data[0])) | |||
continue; | |||
const double time(midiMessage.getTimeStamp()*sampleRate); | |||
const double time = midiMessage.getTimeStamp() * sampleRate; | |||
// const double time = track->getEventTime(i) * sampleRate; | |||
CARLA_SAFE_ASSERT_CONTINUE(time >= 0.0); | |||
fMidiOut.addRaw(static_cast<uint64_t>(time), midiMessage.getRawData(), static_cast<uint8_t>(dataSize)); | |||
@@ -325,6 +325,11 @@ public: | |||
return !fLocked; | |||
} | |||
bool tryAgain() const noexcept | |||
{ | |||
return fMutex.tryLock(); | |||
} | |||
private: | |||
const Mutex& fMutex; | |||
const bool fLocked; | |||