Old 04-27-2021, 07:36 PM   #81
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 396
Default

I have managed to patch in support for the VST3 interfaces REAPER needs (to use the REAPER native API, and to embed plugin UI's in the TCP/MCP) to the JUCE source.

(With much help from Xenakios and Robbert-VDH)

Here is the patch set:

juce_AudioProcessor.h.patch
Code:
diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/modules/juce_audio_processors/processors/juce_AudioProcessor.h
index ad9bc4367..afb17c7cf 100644
--- a/modules/juce_audio_processors/processors/juce_AudioProcessor.h
+++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.h
@@ -23,6 +23,19 @@
   ==============================================================================
 */
 
+#ifdef JUCE_VST3_ENABLE_PASS_HOST_CONTEXT_TO_AUDIO_PROCESSOR_ON_INITIALIZE
+namespace Steinberg {
+        class FUnknown;
+        namespace Vst {
+            class IHostApplication;
+        }
+}
+namespace juce {
+    class JuceVST3EditController;
+	class JuceAudioProcessor;
+}
+#endif
+
 namespace juce
 {
 
@@ -71,6 +84,13 @@ protected:
     }
 
 public:
+#ifdef JUCE_VST3_ENABLE_PASS_HOST_CONTEXT_TO_AUDIO_PROCESSOR_ON_INITIALIZE
+    virtual void handleVST3HostContext(
+        Steinberg::FUnknown* hostContext,
+        Steinberg::Vst::IHostApplication* host,
+	    JuceAudioProcessor* comPluginInstance,
+	    JuceVST3EditController* juceVST3EditController) {};
+#endif
     //==============================================================================
     enum ProcessingPrecision
     {
juce_VST3_wrapper.cpp.patch
Code:
diff --git a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
index bb2ae6535..df37b6bf7 100644
--- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
+++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
@@ -2116,6 +2116,16 @@ public:
         if (host != hostContext)
             host.loadFrom (hostContext);
 
+    	
+#ifdef JUCE_VST3_ENABLE_PASS_HOST_CONTEXT_TO_AUDIO_PROCESSOR_ON_INITIALIZE
+        getPluginInstance().handleVST3HostContext(
+           hostContext,
+           this->host.get(),
+           this->comPluginInstance.get(),
+           this->juceVST3EditController.get()
+        );
+#endif
+
         processContext.sampleRate = processSetup.sampleRate;
         preparePlugin (processSetup.sampleRate, (int) processSetup.maxSamplesPerBlock);
This allows you to implement a method called handleVST3HostContext() on your PluginProcessor that you must override from the base juce::AudioProcessor method.

Here's an example, which uses the pointer to the host to cast to the interface (it creates a reference-counted COM Smart Pointer underneath, like manually calling .queryInterface()) and grabbing "reaper.ShowConsoleMsg()" then popping up a message on init:

Code:
#include "PluginProcessor.h"
#include "PluginEditor.h"

#include "./ReaperVST3InterfaceWrapper.hpp"

//==============================================================================
AudioPluginAudioProcessor::AudioPluginAudioProcessor()
    : AudioProcessor(
          BusesProperties()
              .withInput("Input", juce::AudioChannelSet::stereo(), true)
              .withOutput("Output", juce::AudioChannelSet::stereo(), true)
      ) {
}

AudioPluginAudioProcessor::~AudioPluginAudioProcessor() {}

void AudioPluginAudioProcessor::handleVST3HostContext(
  Steinberg::FUnknown* hostContext,
  Steinberg::Vst::IHostApplication* host,
  juce::JuceAudioProcessor* comPluginInstance,
  juce::JuceVST3EditController* juceVST3EditController) 
{
  // Credit: Robbert van der Helm (https://github.com/robbert-vdh)
  auto reaper = FUnknownPtr<IReaperHostApplication>(hostContext);
  auto showConsoleMsg = static_cast<void(*)(const char* msg)>(reaper->getReaperApi("ShowConsoleMsg"));
  showConsoleMsg("Test from IReaperHostApplication");
  return;
}
The VST3 interface for TCP FX embedding can be constructed in the same way that the <IReaperHostApplication> is, like below:
Code:
FUnknownPtr<IReaperUIEmbedInterface> reaper_ui_embedder(juceVST3EditController);
Where that "reaper vst3 interface wrapper" header is just:
Code:
// Need to fix up the "reaper_vst3_interfaces.h" file to make it like this:
// https://gist.github.com/GavinRay97/2b2ec9bf2997ab13218166c871c4b6f8
#include "pluginterfaces/base/funknown.h"

using namespace Steinberg;
#include "./include/vendor/reaper-sdk/sdk/reaper_vst3_interfaces.h"

DEF_CLASS_IID(IReaperHostApplication)
DEF_CLASS_IID(IReaperUIEmbedInterface)


--------------

I will put this all up on a repo tomorrow, sorry it's all disorganized. The idea is that it'll be an automatic build setup with .patch files that get applied on top of official JUCE sources so it's not reliant on a PR.
__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)

Last edited by gxray; 04-27-2021 at 08:10 PM.
gxray is offline   Reply With Quote
Old 04-27-2021, 10:00 PM   #82
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

That is awesome !!
I wonder how you were able to get the necessary support for this

I sincerely hope this will support attaching the Reaper API, as well. I'll try that ASAP !

Maybe there is hope that this gets "official" some day soon ...
Thanks a lot !
-Michael

Last edited by mschnell; 04-27-2021 at 10:25 PM.
mschnell is offline   Reply With Quote
Old 04-27-2021, 10:04 PM   #83
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

Quote:
Originally Posted by mschnell View Post
BTW.:

The CMakeLists.txt file is done in a way that you are to place the "JUCE" folder besides your project folder (to which this is unzipped to and which holds this file and the project sources (instead of inside).
This allows for easily doing multiple projects with a single JUCE library installation.

-Michael

Last edited by mschnell; 04-28-2021 at 06:21 AM.
mschnell is offline   Reply With Quote
Old 04-27-2021, 10:08 PM   #84
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

Quote:
Originally Posted by gxray View Post
This allows you to implement a method called handleVST3HostContext() on your PluginProcessor that you must override from the base juce::AudioProcessor method.
Is it really necessary / sensible for the "user" code to know if this is a VST3 or a VST2 ? (Unless using dedicated features of either) I had hoped that this would be transparent...
Thanks again !!!
-Michael
mschnell is offline   Reply With Quote
Old 04-28-2021, 04:39 AM   #85
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

VST3 Patch:

When I include the ReaperVST3InterfaceWrapper.hpp file, I get the very weird error message "int32 is ambiguous" (or similar as it's German here) multiple times

e.g. in the line

virtual void* PLUGIN_API getReaperParent(uint32 w) = 0; // get parent track(=1), take(=2), project(=3), fxdsp(=4), trackchan(=5)

in file reaper_vst3_interfaces.h

As I don't understand at all, what is happening, I can't find out what to do.

-Michael

Last edited by mschnell; 04-28-2021 at 05:01 AM.
mschnell is offline   Reply With Quote
Old 04-28-2021, 07:49 AM   #86
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 396
Default

Quote:
Originally Posted by mschnell View Post
Is it really necessary / sensible for the "user" code to know if this is a VST3 or a VST2 ? (Unless using dedicated features of either) I had hoped that this would be transparent...
Thanks again !!!
-Michael
Yes, though the code for
Code:
class IReaperUIEmbedInterface {
   // note: VST2 uses CanDo "hasCockosEmbeddedUI"==0xbeef0000, then opcode=effVendorSpecific, index=effEditDraw, opt=(float)msg, value=parm2, ptr=parm3
  virtual Steinberg::TPtrInt embed_message(int msg, Steinberg::TPtrInt parm2, Steinberg::TPtrInt parm3)
}
and beyond should be identical to the VST2 implementation I think.

So the way in which you set up the environment for embedding TCP FX UI is entirely different different based on VST2/VST3, then the actual implementation function (I really, really hope) can be shared by them.

It should be possible to hide all of this though, and just expose like:
Code:
bool AudioProcessor::enableReaperEmbeddedUI()
// Optionally
juce::Image::BitmapData AudioProcessor::paintCustomReaperEmbeddedUI()
But that's an implementation detail down the line after everything is perfectly working and the rest of the bits are sorted out IMO =)
__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)

Last edited by gxray; 04-28-2021 at 08:01 AM.
gxray is offline   Reply With Quote
Old 04-28-2021, 07:56 AM   #87
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 396
Default

Quote:
Originally Posted by mschnell View Post
VST3 Patch:

When I include the ReaperVST3InterfaceWrapper.hpp file, I get the very weird error message "int32 is ambiguous" (or similar as it's German here) multiple times

e.g. in the line

virtual void* PLUGIN_API getReaperParent(uint32 w) = 0; // get parent track(=1), take(=2), project(=3), fxdsp(=4), trackchan(=5)

in file reaper_vst3_interfaces.h

As I don't understand at all, what is happening, I can't find out what to do.

-Michael
I get a lot of warnings, and two specifically for that file, but no errors and the build succeeds:

https://gist.github.com/GavinRay97/2...002a40f8b016ac

Let me take a moment and try to sort out getting this up on a repo properly with the submodules and right history
preserved

---

Thank you for sharing the current state of your VST2 implementation =D
I have none of the drawing code figured out so it's likely we can use the grand majority of this
__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)

Last edited by gxray; 04-28-2021 at 08:05 AM.
gxray is offline   Reply With Quote
Old 04-28-2021, 10:29 AM   #88
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 396
Default

Repo is here, it will pull the latest JUCE code from develop branch as a submodule and then the CMake build will automatically apply the necessary source-code patches for you (only once, after that it will skip them):

https://github.com/GavinRay97/JUCE-r...mbedded-fx-gui
__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)
gxray is offline   Reply With Quote
Old 04-28-2021, 12:55 PM   #89
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

I seem to need to provide an access token to clone the git.

It was easy enough to manually apply the pach to the two JUCE files, create the wrapper .hpp file, and update the Reaper .h file.

Do you suggest something went wrong on that behalf, and results oimn the weired "int32" problem ?

-Michael

Last edited by mschnell; 04-28-2021 at 01:01 PM.
mschnell is offline   Reply With Quote
Old 04-28-2021, 03:35 PM   #90
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 396
Default

Quote:
Originally Posted by mschnell View Post
I seem to need to provide an access token to clone the git.

It was easy enough to manually apply the pach to the two JUCE files, create the wrapper .hpp file, and update the Reaper .h file.

Do you suggest something went wrong on that behalf, and results oimn the weired "int32" problem ?

-Michael
I'm not sure, and it's difficult to say if we're not looking at the same project.

Your project that you shared here is VST2 so if that's one, this code won't work (it would never get invoked).

It's also next to impossible to collaborate with other people if they aren't on Github or some other form of version-controlled software.

If you clone my Github project and can't get it to build I can try to offer some help, or if you create a repository and commit + push your source to Github (or some other platform/system -- Bitbucket, Mercurial, Fossil, literally anything) then we'd have a way to view it and collaborate but otherwise I'm not sure how much I'll be able to be helpful.
__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)
gxray is offline   Reply With Quote
Old 04-28-2021, 10:23 PM   #91
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

