Hi, I have JUCE GUI application that I now need to run on a Linux server (no desktop installed). Is there some way I can do this? The GUI is really only used for displaying during debug/analysis, so I need it, but I’d also like to be able to run with those displays disabled. I can easily disable my program from generating any GUI; but when I try to do that on a system that doesn’t have a desktop, I still get
“Failed to connect to the X Server”.
Do I need to step back and create a different project in console mode or is there some other way?
Thanks,
The “Failed to connect to the X Server.” message implies that you are using an older version of JUCE. This error message was removed and some improvements were made to headless support since JUCE 6. Please pull the latest and try that.
Thanks, the only reason I have an old version (I inherited this code) is because when I run
with the latest, there are issues with loadImpulseResponse()…
I converted from (File,bool, bool, size_t, bool) to (File, Stereo, Trim, size_t, Normalise)
so things would build with latest JUCE tree and now it crashes…
Still trying to figure that out.
Where are you calling loadImpulseResponse()
? Note the docstring for the Convolution class:
Threading: It is not safe to interleave calls to the methods of this class. If you need to load new impulse responses during processing the
load
calls must be synchronised withprocess
calls, which in practice means making theload
call from the audio thread. TheloadImpulseResponse
functions are wait-free and are therefore suitable for use in a realtime context.
You should try to avoid calling loadImpulseResponse
from the message thread, and should instead call it in prepareToPlay
or processBlock
.
Thanks… its called right at startup, so I think its safe.
UPDATE:
Is this thread safety warning something that is new to a later version of JUCE?
Note that this is not a problem when I use the older version.
Just to be clear, I changed
highPassFilt.loadImpulseResponse(workDir.getChildFile("highPassFilters").getChildFile(filename),
false,
false,
length,
false);
to…
highPassFilt.loadImpulseResponse(workDir.getChildFile("highPassFilters").getChildFile(filename),
dsp::Convolution::Stereo::no,
dsp::Convolution::Trim::no,
length,
dsp::Convolution::Normalise::no);
and then built with the latest JUCE.
The convolution was updated in May 2020 to make it realtime-safe. As a side-effect, the thread-safety guidelines for the class had to change at the same time.
The change you made looks reasonable. It’s difficult to know what’s causing the problem without seeing more of the code, but I’d recommend starting by ensuring that loadImpulseResponse
is not called concurrently with any other Convolution
member functions.
Well, the backtrace shows that it does crash in
juce::dsp::Convolution::Impl::processSamples
which is called as part of getNextAudioBlock() but the calls to loadImpulseResponse() were done prior to that (at startup).
What do you mean by “at startup”? If you are calling these from the message thread in a location that might be called concurrently with the process method (e.g. in your editor’s constructor or similar), that may cause problems.
It might be worth building and running with Thread Sanitizer (add -fsanitize=thread to your compile/link flags) to make sure that the crash isn’t due to a data race.
It was in MainComponent::MainComponent(), but I also tried (based on your earlier comments) moving it to MainComponent::prepareToPlay(). Didn’t make a difference. I’ll see what -fsanitaze=thread does…
Thanks for your patience,
I have not been able to eliminate the crash, so I took a step back to deal with my original question…
can I modify my code so that it can run a Linux system that doesn’t have a desktop?
Since the above change to the loadImpulseResponse
function allows me to build with latest JUCE, I figured I would just try to run the same program in a non-desktop system…
It crashed immediately (much sooner than the crash we’ve been discussing); so in Main.cpp I tried commenting out…
mainWindow.reset (new MainWindow (getApplicationName()));
as suggested by a different forum post, but that just causes it to hang. Strangely, that is actually encouraging because the prior build (with older JUCE) would give me the
“Failed to connect to the X Server”
error. Bottom line, it seems I should stick with latest JUCE and figure out what is causing this crash (which will then hopefully lead me to an executable that can run without a desktop).
By the way, I just tried building in Debug mode instead of Release and that gave me a slew of assertion failures in the bowels of JUCE. Is that normal?
No, assertions are normally used to catch misuse of JUCE APIs, or to indicate that something unexpected happened. In general your program shouldn’t trigger any assertions.
There’s a good chance that fixing the assertions will also resolve the other issues you’ve been seeing, so I’d recommend trying to work out why the assertions are firing. Often, an assertion will have a descriptive comment in the source code explaining the problem and suggesting potential solutions, so make sure to look at the file and line number that’s mentioned in the assertion output.
It sounds like you are dealing with two problems at once edsut, trying to deal with the GUI aspect of things, but also getting your convolution to work correctly.
Attempting to run a GUI in a Linux environment without Xorg will not work, and this will probably interfere with getting your convolution working.
For JUCE 6 you should check How to headless build on Linux using JUCE 6.x.x and CMake and for JUCE 5, Elk Audio made a few changes to their fork to enable headless builds you could refer to: GitHub - elk-audio/JUCE: The JUCE cross-platform C++ framework
As for calling loadImpulseResponse, you’d actually want to do it only in two places, either reset() (if overridden) or processBlock(). You can call it indirectly via a method that takes an AudioParameter value as an argument, for example, that is only different than before if the user selected a new impulse response to load. That way even though the method calling loadImpulseResponse is called every time processBlock() is called, it exits early unless the parameter is different than what it was before.
@reuk
Note that the assertion failures only occur with the new version of JUCE. When I build with 5.4.3, things work just fine. Its just a single assertion failure that happens repeatedly till it eventually crashes. While it seems it should be easy to trace this I haven’t been able to make sense of it yet.
Can you post the jassert ideally as link to github? The forum does a nice job inlining the code snippet
(click on the line in github and copy the URL into a single line)
@kcoul,
I got the impression from a few other forum posts that I would be able to option-out all of the GUI
code and then be able to essentially run a console version of just the audio processing portion.
Is that a bad assumption?
Also, keep in mind that the entire application works when I build with JUCE 5.4.3; I only attempted to the jump to JUCE 6 based on ed95’s suggestion (above) that it may help my situation.
Thanks for all the pointers, I’d like to stick with JUCE 6; but may have to retreat if I can’t figure out this crash…
The calls to loadImpulseResponse() are all done in MainComponent constructor. getNextAudioBlock() processes the audio, but doesn’t do anything until a flag is set indicating that initialization (to include the calls to loadImpulseResponse()) has completed.
Ok, that is actually unrelated to the X11 question. You are accessing samples outside the allocated memory, that explains the crash sooner or later.
You have to work the call stack backwards, why you end up reading or writing outside…
I think you’ll be able to get JUCE 6 working just fine. And yes, I think the first link I shared implies that so long as you don’t use the GUI modules you should be fine. The key sentence in that link from ed95 was “If you are building a JUCE application for Linux using JUCE 6 then the same executable can run on systems both with and without the X11 libraries.”
I guess I’m curious about the exact recommended structure, supposing that one is using CMake (are you edsut?). I think it’s the way to go on Linux without a doubt (although I have compiled Projucer and DemoRunner on both Linux Desktop and Raspberry Pi successfully recently).
As far as I can understand, you would need two totally different CMakeLists.txt, or if you had only 1 you would need conditional logic within it to decide whether or not to include the GUI modules. If you used a cached variable, it could be made visible to any code that needs to decide whether to do anything GUI-related.
I guess an example project that showed the simplest way to build with-GUI and headless from the same CMake project would be useful!
As for your call to loadImpulseResponse(), I think that you may need to move your call into getNextAudioBlock() (the app equivalent of processBlock()).
As far as I know the constructor for MainComponent doesn’t count as being on the audio thread
@daniel,
Right… originally this was a “can I run a JUCE application with all gui-related stuff opted out in a headless system?”. That led me down the “update JUCE” path, and that’s when the crash came into the picture.