Pure DataのPatchからDrumlogueのSynthユニットを作ってみた(2)ノブでパラメータを変更する

前回、PureDataのパッチをhvccでCのコードに変換し、そのコードからDrumlogue用のSynthユニットを作成しました。使用したのは440Hzの正弦波を生成するというだけのパッチで、下図のようなものでした。

このパッチは入力が無いので、Drumlogue側から操作することはできませんでした。
今回は、Drumlogueのノブで入力したパラメータを受け取って、周波数を変えられるようにしてみます。そのPure Dataのパッチは以下です。

このパッチは、receiveオブジェクトで外部からfrequencyを受け取って、osc~オブジェクトに渡します。これだけではfrequencyの送り手が居ないので、何も受け取ることができません。frequencyを送るのはsendオブジェクトで、たとえば下のようなパッチです。このパッチを追加する(別ウインドウで開いてもOK)と、sendオブジェクトの入力のnumberボックスで設定した値を、receiveオブジェクトのfrequencyに送ることができます。

receiveオブジェクトとsendオブジェクトは、共通のメッセージキュー(上の例の場合はfrequency)を通じてメッセージを送受信します。PureDataでは通常、オブジェクト同士は線(パッチコード)で接続され、そこにメッセージが流れるのですが、sendとreceiveは線で接続する代わりにメッセージキューを通じてメッセージをやり取りします。

今回実装するものの概略は、以下の図のようなものです。図の左側、音声を出力する部分は前回実装しました。

図の右側ですが、ノブを操作するとlogue SDKからsetParameter()が呼ばれるので、どのメッセージキューに何を送るのかを決めて、sendToReceiver()でメッセージを送ります。メッセージキューを指定するにはハッシュ値を使うのですが、このハッシュ値は文字列(今回は”frequency”)から計算できます。

上の図で、Pure DataのパッチをhvccがCのコードに翻訳したもののことを「Hvコンテキスト」と呼ぶことにします。Pure Dataのパッチと、それを翻訳したhvコンテキストは等価(同じ入力を与えたら同じ出力が得られるという意味で)です。

というわけで、まずは前回同様、hvccでパッチをCのコードに変換します。変換の対象はreceiveオブジェクトにつながっている部分だけで、sendオブジェクト側はlogue SDKを使ってCで実装します。

今回、-nオプションで指定する名称は”pitch”としました。hvccを使う手順は前回と同じなので省略しますが、生成されたファイルはこんな感じで、前回より少し増えています。

$ ls
HeavyContext.cpp           HvHeavyInternal.h  HvSignalPhasor.c
HeavyContext.hpp           HvLightPipe.c      HvSignalPhasor.h
HeavyContextInterface.hpp  HvLightPipe.h      HvSignalVar.c
Heavy_pitch.cpp            HvMath.h           HvSignalVar.h
Heavy_pitch.h              HvMessage.c        HvTable.c
Heavy_pitch.hpp            HvMessage.h        HvTable.h
HvControlVar.c             HvMessagePool.c    HvUtils.c
HvControlVar.h             HvMessagePool.h    HvUtils.h
HvHeavy.cpp                HvMessageQueue.c
HvHeavy.h                  HvMessageQueue.h

config.mkには以下のようにソースコードを指定します。

# C sources
CSRC = header.c Hv/HvControlVar.c Hv/HvLightPipe.c Hv/HvMessage.c Hv/HvMessagePool.c Hv/HvMessageQueue.c Hv/HvSignalPhasor.c Hv/HvSignalVar.c Hv/HvTable.c Hv/HvUtils.c

# C++ sources
CXXSRC = unit.cc Hv/HeavyContext.cpp Hv/Heavy_pitch.cpp Hv/HvHeavy.cpp

ノブからのパラメータ入力ですが、今回は実験ですので、以下のように最小値100、最大値1000としました。

header.c:

{100, 1000, 440, 440, k_unit_param_type_none, 0, 0, 0, {"Frequency"}},

ノブを変更したときに呼ばれるsetParameter()関数は以下のようにしています。indexはパラメータの番号で、今回は1つしか無いので0番です。valueは32ビット整数で、上で設定した通り100..1000のいずれかが渡されます。

synth.c:

  inline void setParameter(uint8_t index, int32_t value) {
    (void)value;
    switch (index) {
      case 0:
          hv_sendFloatToReceiver(hvContext_, hv_stringToHash("frequency"), value);
        break;
      default:
        break;
    }
  }

関数hv_stringToHash()で、”frequency”という文字列をハッシュ値に変換しています。このハッシュ値自体はどんな条件下でも変わりませんので、実際にはこのように毎回変換する必要はありません。

ちなみに、Pure Dataパッチのreceiverのパラメータを [receive frequency @hv_param MIN MAX DEFAULT](大文字部分は実際には数値が入ります)のようにしておくと、ヘッダファイル内でfrequencyのハッシュ値の定義がHV_HEAVY_PARAM_IN_FREQUENCY = 0xXXXXXXのような形で定義されます。また、MIN/MAX/DEFAULTの数値は、関数hv_getParameterInfo()でCのコード内から参照することが可能になります。

hv_sendFloatToReceiver()は、float値を1つだけ送るための関数ですが、Pure Data/hvccの機能としては不定長のメッセージを含めた様々なメッセージを送ることができます。詳しくはAPI仕様に書かれています。

上記をビルドすると、こんな感じになりました。サイズはちょっとだけ増えています。

   text	   data	    bss	    dec	    hex	filename
  38870	    928	     12	  39810	   9b82	build/pd_param.drmlgunit

Drumlogueにインストールすると、ノブで正弦波の周波数を変更できます。

今回はパラメータの設定をメッセージ送信機能で実装しました。hvccでは、MIDI入力も特定のメッセージキュー(”notein”)として実装されていますので、今回と同様の方法でMIDI入力ができます。

というわけで、次回はMIDI入力に対応させてみます。

コメント