I did log in with my account in github but could not clone, anyway, trying in Windows and in Linux. (That had been no problem with the files you provided earlier in this thread.)

Of course my project will not work, because the VST3 stuff is not called.
I just tried to compile it and got those weird errors (not just warnings). Then deleted close to all code and that did not help.

Somehow the namespaces seem to get messed up.

-Michael

Last edited by mschnell; 04-29-2021 at 03:54 AM.
mschnell is offline   Reply With Quote
Old 04-29-2021, 03:45 AM   #92
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

The suggested modification in
reaper_vst3_interfaces.h
includes
// NOTE: Corrected "IController" -> "IEditController" in comment

If verbally true and exclusive, this might suggest it's not usable "live", which would be very bad.
The extended connection to the DAW should not be limited to the editor (i.e. GUI context / thread).
E.g., in fact what I am up to is doing a plugin that receives midi messages (hence in the context of the thread it is loaded in), and then uses the Reaper API to do a lot of stuff.

Maybe this does not provide any problem at all, as in fact the Reaper API supposedly completely works on the GUI context / thread, that supposedly is connected to the IEditController context in JUCE. And in JUCE we can pass mesages from the Track context to the GUI context.

Do you have any comment on this ?

AFAIU, that file originally was provided by Justin/Cockos. Did they do or approve the modification ?
Thanks again,

