見出し画像

MATLABでVST開発 ~ Map オブジェクト(文字ベクトル Key データ構造体)を使ってVSTパラメータをセットする(R2022a対応版)

まえがき

以前の記事

externalAudioPlugin class ~ MATLABで外部VSTプラグインをホスト ~ 動的変数名を使ってVSTパラメータをセットする
で、

※ R2022aではパラメータの並び順が、
 loadAudioPlugin() : audioPluginParameter の設定順 で変更なし
 パラメータファイル : アルファベット順 に変更
となりインデックスによる突き合わせができなくなったため、パラメータファイルをMATLAB上で適用する手段がなくなりました・・。

と書きました。その後、"Prerelease Customer Survey" にその旨報告したところ MathWorks のサポートさんから連絡があり、何回かやりとりをして解決方法を教えてもらいました!

改めて問題点をおさらいすると・・、
各パラメータが、
 パラメータファイル内では「変数名」
 loadAudioPlugin() では「DisplayName」
と違っていて、「並び順」でしか突き合わせる方法がない、と思っていました。

R2022a ではそれの並び順がパラメータファイルだけ変更になったので突き合わせができなくなっていたのですが、MATLAB上でその対応テーブルを簡単に作成可能とのことです。

具体的には次のようにします。

解決策

"VSTtest_EQU.m" が VSTプラグインのMATLABソースファイルとします。

% Create a map of DisplayName from ParameterName using the plugin class
pm = VSTtest_EQU; % get properties from VST class file (VSTtest_EQU.m)
params = pm.PluginInterface.Parameters; % get GUI parameters as struct
fn = fieldnames(params); % get property names

m = containers.Map('KeyType','char','ValueType','char');

for k = 1:numel(fn)
    m(fn{k}) = params.(fn{k}).DisplayName; % set PropertyNames as keys and set DisplayNames as values
end

これで "m" に、その対応マップが生成されます。
以下、もう少し詳しく説明します。

クラスのプロパティ構造体を取得


まず、class になっていれば、変数に代入するだけで全プロパティを取得できるようです。(知らなかった!)

そこから VST のインターフェース定義部分である PluginInterface の さらに .Parametersパラメータ定義の構造体を取得します。この構造体は、audioPluginParameter() で定義した全てが含まれています

次に、fieldnames() を使って、構造体のフィールド名を cell 配列に返します。(フィールドの値を取るには、struct2cell() を使います)

Map オブジェクト生成


containers.Map 
は、値を一意の Key にマップする Map オブジェクトを生成します。Map オブジェクトは、数値インデックスではなく、実数または文字ベクトルを Key として使用することのできるデータ構造体です。

m = containers.Map('KeyType','char','ValueType','char');

で空の Map オブジェクトを作成し、

m(fn{k}) = params.(fn{k}).DisplayName; % set DisplayNames as index

で、Key として変数名Value として DisplayName をセットすることにより、変数名でアクセスできる変換テーブルを作成します。

もちろん他の方法で変換テーブルを作っても良いのですが、教えていただいたこの方法がスマートかと思います。

m('変数名') で DisplayName が返ってきます。


以前の記事の

displayName = matlab.lang.makeValidName(pInf.DisplayName);
setParameter(hostedPlugin,k,tog_comboVal);
N = sprintf("%s",displayName);
hostedPlugin.(N) = parasVal(k);

部分をそれぞれ、

displayName = matlab.lang.makeValidName(m(parasName(k)));
setParameter(hostedPlugin,m(parasName(k)),tog_comboVal);
hostedPlugin.(displayName) = parasVal(k);

と書き換えれば良いことになります。

2022/06/12 修正


「構造体メンバーとして使用できる文字制限」の適用を忘れていたので、最後の例も含めて修正しました。
hostedPlugin.(m(parasName(k))) = parasVal(k);
  ↓
hostedPlugin.(displayName) = parasVal(k);


2022/09/18 修正


もう一カ所修正が必要だったを追加しました。
displayName = matlab.lang.makeValidName(pInf.DisplayName);
  ↓
displayName = matlab.lang.makeValidName(m(parasName(k)));



コード例

clear
% read VST parameter file
fileID = fopen('VST_R2022a.para');
paraset = fread(fileID);  % read as binary
fclose(fileID);

parasetStr = char(paraset');  % convert to 1 line string
paras = split(parasetStr,["<PARAM id=","value=", "/>"]);  % split at "id", "value" and "/"
paras([1 end],:) = [];  % delete other than "id" and "value"
paras = paras(strlength(paras) > 0);  % delete blank lines
paras = erase(paras,'"');  % delete "
paras = strtrim(paras);  % delete unnecessary spaces

parasTable = cell2table(reshape(paras,2,[]));  % convert to table
parasTable.Properties.VariableNames = table2cell(parasTable(1,:));  % set properties name
parasNameT = table2cell(parasTable(1,:));  % take properties
parasName = string(parasNameT);  % convert to strings

parasVal = str2double(parasTable{2,:});  % take values


% Create a map of DisplayName from ParameterName using the plugin class
pm = VSTtest_EQU;  % get properties
params = pm.PluginInterface.Parameters;  % get GUI parameters as struct
fn = fieldnames(params);  % get property names

m = containers.Map('KeyType','char','ValueType','char');

for k = 1:numel(fn)
   m(fn{k}) = params.(fn{k}).DisplayName;  % set PropertyNames as keys and set DisplayNames as values
end


% VST hosting
hostedPlugin = loadAudioPlugin('VSTtest_EQU.dll');
parameterTuner(hostedPlugin)

for k = 1:numel(fn)
    [pvalN, pInf] = getParameter(hostedPlugin,k);
    displayName = matlab.lang.makeValidName(m(parasName(k)));

    tog_comboFlag = isnan(str2double(pInf.DisplayValue));
    
    if tog_comboFlag
        tog_comboVal = parasVal(k) / (str2double(pInf.Label)-1);  % normarize
        setParameter(hostedPlugin,m(parasName(k)),tog_comboVal);
    else
        hostedPlugin.(displayName) = parasVal(k);
    end
end

サポートさん、数ヶ月に渡り何度もやりとりをして対応策を検討していただき、大変感謝です!

これでやっと、R2022a に完全移行できる!(-_☆)


この記事が気に入ったらサポートをしてみませんか?