I combined the code from AudioRecordingDemo with LoopingAudioSampleBufferAdvancedTutorial. The application allows me to add recordings of my voice when I press “Record”, loop it when I press “Loop”, and it sets up the next loop. It all works fine up to a point. When I record between four and eight loops the UI locks up and the audio has gaps. I’ve narrowed it down to the following two methods:
void LoopRecorder::playLooped()
{
juce::String filePathToOpen;
{
const juce::ScopedLock lock(pathMutex);
filePathToOpen.swapWith(chosenPath);
}
if (filePathToOpen.isNotEmpty())
{
currentPosition = 0;
juce::File file(filePathToOpen);
std::unique_ptr<juce::AudioFormatReader> reader(formatManager.createReaderFor(file));
if (reader.get() != nullptr)
{
// After the first loop is done recording, set the master loop length.
// This is used to truncate any other loops in audioDeviceIOCallback.
if (globalState.getDefaultLoopLength() == 0)
{
globalState.setDefaultLoopLength(reader->lengthInSamples);
}
auto duration = (float)reader->lengthInSamples / reader->sampleRate;
ReferenceCountedBuffer::Ptr newBuffer = new ReferenceCountedBuffer(file.getFileName(),
(int)reader->numChannels,
(int)reader->lengthInSamples);
reader->read(newBuffer->getAudioSampleBuffer(), 0, (int)reader->lengthInSamples, 0, true, true);
{
const juce::SpinLock::ScopedLockType lock(mutex);
currentBuffer = newBuffer;
postionCount = reader->lengthInSamples;
}
buffers.add(newBuffer);
triggerAsyncUpdate();
}
}
}
void LoopRecorder::getNextAudioBlock(const juce::AudioSourceChannelInfo& bufferToFill)
{
auto retainedCurrentBuffer = [&]() -> ReferenceCountedBuffer::Ptr
{
const juce::SpinLock::ScopedTryLockType lock(mutex);
if (lock.isLocked())
return currentBuffer;
isCurrentlyPlaying = false;
return nullptr;
}();
if (retainedCurrentBuffer == nullptr)
{
bufferToFill.clearActiveBufferRegion();
isCurrentlyPlaying = false;
return;
}
isCurrentlyPlaying = true;
auto* currentAudioSampleBuffer = retainedCurrentBuffer->getAudioSampleBuffer();
auto position = retainedCurrentBuffer->position;
auto numInputChannels = currentAudioSampleBuffer->getNumChannels();
auto numOutputChannels = bufferToFill.buffer->getNumChannels();
auto outputSamplesRemaining = bufferToFill.numSamples;
auto outputSamplesOffset = 0;
while (outputSamplesRemaining > 0)
{
auto bufferSamplesRemaining = currentAudioSampleBuffer->getNumSamples() - position;
auto samplesThisTime = juce::jmin(outputSamplesRemaining, bufferSamplesRemaining);
for (auto channel = 0; channel < numOutputChannels; ++channel)
{
bufferToFill.buffer->copyFrom(channel,
bufferToFill.startSample + outputSamplesOffset,
*currentAudioSampleBuffer,
channel % numInputChannels,
position,
samplesThisTime);
// Fill the individual layer's meter.
juce::Range<float> rng(0.0f, 1.0f);
rng = bufferToFill.buffer->findMinMax(channel, 0, bufferToFill.numSamples);
auto min = -(rng.getStart());
auto max = rng.getEnd();
auto volume = (min < max) ? max : min;
// Process the pan pots.
if (numOutputChannels > 0)
{
// Not mono.
if (! (channel % 2))
{
// The left channel of a pair.
inputLevelLeft = (volume * 4.0f) * juce::jmin(1 - panValue, 1.0);
}
else
{
// The right channel of a pair.
inputLevelRight = (volume * 4.0f) * juce::jmin(1 + panValue, 1.0);
}
}
else
{
// Mono channel.
auto angle = juce::jmap((float)panValue, -1.0f, 1.0f, 0.0f, juce::float_Pi * 0.5f);
inputLevelLeft = std::cos(angle);
inputLevelRight = std::sin(angle);
}
}
// Check if muted and apply gain.
(isMuted) ? bufferToFill.buffer->applyGain(0.0f) : bufferToFill.buffer->applyGain(1.0f);
if (loopAndLayerName == "Layer 1.1")
{
globalState.setLoopPosition(currentPosition);
}
// End of processing for samples this time.
outputSamplesRemaining -= samplesThisTime;
outputSamplesOffset += samplesThisTime;
position += samplesThisTime;
currentPosition = position;
// Let the UI know what we are doing with the position..
updateOnMessageThread(*this);
// The end of the loop has been reached.
if (position == currentAudioSampleBuffer->getNumSamples())
{
currentPosition = position = 0;
++loopCount;
updateOnMessageThread(*this);
}
}
retainedCurrentBuffer->position = position;
}
It all works fine commenting out the above, only it doesn’t loop. What am I missing?