前回、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入力に対応させてみます。
コメント