free-audio / clap-juce-extensions

MIT License
149 stars 20 forks source link

Support for REAPER's cockos.reaper_extension? #110

Open nofishonfriday opened 1 year ago

nofishonfriday commented 1 year ago

Hi,

Cockos has just added support for CLAP plugins being able to access the REAPER API in their pre-release versions ( link to changelog) . Curious (as I'm still a beginner with the clap-juce-extensions), can we use/access this out of the box with the current version or would it need a code modification? And if the latter, would it be something considerable for the future?

Thanks.

baconpaul commented 1 year ago

So I was looking at this for surge also. I haven't written the code yet but let me jot notes here (especially if you are interested in taking a shot at it).

the answer is "yes" but we just need to plumb it through.

The extension is something you can just get with HostProxy::getExtension (https://github.com/free-audio/clap-helpers/blob/4d4e297947382d2a9d00a469af61e43332463ebf/include/clap/helpers/host-proxy.hh#L24) so the question is how do you get from your AudioProcessor to a HostProxy instance. Basically we want your AudioProcessor to be able to do something like (clap-juce-extensions-somewhere)->getHost()->getExtension(&reaper_info, "cokocs.reaper-info") or what not.

The way to do this, I think (and how I would do it) is

in include/clap-juce-extensions/clap-juce-extensions.h

  1. At the end of the capabilities class in the private section add a new function which is probably something like std::function<bool(void *, const char*)> extensionGet{nullptr}
  2. In the public section add

    
      { auto res = extensionGet((void *)&t, id);  
      }
      else return false; }```
    
    so this will give you, if you implement the extensions, the ability to do `reaper_info_t info; if (getExtension(info, "cokocs.reaper-thingy")) ... ` in your juce class

So the only thing left then is to set that lambda to something in the startup path. In src/wrapper/clap-juce-wrapper.cpp you can see this code

        if (processorAsClapExtensions != nullptr)
        {
            processorAsClapExtensions->parameterChangeHandler =
                [this](const clap_event_param_value *paramEvent) {
                    handleParameterChangeEvent(paramEvent);
                };
            processorAsClapExtensions->lookupParamByID = [this](clap_id param_id) {
                return findVariantByParamId(param_id);
            };
        };

soo to that you should be able to do something like set the extensionGet to [this](a, b) {host->extensioNGet(a,b);}

anyway that's shorthand. If you think you want to take a swing at it would be happy to review, and if not, can def get to it after thanksgiving or earlier if its pressing.

nofishonfriday commented 1 year ago

Thanks for the quick and thorough reply. It's definitely not pressing (hadn't planned to do anything specific with it currently). I may give it a shot (aka PR), but if you get round to it earlier that's also fine (and much appreciated). Thanks again.

baconpaul commented 1 year ago

Yeah we’ve kinda used surge to highlight the things which are possible so I kinda want to do something. The only thing is I don’t know reaper well enough to know what a cool feature would be with this api!! All ears for ideas

nofishonfriday commented 1 year ago

Admittedly I don't come up spontaneously with something too. But in general all Reaper's API functions should be accessible with it. Not sure if the host functions/context available for VST are available for CLAP also. Anyway I'll post back if I come up with something which I think that could be cool. :)

jatinchowdhury18 commented 1 year ago

Yeah, this seems like a cool thing to have! And it makes sense that the same approach could be used for other DAWs with host-specific APIs. I think Paul's sketch of the implementation seems about right. I think it would also be nice to have an example plugin in this repo that does something simple: I'm thinking a drop-down menu that changes the colour of the track the plugin is on? I should have time for some hacking on this over the Thanksgiving holiday.

giohappy commented 1 year ago

@jatinchowdhury18 did you have the chance to make any progress on this?

I'm totally new to CLAP but I would certainly digg into it if the interoperability with Reaper API and its host methods was confirmed.

jatinchowdhury18 commented 1 year ago

@giohappy I did not... but I spent 5-10 minutes on it just now. I've pushed my work to a branch in case you (or anyone else) wants to take a look: https://github.com/free-audio/clap-juce-extensions/compare/main...jatinchowdhury18:clap-juce-extensions:reaper-ext

We can query Reaper for the extension, and Reaper gives us back something that is not nullptr, so that seems promising! I haven't yet tried to cast it to a reaper_plugin_info_t and then actually try to do anything useful with it, but that seems like it would be the logical next step.

jatinchowdhury18 commented 1 year ago

Another few minutes of hacking today... muting a track through the Reaper API works!

https://github.com/free-audio/clap-juce-extensions/assets/13724188/2c3dcd4e-1913-40a9-88ce-9219c42fcc03

giohappy commented 1 year ago

@jatinchowdhury18 great! Thanks for sharing it, I will test it ASAP.

giohappy commented 1 year ago

@jatinchowdhury18 I was able to run it. Thanks!

I'm not an expert in C++. Is there any difference between this (your code):

MediaTrack *(*getTrackFunc)(ReaProject *, int);
 *((void **)&getTrackFunc) = reaperPluginExtension->GetFunc("GetTrack");
 auto *track0 = (*getTrackFunc)(nullptr, 0);

and this (what I would have done)?:

void *(*getTrackFunc)(ReaProject *, int);
getTrackFunc = reinterpret_cast<void *(*)(ReaProject *, int)>(reaperPluginExtension->GetFunc("GetTrack"));
auto *track0 = reinterpret_cast<MediaTrack *>((*getTrackFunc)(nullptr, 0));

And are you going to open a PR to have getExtension/extensionGet added to clap-juce-exntesions?

jatinchowdhury18 commented 1 year ago

No problem!

Those code snippets look equivalent to me. I was using this example from the Reaper SDK as a reference.

You might be able to simply your version a little bit if you define the function pointer return type, since then you shouldn't need the second reinterpret_cast.

MediaTrack *(*getTrackFunc)(ReaProject *, int);
getTrackFunc = reinterpret_cast<MediaTrack *(*)(ReaProject *, int)>(reaperPluginExtension->GetFunc("GetTrack"));
auto *track0 = (*getTrackFunc)(nullptr, 0);

There's probably some ways to clean it up further with using declarations as well.


I do plan to open a PR for the extension-related changes, but there's a couple things I'd like to do first:

  1. Currently the plugin can't access use getExtension() in its constructor, since it hasn't been initialized yet. I'd like to see if there's a way to re-order some things which could make that possible.
  2. I want to clean up the example a bit more... maybe even fork those changes into their own example plugin for better clarity, and to avoid adding too much stuff to the current example.
jatinchowdhury18 commented 1 year ago

Having some fun with track colours:

136 should be ready soon which will make getExtension() available and add this new example plugin.