Browse Source

Working loop-mode for audio-file plugin, turn it on by default

tags/v1.9.11
falkTX 6 years ago
parent
commit
3248f8019d
3 changed files with 147 additions and 86 deletions
  1. +23
    -0
      source/native-plugins/_data.base.cpp
  2. +113
    -79
      source/native-plugins/audio-base.hpp
  3. +11
    -7
      source/native-plugins/audio-file.cpp

+ 23
- 0
source/native-plugins/_data.base.cpp View File

@@ -164,6 +164,29 @@ static const NativePluginDescriptor sNativePluginDescriptors[] = {
DESCFUNCS DESCFUNCS
}, },


// --------------------------------------------------------------------------------------------------------------------
// Audio file

{
/* category */ NATIVE_PLUGIN_CATEGORY_UTILITY,
/* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_RTSAFE
|NATIVE_PLUGIN_HAS_UI
|NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE
|NATIVE_PLUGIN_USES_TIME),
/* supports */ NATIVE_PLUGIN_SUPPORTS_NOTHING,
/* audioIns */ 0,
/* audioOuts */ 2,
/* midiIns */ 0,
/* midiOuts */ 0,
/* paramIns */ 1,
/* paramOuts */ 0,
/* name */ "Audio File",
/* label */ "audiofile",
/* maker */ "falkTX",
/* copyright */ "GNU GPL v2+",
DESCFUNCS
},

// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
// MIDI sequencer // MIDI sequencer




+ 113
- 79
source/native-plugins/audio-base.hpp View File

