Electro-smith Daisy seedでリバーブをつくる
Daisyとは?
オーディオ処理に特化したArduino、みたいなやつです。以前にnoteに紹介記事を書いたので興味ある方はこちらもご参照ください。
Daisy seedと他のボードの違う点
Daisyシリーズには、今回使うSeed以外にPod/Patch/Petalなどがあります。
Seedがほかと違うのは、ツマミやボタンなどの入出力は自分で用意する必要があるという点です。Pod/Patch/Petalにはすでにツマミやボタンがついているので、その辺はライブラリで抽象化されていて簡単にアクセスできるようになっています。サンプルコードはこれを前提にしているものが多く、Seedで同じことをしようとするときに少し手こずりました。
Parameter
たとえば、DaisyにはParameter
というクラスがあります。ADCからの入力を、最大値・最小値・カーブの形状(線形、指数、ログなど)を指定して値にマッピングできるという便利なユーティリティです。サンプルコードを見ると、
lpParam.Init(patch.controls[3], 20, 20000, Parameter::LOGARITHMIC);
みたいな感じで指定されていて、patch.controls[3]
というのはADCを表すなにかのようです。Patchの場合はこれでいいんですが、Seedの場合はこの「ADCを表すなにか」を扱うために多少コードを書く必要があります。
具体的には。Parameter
のInit()
はこういうシグネチャになっています。
void daisy::Parameter::Init(
AnalogControl input,
float min,
float max,
Curve curve
)
で、AnalogControl
はどうやってつくるかというと、こういうInit()
になっています。
void daisy::AnalogControl::Init(
uint16_t * adcptr,
float sr,
bool flip = false,
bool invert = false,
float slew_seconds = 0.002f
)
このadcptr
の説明を読むと、
is a pointer to the raw adc read value – This can be acquired with
dsy_adc_get_rawptr()
, ordsy_adc_get_mux_rawptr()
とか書かれてるんですが、実際にはdsy_adc_get_rawptr()
は公開されていない関数なので使えません...。
いったいどうすれば??、と混乱したんですが、Patchをラップしているライブラリの中身を覗くとこうなってました。seed.adc.GetPtr()
という関数が使えるようです。
controls[i].Init(seed.adc.GetPtr(i), AudioCallbackRate(), true);
これを使うと、こんな感じのコードになります。15〜18ピンにツマミを接続することを想定しています。
#define PIN_REVERB_CONTROL1 15
#define PIN_REVERB_CONTROL2 16
#define PIN_REVERB_CONTROL3 17
#define PIN_REVERB_CONTROL4 18
static DaisySeed seed;
// ADCの設定。配列の長さは必要なツマミの数と同じ。
AdcChannelConfig adcConfig[4];
// ツマミを接続するピンをそれぞれ指定する
adcConfig[0].InitSingle(seed.GetPin(PIN_REVERB_CONTROL1));
adcConfig[1].InitSingle(seed.GetPin(PIN_REVERB_CONTROL2));
adcConfig[2].InitSingle(seed.GetPin(PIN_REVERB_CONTROL3));
adcConfig[3].InitSingle(seed.GetPin(PIN_REVERB_CONTROL4));
// ADCを初期化(4は使うADCの数)
seed.adc.Init(adcConfig, 4);
AnalogControl verb_control1, verb_control2, verb_control3, verb_control4;
Parameter verb_feedback, verb_lp_freq, verb_mix, verb_send;
// 各ADCをParameterに割り当てる
verb_control1.Init(seed.adc.GetPtr(0), sample_rate);
verb_feedback.Init(verb_control1, 0.f, 0.99f, Parameter::LINEAR);
verb_control2.Init(seed.adc.GetPtr(1), sample_rate);
verb_lp_freq.Init(verb_control2, 0.f, 20000.0f, Parameter::EXPONENTIAL);
verb_control3.Init(seed.adc.GetPtr(2), sample_rate);
verb_mix.Init(verb_control3, 0.f, 1.0f, Parameter::LINEAR);
verb_control4.Init(seed.adc.GetPtr(3), sample_rate);
verb_send.Init(verb_control4, 0.f, 1.0f, Parameter::LINEAR);
seed.adc.Start();
こうしてParameter
でラップしておくと、ADCの値をリバーブのパラメータとして使う時に、自分で値をマップしなくても、<Parameter>.Process()
でマップ済みの値が得られます。
RevebSc
リバーブ用にはReverbSc
というクラスが用意されています。フィードバックの量と、フィードバックにかけるローパスフィルタのカットオフ周波数、という2つのパラメータを持っています。センドリターンやドライウェットといったところは自分で実装する必要があります。(pre-delayの扱いはちょっとよくわかりませんでした。内部的にはもう少しいくつかパラメータがありそうなので、そのへんを改造すればいいのかもしれません)。https://github.com/electro-smith/DaisyExamples/blob/master/patch/verb/ex_verb.cpp#L63を参考に、こういう感じのコードになります。
ReverbSc verb;
static void AudioCallback(float *in, float *out, size_t size) {
float sig, sig_tmp, dry_rate, send_rate, wet1, wet2;
for (size_t i = 0; i < size; i += 2) {
sig = in[i];
// dry/wet、sendの量を決める
dry_rate = verb_mix.Process();
send_rate = verb_send.Process();
// sigはdryとして使うので、リバーブに送る分は別の変数sig_tmpに入れておく
sig_tmp = sig * send_rate;
// パラメータを設定
verb.SetFeedback(verb_feedback.Process());
verb.SetLpFreq(verb_lp_freq.Process());
// リバーブをかける
verb.Process(sig_tmp, sig_tmp, &wet1, &wet2);
// dry/wetを合わせる
out[i] = sig * dry_rate + wet1;
out[i + 1] = sig * dry_rate + wet2;
}
}
感想
Daisy Seedは3000円くらいで買えるのでDaisyシリーズの中でいちばん気軽な選択肢ですが、その割にドキュメントのどこを見ればいいかよくわからなくて躓きがちな気がします。気付いたところをちょこちょこメモっていきたいと思います。
Discussion