パート1.紹介とセットアップ
パート2.コードの学習
パート3. VSTおよびAU
パート4.デジタル歪み
パート5.プリセットとGUI
パート6.信号合成
パート7. MIDIメッセージの受信
パート8.仮想キーボード
パート9.封筒
パート10. GUIの改善
パート11.フィルター
パート12.低周波発振器
パート13.再設計
パート14.ポリフォニー1
パート15.ポリフォニー2
パート16.アンチエイリアス
REAPERで仮想キーボードを設定することはそれほど明白ではありません。さらに、ユーザーホストにはそのような機能がまったくない場合があります。 GUIに小さなオンスクリーンキーボードを追加しましょう。
GUI要素
WDL-OLでは、GUI要素はcontrolsと呼ばれます 。 このライブラリには、このタスクに必要なすべての機能を備えた
IkeyboardControl
      
      クラスがあります。
1つの背景画像と2つの追加のスプライトを使用します。1つは黒のキーを押し、もう1つは白のキーを押します。 論理的です。すべての黒のキーは同じ形状で、白のキーは異なります。 キーを押すと、これらのスプライトがキーボードの背景画像の上に表示され、常に表示されます。
すばらしいカスタムキーを作成する場合は、 このガイドをご覧ください 。 さて、ライブラリに付属するものは次のようになります。
 
       
       
      これらのファイルをダウンロードして、プロジェクトフォルダー/ resources / img /にドロップします。 Xcodeを使用する場合は、それらをウィンドウにドラッグしてプロジェクトに追加します。 いつものように、グラフィックの操作は、ファイル名をresource.hに追加することから始まります。 同時に、そこにいる間に、 knob.pngとbackground.pngへのリンクを削除し、プロジェクトからファイル自体を削除します。
 // Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png"
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      より大きなウィンドウサイズが必要:
 // GUI default dimensions #define GUI_WIDTH 434 #define GUI_HEIGHT 66
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      Windowsでは、 .pngファイルをアセンブリに含めるには、 Synthesis.rcヘッダーも編集する必要があります。
 #include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      次に、 Synthesis.hファイルの
