Standalone version of my plugin with a native title bar
Place a button in my app that displays the same AudioMidi dialog that the normal Standalone has
But because all the code and classes to display that dialog are private to the StandalonePluginWindow, I have to roll my own, unless I’m missing something.
And to do that, I have to get the AudioDeviceManager from the StandalonePluginHolder with this piece of ugly code:
And then later when I try to recreate the AudioMidiSettings dialog,I get a crazy number for the number of available devices.
const OwnedArray<AudioIODeviceType>& types = deviceManager->getAvailableDeviceTypes();
int num = types.size(); // returns 167904
Am I missing something?
I’d much rather not recreate all that code for that AudioMidiSettings dialog, but it seems like I’m forced to and then I’m getting nonsense from the AudioDeviceManager.
I got around this by overriding the Standalone wrapper (which we’re strongly encouraged to do anyhow, and I assume you’re doing) and then including the .cpp in Processor.cpp and Editor.cpp. In this way one can access the Standalone methods directly, and one can just do all the housekeeping e.g. MIDI settings once, and the processor (and subsequently the editor) are aware of it.
I’m sure there’s a better way, but I’m a UI programmer, not a C++ Wizard like some here (“I only do eyes”) and this way seems to work.
Okay, I’m going to use our Axon 2 for iOS product as an example.
Step 1: Override [edit: replace, I mean] juce_StandaloneFilterWindow.h and juce_StandaloneFilterApp.cpp. Since the only time we use a standalone is on iOS, I made StandaloneApp.h and StandaloneApp.mm. Copy the contents of the juce library files in to these two, and change the include in the .mm (or .cpp if that’s how you roll) and in one of the usual places, define JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1. After you’ve done these things, and build, your app will be using your standalone wrapper instead of the generated one.
In PluginProcessor.cpp and PluginEditor.cpp and any other components that need access, include StandaloneApp.mm (or .cpp, whichever you used), not .h.
You can now access call methods created in StandaloneApp.mm in your processor or editor.
Here is a trigger in StandaloneApp.mm that opens the audio settings:
In your editor, you can just have, in buttonClicked or whatever:
if (JUCEApplication::isStandaloneApp()) axon_OpenSettings();
And then in StandaloneApp.h, you can have your way with that. I am fairly convinced there is a far better way to do this, but for my needs it works fine.
(Side note: if you make your standalone .mm, everything that touches it needs to compile as Obj-C++, so if you’re not using iOS-specific calls like BlueTooth MIDI or whatever, you might want to stick to .cpp)
You say to "override juce_StandaloneFilterWindow.h and juce_StandaloneFilterApp.cpp"but by copying them, it sounds like you really mean copy and rewrite instead of override in the traditional C++ case, right?
Heh, yes. I’ve never taken any programming courses, so I get the terminology wrong all the time. I mean “replace entirely.” I don’t think you can just override the methods in those, as they’re private.
Hey JUCE team, do you think we can get some better documentation or an example for this? None of this is trivial and straightforward and there seems to be quite a few places for stumbling. Thank you.
I’ve included MyStandaloneApp.cpp in my editor class (as an #include “MyStandaloneapp.cpp” and added an openSettings function like yours inside StandaloneFilterApp class, but when I add
if (JUCEApplication::isStandaloneApp()) myOpenSettings();
I get and error: Use of undeclared identifier: myOpenSettings().
You can now access call methods created in StandaloneApp.mm in your processor or editor.
…but there’s no way that the editor class can see inside the StandaloneApp class to call that function…unless you have some glue that I don’t know about.
I have just done this thing this very morning - both of you, thanks for your help!
@pizzafilms, Make sure to import #import "StandaloneFilterWindow.h" in your PluginEditor.m where you have your open settings function. StandalonePluginHolder::getInstance() is a global method that can be called anywhere as long as you have the import.
So, for those following along at home, this is how to use your own StandaloneWindow and still call the showAudioSettingsDialog from inside the editor:
Make copies of juce_StandaloneFilterApp.cpp and juce_StandaloneFilterwindow.h and place them in your source tree.
In your StandaloneFilterApp.cpp, comment out the chunk of #includes at the top and replace them with #include <JuceHeader.h>
In your StandaloneFilterApp.cpp, change #include "juce_StandaloneFilterWindow.h" to #include "StandaloneFilterWindow.h"
In your editor class, add: #include "StandaloneFilterApp.cpp and include "StandaloneFilterwindow.h"
In your jucer file, in Preprocessor Definitions, add: JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1
Style your window however you like in StandaloneFilterWindow.h
If you want to show the audioSettingsDialog from your own editor code instead of the Options button that’s in the original, use this from inside your editor:
Thank you @pizzafilms for this summary. We should make it easier to use a custom version of the StandaloneFilterWindow.
For many, it may be easier to just roll your own Application and Window. Luckily doing this is just as easy as creating any standalone GUI app with JUCE. Just create your GUI app as you would any other JUCE standalone GUI app but add JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1 to the list of preprocessor defines in the Projucer.
Thank you @fabian, I definitely agree that JUCE should have an easier way to make a StandaloneFilterWindow, but I would think that considering that all/most of the necessary functionality is already there, it’s more an issue of having some hooks to modify it and less of a desire to start allover from scratch.
In my quest for that desired additional functionality, I hit some major roadblocks trying to go fullscreen. Please take a look at the more complete post here: Standalone issues
Yes, we’ve seen that post and discussing this a bit internally. I’m currently quite consumed with the following one, so I probably won’t have time to answer your other post today.
Thanks for the great tutorial. I tried it and got a linker error:
ndefined symbols for architecture x86_64:
"juce_CreateApplication()", referenced from:
_main in include_juce_audio_plugin_client_Standalone.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Somehow the linker does not find the class that derivates from JUCEApplication.
I did set JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1. Is there something else i have to do?
I was able to fix it. I just had to add this line:
Yes, thanks for the great tutorial. In Juce 6.0.5 I am getting a linker error as well. Ensured that JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1 and START_JUCE_APPLICATION(StandaloneFilterApp) is added.
2>include_juce_audio_plugin_client_Standalone.obj : error LNK2019: unresolved external symbol “class juce::JUCEApplicationBase * __cdecl juce_CreateApplication(void)” (?juce_CreateApplication@@YAPEAVJUCEApplicationBase@juce@@XZ) referenced in function WinMain
2>C:\Users\mitch\Desktop\Builds\VisualStudio2019\x64\Debug\Standalone Plugin\HLConvolver.exe : fatal error LNK1120: 1 unresolved externals
Looking in juce_audio_plugin_client_Standalone.cpp I see that:
extern juce::JUCEApplicationBase* juce_CreateApplication(); says Function definition for juce_CreatApplication() not found.
Not sure if related but JUCE_MAIN_FUNCTION_DEFINITION gave a warning C2851: Inconsistent annotation from WinMain. See c:\program files(X86)\windows kits\10\include\10.0.18362.0\um\winbase.h(933).