in pedalboard/io/AudioStream.h [295:391]
virtual void audioDeviceIOCallback(const float **inputChannelData,
int numInputChannels,
float **outputChannelData,
int numOutputChannels, int numSamples) {
// Live processing mode: run the input audio through a Pedalboard object.
if (playBufferFifo && recordBufferFifo) {
for (int i = 0; i < numOutputChannels; i++) {
const float *inputChannel = inputChannelData[i % numInputChannels];
std::memcpy((char *)outputChannelData[i], (char *)inputChannel,
numSamples * sizeof(float));
}
auto ioBlock = juce::dsp::AudioBlock<float>(
outputChannelData, numOutputChannels, 0, numSamples);
juce::dsp::ProcessContextReplacing<float> context(ioBlock);
juce::SpinLock::ScopedTryLockType tryLock(livePedalboardMutex);
if (tryLock.isLocked()) {
for (auto plugin : livePedalboard.getPlugins()) {
std::unique_lock<std::mutex> lock(pedalboard->mutex,
std::try_to_lock);
// If someone's running audio through this plugin in parallel
// (offline, or in a different AudioStream object) then don't corrupt
// its state by calling it here too; instead, just skip it:
if (lock.owns_lock()) {
plugin->process(context);
}
}
}
} else if (recordBufferFifo) {
// If Python wants audio input, then copy the audio into the record
// buffer:
for (int attempt = 0; attempt < 2; attempt++) {
int samplesWritten = 0;
{
const auto scope = recordBufferFifo->write(numSamples);
if (scope.blockSize1 > 0)
for (int i = 0; i < numInputChannels; i++) {
std::memcpy(
(char *)recordBuffer->getWritePointer(i, scope.startIndex1),
(char *)inputChannelData[i],
scope.blockSize1 * sizeof(float));
}
if (scope.blockSize2 > 0)
for (int i = 0; i < numInputChannels; i++) {
std::memcpy(
(char *)recordBuffer->getWritePointer(i, scope.startIndex2),
(char *)(inputChannelData[i] + scope.blockSize1),
scope.blockSize2 * sizeof(float));
}
samplesWritten = scope.blockSize1 + scope.blockSize2;
}
if (samplesWritten < numSamples) {
numDroppedInputFrames += numSamples - samplesWritten;
// We've dropped some frames during recording; not great.
// Favour capturing more recent audio instead, so clear out enough
// space in the buffer to keep up and retry:
const auto scope = recordBufferFifo->read(numSamples);
// Do nothing here; as soon as `scope` goes out of scope,
// the buffer will now allow us to append `numSamples`.
} else {
break;
}
}
} else if (playBufferFifo) {
for (int i = 0; i < numOutputChannels; i++) {
std::memset((char *)outputChannelData[i], 0,
numSamples * sizeof(float));
}
const auto scope = playBufferFifo->read(numSamples);
if (scope.blockSize1 > 0)
for (int i = 0; i < numOutputChannels; i++) {
std::memcpy((char *)outputChannelData[i],
(char *)playBuffer->getReadPointer(i, scope.startIndex1),
scope.blockSize1 * sizeof(float));
}
if (scope.blockSize2 > 0)
for (int i = 0; i < numOutputChannels; i++) {
std::memcpy((char *)(outputChannelData[i] + scope.blockSize1),
(char *)playBuffer->getReadPointer(i, scope.startIndex2),
scope.blockSize2 * sizeof(float));
}
} else {
for (int i = 0; i < numOutputChannels; i++) {
std::memset((char *)outputChannelData[i], 0,
numSamples * sizeof(float));
}
}
}