-Michael (going to test this as soon I can compile such a project...)

Last edited by mschnell; 04-29-2021 at 03:56 AM.
mschnell is offline   Reply With Quote
Old 04-29-2021, 09:04 AM   #93
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 396
Default

Quote:
Originally Posted by mschnell View Post
The suggested modification in
reaper_vst3_interfaces.h
includes
// NOTE: Corrected "IController" -> "IEditController" in comment

If verbally true and exclusive, this might suggest it's not usable "live", which would be very bad.
The extended connection to the DAW should not be limited to the editor (i.e. GUI context / thread).
E.g., in fact what I am up to is doing a plugin that receives midi messages (hence in the context of the thread it is loaded in), and then uses the Reaper API to do a lot of stuff.

Maybe this does not provide any problem at all, as in fact the Reaper API supposedly completely works on the GUI context / thread, that supposedly is connected to the IEditController context in JUCE. And in JUCE we can pass mesages from the Track context to the GUI context.

Do you have any comment on this ?

AFAIU, that file originally was provided by Justin/Cockos. Did they do or approve the modification ?
Thanks again,

-Michael (going to test this as soon I can compile such a project...)
I am 99% certain Justin meant to use IEditController, because IController is a VSTGUI framework specific interface.

It's like saying "Must implement this JUCE/iPlug2 component interface", you probably wouldn't do that as that would mean anyone who wanted to use your feature would only be able to do it from the framework with that component.

I was waiting until I had finished the modifications to extend JuceVST3EditController in juce_VST3_wrapper.cpp to subclass IReaperUIEmbed and check if it works/does not work
(and I almost have, will likely have it done after work this evening)
before posting a question here to ask Justin to clarify about this.

But to me, yeah that doesn't make any sense and is probably just a typo.



__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)
gxray is offline   Reply With Quote
Old 04-29-2021, 12:29 PM   #94
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

Quote:
Originally Posted by gxray View Post
(and I almost have, will likely have it done after work this evening)
Awesome !
Looking forward to see (most of) the code I uploaded running as a VST3

-Michael
mschnell is offline   Reply With Quote
Old 04-29-2021, 02:29 PM   #95
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

Quote:
Originally Posted by gxray View Post
I now have been able to clone this.

Thanks,
-Michael
mschnell is offline   Reply With Quote
Old 04-30-2021, 02:55 AM   #96
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

Unfortunately I encounter some problems with the cloned project.

The file JuceAppConfigHeader.hpp has Zero bytes. I don't suppose this is intentional.

The file reaper_vst3_interfaces.h is not included, even though the folder include/vendor/reaper-sdk/sdk has been created.

