Unintended repeating of sound when playback is stopped

void AudioController::ProcessBlock(const AudioSourceChannelInfo& buffer_to_fill) {
    const ScopedLock sl(cs_);
    if (main_processor_ == nullptr) {
        return;
    }

    if (!main_processor_->isSuspended()) {
        auto* output_buffer(buffer_to_fill.buffer);
        if (output_buffer == nullptr) {
            return;
        }

        MidiBuffer dummy;  // not used
        main_processor_->processBlock(*output_buffer, dummy);
    }

    if (!audio_play_head_.IsPlaying()){
        return;
    }

    audio_play_head_.MoveTimeInSamples(buffer_to_fill.numSamples);
}

Here it looks like you are calling main_processor_->processBlock before checking audio_play_head_.IsPlaying(). So IsPlaying could return false but you would still attempt to process audio. Is that the cause of the timing problems?

Thanks for checking the code.

There are many plug-ins out there that play sound even if they are not playing(ex. when a pad in the plug-in is pressed, the assigned sound will be played in a single shot), and such plug-ins will not work properly without this implementation.

In addition, the bug is clearly more than one frame (20-30 frames worth of audio as I listened) of repeated audio, and I don’t believe it is caused by the part you pointed out.
If the part you pointed out was the cause, I believe the bug would be independent of the audio device or OS.

(Edit)
Just to be safe, I moved the check to the beginning of the function to see if it is playing or not, but the bug still occurs.

Anything that might leave some stale data in an audio buffer could affect different hardware in different ways. We don’t know how different hardware vendors will use (or re-use) different areas of memory, which could include the previous 20-30 frames.

We also confirmed that the structure without sub-graphs plays without problems.

Your example is still quite complex, with a fair few places that bugs could be introduced. Would it be possible to reproduce the behaviour in a much simpler app that plays a sine tone? What sub graphs did you need to add? Do you also see the behaviour if you remove the custom playhead?

Would it be possible to reproduce the behaviour in a much simpler app that plays a sine tone?

After replacing the audio loaded in the AudioFileProcessor class with a sine wave, the bug no longer occurs.
I think I finally have a clue. Thanks again and again for your answers!
I’ll keep looking into it!

I found the cause.
I believe that the audio device thread was not working properly because of sleep() in juce::AudioTransportSource::stop().

void AudioTransportSource::stop()
{
    if (playing)
    {
        playing = false;

        int n = 500;
        while (--n >= 0 && ! stopped)
            Thread::sleep (2);

        sendChangeMessage();
    }
}

The other devices worked because the audio device was processed in a separate thread.
With the default audio device on the Apple machine and macOS, it seems that the device processing was also being done in the audio thread, and the processing did not proceed properly.
I have found that my implementation does not have any problem without calling the stop() function, so I will modify my implementation to not use the stop() function.

Thanks for the days of consulting with me!

I think it is important to check where you are calling stop() from:
It is intended to be called from the message thread or any low priority thread, because it intentionally blocks until the audio thread had a chance to finish the last call.

The code you posted above (AudioTransportSource::stop()) will check that stopped flag and has a timeout of 1 second, e.g. if the AudioTransportSource is not connected or the connected device is stopped.

I am not sure, but maybe a macro JUCE_ASSERT_MESSAGE_THREAD would help to catch those errors early on, but that would prevent calling stop() from other non-realtime threads.

Thanks for comment.
This bug was caused by calling the stop() function in AudioProcessor::processBlock().
I also thought it needed to be checked with something like the JUCE_ASSERT_MESSAGE_THREAD macro in stop().
But I don’t know how to fix it properly.

@t0m
Do you plan to implement any improvements?

If you want to trigger start() and stop() synchronously from the audio thread (which is what you do when calling from processBlock()) I would recommend not to use AudioTransportSource at all but implement your own class.

Like I wrote before, adding the JUCE_ASSERT_MESSAGE_THREAD would give you a heads up, but it doesn’t solve your problem.

That’s the only way to do it. I will implement it in that direction this time.