Code review : Code works fine individually, but causes audio dropouts when you reach a certain point

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?

I solved it myself. The cause was completely unrelated to anything to do with audio.