Last active
October 9, 2020 11:54
-
-
Save kyab/6b667beb3dab8b5666136f6335c86b85 to your computer and use it in GitHub Desktop.
vst3host2 wip
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
typedef IPluginFactory* (*THEAPI)(); | |
typedef bool (*BundleEntryFunc) (CFBundleRef); | |
//#define BUNDLE_PATH @"/Library/Audio/Plug-Ins/VST/OldSkoolVerb.vst3" | |
//#define BUNDLE_PATH @"/Library/Audio/Plug-Ins/VST/SPAN.vst3" | |
#define BUNDLE_PATH @"/Library/Audio/Plug-Ins/VST3/UpStereo.vst3" | |
//#define BUNDLE_PATH @"/Users/koji/work/VST_SDK/VST3_SDK/build/VST3/Debug/helloworld.vst3" | |
//#define BUNDLE_PATH @"/Library/Audio/Plug-Ins/VST3/TAL-Chorus-LX.vst3" | |
#define BUNDLE_PATH @"/Library/Audio/Plug-Ins/VST3/TDR VOS SlickEQ.vst3" | |
- (IBAction)testVST:(id)sender { | |
NSBundle *bundle = [NSBundle bundleWithPath:BUNDLE_PATH]; | |
[bundle load]; | |
NSLog(@"bundle name = %@", bundle); | |
tresult res = 0; | |
{ | |
NSString *bundlePath = BUNDLE_PATH; | |
NSURL *bundleURL = [NSURL fileURLWithPath:bundlePath]; | |
CFBundleRef cfBundle = NULL; | |
cfBundle = CFBundleCreate(kCFAllocatorDefault, (CFURLRef)bundleURL); | |
BundleEntryFunc entryFunc = (BundleEntryFunc)CFBundleGetFunctionPointerForName(cfBundle,CFSTR("bundleEntry")); | |
if (!entryFunc){ | |
NSLog(@"Bundle does not export the required 'bundleEntry' function"); | |
return; | |
} | |
bool ret = entryFunc(cfBundle); | |
NSLog(@"bundleEntry return %d", ret); | |
THEAPI func = (THEAPI)CFBundleGetFunctionPointerForName(cfBundle, CFSTR("GetPluginFactory")); | |
if(!func){ | |
NSLog(@"GetPluginFactory() not found. Maybe not VST3 Plugin"); | |
return; | |
} | |
IPluginFactory *pluginFactory = func(); | |
if (!pluginFactory){ | |
NSLog(@"failed with get IPluginFactory"); | |
return; | |
} | |
NSLog(@"pluginFactory has %d classes.", pluginFactory->countClasses()); | |
int32 classNum = pluginFactory->countClasses(); | |
for (int i = 0; i < classNum; i++){ | |
PClassInfo classInfo; | |
pluginFactory->getClassInfo(i, &classInfo); | |
NSLog(@"class[%d],%s,category=%s", i, classInfo.name, classInfo.category); | |
if (0 == strncmp(classInfo.category, kVstAudioEffectClass, strlen(kVstAudioEffectClass))){ | |
FUnknown *iUnknown = NULL; | |
res = pluginFactory->createInstance(classInfo.cid, FUnknown::iid, (void **)&iUnknown); | |
if (res != kResultOk){ | |
NSLog(@"createInstance failed with %d", res); | |
continue; | |
} | |
Vst::IComponent *iComponent = NULL; | |
res = iUnknown->queryInterface(Vst::IComponent::iid, (void **)&iComponent); | |
if (res != kResultOk){ | |
NSLog(@"queryIntarface for IComponent failed with %d", res); | |
continue; | |
} | |
res = iComponent->initialize(&hostApplication); | |
if(res != kResultOk){ | |
NSLog(@"failed with IComponent::initialize() %d", res); | |
continue; | |
} | |
res = iComponent->setActive(1); | |
if(res != kResultOk){ | |
NSLog(@"failed with IComponent::setActive(1). %d", res); | |
} | |
//get IAudioProcessor and do some stuff. | |
Vst::IAudioProcessor *iAudioProcessor = NULL; | |
res = iUnknown->queryInterface(Vst::IAudioProcessor::iid, (void **)(&iAudioProcessor)); | |
if (res != kResultOk){ | |
NSLog(@"failed for queryInterface IAudioProcessor"); | |
continue; | |
} | |
Vst::SpeakerArrangement sa = Vst::SpeakerArr::kStereo; | |
res = iAudioProcessor->setBusArrangements(&sa, 1, &sa, 1); | |
if (res != kResultOk){ | |
NSLog(@"failed for setBusArrangements res = %d", res); | |
continue; | |
} | |
Vst::ProcessSetup processSetup; | |
processSetup.maxSamplesPerBlock =32; | |
processSetup.processMode = Vst::kRealtime; | |
processSetup.sampleRate = 44100; | |
processSetup.symbolicSampleSize = Vst::kSample32; | |
res = iAudioProcessor->setupProcessing(processSetup); | |
if (res != kResultOk){ | |
NSLog(@"failed for setupProcessing res = %d", res); | |
continue; | |
} | |
//getControllerClassId()で取得したEditControllerのCIDを使ってcreateInstance()する | |
TUID controllerClassId; | |
res = iComponent->getControllerClassId(controllerClassId); | |
if (res != kResultOk){ | |
NSLog(@"failed with getControllerClassId(fx) with %d", res); | |
continue; | |
} | |
Vst::IEditController *editController = NULL; | |
res = pluginFactory->createInstance(controllerClassId, Vst::IEditController::iid, | |
(void **)&editController); | |
if (editController){ | |
[self IEditorControllerObtained:editController]; | |
}else{ | |
NSLog(@"failed for createInstance for controller, IEditController. res = %d", res); | |
continue; | |
} | |
}else if (0 == strncmp(classInfo.category, kVstComponentControllerClass, strlen(kVstComponentControllerClass))){ | |
// FUnknown *iUnknown = NULL; | |
// res = pluginFactory->createInstance(classInfo.cid, FUnknown::iid, (void **)&iUnknown); | |
// if (iUnknown){ | |
// Vst::IEditController *editController = NULL; | |
// res = iUnknown->queryInterface(Vst::IEditController::iid, (void **)&editController); | |
// if (editController){ | |
// [self IEditorControllerObtained:editController]; | |
// } | |
// } | |
} | |
} | |
} | |
} | |
// | |
-(void)IEditorControllerObtained:(Vst::IEditController *)editorController{ | |
tresult res = 0; | |
//適当なやつを渡す。 | |
res = editorController->initialize(&hostApplication); | |
if (res != kResultOk){ | |
NSLog(@" failed with initialize"); | |
return; | |
}else{ | |
NSLog(@" OK to initialize"); | |
} | |
//適当なやつを渡す。 | |
res = editorController->setComponentHandler(&componentHandler); | |
if(res != kResultOk){ | |
NSLog(@" failed with setComponentHandler"); | |
return; | |
}else{ | |
NSLog(@" OK to setComponentHandler"); | |
} | |
//ここまではエラーなしで来ることを確認済み。 | |
int32 c = editorController->getParameterCount(); | |
NSLog(@" has %d parameters",c); | |
for (int i=0; i < c; i++){ | |
Vst::ParameterInfo paramInfo = Vst::ParameterInfo(); | |
res = editorController->getParameterInfo(i, paramInfo); | |
NSString *title = [[NSString alloc] initWithCharacters:(const unichar *)paramInfo.title length:128]; | |
NSString *units = [[NSString alloc] initWithCharacters:(const unichar *)paramInfo.units length:128]; | |
Vst::ParamID paramId = paramInfo.id; | |
Vst::ParamValue defNormalizedValue = paramInfo.defaultNormalizedValue; | |
NSLog(@" parameter[%d]:%@(%d)=%f%@", i, title, paramId,defNormalizedValue,units); | |
} | |
IPlugView *view = editorController->createView("editor"); | |
NSLog(@" view = %p", view); | |
if(view){ | |
res = view->isPlatformTypeSupported(kPlatformTypeNSView); | |
NSLog(@" isPlatformTypeSupported(NSView) res = %d", res); | |
res = view->attached((__bridge void *)_pluginEditorSuperView, kPlatformTypeNSView); | |
NSLog(@" attached res = %d",res); | |
} | |
} | |
//------------------------------------------------------------------------header for IHostApplication side | |
using namespace Steinberg; | |
class MyComponentHandler : public Vst::IComponentHandler, public Vst::IComponentHandler2 | |
{ | |
public: | |
tresult PLUGIN_API beginEdit (Vst::ParamID id) override | |
{ | |
NSLog(@"beginEdit called (%d)\n", id); | |
return kNotImplemented; | |
} | |
tresult PLUGIN_API performEdit (Vst::ParamID id, Vst::ParamValue valueNormalized) override | |
{ | |
NSLog(@"performEdit called (%d, %f)\n", id, valueNormalized); | |
return kNotImplemented; | |
} | |
tresult PLUGIN_API endEdit (Vst::ParamID id) override | |
{ | |
NSLog(@"endEdit called (%d)\n", id); | |
return kNotImplemented; | |
} | |
tresult PLUGIN_API restartComponent (int32 flags) override | |
{ | |
NSLog(@"restartComponent called (%d)\n", flags); | |
return kNotImplemented; | |
} | |
tresult PLUGIN_API setDirty (TBool state) override { | |
NSLog(@"setDirty called"); | |
return kNotImplemented; | |
} | |
tresult PLUGIN_API requestOpenEditor (FIDString name = Vst::ViewType::kEditor) override { | |
NSLog(@"requestOpenEditor"); | |
return kNotImplemented; | |
} | |
tresult PLUGIN_API startGroupEdit() override { | |
NSLog(@"startGroupEdit called"); | |
return kNotImplemented; | |
} | |
/** Finishes the group editing started by a \ref startGroupEdit (call after a \ref IComponentHandler::endEdit). */ | |
tresult PLUGIN_API finishGroupEdit () override { | |
NSLog(@"finishGroupEdit"); | |
return kNotImplemented; | |
} | |
private: | |
tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) override | |
{ | |
NSUUID *uuid = [[NSUUID alloc] initWithUUIDBytes:(const unsigned char *)_iid]; | |
NSLog(@"COmponentHandler::queryInterface get called for TUID:%@", uuid.UUIDString); | |
*obj = (Vst::IComponentHandler2 *)this; | |
return kResultOk; | |
} | |
uint32 PLUGIN_API addRef () override { return 1000; } | |
uint32 PLUGIN_API release () override { return 1000; } | |
}; | |
namespace Steinberg{ | |
namespace Vst{ | |
class HostApplication : public IHostApplication | |
{ | |
public: | |
HostApplication(); | |
virtual ~HostApplication() { FUNKNOWN_DTOR } | |
//--- IHostApplication --------------- | |
tresult PLUGIN_API getName (String128 name) SMTG_OVERRIDE; | |
tresult PLUGIN_API createInstance (TUID cid, TUID _iid, void** obj) SMTG_OVERRIDE; | |
DECLARE_FUNKNOWN_METHODS | |
PlugInterfaceSupport* getPlugInterfaceSupport () const { | |
NSLog(@"HostApplication::getPlugInterfaceSupport called"); | |
return mPlugInterfaceSupport; | |
} | |
protected: | |
IPtr<PlugInterfaceSupport> mPlugInterfaceSupport; | |
}; | |
} | |
} | |
//-----------------------------------cpp for IComponent, IHostApplication | |
namespace Steinberg{ | |
namespace Vst{ | |
HostApplication::HostApplication () | |
{ | |
FUNKNOWN_CTOR | |
mPlugInterfaceSupport = owned (NEW PlugInterfaceSupport); | |
} | |
tresult PLUGIN_API HostApplication::getName (String128 name) | |
{ | |
NSLog(@"hogehoge"); | |
return kResultTrue; | |
} | |
tresult PLUGIN_API HostApplication::createInstance (TUID cid, TUID _iid, void** obj) | |
{ | |
NSLog(@"HostApplication::createInstance"); | |
FUID classID (FUID::fromTUID (cid)); | |
FUID interfaceID (FUID::fromTUID (_iid)); | |
if (classID == IMessage::iid && interfaceID == IMessage::iid) | |
{ | |
NSLog(@"IMessage"); | |
// *obj = new HostMessage; | |
return kResultTrue; | |
} | |
else if (classID == IAttributeList::iid && interfaceID == IAttributeList::iid) | |
{ | |
NSLog(@"IAttributeList"); | |
// *obj = new HostAttributeList; | |
return kResultTrue; | |
} | |
*obj = nullptr; | |
return kResultFalse; | |
} | |
//----------------------------------------------------------------------------- | |
tresult PLUGIN_API HostApplication::queryInterface (const char* _iid, void** obj) | |
{ | |
NSUUID *uuid = [[NSUUID alloc] initWithUUIDBytes:(const unsigned char *)_iid]; | |
NSLog(@"HostApplication::queryInterface get called for TUID:%@", uuid.UUIDString); | |
QUERY_INTERFACE (_iid, obj, FUnknown::iid, IHostApplication) | |
QUERY_INTERFACE (_iid, obj, IHostApplication::iid, IHostApplication) | |
if (mPlugInterfaceSupport && mPlugInterfaceSupport->queryInterface (iid, obj) == kResultTrue) | |
return kResultOk; | |
NSLog(@"failed ?"); | |
*obj = nullptr; | |
return kResultFalse; | |
} | |
//----------------------------------------------------------------------------- | |
uint32 PLUGIN_API HostApplication::addRef () | |
{ | |
return 1; | |
} | |
//----------------------------------------------------------------------------- | |
uint32 PLUGIN_API HostApplication::release () | |
{ | |
return 1; | |
} | |
#include <algorithm> | |
//----------------------------------------------------------------------------- | |
PlugInterfaceSupport::PlugInterfaceSupport () | |
{ | |
// add minimum set | |
//---VST 3.0.0-------------------------------- | |
addPlugInterfaceSupported (IComponent::iid); | |
addPlugInterfaceSupported (IAudioProcessor::iid); | |
addPlugInterfaceSupported (IEditController::iid); | |
addPlugInterfaceSupported (IConnectionPoint::iid); | |
addPlugInterfaceSupported (IUnitInfo::iid); | |
addPlugInterfaceSupported (IUnitData::iid); | |
addPlugInterfaceSupported (IProgramListData::iid); | |
//---VST 3.0.1-------------------------------- | |
addPlugInterfaceSupported (IMidiMapping::iid); | |
//---VST 3.1---------------------------------- | |
addPlugInterfaceSupported (IEditController2::iid); | |
/* | |
//---VST 3.0.2-------------------------------- | |
addPlugInterfaceSupported (IParameterFinder::iid); | |
//---VST 3.1---------------------------------- | |
addPlugInterfaceSupported (IAudioPresentationLatency::iid); | |
//---VST 3.5---------------------------------- | |
addPlugInterfaceSupported (IKeyswitchController::iid); | |
addPlugInterfaceSupported (IContextMenuTarget::iid); | |
addPlugInterfaceSupported (IEditControllerHostEditing::iid); | |
addPlugInterfaceSupported (IXmlRepresentationController::iid); | |
addPlugInterfaceSupported (INoteExpressionController::iid); | |
//---VST 3.6.5-------------------------------- | |
addPlugInterfaceSupported (ChannelContext::IInfoListener::iid); | |
addPlugInterfaceSupported (IPrefetchableSupport::iid); | |
addPlugInterfaceSupported (IAutomationState::iid); | |
//---VST 3.6.11-------------------------------- | |
addPlugInterfaceSupported (INoteExpressionPhysicalUIMapping::iid); | |
//---VST 3.6.12-------------------------------- | |
addPlugInterfaceSupported (IMidiLearn::iid); | |
//---VST 3.7----------------------------------- | |
addPlugInterfaceSupported (IProcessContextRequirements::iid); | |
addPlugInterfaceSupported (IParameterFunctionName::iid); | |
addPlugInterfaceSupported (IProgress::iid); | |
*/ | |
} | |
//----------------------------------------------------------------------------- | |
tresult PLUGIN_API PlugInterfaceSupport::isPlugInterfaceSupported (const TUID _iid) | |
{ | |
auto uid = FUID::fromTUID (_iid); | |
if (std::find (mFUIDArray.begin (), mFUIDArray.end (), uid) != mFUIDArray.end ()) | |
return kResultTrue; | |
return kResultFalse; | |
} | |
//----------------------------------------------------------------------------- | |
void PlugInterfaceSupport::addPlugInterfaceSupported (const TUID _iid) | |
{ | |
mFUIDArray.push_back (FUID::fromTUID (_iid)); | |
} | |
//----------------------------------------------------------------------------- | |
bool PlugInterfaceSupport::removePlugInterfaceSupported (const TUID _iid) | |
{ | |
return std::remove (mFUIDArray.begin (), mFUIDArray.end (), FUID::fromTUID (_iid)) != | |
mFUIDArray.end (); | |
} | |
} | |
} |
市販(といってもフリーのいろいろ)だとダメ
調べてみた感じ、 IEditController は単体だとパラメータ状態が取れないことがあるようで、先に Vst::IConnectionPoint を使って IComponent と相互接続をしておく必要がありました。手元で実験してみたところ、この接続処理をやってから getParameterCount() を呼び出せば正しくパラメータ数が取れました。
他には、次のような点が気になりました。
- IComponent と IEditController が一つのコンポーネントで実装されている場合に、 getControllerClassId() では IEditController の CID が取れないので、そのケースに対処する必要がある。
- ロード直後の IComponent の状態をもとに IEditController の状態を初期化するために、 IEditController の setComponentState() も呼び出しておいたほうがいい。
コードでいうと、こんな感じになりますねー
動きました! kyab/Late5-Studio@233b276
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
参照カウントを正しく実装するようにします。
後デバッグも参考になります。 なぜかADelayとか使うとうまくパラメータ取れちゃった。。