Old 04-29-2021, 07:14 PM   #1
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 394
Default IReaperUIEmbedInterface question

So I have spent the past week or so trying to minimally/generically patch JUCE so that an upstream patch can be landed with support for REAPER's VST3 interfaces -- particularly interested in TCP FX embedding.

(Have spoken briefly w/ JUCE devs about this, there is some interest/receptiveness on their side depending on implementation)

To get the base REAPER SDK working (IReaperApplication->getReaperApi()) with a "reaper.ShowConsoleMsg()" was very few changes.

---

However I wanted to ask two quick questions, as trying to get IReaperUIEmbedInterface functional did not work based on what I assumed.
  1. In "reaper_vst3_interfaces.h" and "reaper_plugin_fx_embed.h", the interface given for IReaperUIEmbedInterface is IController. But IController is a VSTGUI-specific interface, I assumed this was a typo and instead it was IEditController from VST3 SDK. Is this right or wrong?

    Ref: https://i.imgur.com/uEgPNVO.png
    -------
  2. If it's right, part of what I have done is modified JUCE's JuceVST3EditController (see below) to subclass/implement IReaperUIEmbedInterface, and I have overriden the ::embed_message() method, but nothing is ever called. Is there any way of debugging this from REAPER?

Here's the code for:
Would be grateful for any advise/feedback from folks who have a better clue about what they're doing than me. Getting this working and publishing it for others would be stellar!

JUCE patches made:


__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)

Last edited by gxray; 04-29-2021 at 07:20 PM.
gxray is offline   Reply With Quote
Old 04-30-2021, 07:42 AM   #2
robbert-vdh
Human being with feelings
 
Join Date: Nov 2020
Posts: 274
Default

I've never looked at these REAPER VST3 extensions, but did you add IReaperUIEmbedInterface to your edit controller's query interface?
robbert-vdh is offline   Reply With Quote
Old 04-30-2021, 07:48 AM   #3
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 394
Default

Quote:
Originally Posted by robbert-vdh View Post
I've never looked at these REAPER VST3 extensions, but did you add IReaperUIEmbedInterface to your edit controller's query interface?
Yeah -- the diff does not provide enough context I think, but the actual queryInterface changes are here:

https://github.com/GavinRay97/JUCE-r....cpp#L602-L667

PHP Code:
#ifdef JUCE_VST3_ENABLE_PASS_HOST_CONTEXT_TO_AUDIO_PROCESSOR_ON_INITIALIZE
#include "../../include/vendor/reaper-
DEF_CLASS_IID(IReaperUIEmbedInterface)
#endif
class JuceVST3EditController : public Vst::EditController,
                               public 
Vst::IMidiMapping,
                               public 
Vst::IUnitInfo,
                               public 
Vst::ChannelContext::IInfoListener,
                               public 
AudioProcessorListener
#ifdef JUCE_VST3_ENABLE_PASS_HOST_CONTEXT_TO_AUDIO_PROCESSOR_ON_INITIALIZE
                             