The reference to that file in CMakeLists.txt (provided in the "PUBLIC" setting JUCE_APP_CONFIG_HEADER points to an inappropriate folder.

After trying to overcome these issues, when compiling I get the error message "iid: Multiple Initialization (C2374)" in both lines

DEF_CLASS_IID(IReaperHostApplication)
DEF_CLASS_IID(IReaperUIEmbedInterface)

in the file ReaperVST3InterfaceWrapper.hpp.

Thanks for any help...
-Michael

Last edited by mschnell; 04-30-2021 at 03:51 AM.
mschnell is offline   Reply With Quote
Old 04-30-2021, 09:12 AM   #97
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 396
Default

Quote:
Originally Posted by mschnell View Post
Unfortunately I encounter some problems with the cloned project.

The file JuceAppConfigHeader.hpp has Zero bytes. I don't suppose this is intentional.

The file reaper_vst3_interfaces.h is not included, even though the folder include/vendor/reaper-sdk/sdk has been created.

The reference to that file in CMakeLists.txt (provided in the "PUBLIC" setting JUCE_APP_CONFIG_HEADER points to an inappropriate folder.

After trying to overcome these issues, when compiling I get the error message "iid: Multiple Initialization (C2374)" in both lines

DEF_CLASS_IID(IReaperHostApplication)
DEF_CLASS_IID(IReaperUIEmbedInterface)

in the file ReaperVST3InterfaceWrapper.hpp.

Thanks for any help...
-Michael
Hey Michael -- as odd as it might sound, that "JuceAppConfigHeader.hpp" is meant to be empty. I experimented with it for some global defines at one point then dropped it, I should delete it. Sorry for the confusion!

> "The file reaper_vst3_interfaces.h is not included, even though the folder include/vendor/reaper-sdk/sdk has been created."

For this you have to follow the readme part about the "git clone" flags and pass --recursive so that it also fetches Justin's reaper-sdk repo (otherwise you get an empty folder)
Code:
git clone --recursive https://github.com/GavinRay97/JUCE-reaper-embedded-fx-gui
----

Quote:
After trying to overcome these issues, when compiling I get the error message "iid: Multiple Initialization (C2374)" in both lines

DEF_CLASS_IID(IReaperHostApplication)
DEF_CLASS_IID(IReaperUIEmbedInterface)

in the file ReaperVST3InterfaceWrapper.hpp.
I pushed some changes recently to this header, it no longer declares the class IID's there for technical reasons
  • (juce_VST3_Wrapper.cpp needs it for JuceVST3EditController, but you can only have a single instance of that declare in an entire project or else it'll cause stuff to fail)
  • So I used it directly in that file.
  • This needs to be refactored later but for now -- eh, fuck it.

The current implementation should be very close to working, however we will need to wait until the next dev release or so, there's a bug with the VST3 interface function for handling the REAPER messages, it doesn't get invoked:
https://forum.cockos.com/showpost.ph...27&postcount=5
__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)
gxray is offline   Reply With Quote
Old 04-30-2021, 10:53 AM   #98
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

I'll give that a try ASAP.

BTW.: I have a problem with the CMake stuff.

1)
If the user code need to know if it's going to be compiled to a VST2 or a VST3, how can I do a source that provides both ? In the JUCE forum I was told that it's not just a #define symbol that can be used to know about that.

2)

It seems that I can't simply place *.h files in the main (project) directory to allow them to be included in whatever file that is compiled during the Make process. I also did not fine other fo9lders for that purpose (I tried that to overcome the issues stated before). Where to place header files and make sure they will be found. I understand that ther is a difference between

#include "...." and
#include <...>

But how to find out what is happening under CMake ?
-Michael
mschnell is offline   Reply With Quote
Old 04-30-2021, 11:26 AM   #99
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

> git clone --recursive https://github.com/GavinRay97/JUCE-r...embedded-fx-gu
Tried this.

Got lots of syntax errors. No include or NHamespace problems such as I had before.

Edit /1:
I found that the patch line was deactivated in cmaklists.txt.
I activated same but that does not seem to work

Edit /2:
Copied the patch files that came in the patch folder. onto the originals in the JUCE folde.
Now it does compile

And I can load in in Reaper.
But when I set a breakpoint at the "Steinberg::" line, it will never arrive there.


-Michael

Last edited by mschnell; 04-30-2021 at 12:50 PM.
mschnell is offline   Reply With Quote
Old 04-30-2021, 12:33 PM   #100
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 396
Default

Quote:
Originally Posted by mschnell View Post
1)
If the user code need to know if it's going to be compiled to a VST2 or a VST3, how can I do a source that provides both ? In the JUCE forum I was told that it's not just a #define symbol that can be used to know about that.
-Michael
Yes this should be possible, I had intended to make a wrapper class that has the implementations for the REAPER-specific stuff, and then handle how to "connect" to the host (VST2 with callbacks, VST3 with interface implementations) based on which is being used.

That way it's all abstracted and you don't have to worry about VST2/VST3. But I haven't gotten far on this yet, maybe it is not possible (though I feel like it probably is).

Quote:
Originally Posted by mschnell View Post
I'll give that a try ASAP.

BTW.: I have a problem with the CMake stuff.

1)
If the user code need to know if it's going to be compiled to a VST2 or a VST3, how can I do a source that provides both ? In the JUCE forum I was told that it's not just a #define symbol that can be used to know about that.

2)

It seems that I can't simply place *.h files in the main (project) directory to allow them to be included in whatever file that is compiled during the Make process. I also did not fine other fo9lders for that purpose (I tried that to overcome the issues stated before). Where to place header files and make sure they will be found. I understand that ther is a difference between

#include "...." and
#include <...>

But how to find out what is happening under CMake ?
-Michael
So for the includes, the difference is that #include <file.h> says "include a header called file.h from the -I include paths that were passed to the compiler.

You generally use the angle brackets for libraries that are external to the project (system libs, etc) or when you have more complicated/nested folder structures.

Because what is happened is that "./JUCE/modules" is added to include path, with -I"./JUCE/modules" given to the compiler during build (the JUCE CMake macros hide this).

So then doing this:
PHP Code:
#include <juce_audio_processors/juce_audio_processors.h> 
Will start going through every single "-I" flag path that was given and checks if there exists a file at "-I${PATH}/juce_audio_processors/juce_audio_processors.h"

---

The quotes, with #include "MyFile.h" are a literal path to a file. That actually says #include "./MyFile.h".
So if you have a directory structure like below:
Code:
/JUCE
  /modules
    /juce_audio_processors
      juce_audio_processors.h
/src
  MyFile.cpp
Then you could equivalently use either of these inside of MyFile.cpp, assuming that you compile from the root of the tree with -I"./JUCE/modules"
PHP Code:
#include "../JUCE/modules/juce_audio_processors/juce_audio_processors.h"
#include <juce_audio_processors/juce_audio_processors.h> 
I like to use relative path includes with quotes a lot, they're less complicated/harder to screw up (to me).

