Interesting zero-gain audio glitch

I’m having an audio glitch (short scraping sound) when loading audio. I’ve tried to mitigate this via the following process:

  1. audio load event is triggered
  2. ramp gain of output down to zero (interpolated per sample)
  3. load audio file into memory
  4. ramp gain of output up to 1.0 again (interpolated per sample)

The weird thing is that I’m still getting an audio glitch even though the gain of buffer is zero whilst the audio is being loaded into memory. I would’ve thought that setting the gain to zero would eliminate this. I can hear the output audio volume ramping down to zero before the glitch happens, then ramping up again.

Does anybody have any idea of what’s going on and/or how to remedy this?

It seems to me you are loading the sample in your audio callback.

Yes I am, how else should I call it? In a timer in the processor?

Load it on your editor or background thread and then pass it to your processor when ready : )

1 Like

I.e. the glitch while muted — you’ll still cause dropouts if you block the processor callback for too long, even if muted

1 Like

As @jakemumu says, doing things lie this on the audio thread are likely to cause issues. In general, you shouldn’t do anything which you don’t know to be lock-free on the audio thread. This includes reading from disk, allocating memory, and a lot more.

This is the first thing I make new audio developers read, maybe it will help you in the future as well Ross Bencina » Real-time audio programming 101: time waits for nothing

1 Like

Thanks.

I can’t run it in the editor as I need this code to work when the editor is closed.

Regarding threads, I’ve always just relied on the editor vs processor architecture and have never had to manually create a separate thread.

  1. does using a timer in the processor constitute a background thread?
  2. if not, could you point me in the right direction regarding creation of a background thread, since it’s new to me

Cheers

Before going down that road what is the action which triggers the sample to load?

All of these require loading up to 2 minutes of audio:

  1. loading state (not via gui)
  2. switching preset (via gui)
  3. switching audio file (via gui)

The weird thing is, I never had an issue with this in the past. The audio glitch seemed to get bad after updating to the latest version of JUCE (the previous version I was on was from May 2019).

Also, I did attempt moving this code (the gui parts) into the editor but I was having issues (audio cutting out) upon calling

reader->read(buffer, 0, numberFilledSamples, 0, true , true );

…so I scrapped that at the time. I didn’t investigate further as I wanted to get this working in the processor.

Hmm it doesn’t make much sense that would cause an audio dropout when called via the editor. You don’t need to actually put the code in your editor, but just call the function from your editor.

That all sounds pretty typical and if the sample loading is always triggered via user interaction the only reason to move it to a background thread would be to stop your UI from hanging during the operation.

For instance a single file can usually load without too much fuss. If you need to load a lot of audio files then a thread pool can be the way to go so you can make it parallel. If you’re getting dropouts I’d audit what’s happening with the architecture before moving to threads.

Try placing a print of isThisTheMessageThread in the function to double check what thread is calling it. Also make sure you setup a queue to swap the already loaded sample during the processing loop, or if acceptable, simply call the process lock for a very short scope to do the swap which can work if the sample is already loaded and you’re just switching the pointer to the sample.

The general point is make sure the loading part doesn’t happen on the audio thread during real time portions, and when you swap make sure it’s very fast so you don’t interrupt. I wouldn’t worry about preset load via set state information since generally the entire daw is in a loading state during that

I think some of the confusion here is around what the thread we’re talking about does – it’s the message thread. It exists whether or not there are editors attached to a processor. When an editor is open, it does call the drawing methods for it, but it exists with or without an editor.

In fact, whether the code is in the editor or processor classes is entirely unrelated to what thread it’s called in – you can have a processor method called from the editor on the Message thread, or an editor method called from the audio thread (although you should be careful going this direction).

If you put a method in your processor which loads the audio you need, you can call that method from anywhere which is executed on the message thread. In practice, as long as the method isn’t called from ProcessBlock() or any method called by it you’re probably fine.

For switching presets or audio files, or for loading state – all things which can be expected to take some time – in many cases it is appropriate to use the AudioProcessor::suspendProcessing(bool) method to ensure that the operation doesn’t cause glitches on the processing callback. If you want to arbitrarily load a file from the message thread, you have a lot of options. juce::Timer regularly calls a virtual method on the message thread (derive your processor from Timer and implement the callback), as do calls to ActionListener::actionListenerCallback and ChangeListener::changeListenerCallback.