@@ -1,6 +1,6 @@
/* /*
* Carla Native Plugins * Carla Native Plugins
* Copyright (C) 2013-2017 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2013-2018 Filipe Coelho <falktx@falktx.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
@@ -64,7 +64,7 @@ struct AudioFilePool {
CARLA_ASSERT(startFrame == 0); CARLA_ASSERT(startFrame == 0);
CARLA_ASSERT(size == 0); CARLA_ASSERT(size == 0);


size = srate * 5;
size = srate * 8;
sampleRate = srate; sampleRate = srate;


buffer[0] = new float[size]; buffer[0] = new float[size];
@@ -118,9 +118,13 @@ public:
AudioFileThread(AbstractAudioPlayer* const player, const double sampleRate) AudioFileThread(AbstractAudioPlayer* const player, const double sampleRate)
: CarlaThread("AudioFileThread"), : CarlaThread("AudioFileThread"),
kPlayer(player), kPlayer(player),
fLoopingMode(true),
fNeedsRead(false), fNeedsRead(false),
fQuitNow(true), fQuitNow(true),
fFilePtr(nullptr)
fFilePtr(nullptr),
fMaxPlayerFrame(0),
fPollTempData(nullptr),
fPollTempSize(0)
{ {
CARLA_ASSERT(kPlayer != nullptr); CARLA_ASSERT(kPlayer != nullptr);


@@ -143,7 +147,17 @@ public:
CARLA_ASSERT(! isThreadRunning()); CARLA_ASSERT(! isThreadRunning());


if (fFilePtr != nullptr) if (fFilePtr != nullptr)
{
ad_close(fFilePtr); ad_close(fFilePtr);
fFilePtr = nullptr;
}

if (fPollTempData != nullptr)
{
delete[] fPollTempData;
fPollTempData = nullptr;
fPollTempSize = 0;
}


fPool.destroy(); fPool.destroy();
} }
@@ -166,20 +180,25 @@ public:
fPool.reset(); fPool.reset();
} }


uint32_t getMaxFrame() const
uint32_t getMaxFrame() const noexcept
{
return fMaxPlayerFrame;
}

void setLoopingMode(const bool on) noexcept
{ {
return fFileNfo.frames > 0 ? fFileNfo.frames : 0;
fLoopingMode = on;
} }


void setNeedsRead()
void setNeedsRead() noexcept
{ {
fNeedsRead = true; fNeedsRead = true;
} }


bool loadFilename(const char* const filename) bool loadFilename(const char* const filename)
{ {
CARLA_ASSERT(! isThreadRunning());
CARLA_ASSERT(filename != nullptr);
CARLA_SAFE_ASSERT_RETURN(! isThreadRunning(), false);
CARLA_SAFE_ASSERT_RETURN(filename != nullptr && *filename != '\0', false);


fPool.startFrame = 0; fPool.startFrame = 0;


@@ -189,6 +208,13 @@ public:
ad_close(fFilePtr); ad_close(fFilePtr);
fFilePtr = nullptr; fFilePtr = nullptr;
} }
if (fPollTempData != nullptr)
{
delete[] fPollTempData;
fPollTempData = nullptr;
fPollTempSize = 0;
fMaxPlayerFrame = 0;
}


ad_clear_nfo(&fFileNfo); ad_clear_nfo(&fFileNfo);


@@ -200,16 +226,30 @@ public:


ad_dump_nfo(99, &fFileNfo); ad_dump_nfo(99, &fFileNfo);


if (fFileNfo.frames == 0)
carla_stderr("L: filename \"%s\" has 0 frames", filename);

// Fix for misinformation using libsndfile // Fix for misinformation using libsndfile
if (fFileNfo.frames % fFileNfo.channels) if (fFileNfo.frames % fFileNfo.channels)
--fFileNfo.frames; --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) if ((fFileNfo.channels == 1 || fFileNfo.channels == 2) && fFileNfo.frames > 0)
{ {
// valid // 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 = fFileNfo.frames/fFileNfo.channels;
fPollTempSize = pollTempSize;

readPoll(); readPoll();
return true; return true;
} }
@@ -255,24 +295,12 @@ public:


if (lastFrame >= maxFrame) if (lastFrame >= maxFrame)
{ {
#if 0
if (false)
//if (handlePtr->loopMode)
if (fLoopingMode)
{ {
carla_stderr("R: DEBUG read loop, lastFrame:%i, maxFrame:%i", lastFrame, maxFrame);

if (maxFrame >= static_cast<int64_t>(fPool.size))
{
readFrame %= maxFrame;
}
else
{
readFrame = 0;
lastFrame -= lastFrame % maxFrame;
}
carla_debug("R: transport out of bounds for loop");
readFrame %= fMaxPlayerFrame;
} }
else else
#endif
{ {
carla_stderr("R: transport out of bounds"); carla_stderr("R: transport out of bounds");
fNeedsRead = false; fNeedsRead = false;
@@ -281,10 +309,7 @@ public:
} }


// temp data buffer // temp data buffer
const size_t tmpSize = fPool.size * fFileNfo.channels;

float tmpData[tmpSize];
carla_zeroFloats(tmpData, tmpSize);
carla_zeroFloats(fPollTempData, fPollTempSize);


{ {
carla_debug("R: poll data - reading at %li:%02li", readFrame/fPool.sampleRate/60, (readFrame/fPool.sampleRate) % 60); carla_debug("R: poll data - reading at %li:%02li", readFrame/fPool.sampleRate/60, (readFrame/fPool.sampleRate) % 60);
@@ -292,71 +317,73 @@ public:
ad_seek(fFilePtr, readFrame); ad_seek(fFilePtr, readFrame);
size_t i = 0; size_t i = 0;
ssize_t j = 0; ssize_t j = 0;
ssize_t rv = ad_read(fFilePtr, tmpData, tmpSize);
ssize_t rv = ad_read(fFilePtr, fPollTempData, fPollTempSize);

if (rv < 0)
{
carla_stderr("R: ad_read failed");
fNeedsRead = false;
return;
}

// see if we can read more
if (readFrame + rv >= static_cast<ssize_t>(fFileNfo.frames) && static_cast<size_t>(rv) < fPollTempSize)
{
carla_debug("R: from start");
ad_seek(fFilePtr, 0);
rv += ad_read(fFilePtr, fPollTempData+rv, fPollTempSize-rv);
}


// lock, and put data asap // lock, and put data asap
const CarlaMutexLocker cml(fMutex); const CarlaMutexLocker cml(fMutex);


for (; i < fPool.size && j < rv; ++j)
{
if (fFileNfo.channels == 1)
{
fPool.buffer[0][i] = tmpData[j];
fPool.buffer[1][i] = tmpData[j];
i++;
}
else
do {
for (; i < fPool.size && j < rv; ++j)
{ {
if (j % 2 == 0)
if (fFileNfo.channels == 1)
{ {
fPool.buffer[0][i] = tmpData[j];
}
else
{
fPool.buffer[1][i] = tmpData[j];
fPool.buffer[0][i] = fPollTempData[j];
fPool.buffer[1][i] = fPollTempData[j];
i++; i++;
} }
}
}

#if 0
if (false)
//if (handlePtr->loopMode && i < fPool.size)
{
while (i < fPool.size)
{
for (j=0; i < fPool.size && j < rv; ++j)
else
{ {
if (fFileNfo.channels == 1)
if (j % 2 == 0)
{ {
fPool.buffer[0][i] = tmpData[j];
fPool.buffer[1][i] = tmpData[j];
i++;
fPool.buffer[0][i] = fPollTempData[j];
} }
else else
{ {
if (j % 2 == 0)
{
fPool.buffer[0][i] = tmpData[j];
}
else
{
fPool.buffer[1][i] = tmpData[j];
i++;
}
fPool.buffer[1][i] = fPollTempData[j];
i++;
} }
} }
} }
}
else
#endif
{
for (; i < fPool.size; ++i)

if (i >= fPool.size)
break;

if (rv == fFileNfo.frames)
{ {
fPool.buffer[0][i] = 0.0f;
fPool.buffer[1][i] = 0.0f;
// full file read
j = 0;
carla_debug("R: full file was read, filling buffers again");
} }
}
else
{
carla_debug("read break, not enough space");

// FIXME use carla_zeroFloats
for (; i < fPool.size; ++i)
{
fPool.buffer[0][i] = 0.0f;
fPool.buffer[1][i] = 0.0f;
}

break;
}

} while (i < fPool.size);


fPool.startFrame = lastFrame; fPool.startFrame = lastFrame;
} }
@@ -369,9 +396,10 @@ protected:
{ {
while (! fQuitNow) while (! fQuitNow)
{ {
const uint32_t lastFrame(kPlayer->getLastFrame());
const uint32_t lastFrame = kPlayer->getLastFrame();
const uint32_t loopedFrame = fLoopingMode ? lastFrame % fMaxPlayerFrame : lastFrame;


if (fNeedsRead || lastFrame < fPool.startFrame || (lastFrame - fPool.startFrame >= fPool.size*3/4 && lastFrame < fFileNfo.frames))
if (fNeedsRead || lastFrame < fPool.startFrame || (lastFrame - fPool.startFrame >= fPool.size*3/4 && loopedFrame < fMaxPlayerFrame))
readPoll(); readPoll();
else else
carla_msleep(50); carla_msleep(50);
@@ -381,12 +409,18 @@ protected:
private: private:
AbstractAudioPlayer* const kPlayer; AbstractAudioPlayer* const kPlayer;


bool fLoopingMode;
bool fNeedsRead; bool fNeedsRead;
bool fQuitNow; bool fQuitNow;


void* fFilePtr; void* fFilePtr;
ADInfo fFileNfo; ADInfo fFileNfo;


uint32_t fMaxPlayerFrame;

float* fPollTempData;
size_t fPollTempSize;

AudioFilePool fPool; AudioFilePool fPool;
CarlaMutex fMutex; CarlaMutex fMutex;
}; };


+ 11
- 7
source/native-plugins/audio-file.cpp View File

@@ -1,6 +1,6 @@
/* /*
* Carla Native Plugins * Carla Native Plugins
* Copyright (C) 2013 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2013-2018 Filipe Coelho <falktx@falktx.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
@@ -29,7 +29,7 @@ public:
AudioFilePlugin(const NativeHostDescriptor* const host) AudioFilePlugin(const NativeHostDescriptor* const host)
: NativePluginClass(host), : NativePluginClass(host),
AbstractAudioPlayer(), AbstractAudioPlayer(),
fLoopMode(false),
fLoopMode(true),
fDoProcess(false), fDoProcess(false),
fLastFrame(0), fLastFrame(0),
fMaxFrame(0), fMaxFrame(0),
@@ -55,7 +55,7 @@ protected:


uint32_t getParameterCount() const override uint32_t getParameterCount() const override
{ {
return 0; // TODO - loopMode
return 1;
} }


const NativeParameter* getParameterInfo(const uint32_t index) const override const NativeParameter* getParameterInfo(const uint32_t index) const override
@@ -67,7 +67,7 @@ protected:


param.name = "Loop Mode"; param.name = "Loop Mode";
param.unit = nullptr; param.unit = nullptr;
param.hints = static_cast<NativeParameterHints>(NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_BOOLEAN);
param.hints = static_cast<NativeParameterHints>(NATIVE_PARAMETER_IS_AUTOMABLE|NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_BOOLEAN);
param.ranges.def = 1.0f; param.ranges.def = 1.0f;
param.ranges.min = 0.0f; param.ranges.min = 0.0f;
param.ranges.max = 1.0f; param.ranges.max = 1.0f;
@@ -102,6 +102,7 @@ protected:
return; return;


fLoopMode = b; fLoopMode = b;
fThread.setLoopingMode(b);
fThread.setNeedsRead(); fThread.setNeedsRead();
} }


@@ -149,9 +150,12 @@ protected:
fThread.tryPutData(fPool); fThread.tryPutData(fPool);


// out of reach // out of reach
if (timePos->frame + frames < fPool.startFrame || timePos->frame >= fMaxFrame) /*&& ! loopMode)*/
if (timePos->frame + frames < fPool.startFrame || (timePos->frame >= fMaxFrame && !fLoopMode))
{ {
//carla_stderr("P: out of reach");
if (fLoopMode) {
carla_stderr("P: out of reach");
}

fLastFrame = timePos->frame; fLastFrame = timePos->frame;


if (timePos->frame + frames < fPool.startFrame) if (timePos->frame + frames < fPool.startFrame)
@@ -254,7 +258,7 @@ static const NativePluginDescriptor audiofileDesc = {
/* audioOuts */ 2, /* audioOuts */ 2,
/* midiIns */ 0, /* midiIns */ 0,
/* midiOuts */ 0, /* midiOuts */ 0,
/* paramIns */ 0, // TODO - loopMode
/* paramIns */ 1,
/* paramOuts */ 0, /* paramOuts */ 0,
/* name */ "Audio File", /* name */ "Audio File",
/* label */ "audiofile", /* label */ "audiofile",


Loading…
Cancel
Save