You don't need to do anything with headers (.h, .hpp) in CMake since they don't (well, aren't usually meant to) be compiled, you #include them in implementation files (.c, .cpp) and those files are compiled.

They're like "interface" files that generally:
1) Have the type signatures meant to be present in the implementation file
2) Sometimes have function implementations if a function is static and very small

Quote:
Originally Posted by mschnell View Post
> git clone --recursive https://github.com/GavinRay97/JUCE-r...embedded-fx-gu
Tried this.

Got lots of syntax errors. No include problems such as I had before.

-Michael
I just checked on a fresh pull of the repo, you're right.
I think it may be the patch not applying, as I see that it says "handleVST3HostContext" and "handleReaperEmbedMessage" functions not found.

There's a Makefile in the root of the repo, try to run the "apply_juce_patches_to_source" commands in your shell:


Then confirm it worked:


After that -- no red in the build log:
__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)

Last edited by gxray; 04-30-2021 at 12:46 PM.
gxray is offline   Reply With Quote
Old 04-30-2021, 12:46 PM   #101
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 396
Default

Ahhh f*ck me ahaha, I can't remember why I did this last night but I had commented the line that applies the patches to JUCE

D'oh

__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)
gxray is offline   Reply With Quote
Old 04-30-2021, 12:59 PM   #102
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

Edit /1:
I found that the patch line was deactivated in cmaklists.txt.
I activated same but that does not seem to work

Edit /2:
Copied the *.cpp and *.h files that came in the patch folder. onto the originals in the JUCE folder.
Now it does compile

And I can load in in Reaper.
But when I set a breakpoint at the "Steinberg::" line, it will never arrive there.


-Michael
mschnell is offline   Reply With Quote
Old 04-30-2021, 01:04 PM   #103
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

I did see that you use relative paths in the #include clause.

I unsuccessfully tried this,as well.

I don't seem to understand what the start of the relative notation is supposed to be.

-Michaelo

Last edited by mschnell; 05-02-2021 at 03:13 AM.
mschnell is offline   Reply With Quote
Old 05-01-2021, 04:52 AM   #104
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

Quote:
Originally Posted by mschnell View Post
But when I set a breakpoint at the "Steinberg::" line, it will never arrive there.l
I tried to resaerch this. but with my still limited C++ experience I don't get too far.

In PluginProcessor.cpp I see:
Steinberg::TPtrInt AudioPluginAudioProcessor::handleReaperEmbedMessag e(int msg, Steinberg::TPtrInt parm2, Steinberg::TPtrInt parm3) ...
This is where I placed a breakpoint.

In PluginProcessor.h I see:
Steinberg::TPtrInt handleReaperEmbedMessage(int msg, Steinberg::TPtrInt parm2, Steinberg::TPtrInt parm3) override;
meaning that handleReaperEmbedMessage should override a (dummy ?) function defined in the JUCE library.


Same seemingly is called in juce_VST3_Wrapper.cpp line 627:
return this->audioProcessor.get()->get()->handleReaperEmbedMessage(msg, parm2, parm3);
Placing a breakpoint there and same never is reached.

This is part inside the function declared in line 625:
Steinberg::TPtrInt embed_message(int msg, Steinberg::TPtrInt parm2, Steinberg::TPtrInt parm3) override
which seemingly should override a function (???) embed_message declared somewhere else.

embed_message
seems only be otherwise referenced in reaper_vst3_interfaces.h line 25:
virtual Steinberg::TPtrInt embed_message(int msg, Steinberg::TPtrInt parm2, Steinberg::TPtrInt parm3) = 0;
I don't understand this code. Is this a function pointer ? (Can same be overriden ? )

I suppose somewhere something should be assigned to same, but this does not seem to be the case.

(Debugging this project is especially hard, as - other than with my other JUCE projects in VSCode - , the "Code" IDE does not work as it should: no searching for references, no jumping to references by ctrl-click, no suggesting of code completion )

-Michael

Last edited by mschnell; 05-02-2021 at 03:12 AM.
mschnell is offline   Reply With Quote
Old 05-03-2021, 01:07 AM   #105
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

In fact for me, it does not provide support for embedding, but it does show the console message "Test from IReaperHostApplication", which is very important for me.
I can set a breakpoint at this line and it does stop there
Trying top understand the C++ code of tat section...
But I seem to understand that the nice encapsulation of all the currently 810 API functions as provided in reaper_plugin_functions.h easily ready to be called after a simple initialization. seems a not yet to be used here- Should be rather easy to implement.

I suppose, for support for GUI embedding, IReaperUIEmbedInterface needs to be called similar to how IReaperHostApplication is called (in line 93). But this is not yet in place.

DEF_CLASS_IID(IReaperUIEmbedInterface)
....
auto reaperEGUI = FUnknownPtr<IReaperUIEmbedInterface>(hostContext);


does compile. But what to do with same ? And its provides a "ptr: <NULL>"

Any pointers ?

I'm really interested in proceeding in that issue...
-Michael

Last edited by mschnell; 05-03-2021 at 03:16 AM.
mschnell is offline   Reply With Quote
Old 05-03-2021, 07:36 AM   #106
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 396
Default

Hey Michael, you will need to be running the dev version of REAPER that is from 4/30 or afterwards:

