Skip to content

Commit

Permalink
WIP: Allows fully random audio access for supported formats (currentl…
Browse files Browse the repository at this point in the history
…y AudioSuite).
  • Loading branch information
talaviram committed Jul 3, 2018
1 parent 1618927 commit 1242c86
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 4 deletions.
63 changes: 59 additions & 4 deletions modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp
Expand Up @@ -587,9 +587,12 @@ namespace AAXClasses
{
public:
#if JucePlugin_EnhancedAudioSuite
class JUCE_EnhancedAudioSuite : public AAX_CHostProcessor
class JUCE_EnhancedAudioSuite : public AAX_CHostProcessor,
public juce::AudioFormatReader
{
public:
JUCE_EnhancedAudioSuite() : juce::AudioFormatReader (nullptr, "AudioSuiteReader") {}

AAX_Result AnalyzeAudio (const float * const iAudioIns [], int32_t iAudioInCount, int32_t * ioWindowSize) override
{
Array<const float*> inputChannelList;
Expand All @@ -608,6 +611,7 @@ namespace AAXClasses
if (mIsFirstPass)
{
UpdateMainInputBus(numOfMainInputs);
initRandomAccessReader(iAudioIns, numOfMainInputs, iAudioInCount);
mIsFirstPass = false;
}

Expand Down Expand Up @@ -635,7 +639,7 @@ namespace AAXClasses
if (mIsFirstPass)
{
UpdateMainInputBus(numOfMainInputs);

initRandomAccessReader(inAudioIns, numOfMainInputs, inAudioInCount);
float* tempOutBuffer[AAX_eMaxAudioSuiteTracks];
for (decltype(inAudioOutCount) ch = 0; ch < inAudioOutCount; ++ch)
tempOutBuffer[ch] = new float[*ioWindowSize];
Expand All @@ -645,7 +649,7 @@ namespace AAXClasses
{
int32_t numSamplesToPrime = std::min(*ioWindowSize, remainingDelaySamplesToPrime);
const int64_t firstSampleLocation = GetLocation() + (latencyOffset - remainingDelaySamplesToPrime);
GetAudio(inAudioIns, inAudioInCount, firstSampleLocation, ioWindowSize);
GetAudio (inAudioIns, inAudioInCount, firstSampleLocation, ioWindowSize);
getAAXProcessor().process (inAudioIns, tempOutBuffer, sideChainBufferIdx, numSamplesToPrime, false, nullptr, nullptr, nullptr);
remainingDelaySamplesToPrime -= numSamplesToPrime;
}
Expand All @@ -660,7 +664,7 @@ namespace AAXClasses

// Look ahead in the input audio by latencyOffset. After this call to GetAudio(),
// inAudioIns will be populated with the randomly-accessed lookahead samples.
GetAudio(inAudioIns, inAudioInCount, GetLocation()+latencyOffset, ioWindowSize);
GetAudio (inAudioIns, inAudioInCount, GetLocation()+latencyOffset, ioWindowSize);
getAAXProcessor().process (inAudioIns, inAudioOuts, sideChainBufferIdx, *ioWindowSize, false, nullptr, nullptr, nullptr);

return AAX_SUCCESS;
Expand Down Expand Up @@ -695,7 +699,56 @@ namespace AAXClasses
return AAX_SUCCESS;
}

AAX_Result PostRender () override
{
getAAXProcessor().getPluginInstance().setRandomAudioReader(nullptr);
return AAX_SUCCESS;
}

bool readSamples (int** destSamples,
int numDestChannels,
int startOffsetInDestBuffer,
int64 startSampleInFile,
int numSamples) override
{
if ( (lengthInSamples - startSampleInFile <= 0) || (numSamples > lengthInSamples - startSampleInFile) )
return false;

int32_t numSamplesToCopy = numSamples;
auto readWindow = lastValidRandomInput;
auto channelsToCopy = jmin ((int)numChannels, numDestChannels);

if (GetAudio (readWindow, numOfReportedInputs, GetSrcStart()+startSampleInFile, &numSamplesToCopy) != AAX_SUCCESS)
return false;

int validIn = -1;
for (int i = 0; i < channelsToCopy; ++i)
{
// should forward with i (unless needs to skip)
validIn++;
while ( (readWindow[validIn] == nullptr) && (validIn < numOfReportedInputs) )
validIn++;

if (destSamples[i] == nullptr)
continue;

memcpy (destSamples[i]+startOffsetInDestBuffer, readWindow[validIn], (size_t) numSamples * sizeof (float));
}
return true;
}

private:
void initRandomAccessReader(const float * const inAudioIns[], int numOfActualInputs, int numOfInputsInBuffer)
{
sampleRate = getAAXProcessor().sampleRate;
usesFloatingPointData = true;
lastValidRandomInput = inAudioIns;
numChannels = numOfActualInputs + (GetSideChainInputNum() > 0 ? 1 : 0);
numOfReportedInputs = numOfInputsInBuffer;
lengthInSamples = GetInputRange();
getAAXProcessor().getPluginInstance().setRandomAudioReader(this);
}

void UpdateMainInputBus(int numOfMainInputs)
{
// update plug-in on number of inputs
Expand All @@ -707,6 +760,8 @@ namespace AAXClasses
return static_cast<JuceAAX_Processor&> (*GetEffectParameters());
}

const float* const* lastValidRandomInput;
unsigned int numOfReportedInputs;
bool mIsFirstPass {true};
};
#endif
Expand Down
6 changes: 6 additions & 0 deletions modules/juce_audio_processors/juce_audio_processors.h
Expand Up @@ -54,6 +54,12 @@
#include <juce_gui_basics/juce_gui_basics.h>
#include <juce_audio_basics/juce_audio_basics.h>

#if JucePlugin_EnhancedAudioSuite
#if ! JUCE_MODULE_AVAILABLE_juce_audio_formats
#error To compile random access support plug-in formats, you need to add the juce_audio_formats module!
#endif
#include <juce_audio_formats/juce_audio_formats.h>
#endif

//==============================================================================
/** Config: JUCE_PLUGINHOST_VST
Expand Down
Expand Up @@ -47,6 +47,7 @@ void AudioProcessor::initialise (const BusesProperties& ioConfig)

wrapperType = wrapperTypeBeingCreated.get();
playHead = nullptr;
randomAudioReader = nullptr;
currentSampleRate = 0;
blockSize = 0;
latencySamples = 0;
Expand Down Expand Up @@ -360,6 +361,11 @@ void AudioProcessor::setPlayHead (AudioPlayHead* const newPlayHead)
playHead = newPlayHead;
}

void AudioProcessor::setRandomAudioReader (AudioFormatReader* const newAudioFormatReader)
{
randomAudioReader = newAudioFormatReader;
}

void AudioProcessor::addListener (AudioProcessorListener* const newListener)
{
const ScopedLock sl (listenerLock);
Expand Down
24 changes: 24 additions & 0 deletions modules/juce_audio_processors/processors/juce_AudioProcessor.h
Expand Up @@ -720,6 +720,20 @@ class JUCE_API AudioProcessor
*/
AudioPlayHead* getPlayHead() const noexcept { return playHead; }

//==============================================================================
/** Returns the current AudioFormatReader object that should allow random access
to processed audio (if supported).
You can ONLY call this from your analyseBlock() / processBlock() method!
Calling it at other times will produce undefined behaviour.
The AudioFormatReader object that is returned can be used to get current
audio in a random access manner.
If the host can't or won't provide any time info, this will return nullptr.
*/
AudioFormatReader* getRandomAudioReader() const noexcept { return randomAudioReader; }

//==============================================================================
/** Returns the total number of input channels.
Expand Down Expand Up @@ -1297,6 +1311,13 @@ class JUCE_API AudioProcessor
*/
virtual void setPlayHead (AudioPlayHead* newPlayHead);

//==============================================================================
/** Tells the processor to use this AudioFormatReader object.
The processor will not take ownership of the object, so the caller must delete it when
it is no longer being used.
*/
virtual void setRandomAudioReader (AudioFormatReader* newRandomAudioMapper);

//==============================================================================
/** This is called by the processor to specify its details before being played. Use this
version of the function if you are not interested in any sidechain and/or aux buses
Expand Down Expand Up @@ -1508,6 +1529,9 @@ class JUCE_API AudioProcessor
/** @internal */
AudioPlayHead* playHead;

/** @internal */
AudioFormatReader* randomAudioReader;

/** @internal */
void sendParamChangeMessageToListeners (int parameterIndex, float newValue);

Expand Down

0 comments on commit 1242c86

Please sign in to comment.