Q: Channel Swapping segfaults (Linux/Jack) and best practices for Audio Apps?

Hello Juce team and community. I want to start off by saying thanks to the devs for your work on this software. You guys have probably helped me reduce months of work into what will hopefully result in a functional prototype for my audio app in the next week or so. I’ve only been at the Juce development stage for a few days, and I’m pretty satisfied with the progress I’m making.

Now on to Business:

I’m new to Juce and C++, and I was using some snippets from the “Processing Audio Input” tutorial. In the first line in the body of getNextAudioBlock(), you guys initialize a pointer to deviceManager.getCurrentAudioDevice().

  • It seems when a juce project is executed while the linux jack server is running, there is quite a delay in start up. The audio device pointers seems to be null when the audio app component first calls getNextAudioBlock(), and this causes a segfault.

I wrapped device in an if statement, and things were running smoothly. At this point, I can pass an input buffer sample to the AudioVisualiserComponent and see a waveform, so I’m know I’m getting data through. Great! Now if I start playing around with the input and output channel options produced by AudioSelectorComponent, I get three problems.

  1. The gui is not responsive after disabling all channels. E.g. after turning off all output channels it becomes difficult to turn them back on.

  2. Toggling the channels on and off inside the project eventually causes a segfault.

  3. Toggling the connections in jack eventually causes the error “AddressSanitizer: heap-use-after-free”

Currently, I’m reading up on c++/juce debugging, coredumps, etc. and trying to figure out how to help you help me now and in the future in you’re so inclined. If it matters, my project deviates from the default due to me adding -fsanitize=address compiler and linker flags in my linux makefile, and I added the JUCE_JACK definition to the juce_audio_devices module.

All of this leads to my final questions:

  • What is the preferred way of showing debug info to you all? I’ve seen valgrind and gdb mentioned.

  • Should I be guarding every pointer with an if statement before use? The audio processing tutorial didn’t show this idiom, but this seems to be the thing to do in C++.

  • Are there any general best practices when dealing with audiobuffers, midi buffers, audio devices and gui updating? Now that I’ve gotten this far, I get the feeling I’m going to be running into more memory issues than anything else as I proceed.

I can see from a few gdb sessions that multiple threads are started up when the binary is started. I can imagine there would be some issues between the gui sending messages to turn audio device features on and off, and actual processing of the audio data, but async programming isn’t really my forte, so I’m not sure how to proceed.

Thanks for the comprehensive bug report. I tried to repro the issue with the latest develop under Manjaro 20. I started JACK, then built the DemoRunner app with ASAN and JUCE_JACK enabled and ran it. Then, I tried switching the outputs between ALSA and JACK, and toggling the output channels on and off. I didn’t see any sluggish UI issues, or any segfauts/heap-use-after-free.

I’d be interested to know which OS you’re using, and also whether the issues you’re seeing are present in the DemoRunner, or only in the tutorial code.

In this case, it’d be useful to see the full ASAN output when it complains about a heap-use-after-free.

Hi Reuk,

I was playing around with everything this morning, and I have some updates for you.

You can find my source code at the link below, with some error logs in the LinuxMakefile path:

OS info
Primary: Ubuntu 18.04 LTS with manually installed jack/qjackctl
Secondary: Ubuntu 20.04 LTS - ubuntu studio with everything auto installed

Seg Faults
These come in two varieties it seems.

  1. I have the AudioVisualiserComponent active with jack running and selected as my device, and altering the channels eventually causes a segfault/ memory misalignment issues. I think I’m just using the visualiser component incorrectly.

  2. I have the AudioVisualiserComponent inactive with Alsa selected as my device, and I try to switch outputs from channel 1 to channel 2 within the AudioSelectorComponent gui. I specify a single output and input channel when I create my audio app, so this is moving to channels that don’t exists?

heap use after free
This happened when connecting to the AudioSelectorComponent or Settings view of my app. I played a sound and I could see the input sound meter respond. I played the sound again, and switched away from the settings tab, and the error occurred. This seems to be a product of my use of tabs. The demorunner doesn’t have this issue.

Delayed started up
I moved my project over to a fairly fresh install of ubuntu studio 20.04, and it also had a delay when qjackctl was running with the jack server started. The demo runner has this delay on both of my machines as well. It’s approximately 5 seconds.

Project description and possible idiosyncracies if it helps
My project makes use of:
AudioApp - the MainComponent with a unique ptr holding an AudioSelectorComponent and a variable holding the AudioDeviceManager
TabComponent - inherited TabbedComponent with constructor placing the tabs on top
A midi modes tab - inherited Component
A settings tab - AudioSelectorComponent passed to addTab()
A visuals tab - with an AudioVisualiserComponent child

The Audio app holds the tab component and its 3 tabs. The 3 tabs are passed to the tab component during app initialization. I wanted access to changes in the settings, and I also wanted to be able to update the visuals from inside the main component, so I thought this would be the way to go.

Additionally, I set this app to use 1 input and 1 output, and I set the max in and out channels for AudioSelectorComponent to 1 as well.

I think I may have replied to the main topic instead of you directly.

I know I can be wordy, so I’ll try to be brief here. The reason the DemoRunner seemed to not have an issue while my app did, was because I was looping through channels in the getNextAudioBlock callback while seemingly activating channels that don’t exists for the loop in the AudioSelectorComponent. If I raised the number of channels passed to my AudioDeviceManager, I could select more output channels before causing a crash.

The only question this raises for me as a newbie is why does the AudioSelectorComponent show more output channels for selection than what was requested in the AudioDeviceManager ?

Sorry for the delay in getting back to you. Unfortunately I’m not going to have time to investigate this for a couple of weeks, but I’ll take another look when I get a chance.

No worries. For now the solution is to avoid altering the channels, which is fine for my current purposes. I’ll keep a look out for any updates.

If it’s any help, there may be an issue with an assertions in juce_AudioSamplebuffer.h, and any logic that relies on it.

There are multiple lines calling

jassert (isPositiveAndBelow (channelNumber, numChannels));

numChannels seems to return the result from BigInteger.countNumberOfSetBits(). If this is true, consider this scenario.

I have turned off the first output channel so:

  • active output channels = 2,3,4,5
  • active output indices = 1,2,3,4
  • numChannels = 4

when referencing channel 5, channelNum = index 4, won’t this assertion fail?