Code:
v6.28+dev0430 - April 30 2021
  + API: fix typo in documentation of IReaperUIEmbedInterface [p=2438620]
  + Project metadata: disallow spaces in IXML element identifiers
  + Ruler: add option to hide region number if region is named
  + VST3: actually support third-party embeddable UIs via IReaperUIEmbedInterface interface [t=252907]
  # MIDI editor: autoscroll when reordering note rows
__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)
gxray is offline   Reply With Quote
Old 05-03-2021, 10:42 AM   #107
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

Ooops.
I did not know yet that there had been something under development with Reaper. I'll check this out ASAP.

Yep. works - Kind of.

I can see the Mini GUI in TCP and MCP. Some weird stuff happens, but I suppose you are aware of that.

BTW.: I am trying to do code that should run as VST2 and VST3. I just use a define right now. I am not sure if same can be decently triggered by the CMake environment-.

Maybe I can make it work decently...

-Michael

Last edited by mschnell; 05-03-2021 at 03:12 PM.
mschnell is offline   Reply With Quote
Old 05-03-2021, 03:11 PM   #108
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

Trying to add VST3 awareness to my existing project again produces these weird namespace issues, which I am unable to understand

-Michael
mschnell is offline   Reply With Quote
Old 05-04-2021, 04:48 AM   #109
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

Trying to do a universal example:

Current state:

I modified the main CMAKE files so that the JUCE and the INCLUDE folders are besides the project folders, so that I can decently manage multiple projects.

(1) A project started with the VST3 GUI embedding code:
- works with GUI embedding
- the call for handleVST3HostContext() is reached in the debugger and Reaper happily shows a message box.
- VST3 only
- code completion and finding targets by ctrl Click does not work in VS-Code (while it does work in all other projects), this makes using the IDE very hard.
- trying to set up REAPERAPI_LoadAPI() results in compiler errors which I am unable to analyze.

(2) A project started with the VST2 GUI embedding code:
- works with GUI embedding
- works with REAPERAPI_LoadAPI() and hence easily provides the complete Reaper API.
- VST2 only
- the VS Code IDE works perfectly
- trying to enhance the project to using VST3 results in namespace problems that I am unable to analyze.

-Michael

Last edited by mschnell; 05-04-2021 at 05:27 AM.
mschnell is offline   Reply With Quote
Old 05-04-2021, 09:26 AM   #110
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 396
Default

Quote:
Originally Posted by mschnell View Post
Trying to do a universal example:

Current state:

I modified the main CMAKE files so that the JUCE and the INCLUDE folders are besides the project folders, so that I can decently manage multiple projects.

(1) A project started with the VST3 GUI embedding code:
- works with GUI embedding
- the call for handleVST3HostContext() is reached in the debugger and Reaper happily shows a message box.
- VST3 only
- code completion and finding targets by ctrl Click does not work in VS-Code (while it does work in all other projects), this makes using the IDE very hard.
- trying to set up REAPERAPI_LoadAPI() results in compiler errors which I am unable to analyze.

(2) A project started with the VST2 GUI embedding code:
- works with GUI embedding
- works with REAPERAPI_LoadAPI() and hence easily provides the complete Reaper API.
- VST2 only
- the VS Code IDE works perfectly
- trying to enhance the project to using VST3 results in namespace problems that I am unable to analyze.