, public IReaperUIEmbedInterface
#endif
{
public:
    
JuceVST3EditController (Vst::IHostApplicationhost)
    {
        if (
host != nullptr)
            
host->queryInterface (FUnknown::iid, (void**) &hostContext);
    }

#ifdef JUCE_VST3_ENABLE_PASS_HOST_CONTEXT_TO_AUDIO_PROCESSOR_ON_INITIALIZE
    
Steinberg::TPtrInt embed_message(int msgSteinberg::TPtrInt parm2Steinberg::TPtrInt parm3override
    
{
        return 
this->audioProcessor.get()->get()->handleReaperEmbedMessage(msgparm2parm3);
    }
#endif
    //==============================================================================
    
static const FUID iid;

    
//==============================================================================
    
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Winconsistent-missing-override")

    
REFCOUNT_METHODS (ComponentBase)

    
JUCE_END_IGNORE_WARNINGS_GCC_LIKE

    tresult PLUGIN_API queryInterface 
(const TUID targetIIDvoid** objoverride
    
{
        
TEST_FOR_AND_RETURN_IF_VALID (targetIIDFObject)
        
TEST_FOR_AND_RETURN_IF_VALID (targetIIDJuceVST3EditController)
        
TEST_FOR_AND_RETURN_IF_VALID (targetIIDVst::IEditController)
        
TEST_FOR_AND_RETURN_IF_VALID (targetIIDVst::IEditController2)
        
TEST_FOR_AND_RETURN_IF_VALID (targetIIDVst::IConnectionPoint)
        
TEST_FOR_AND_RETURN_IF_VALID (targetIIDVst::IMidiMapping)
        
TEST_FOR_AND_RETURN_IF_VALID (targetIIDVst::IUnitInfo)
        
TEST_FOR_AND_RETURN_IF_VALID (targetIIDVst::ChannelContext::IInfoListener)
        
TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (targetIIDIPluginBaseVst::IEditController)
        
TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (targetIIDIDependentVst::IEditController)
        
TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (targetIIDFUnknownVst::IEditController)

#ifdef JUCE_VST3_ENABLE_PASS_HOST_CONTEXT_TO_AUDIO_PROCESSOR_ON_INITIALIZE
        
TEST_FOR_AND_RETURN_IF_VALID (targetIIDIReaperUIEmbedInterface)
#endif

        
if (doUIDsMatch (targetIIDJuceAudioProcessor::iid))
        {
            
audioProcessor->addRef();
            *
obj audioProcessor;
            return 
kResultOk;
        }

        *
obj nullptr;
        return 
kNoInterface;
    } 
Where the macro TEST_FOR_AND_RETURN_IF_VALID is:
https://github.com/juce-framework/JU...T3Common.h#L49

PHP Code:
#define TEST_FOR_AND_RETURN_IF_VALID(iidToTest, ClassType) \
    
if (doUIDsMatch (iidToTestClassType::iid)) \
    { \
        
addRef(); \
        *
obj dynamic_cast<ClassType*> (this); \
        return 
Steinberg::kResultOk; \
    } 
__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)

Last edited by gxray; 04-30-2021 at 12:49 PM.
gxray is offline   Reply With Quote
Old 04-30-2021, 08:09 AM   #4
schwa
Administrator
 
schwa's Avatar
 
Join Date: Mar 2007
Location: NY
Posts: 15,423
Default

Quote:
Originally Posted by gxray View Post
In "reaper_vst3_interfaces.h" and "reaper_plugin_fx_embed.h", the interface given for IReaperUIEmbedInterface is IController. But IController is a VSTGUI-specific interface, I assumed this was a typo and instead it was IEditController from VST3 SDK. Is this right or wrong?
You're correct, this is a typo in the documentation. The plugin's IEditController should implement IReaperUIEmbedInterface.
schwa is offline   Reply With Quote
Old 04-30-2021, 08:20 AM   #5
schwa
Administrator
 
schwa's Avatar
 
Join Date: Mar 2007
Location: NY
Posts: 15,423
Default

Quote:
Originally Posted by gxray View Post
I have overriden the ::embed_message() method, but nothing is ever called
This is a bug on our side, sorry for the trouble! We'll fix this for the next +dev build.
schwa is offline   Reply With Quote
Old 04-30-2021, 08:40 AM   #6
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 394
Default

Quote:
Originally Posted by schwa View Post
This is a bug on our side, sorry for the trouble! We'll fix this for the next +dev build.
Thank you Schwa -- appreciate you guys a ton!!

Will keep doing my part to keep convo going with JUCE folks and see if we can get an acceptable generic implementation merged upstream, so that all JUCE projects have optional out-of-the-box functionality for REAPER UI embedding + SDK hosting in both VST2 + VST3

<3
__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)
gxray is offline   Reply With Quote
Old 04-30-2021, 08:56 PM   #7
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 394
Default

Haven't had time to test thoroughly, but the greyed-out "Show/Embed FX in TCP" option is now clickable in my plugin FX slot in 6.30 dev, and after enabling it and trying to expand the track the plugin crashed.

Which is good! It means I fucked up, but that that REAPER let me fuck up.
So it sounds like this is going to be a fun and productive weekend =)

