<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      VST音頻插件架構分析

      VST音頻插件架構分析

      前言

      Virtual Studio Technology (VST),可以翻譯為虛擬工作室技術,是一種音頻插件軟件接口,由德國Steinberg公司發布,用于在Digital Audio Workstation (DAW,可以譯為數字音頻工作站)中集成合成器和效果器。

      現在主要還在用的是VST 2和VST 3這兩個版本,VST 2是對VST 1的擴充,所以一塊講;但VST 3則是相當于一個全新的架構,所以要和VST 2分開來講。

      官方提供了VST的SDK,其中VST 2的已經沒有了(但是可以在其他地方找到),只有VST 3的可以在官網找到。不過本文并不分析完整的SDK,而是分析其架構,所以只會分析SDK中關于VST的API部分。

      你可以在這里找到VST 3的官方英文文檔,也可以在這里找到VST 3 SDK的下載鏈接,其API也在SDK中;或者可以去他們的GitHub倉庫去Clone完整SDK代碼。

      如果你需要VST 2 SDK,可以去這個GitHub倉庫下載,有需要的建議還是下了,萬一以后真沒了也不好說。不過VST 2我是真的找不到文檔了。

      不得不說,Steinberg為了強推VST 3而拋棄VST 2,甚至下架其SDK以及停止授權等行為,確實飽受詬病,不過這是人家公司自己的選擇,而我們依然可以接著用,甚至用競爭對手的技術,隨他怎么搞呢,反正哪個流行用哪個就是了。只不過唯一的問題就是如果銷售的插件或者DAW帶有VST 2,估計得給人家告死,所以想拿來賣只能用VST 3,當然如果開源免費的話肯定無所謂。

      本文要分析的所有代碼均來自pluginterfaces文件夾。

      目錄

      VST 2

      核心文件就是在pluginterfaces/vst2.x文件夾下的aeffect.haeffectx.h,其中第一個是VST 1的,第二個是VST 2的補充,所以其整體架構就是延續了VST 1的,因此先分析一下aeffect.h的東西。

      跳過前面無聊的部分,我們直奔主題——幾個重要回調函數和核心結構體(刪了部分注釋和條件編譯,同時不考慮VST 2.4不使用的內容,也就是標了DECLARE_VST_DEPRECATED的):

      // 主機回調,這個是需要由主機實現一系列的opcode,然后給插件調用的
      typedef VstIntPtr (VSTCALLBACK *audioMasterCallback) (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt);
      // 調度函數,插件實現一系列的opcode,提供給主機調用的
      typedef VstIntPtr (VSTCALLBACK *AEffectDispatcherProc) (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt);
      // 音頻處理函數,插件實現,由主機提供輸入和輸出緩沖區,以及要處理的采樣數,通常在單獨的線程調用
      typedef void (VSTCALLBACK *AEffectProcessProc) (AEffect* effect, float** inputs, float** outputs, VstInt32 sampleFrames);
      // 同上,但是緩沖區的double類型的浮點數,而不是float類型的,有更高的精度
      typedef void (VSTCALLBACK *AEffectProcessDoubleProc) (AEffect* effect, double** inputs, double** outputs, VstInt32 sampleFrames);
      // 參數設置函數,主機提供一個索引和一個參數值,插件負責保存對應的參數
      typedef void (VSTCALLBACK *AEffectSetParameterProc) (AEffect* effect, VstInt32 index, float parameter);
      // 參數獲取函數,主機提供一個索引,插件返回該索引對應參數的值
      typedef float (VSTCALLBACK *AEffectGetParameterProc) (AEffect* effect, VstInt32 index);
      
      // 前面幾個回調函數的第一個參數需要的結構體
      struct AEffect
      {
          VstInt32 magic; // "VstP",按32位整數保存,注意'V'是在地址高位的,即0x56737450
          AEffectDispatcherProc dispatcher; // AEffectDispatcherProc函數指針
          AEffectProcessProc DECLARE_VST_DEPRECATED (process);
          AEffectSetParameterProc setParameter; // AEffectSetParameterProc函數指針
          AEffectGetParameterProc getParameter; // AEffectGetParameterProc函數指針
          VstInt32 numPrograms; // 預設的數量
          VstInt32 numParams;   // 參數的數量
          VstInt32 numInputs;   // 輸入通道數量
          VstInt32 numOutputs;  // 輸出通道數量
          VstInt32 flags; // 見VstAEffectFlags
          VstIntPtr resvd1; // 給主機留著的,置0
          VstIntPtr resvd2; // 同上
          VstInt32 initialDelay; // 大概是某些算法有延遲,這個參數告訴主機延遲的采樣數,一般就0
          VstInt32 DECLARE_VST_DEPRECATED (realQualities);
          VstInt32 DECLARE_VST_DEPRECATED (offQualities);
          float    DECLARE_VST_DEPRECATED (ioRatio);
          void* object; // 指針,指向SDK里的類,不用SDK的話可以自定義使用,每個實例都不一樣
          void* user;   // 留給插件開發者自由分配的指針
          VstInt32 uniqueID; // 每個插件要一個獨一無二的標志,得找Steinberg申請,現在也沒啥用了,盡可能獨一無二
          VstInt32 version;  // 插件的版本
          AEffectProcessProc processReplacing; // 替換模式的process函數,這個替換應該是指輸入輸出緩沖區是同一個
          AEffectProcessDoubleProc processDoubleReplacing; // 同上,但是使用double類型的采樣
          char future[56]; // 留給未來的(然而沒有了),置0
      };
      
      // 上面AEffect里flags需要的標志位
      enum VstAEffectFlags
      {
          effFlagsHasEditor     = 1 << 0, // 插件有編輯器(UI界面)
          effFlagsCanReplacing  = 1 << 4, // 支持processReplacing,必須要設置
          effFlagsProgramChunks = 1 << 5, // 沒用過,感覺沒啥用
          effFlagsIsSynth       = 1 << 8, // 合成器得設置這個
          effFlagsNoSoundInStop = 1 << 9, // 沒用過,大概是說插件保證process的時候輸入為0就不會輸出聲音
          effFlagsCanDoubleReplacing = 1 << 12, // 支持processDoubleReplacing,可選
          DECLARE_VST_DEPRECATED (effFlagsHasClip) = 1 << 1,
          DECLARE_VST_DEPRECATED (effFlagsHasVu)   = 1 << 2,
          DECLARE_VST_DEPRECATED (effFlagsCanMono) = 1 << 3,
          DECLARE_VST_DEPRECATED (effFlagsExtIsAsync)   = 1 << 10,
          DECLARE_VST_DEPRECATED (effFlagsExtHasBuffer) = 1 << 11
      };
      

      注意回調函數那邊的VSTCALLBACK其實就是C語言默認的函數調用約定,前面有定義,但實際上用C開發的話沒必要。這幾個回調函數里,一般情況下除了音頻處理的函數需要在單獨的線程中進行,其它的都是在UI線程進行的。

      結構體主要提供插件的基本信息以及一些回調函數的指針,根據需要設置就行了,還有這個flags必須設置effFlagsCanReplacing,因為大部分插件都是有界面的,所以基本上都要設置effFlagsHasEditor,如果是合成器就設置effFlagsIsSynth,不然默認是效果器,最后這個effFlagsCanDoubleReplacing看需不需要雙精度浮點數吧,建議是要的,因為現在大部分軟件和插件都是64位的,一般64位CPU處理double很快而且精度更高,大部分DAW內部也是用double類型的數據。

      插件的入口函數一般是這么定義的:

      AEffect *VSTPluginMain(audioMasterCallback hostCallback)
      {
          new MyPlugin(hostCallback)->getAEffect();
      }
      

      這里的MyPlugin是一個C++的類,參數就是audioMasterCallback的回調,然后調用getAEffect獲取該類對應的AEffect結構體指針,每個插件的實例都要這樣,不然插件之間會影響。

      入口函數的名稱除了VSTPluginMain也可以是main,一般只要前者,記得要在DLL導出,不同編譯器有不同的方法。

      接著說幾個關鍵opcode,插件必須要實現的或者是實現基本功能需要的,否則插件跑不起來,opcode的定義在aeffect.haeffectx.h里,分別是枚舉AEffectOpcodesAudioMasterOpcodesX

      • effGetVstVersion,一定要實現,返回2400,不然主機(大概率)會認為這個插件是老版本的
      • effGetParamLabel、effGetParamDisplay、effGetParamName,可選,和參數的顯示有關,可以在DAW看到
      • effSetSampleRate、effSetBlockSize,有些效果需要采樣率、塊大小之類的參數
      • effCanBeAutomated,參數是否可以被自動化,挺有用的
      • effEditGetRect、effEditOpen、effEditClose,界面相關的,有插件界面肯定要實現
      • effEditIdle,主機在UI線程調用,用來給插件更新界面用的
      • effGetChunk、effSetChunk,保存或設置所有參數的值,給主機用來實現插件參數狀態的保存

      除了插件要實現opcode,有些功能需要調用主機提供的回調函數,在枚舉AudioMasterOpcodesAudioMasterOpcodesX中。

      • audioMasterAutomate,比如用戶拖動界面的控件,調用這個告訴主機這個參數可以被自動化
      • audioMasterBeginEdit、audioMasterEndEdit,在用戶開始修改參數到結束修改參數時調用,通常是鼠標按下到松開
      • audioMasterGetDirectory,向主機詢問插件DLL的路徑,一般用來加載資源用

      所以你只要實現最基本的AEffect結構體,實現幾個核心回調函數,就可以實現一個最簡單的VST 2.4插件了。

      至于其他沒提到的opcode,還有一大堆結構體、常量,不在本文討論之列。

      這樣看來,VST 2架構的本質就是用到了C風格的回調函數,這很像Windows的窗口機制(WndProc函數)。

      VST 3

      VST 3之于VST 2,和VST 2與VST 1的關系完全不同,前者更像是重寫的架構,而不是后者一樣的擴充。

      VST 3是由一組類似于COM的接口和一系列數據結構組成的。

      如果說詳細點,VST 3是由VST MAVST 3 API組成的,分別在pluginterfaces/basepluginterfaces/vst這兩個文件夾里。

      熟悉COM的看這個應該沒什么問題, 不熟悉的話建議先了解一下COM技術,會稍微好一點。

      與COM的IUnknown不同,VST自己定義了FUnknown,不過其實是ABI兼容的,VST的所有和COM類似的部分都是自己定義的,目的就是去除對Windows平臺(COM有關頭文件)的依賴,實現跨平臺。

      class FUnknown
      {
      public:
          virtual tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) = 0;
          virtual uint32 PLUGIN_API addRef () = 0;
          virtual uint32 PLUGIN_API release () = 0;
      
          static const FUID iid;
      };
      DECLARE_CLASS_IID (FUnknown, 0x00000000, 0x00000000, 0xC0000000, 0x00000046)
      

      類型tresult類似于HRESULT,其實就是一個32位整數,0表示OK,其余都是一個負數,表示各種錯誤。

      類型TUID是這么定義的typedef int8 TUID[16];,其實和GUID是兼容的。

      類型FUID是一個類,但是沒有虛函數,其實可以理解為僅有一個TUID成員的帶方法的高級結構體。

      調用約定是PLUGIN_API,在Windows我平臺是stdcall,在其他平臺是cdecl。

      源代碼文件里好多東西,真正有用的其實不多,當然用C++搞這個COM接口確實蛋疼,畢竟語言級別的支持沒有,比如每個接口都有的靜態常量iid,實際上Steinberg是自己實現了一個FObject對象來實現的各種功能,還有一堆宏。不過實現的方法有很多,如果說把VST 3的接口翻譯到別的語言,有些功能實現起來可能會輕松不少。

      接下來是VST MA里的重要接口,IPluginBase,這個接口可以說是VST 3所有插件的基礎了。

      class IPluginBase: public FUnknown
      {
      public:
          // 初始化,給的接口必須可以獲取IHostApplication,同時插件應該在這個方法里實現內存分配等任務,而不是構造函數里
          virtual tresult PLUGIN_API initialize (FUnknown* context) = 0;
          // 終止,之前分配的內存在這里銷毀
          virtual tresult PLUGIN_API terminate () = 0;
      
          static const FUID iid;
      };
      DECLARE_CLASS_IID (IPluginBase, 0x22888DDB, 0x156E45AE, 0x8358B348, 0x08190625)
      

      主機保證會在需要的時候調用initialize,同時確保會調用terminate讓插件釋放資源。

      之后會講到的幾個VST 3接口,都是在其基礎之上的,不過在此之前,最后講一下VST MA里最后一個關鍵接口——IPluginFactory,很顯然,用的工廠模式。

      class IPluginFactory : public FUnknown
      {
      public:
          // 獲取插件工廠信息
          virtual tresult PLUGIN_API getFactoryInfo (PFactoryInfo* info) = 0;
          // 獲取類的數量,一般都是2
          virtual int32 PLUGIN_API countClasses () = 0;
          // 獲取索引index的類的信息
          virtual tresult PLUGIN_API getClassInfo (int32 index, PClassInfo* info) = 0;
          // 根據類的16位UID和接口的16位UID創建實例,FIDString就是char *
          virtual tresult PLUGIN_API createInstance (FIDString cid, FIDString _iid, void** obj) = 0;
      
          static const FUID iid;
      };
      
      DECLARE_CLASS_IID (IPluginFactory, 0x7A4D811C, 0x52114A1F, 0xAED9D2EE, 0x0B43BF9F)
      

      其中PFactoryInfoPClassInfo如下:

      struct PFactoryInfo
      {
          enum FactoryFlags
          {
              kNoFlags                 = 0,
              kClassesDiscardable      = 1 << 0, // 工廠的類可變,主機得累死,一般不用
              kLicenseCheck            = 1 << 1, // 最新版已經棄用了,應該是檢查許可證
              kComponentNonDiscardable = 1 << 3, // 組件不會被卸載,直到進程退出
              kUnicode                 = 1 << 4  // 組件支持Unicode,其實是UTF-16,基本都要支持,要國際化的嘛
          };
          enum
          {
              kURLSize = 256,
              kEmailSize = 128,
              kNameSize = 64
          };
          char8 vendor[kNameSize]; // 供應商名稱
          char8 url[kURLSize];     // 供應商網站
          char8 email[kEmailSize]; // 供應商電子郵箱
          int32 flags; // 上面的FactoryFlags
      };
      
      struct PClassInfo
      {
          enum ClassCardinality
          {
              kManyInstances = 0x7FFFFFFF // 允許多個實例,也就是加載好幾個
          };
          enum
          {
              kCategorySize = 32,
              kNameSize = 64
          };
          TUID cid; // 這個類的UID,自己隨機生成一個就行了,基本確保唯一性
          int32 cardinality; // 設置上面那個kManyInstances
          char8 category[kCategorySize]; // 類的類別,比如"Audio Module Class"、"Component Controller Class"這種
          char8 name[kNameSize]; // 類的名稱,自己起一個
      };
      

      這個工廠后來還擴充了IPluginFactory2IPluginFactory3,接口如下:

      class IPluginFactory2 : public IPluginFactory
      {
      public:
          // 獲取PClassInfo2的類信息
          virtual tresult PLUGIN_API getClassInfo2 (int32 index, PClassInfo2* info) = 0;
      
          static const FUID iid;
      };
      
      DECLARE_CLASS_IID (IPluginFactory2, 0x0007B650, 0xF24B4C0B, 0xA464EDB9, 0xF00B2ABB)
      
      struct PClassInfo2
      {
          TUID cid; // 兼容PClassInfo結構體
          int32 cardinality; // 同上
          char8 category[PClassInfo::kCategorySize]; // 同上
          char8 name[PClassInfo::kNameSize]; // 同上
          enum {
              kVendorSize = 64,
              kVersionSize = 64,
              kSubCategoriesSize = 128
          };
          uint32 classFlags; // 目前僅針對"Audio Module Class",有kDistributable和kSimpleModeSupported
          char8 subCategories[kSubCategoriesSize]; // 子類別,比如"Fx"這種,有具體的常量定義
          char8 vendor[kVendorSize]; // 供應商名稱
          char8 version[kVersionSize]; // 版本,比如"1.0.0.0"
          char8 sdkVersion[kVersionSize]; // VST版本,有常量定義,比如"VST 3.7.8"
      };
      
      class IPluginFactory3 : public IPluginFactory2
      {
      public:
          // 沒啥好說的
          virtual tresult PLUGIN_API getClassInfoUnicode (int32 index, PClassInfoW* info) = 0;
          // 主機提供一個上下文接口,應該也是實現了IHostApplication
          virtual tresult PLUGIN_API setHostContext (FUnknown* context) = 0;
      
          static const FUID iid;
      };
      
      DECLARE_CLASS_IID (IPluginFactory3, 0x4555A2AB, 0xC1234E57, 0x9B122910, 0x36878931)
      
      // 相比于PClassInfo2,就一些char8換成了char16,沒啥好說的
      struct PClassInfoW
      {
          TUID cid;
          int32 cardinality;
          char8 category[PClassInfo::kCategorySize]; // 這個沒變,因為有常量定義,只能是ASCII字符串
          char16 name[PClassInfo::kNameSize];
          enum {
              kVendorSize = 64,
              kVersionSize = 64,
              kSubCategoriesSize = 128
          };
          uint32 classFlags;
          char8 subCategories[kSubCategoriesSize];
          char16 vendor[kVendorSize];
          char16 version[kVersionSize];
          char16 sdkVersion[kVersionSize];
      };
      

      實際開發中,一般只要自己設置PClassInfo2,然后轉成PClassInfoW就行了,如果用Steinberg提供的工具的話,甚至都不用寫這些代碼。

      作為插件,在動態鏈接庫里需要導出名為GetPluginFactory的函數,用來獲得工廠對象,一個可能的實現如下:

      SMTG_EXPORT_SYMBOL IPluginFactory* PLUGIN_API GetPluginFactory ()
      {
          static MyPluginFactory *gFactory = nullptr;
          if (!gFactory) {
              gFactory = new MyPluginFactory;
          }
          else {
              gFactory->addRef();
          }
          return gFactory;
      }
      

      在Windows平臺,還有可選的InitDllExitDll,可選是因為Windows有DllMain,可以實現加載/卸載時的資源申請/釋放操作,但是Linux和macOS就不這樣了。在Linux平臺,使用ModuleEntryModuleExit;而在macOS平臺,使用BundleEntryBundleExit。主機必須在加載插件后調用XxxEntry,且在卸載前調用XxxExit

      主機獲取到了插件的工廠之后,就會去依次調用幾個方法獲取相關信息,同時還會嘗試調用queryInterface來獲取IPluginFatory2IPluginFactory3的對象,最后調用createInstance來創建IComponentIEditController的對象。

      主機一般會先調用createInstance獲取IComponent,然后通過queryInterface獲取IAudioProcessor

      Steinberg建議插件的音頻處理部分和參數控制部分分離,這個需要在classFlags里設置kDistributable,如果沒有設置,那么主機就會嘗試通過IComponent對象接著獲取IEditController,否則就重新調用createInstance。分離的好處是靈活,不分離好處是代碼簡單,因為這幾個接口都可以在同一個類里面實現。

      可以參考一下Audio Processor調用過程圖Edit Controller調用過程圖

      關于IComponentIAudioProcessorIEditController就不多介紹了,和底層架構關系不大,已經是特定功能的接口了。

      要實現其他功能的話,比如GUI,還得有IPlugViewIPlugFrame,如果要在GUI控制參數的話還要IComponentHandler,還有好多接口以及各種各樣的結構體,反正突出一個字——亂。

      其實到這里,我也只是講了VST 3那么多機制的冰山一角,當然底層架構基本就算是說完了,剩下的就是在這套架構的基礎上增加的各種各樣的功能了。

      VST 2和VST 3的對比

      在開始對比之前,我想先說一下歷史,來捋一下時間線。

      COM是微軟在1993年首次提出的,VST 1.0是在1996年推出的,在90年代那會,COM逐漸流行起來,然后在1999年推出了VST 2,接下來幾年就不斷更新,到了2006年的2.4就不動了,隨后就是2008年的VST 3。2013年,Steinberg宣布不再維護VST 2 SDK,但是開發者基本不受影響,直到2018年,Steinberg準備停止VST 2的支持,也就意味著18年之后,任何開發者如果沒有在之前和Steinberg簽了協議的話,就不能開發商業的VST 2插件了。而到了2022年,Steinberg更是直接宣布他家軟件將最后支持VST 2插件24個月,然后就會移除VST 2的支持,然后就引起了罵聲一片。

      奇怪的是,明明1993年就有了COM,為啥2008年的VST 3才用了這個呢?事實上并不奇怪,因為VST 1到VST 2那段時間,VST插件可以說是非常火爆的,即使Steinberg在自己的軟件Cubase和Nuendo已經用了類似COM的技術,也就是VST MA,但是想要推廣開來還是需要時間的,所以他們也只能硬著頭皮先更新VST 2再說。不過由于VST 2不符合Steinberg的利益需要了,所以他們推出了新架構的VST 3,但是實際上如果接著擴充VST 2,未嘗不可實現現在的一下新功能,只不過VST 2的架構勢必會變得更加臃腫——增加更多opcode,棄用一些老的opcode,甚至增加新的回調函數。

      但不論如何,對于廣大開發者而言,VST 3相比于VST 2并沒有什么升級,而是換了一種Steinberg喜歡的開發方式而已,插件該有的功能基本都有,沒有的很多主機也不會去實現,而且給VST 2接著打補丁也是可以實現的,況且VST 3更龐大,更復雜。但是Steinberg偏不,強推新的VST 3,甚至到現在打算完全放棄支持舊的VST 2。

      可以看看國外的這篇文章,或者國內有人發布的翻譯版本,基本說了二者的一些區別與好壞。

      國外也有人討論關于VST 3為數不多的好功能——樣本精確自動化(提供采樣點和參數的列表,能提高參數自動化時的精度,同時性能損失較少),不過大部分DAW很難支持,因為其他的插件格式不一定有這個功能,況且目前也有DAW(說的就是某水果形狀的軟件)做了減小單次處理的樣本數量這樣的方法來提高參數自動化的精度(對有些插件來說這樣會出現問題)。當然這個功能確實非常不錯,但是實際上在VST 2的架構上擴充,未嘗不能實現。

      從技術架構的角度出發,顯然VST 2更加通用,兼容性更好,因為插件的底層就是C風格的回調函數,這適用于絕大多數的語言,所以可以很輕松地在其他語言實現,比如現在比較火的Rust、Zig這類的。

      而VST 3的SDK則是和C++深度綁定,雖然官方也提供了C風格的接口,實際上就是微軟COM的早期模式,用C語言的結構體來模擬現在C++的虛函數表。但是和C++的過度耦合,使得其他語言很難實現,但是也不是沒有,比如Rust也有人做過。

      至于我為什么會對這倆玩意這么熟悉,那就得回到2021年,那會對VST 2插件的開發有些興趣,又不想用C/C++這樣的技術開發,想用我之前比較熟悉的Object Pascal開發,同時配合Delphi的VCL或者Lazarus的LCL庫可以輕松實現GUI界面的開發,尤其是LCL可以輕松跨平臺。

      所以在那年陸陸續續花了幾個月就搞清楚了VST 2的架構,就是用最基礎的C風格的回調構建代碼,寫了一些基礎的插件,當然是因為DSP那塊不是很熟悉,也不會復雜的算法。

      后來考慮到VST 2確實也比較老了,加上我又沒趕上當時最后一波的機會(2018年之后就無法獲取VST 2專用授權許可證了),所以也不可能再發布VST 2的任何插件了,所以就將目光轉向了VST 3(在專用授權許可證之外,還可以選用GPLv3許可證),然后就斷斷續續花了兩年時間,到最近(2023年7月)才終于完成最后一步——摸清架構,寫出基礎插件。花了這么久的時間,主要還是C++的代碼翻起來太難受了,加上代碼量巨大,光是翻譯成Object Pascal的接口部分代碼,就將近4000行,再加上還有實現一個基礎插件還需要1000多行的代碼,這和VST 2完全不能比(接口1000多行,基礎插件幾百行)。

      如果你有興趣,可以光顧一下我的GitHub,關于VST 2和VST 3的Pascal版本的鏈接就放這里了:

      結語

      VST 2和VST 3是兩個獨立的技術路線,只因創造者Steinberg換了他們的口味,導致VST 2這一簡單好用且流行的格式被迫隨著時間而逐漸淡出人們視野。當然VST 3也不是一無是處,畢竟COM技術也是微軟多年實踐驗證過的,也沒什么問題。而且Steinberg提供了高度封裝的VST 3 SDK,以及AAX、AU等其他格式的包裝器,還有一系列的工具,使得實際的開發得到了簡化(當然大部分人可能用JUCE這種庫)。

      但是Steinberg做的不好的就是想要強行改變開發者習慣,在VST 2用的好好的情況下,居然用各種手段迫使開發者轉換到VST 3來,而且甚至還想要放棄支持VST 2。強如微軟也依然保持著基本的兼容性,甚至MIDI 2.0也是對MIDI 1.0的補充,要知道MIDI 1.0那是1983年的古老標準了。不是誰都是蘋果,可以有那么強的實力隨意破壞向后兼容性。

      希望Steinberg好自為之,接著支持原有的VST 2,好好經營VST 3,同時不要再讓未來可能的VST 4也這樣了。

      posted @ 2023-08-02 23:10  PeaZomboss  閱讀(650)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲日韩国产成网在线观看| 国产精品污一区二区三区| 亚洲精品日韩在线丰满| 在线观看无码不卡av| 汨罗市| 92国产精品午夜福利| 国产99精品成人午夜在线| 毛片在线播放网址| 日本中文字幕有码在线视频| 无码专区视频精品老司机| 亚洲精品日韩在线丰满| 国产毛片欧美毛片久久久| 国产91丝袜在线播放动漫| 国产精品粉嫩嫩在线观看| 日本高清免费不卡视频| 亚洲va久久久噜噜噜久久狠狠| 亚洲偷自拍另类一区二区| 亚洲嫩模喷白浆在线观看| 亚洲va韩国va欧美va| 亚洲国产成人综合精品| 国产精品一品二区三四区| 377人体粉嫩噜噜噜| 色综合人人超人人超级国碰| 国产黄色一区二区三区四区| 视频一区二区三区四区五区| 久久精品国产免费观看频道| 欧美一区二区三区性视频| 久久精品国产99国产精品严洲| 日韩精品无码一区二区视频| 久久中文字幕国产精品| 朔州市| 国产精品免费重口又黄又粗| 国产人妻精品无码av在线| 天天躁日日躁狠狠躁中文字幕| 樱桃视频影院在线播放| 精品福利一区二区三区免费视频| 又湿又紧又大又爽A视频男| 国产精品亚洲二区亚瑟| 日韩高清视频 一区二区| 临漳县| 成人精品网一区二区三区|