-Michael
Don't have enough time to write longform explanation but here's some bullet points:
  • REAPERAPI_LoadAPI() does not provide types for intellisense, and also you don't need to call it if you are running a VST2/VST3 plugin and not a native REAPER extension.
    • REAPER already provides the extension with the "host context", so when it's instantiated it has a pointer to REAPER and can use either callbacks (VST2) or query for an interface (VST3) to load API functions from REAPER directly.
    • See: https://www.reaper.fm/sdk/vst/vst_ext.php#vst_host
    • You only need to call it when you're writing a native extension and don't have this context available
    • (Well technically, you don't need to call it at all. My D language port of the REAPER C++ SDK uses it's own implementation of the LoadAPI function, since it's more convenient -- see below)



You can see this in action in the SDK examples here:
https://github.com/justinfrankel/rea...port.h#L19-L29

IMPORT_LOCALIZE_VST is used to get plugin functions from a VST context, and IMPORT_LOCALIZE_RPLUG when running as a standalone native REAPER C++ extension:
PHP Code:
#define IMPORT_LOCALIZE_VST(hostcb) \
    
*(VstIntPtr *)&importedLocalizeFunc hostcb(NULL,0xdeadbeef,0xdeadf00d,0,(void*)"__localizeFunc",0.0); \
    *(
VstIntPtr *)&importedLocalizeMenu hostcb(NULL,0xdeadbeef,0xdeadf00d,0,(void*)"__localizeMenu",0.0); \
    *(
VstIntPtr *)&importedLocalizeInitializeDialog hostcb(NULL,0xdeadbeef,0xdeadf00d,0,(void*)"__localizeInitializeDialog",0.0); \
    *(
VstIntPtr *)&importedLocalizePrepareDialog hostcb(NULL,0xdeadbeef,0xdeadf00d,0,(void*)"__localizePrepareDialog",0.0);

#define IMPORT_LOCALIZE_RPLUG(rec) \
  
*(void **)&importedLocalizeFunc rec->GetFunc("__localizeFunc"); \
  *(
void **)&importedLocalizeMenu rec->GetFunc("__localizeMenu"); \
  *(
void **)&importedLocalizeInitializeDialog rec->GetFunc("__localizeInitializeDialog"); \
  *(
void **)&importedLocalizePrepareDialog rec->GetFunc("__localizePrepareDialog"); 
Sample of D-lang translated version of REAPER C++ SDK I wrote, and it's implementation of REAPERAPI_LoadAPI():
PHP Code:
extern (C++) struct reaper_plugin_info_t
{
    public 
int caller_version;
    public 
HWND hwnd_main;
    public 
int function(const(char)*, void*) Register;
    public 
void* function(const(char)*) GetFunc;
}

__gshared struct Reaper
{
static:
    
bool function(const(char)*, const(char)*, const(char)*, boolAddCustomizableMenu;
    
bool function() AddExtensionsMainMenu;
    
MediaItem* function(MediaTrack*) AddMediaItemToTrack;
    
// etc...
}

// reaper.reaper_plugin_info_t is a struct of empty function pointers
// We can load the function pointers in by calling rec.GetFunc()
// and then use reflection to pass in the name of the function and cast the result to the type of itself
void load_reaper_api(reaper.reaper_plugin_info_trec)
{
    foreach (
name__traits(allMembersreaper.Reaper))
    {
    
mixin(`alias tmp = reaper.Reaper.`, name);
    
mixin(q{
      
tmp cast(typeof(tmp)) rec.GetFunc(name);
      if (
tmp is null) throw new Exception("Failed to load REAPER function: " name);
    });
    }
}

extern (Cexport int ReaperPluginEntry(HINSTANCE hInstancereaper.reaper_plugin_info_trec)
{
  if (!
rec)
      return 
0;
  if (
rec.caller_version != reaper.REAPER_PLUGIN_VERSION)
      return 
0;
  if (!
rec.GetFunc)
      return 
0;

  
// Dlang mixin voodoo
  
load_reaper_api(rec);
  
reaper.Reaper.ShowConsoleMsg("Hello from D!");
  
  return 
1;

==================================================

About the VS Code stuff -- you need to have a "hermetic"/self-contained build environment.

Everything your project needs, including all VS Code settings should be contained inside of a repo. So no reliance on projects on your external filesystem or symlinks whatnot.

You should have all your settings configured like this:
This is the only way you'd be able to have a productive discussion about issues, because C++ is so fickle with regards to compilers, versions, host libs, compile flags and macros etc.

Everyone needs to be able to clone the repo and have an EXACT, REPRODUCIBLE editor environment, otherwise it's like trying to read minds.
__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)
gxray is offline   Reply With Quote
Old 05-04-2021, 10:36 AM   #111
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

Trying to make some sense out of this.....
Re: REAPERAPI_LoadAPI() : while it's not strictly necessary, and Reaper can decode all API function each time from their text name (as well in VST2 (hostbc("fucntionname")) as in VST3 (static_cast<void (*)(const char* msg)>(reaper->getReaperApi("functionname") ), creating a list in advanced and directly calling the functions in C code is a lot more efficient. And in theory there is no obstacle with using REAPERAPI_LoadAPI() with VST3 (and it works absolutely fine with VST2).

Re: self "contained" projects. OK, I can do this (at least for a start). But it does seems rather clumsy to copy the complete library in each project. In fact I'd like to use git for the upcoming project(s), but I have no idea how to handle such a structure.

Re: code completion etc: the project I cloned did not work decently on that behalf in VS Code directly after cloning.

I suppose I'll try to create a new project and add the code piece per piece. Unfortunately this will drop the step "doing an universal example with GUI embedding", as my project will not use GUI embedding, at all.

-Michael

Last edited by mschnell; 05-04-2021 at 11:40 AM.
mschnell is offline   Reply With Quote
Old 05-04-2021, 12:24 PM   #112
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

Sorry for being a PITA, but I do think that this is important, not only for myself, but for anybody who wants to use extended Reaper integration with JUCE, including - but not limited to - embedding the GUI.

Here an update:

1) Mystically now the IDE works with the VST3 project. I have no Idea what I might have changed to get this effect, but HURRAY I can go on working on that code.

2) REAPERAPI_LoadAPI() does work in the VST3 project !
I needed to eliminate the struct typedef "PCM_source_peaktransfer_t" from the file reaper_plugin.h. I supposedly will not need this type and the classes that use it (MP3 etc). But of course we should find out what is going on and pinpoint the problem to Justin.

As the type is needed for some of the 800+ API functions generated by REAPERAPI_LoadAPI(), I need to restrict the list to the necessary lot, which can be done by doing
#define REAPERAPI_IMPLEMENT
#define REAPERAPI_MINIMAL
#define REAPERAPI_WANT_ShowConsoleMsg
#include "../include/vendor/reaper-sdk/sdk/reaper_plugin_functions.h"

With that only ShowConsoleMsg() is created in the list and same can be successfully used in my code.

Thanks for listening,
-Michael

Last edited by mschnell; 05-05-2021 at 03:25 AM.
mschnell is offline   Reply With Quote
Old 05-05-2021, 09:21 AM   #113
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

Nonetheless trying to create a universal example for GUI embedding ...

@gxray:

The VST2 example seems to work with two different GUI layouts (seemingly created using Projucer, which I never touched). This makes perfect sense: a big one for detailed editing use, and a small one for embedding. This of course is what I want to include in the example code, and for checking for optimization.

The VST3 example only uses a single GUI layout for both purposes.

As you already did this on VST2, maybe it makes more sense if you look at that endeavor, than having do that.... Or tell me some details on how to proceed.

Thanks,
-Michael
mschnell is offline   Reply With Quote
Old 05-05-2021, 10:59 AM   #114
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

Ranting on....

The weird error in reaper_plugin.h in the Reaper SDK folder goes away when I deactivate the line

set(CMAKE_CXX_FLAGS "/std:c++latest ${CMAKE_CXX_FLAGS}")
in CMakeLists.txt.

Is there any disadvantage in doing so ?

-Michael
mschnell is offline   Reply With Quote
Old 05-06-2021, 04:51 AM   #115
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

@gxray:

Sorry, but the GUIembed VST3 code cloned from the git does not work for me with the dev-version of Reaper as it should.

The embedded GUI is only shown once the mouse is hovered above the embedded frame. Then you can move the Reaper GUI "below" that frame, whoch sticks fixed in place on he screen.

This is because only the "edit" variant of the embedded GUI is displayed - and then it stays in place. The BMP copy of the frame never is shown. If I disable the code in REAPER_FXEMBED_WM_PAINT, the behavior does not change. Hence the content of the bitmap provided by Reaper it does not matter.

Maybe a bug in Reaper ?

I filed a messages in the prerelease forum.

-Michael

Last edited by mschnell; 05-06-2021 at 05:25 AM.
mschnell is offline   Reply With Quote
Old 05-06-2021, 06:21 AM   #116
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 396
Default

Quote:
Originally Posted by mschnell View Post
Nonetheless trying to create a universal example for GUI embedding ...

@gxray:

The VST2 example seems to work with two different GUI layouts (seemingly created using Projucer, which I never touched). This makes perfect sense: a big one for detailed editing use, and a small one for embedding. This of course is what I want to include in the example code, and for checking for optimization.

The VST3 example only uses a single GUI layout for both purposes.

As you already did this on VST2, maybe it makes more sense if you look at that endeavor, than having do that.... Or tell me some details on how to proceed.

Thanks,
-Michael
The original user who posted the VST2 code for JUCE (to whom I am eternally grateful and will credit) did a sensible thing and has his "paint" method set up to draw a specific component.

In JUCE, stuff you display are "juce::Component"s

In my code, I don't care about what gets displayed so long as it's correct (only that the API works), so I did not make a new component.

You can see the embedComponent is set in the top of the method that handles the embed_message() -- you can set that to any valid JUCE component and it'll change what is painted. I have it set an instance of the entire editor.

(This is not the right way to architect this probably, most of this code is just me trying to get this working)
__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)
gxray is offline   Reply With Quote
Old 05-06-2021, 12:17 PM   #117
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