Will continue dev efforts after brief sleep.
Once again, really appreciate you folks
__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)
gxray is offline   Reply With Quote
Old 05-03-2021, 07:34 AM   #8
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 394
Default

I'm going to go off topic from original post here if that's alright

---

I'd like to ask for some basic C++ code-review/help from anyone here experienced and willing to spare it.

I have been working with JUCE devs on allowing users to implement REAPER's VST3 interfaces for TCP/MCP FX embedding and access to it's C++ API.

(So that we can get it merged upstream and all JUCE VST2/VST3 plugins can optionally choose to be REAPER-capable by default)

Got a rough version working but it was hacky (see below), then after talking to one of the JUCE dev's have been swapping it out for his implementation idea.

Problem is, I'm hitting the limits of my knowledge trying to debug why this isn't working.


  • So I have modified JUCE's IEditController implementation to allow runtime registration of VST3 interfaces and lookups during it's queryInterface() method.
  • The instance of the REAPER IReaperUIEmbedInterface class is getting put in here.

PHP Code:
std::string tuidToFUIDString(const TUID iid) {
    const 
auto targetFUID FUID::fromTUID(iid);
    
char tmpBuf[128];
    
targetFUID.toString(tmpBuf);
    return 
std::string(tmpBuf);
}

class 
VST3InterfaceContainer {
  
typedef std::string TUIDRegString;
public:
  
std::unordered_map<TUIDRegStringSteinberg::FUnknown*> vst3InterfaceMap;

  
template<class T>
  
bool registerInterface(const TUID iidT klazz) {
    const 
auto fuidStr tuidToFUIDString(iid);
    
//this->tuidToClassnameMap.insert_or_assign(fuidStr, std::string(typeid(T).name()));
    
const auto result this->vst3InterfaceMap.insert_or_assign(fuidStrdynamic_cast<FObject*>(klazz));
    return 
result.second;
  }

  
Steinberg::FUnknownfindInterface(const TUID iid) {
    const 
auto fuidStr tuidToFUIDString(iid);
    
auto klazz this->vst3InterfaceMap.find(fuidStr);
    if (
klazz != this->vst3InterfaceMap.end())
      return 
klazz->second;
    return 
nullptr;
  }
}; 
PHP Code:
class JuceVST3EditController // ... bunch of VST3 and JUCE classes
                               