public
      
      セクションに、 Synthesisクラスのメンバーをいくつか追加します。
 public: // ... // Needed for the GUI keyboard: // Should return non-zero if one or more keys are playing. inline int GetNumKeys() const { return mMIDIReceiver.getNumKeys(); }; // Should return true if the specified key is playing. inline bool GetKeyStatus(int key) const { return mMIDIReceiver.getKeyStatus(key); }; static const int virtualKeyboardMinimumNoteNumber = 48; int lastVirtualKeyboardNoteNumber;
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      Synthesis.cppの初期化リストに、lastVirtualKeyboardNoteNumberを追加します。
 Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1) { // ... }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      演奏されたノートのソースがホストの場合、プラグインキーボードで押された状態で表示されるはずです。 キーボードは
getNumKeys
      
      と
getKeyStatus
      
      を呼び出して、どのキーが押されたかを調べます。 前回、これらの機能を
MIDIReceiver
      
      実装
MIDIReceiver
      
      であるため、
MIDIReceiver
      
      。
プライベートセクションでは、次の行も追加する必要があります。
 IControl* mVirtualKeyboard; void processVirtualKeyboard();
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      IControl
      
      クラスは、すべてのGUIコントロールの基本クラスです。
IkeyboardControl
      
      で
IkeyboardControl
      
      オブジェクトを宣言することはできません。これは、 .hファイルには「不明」
IkeyboardControl
      
      です。 したがって、ポインターを使用する必要があります。 IKeyboardControl.hには 、「プラグインのクラスを宣言した後にこのヘッダーを追加(#include)する必要があるため、プラグインのメインの.cppファイルに追加するのが最善です」というコメントがあります。
状況を明確にするために、 Synthesis.cppを見てみましょう。
#include resource.h
      
      前に
#include "IKeyboardControl.h"
      
      を追加します。
次に、コンストラクタを変更します。
 Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1) { TRACE; IGraphics* pGraphics = MakeGraphics(this, kWidth, kHeight); pGraphics->AttachBackground(BG_ID, BG_FN); IBitmap whiteKeyImage = pGraphics->LoadIBitmap(WHITE_KEY_ID, WHITE_KEY_FN, 6); IBitmap blackKeyImage = pGraphics->LoadIBitmap(BLACK_KEY_ID, BLACK_KEY_FN); // C# D# F# G# A# int keyCoordinates[12] = { 0, 7, 12, 20, 24, 36, 43, 48, 56, 60, 69, 72 }; mVirtualKeyboard = new IKeyboardControl(this, kKeybX, kKeybY, virtualKeyboardMinimumNoteNumber, /* octaves: */ 5, &whiteKeyImage, &blackKeyImage, keyCoordinates); pGraphics->AttachControl(mVirtualKeyboard); AttachGraphics(pGraphics); CreatePresets(); }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      背景画像を添付すると興味深いことが始まります。 最初に、押された黒と白のキーを
Ibitmap
      
      オブジェクトの形式で
Ibitmap
      
      ます。
LoadIBitmap
      
      (
6
      
      )関数の3番目の引数は、 whitekeys.pngに6つのフレームが含まれていることをグラフィックスシステムに伝えます。
デフォルトでは、pRegularKeysには6つの画像(C / F、D、E / B、G、A、上部C)が含まれている必要がありますが、pSharpKeyには1つの画像(すべてのフラット/シャープ)しか含まれていません。-IKeyboardControl.h
keyCoordinates
      
      配列は、システムに左境界線を基準とした各キーのオフセットを伝えます。 このアクションは1オクターブのみで実行する必要があり、
IKeyboardControl
      
      他のすべてのオクターブのオフセットを計算します。
大まかに言えば、次の行では、新しいオブジェクトnew
IKeyboardControl
      
      を初期化し、名前
mVirtualKeyboard
      
      を割り当てます。 多くの情報を送信します。
-  プラグインのインスタンスへのポインター。 これは委任パターンの例です 。仮想キーボードは、このインスタンス( this
 
 
 
 )に対してGetNumKeys
 
 
 
 とGetKeyStatus
 
 
 
 を呼び出します。
- GUIのキーボード座標。
- 最低音の番号。 左端のキーをクリックすると、この特定のノートが再生されます。
- オクターブの数。
- 押されたキーの写真へのリンク。
- 1オクターブ内の相対X座標。
興味深いことに、仮想キーボードオブジェクトはbg.pngファイルの存在さえも知りません。 彼はただ彼を必要としません、すべてがそのように機能します。 キーボード画像は背景画像の一部になることができ、
IkeyboardControl
      
      コンストラクターに渡すためだけにこのピースをカットする必要があるため、これはプラスです。
C ++でプログラミングの経験がある場合は、条件反射が発生するはずです。コンストラクターには
new
      
      が含まれているため、
delete mVirtualKeyboard
      
      必要があります。 ただし、これを行ってからトラックからプラグインを削除すると、 ランタイム例外がポップアップします。 理由は、呼び出しが行われるときです
pGraphics->AttachControl(mVirtualKeyboard);
メモリ管理をグラフィックシステムに移行し、このメモリ領域の管理は私たちの責任ではなくなりました。
delete
      
      を使用して、既に空きメモリ領域の固定を解除します。
次に、
CreatePresets
      
      関数の本体を削除します。
 void Synthesis::CreatePresets() { }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      そして、kKeybXとkKeybYをELayoutに追加します。
 enum ELayout { kWidth = GUI_WIDTH, kHeight = GUI_HEIGHT, kKeybX = 1, kKeybY = 0 };
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      パフォーマンス上の理由から、
IKeyboardControl
      
      はそれ自体を再描画しません。 これはグラフィックプログラミングの一般的な方法です。GUI要素を「ダーティ」としてマークします。つまり、そのイメージは次の再描画サイクルでのみ更新されます。 IKeyboardControl.h 、特にOnMouseDownと
OnMouseUp
      
      を見ると、
mKey
      
      値
mKey
      
      割り当てられており、(
Draw
      
      ではなく)
SetDirty
      
      関数が
SetDirty
      
      ていることが
SetDirty
      
      ます。
SetDirty
      
      は
IControl
      
      クラスのメンバー関数です(その実装はそれぞれIControl.cppにあります)。 このコントロールの
mDirty
      
      パラメーター
mDirty
      
      を
true
      
      に設定し
true
      
      。 再描画サイクルごとに、グラフィックシステムは
mDirty
      
      が
true
      
      であるすべてのGUI要素を再描画し
true
      
      。 グラフィックシステムのこの側面を理解することが重要なので、このような詳細を掘り下げました。
外部MIDIメッセージへの応答
これまでのところ、キーボードをクリックすると汚れています。
mMIDIReceiver
      
      から
mMIDIReceiver
      
      データを受信しますが、外部MIDIデータも受信する必要があります。
mVirtualKeyboard
      
      と
mMIDIReceiver
      
      はお互いについて何も知らないので、 Synthesis.cppで
ProcessMidiMsg
      
      を編集しましょう。
 void Synthesis::ProcessMidiMsg(IMidiMsg* pMsg) { mMIDIReceiver.onMessageReceived(pMsg); mVirtualKeyboard->SetDirty(); }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      最初に、
mMIDIReceiver
      
      は受信したMIDIデータに従って
mLast...
      
      メンバーを更新します。 その後、
mVirtualKeyboard
      
      はダーティとしてマークされます。 次に、次の再描画サイクルで、
Draw
      
      が
mVirtualKeyboard
      
      に対して
mVirtualKeyboard
      
      れ、次に
GetNumKeys
      
      および
GetKeyStatus
      
      を呼び出します。 最初は複雑に思えるかもしれませんが、実際には、冗長性と不要な動きを避ける透明で明確に構造化された設計です。
仮想キーボードは外部MIDIメッセージに応答し、押されたキーを正しく描画します。
キーボード応答
ホストに組み込まれた仮想キーボードの押下にキーボードを強制的に応答させ、MIDIメッセージを生成し、受信者
mMIDIReceiver
      
      送信します。
forループの直前にこの呼び出しを
ProcessDoubleReplacing
      
      追加します。
processVirtualKeyboard();
そして適切な関数を書きます:
 void Synthesis::processVirtualKeyboard() { IKeyboardControl* virtualKeyboard = (IKeyboardControl*) mVirtualKeyboard; int virtualKeyboardNoteNumber = virtualKeyboard->GetKey() + virtualKeyboardMinimumNoteNumber; if(lastVirtualKeyboardNoteNumber >= virtualKeyboardMinimumNoteNumber && virtualKeyboardNoteNumber != lastVirtualKeyboardNoteNumber) { // The note number has changed from a valid key to something else (valid key or nothing). Release the valid key: IMidiMsg midiMessage; midiMessage.MakeNoteOffMsg(lastVirtualKeyboardNoteNumber, 0); mMIDIReceiver.onMessageReceived(&midiMessage); } if (virtualKeyboardNoteNumber >= virtualKeyboardMinimumNoteNumber && virtualKeyboardNoteNumber != lastVirtualKeyboardNoteNumber) { // A valid key is pressed that wasn't pressed the previous call. Send a "note on" message to the MIDI receiver: IMidiMsg midiMessage; midiMessage.MakeNoteOnMsg(virtualKeyboardNoteNumber, virtualKeyboard->GetVelocity(), 0); mMIDIReceiver.onMessageReceived(&midiMessage); } lastVirtualKeyboardNoteNumber = virtualKeyboardNoteNumber; }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      GetKey
      
      は、押されたキーに対応するノート番号を提供します。
IKeyboardControl
      
      はマルチタッチをサポートしていないため、一度に1つのキーしか押すことができません。 最初の
if
      
      は、もう押されていないキー(ある場合)をリリースします。 この関数は
mBlockSize
      
      サンプルごとに
mBlockSize
      
      れるため、2番目のifは、このクリックに対してメッセージのメモが 1つだけ生成されるようにします(
mBlockSize
      
      サンプルごとではありません)。 関数が呼び出されるたびにこれらの「繰り返しクリック」を避けるために、
lastVirtualKeyboardNoteNumber
      
      の値を覚えています。
行こう!
シンセを再び起動する準備ができました! すべてが正しく行われていれば、彼のキーボードで演奏できます。 そして、仮想ホストキーボードまたは他の接続されたMIDIソースの使用がプラグインキーボードに表示されます(順番に、最後に押されたキーが表示されます)。 確かに、音はこの最後の1つのキーにのみ対応します。 ポリフォニーについては少し後で説明します。
クラシックシンセサイザーサウンドで、お友達に自慢したり、お気に入りのベートーベンを演奏したりできます。 今だけ音は一種の「木」であり、キーを押して放すとクリック音が聞こえます。 これは、洞が生成される場合に特に顕著です。 そのため、封筒を追加する必要があります。 これは次の投稿で行います。
この段階のプロジェクトファイルは、ここからダウンロードできます 。
元の記事:
martin-finke.de/blog/articles/audio-plugins-010-virtual-keyboard