I suppose I will be able to do a decent "dual GUI" example that can be compiled as well as VST2 as as VST3.

I understand, for working decently, you need as well the "direct" GUI (to be able to do editing with the mouse) as the static ("BPM") picture of the GUI state (to allow Reaper to drag it around at will).

But seemingly the VST3 embedding does not work correctly in the current dev version of Reaper. The bpm picture (seemingly decently filled with pixels in the "paint" branch) ) never seems to be displayed, but always a black rectangle is displayed instead (only temporarily hidden by the "direct/editing" GUI you create in your code).

Until this is sorted out it does not make sense to improving the VBST3 code.

Edit: There seems to be a bug in the code in the "paint" branch. I'll try to fix that ...

-Michael

Last edited by mschnell; 05-06-2021 at 12:36 PM.
mschnell is offline   Reply With Quote
Old 05-07-2021, 03:44 AM   #118
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

(The bug was that a new "editor" had been created with each call from the host and hence the bpm never was in sync with what was displayed as an "overlay". Moreover the overlay never was removed, resulting in it sticking on the screen e.g. when the Reaper Gui was moved around.)
I was able to do some primary fixes, optimizations and enhancements.

I suppose, over the weekend I will come up (upload to the Stash) with a hopefully decetly working example of a dual-GUI (main and embed) Juce plugin code, compilable for VST2 and VST3 (can't test mac. Linux maybe later).
I'll not use a GUI builder for the "dual Gui" but just Juce library code.

Here, the "normal" Juce functions will be used that are called for constructing / displaying / resizing both GUI variants, allowing to select the stuff to do in either case by a system wide boolean variable.

-Michael

Last edited by mschnell; 05-07-2021 at 06:28 AM.
mschnell is offline   Reply With Quote
Old 05-09-2021, 06:40 AM   #119
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,686
Default

Hi experts,

I got rather up to speed with the VST3 with embedded FX GUI.
My GUI has a fader. and the small and the main GUI are decently in sync.

I additionally created a DAW parameter for that fader value and when displaying the value as "envelope" in the track header, this also is in sync. moving any of the three updates the other two.
Unfortunately when unloading the VST3 and loading it again, i don't get back the value from Reaper, but I always get 0.5. from the "AudioParameterFloat" call. which I canm see in the debugger in fact is done when loading the VST3.

Maybe I need to tell reaper that it should not only show but additionally store the value ?!?!?
Tjanks for any Pointers,
-Michael
mschnell is offline   Reply With Quote
Old 05-09-2021, 07:41 AM   #120
nofish
Human being with feelings
 
nofish's Avatar
 
Join Date: Oct 2007
Location: home is where the heart is
Posts: 12,096
Default

Quote:
Originally Posted by mschnell View Post
Unfortunately when unloading the VST3 and loading it again, i don't get back the value from Reaper, but I always get 0.5. from the "AudioParameterFloat" call. which I canm see in the debugger in fact is done when loading the VST3.
That's normal behaviour for plugins in all DAWS (I know) I'd say.
When you automate a plugin parameter and remove that plugin the automation envelope gets deleted and so that parameter is reset to default when re-inserting the plugin.

Or did I misunderstand?
nofish is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 04:13 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.