public VST3InterfaceContainer
{
  
tresult PLUGIN_API queryInterface (const TUID targetIIDvoid** objoverride
  
{
      
TEST_FOR_AND_RETURN_IF_VALID (targetIIDFObject)
      
// etc..

      
auto klazz this->findInterface(targetIID);
      if (
klazz != nullptr) {
          
klazz->addRef();
          *
obj klazz;
          return 
kResultOk;
      }

      if (
doUIDsMatch (targetIIDJuceAudioProcessor::iid)) {
          
audioProcessor->addRef();
          *
obj audioProcessor;
          return 
kResultOk;
      }

      *
obj nullptr;
      return 
kNoInterface;
  }

  • In the factory instance for the plugin, I've just done this to test instantiating and inserting an instance of my class into this std::unordered_map

PHP Code:
static FUnknowncreateControllerInstance (Vst::IHostApplicationhost)
{
    
auto editController = new JuceVST3EditController (host);
    const 
auto reaperEditController = new ReaperVST3EditController();
    
editController->registerInterface(ReaperVST3EditController::iidreaperEditController);
    return 
static_cast<Vst::IEditController*>(editController);

  • And the actual implementation of the IReaperUIEmbedInterface class (ReaperVST3EditController) is:

PHP Code:
#pragma once
#include <juce_core/juce_core.h>
#include <juce_audio_processors/format_types/VST3_SDK/base/source/fobject.h>
#include <juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/ftypes.h>

#include "../ReaperVST3InterfaceWrapper.hpp"
#include "../include/vendor/reaper-sdk/sdk/reaper_plugin_fx_embed.h"

// I'm defining the same class IID as the IReaperUIEmbedInterface so that this implementation class
// gets resolved by this IID during lookups
DEF_CLASS_IID(IReaperUIEmbedInterface)
DECLARE_CLASS_IID(ReaperVST3EditController0x049bf9e70xbc74ead00xc4101e860x7f725981)
class 
ReaperVST3EditController : public Steinberg::FObject,
                                 public 
IReaperUIEmbedInterface {
private:
    
// PLUGIN_API = __stdcall
    
virtual Steinberg::uint32 PLUGIN_API addRef () override { return Steinberg::FObject::addRef (); }
    
virtual Steinberg::uint32 PLUGIN_API release () override { return Steinberg::FObject::release (); }
    
public:
    
Steinberg::TPtrInt embed_message(int msgSteinberg::TPtrInt parm2Steinberg::TPtrInt parm3override
    
{
        
DBG("[ReaperVST3EditController::embed_message] msg = " << msg << " parm2 = " << parm2 << " parm3 = " << parm3);
        switch (
msg) {
        case 
REAPER_FXEMBED_WM_IS_SUPPORTED:
            return 
0;
        }
        return 
0;
    };

    
tresult queryInterface(const TUID _iidvoid** objoverride
    
{
        return 
Steinberg::kResultOk;
    };

    static const 
FUID iid;
};

DEF_CLASS_IID(ReaperVST3EditController
================================================== ==========

Here's the problem:
  • when debugging this and setting breakpoints, it DOES call the constructor of ReaperVST3EditController
  • And the queryInterface() *obj gets set to the pointer to the instance of this ReaperVST3EditController
  • It enables the "Show UI in TCP" that's greyed out, and draws a black box
  • But the ReaperVST3EditController::embed_message() and every other function on the class is never called

Wtf is going on, how do I debug this? =/



__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)

Last edited by gxray; 05-03-2021 at 07:43 AM.
gxray is offline   Reply With Quote
Old 05-03-2021, 07:50 AM   #9
schwa
Administrator
 
schwa's Avatar
 
Join Date: Mar 2007
Location: NY
Posts: 15,423
Default

If you want to post a link to a test copy of your plugin, we can let you know what it looks like from REAPER's side.
schwa is offline   Reply With Quote
Old 05-03-2021, 07:58 AM   #10
schwa
Administrator
 
schwa's Avatar
 
Join Date: Mar 2007
Location: NY
Posts: 15,423
Default

Quote:
Originally Posted by gxray View Post
But the ReaperVST3EditController::embed_message() and every other function on the class is never called
Never called at all, or never called after the initial REAPER_FXEMBED_WM_IS_SUPPORTED query? REAPER won't call again if the initial query returns 0.

This is what the code looks like from REAPER's side. if queryInterface() is being called for IReaperUIEmbedInterface and returning properly, embed_message() should be called immediately.

Code:
          IReaperUIEmbedInterface *embed_ui=NULL;
          if (m_vst3->m_controller->queryInterface(IReaperUIEmbedInterface::iid,(void **)&embed_ui) == kResultTrue && embed_ui)
          {
            if (embed_ui->embed_message(0,0,0)) // 0=REAPER_FXEMBED_WM_IS_SUPPORTED
            {
              m_vst3->m_embed_ui = embed_ui;
            }
            else
            {
              embed_ui->release();
            }
          }

If your code is receiving the interface query from REAPER, as your screenshot/breakpoint suggests, my guess is that it's actually working properly, but you're not seeing the debug message in embed_message() for some reason.

I suppose another possibility is that your interface registration code is returning the IReaperUIEmbedInterface interface for the wrong query. You could dump the actual IID there to make sure.

Last edited by schwa; 05-03-2021 at 08:15 AM.
schwa 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 12:26 AM.


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