ずっと積み基板になってるけどそろそろ気合を入れて物理モデリングシンセつくるかー!!、と重い腰を上げようとしたら、基本のKarplus-Strongは標準のライブラリ(DaisySP)に普通に用意されていたので笑、軽く使ってみたメモ。
DaisySPにはCsoundからポーティングしてきたというPluck
クラスと、それをいい感じにラップしたPolyPluck
が用意されている、PolyPluck
の方はDaisyExamplesにも例があったけど、今回は、より基礎的っぽいPluck
を使ってみた。
コード
#include "daisy_seed.h" #include "daisysp.h" using namespace daisy; using namespace daisysp; #define BUFSIZE 256 static DaisySeed seed; Pluck synth; float plkbuff[BUFSIZE]; Switch button1; static void AudioCallback(float *in, float *out, size_t size) { float sig; float trig; // Pluck Vars button1.Debounce(); if (button1.RisingEdge()) { trig = 1.0f; } else { trig = 0.0f; } synth.SetDecay(seed.adc.GetFloat(0)); for (size_t i = 0; i < size; i += 2) { synth.SetFreq(mtof(seed.adc.GetFloat(0) * 127)); sig = synth.Process(trig); // left out out[i] = sig; // right out out[i + 1] = sig; } } int main(void) { seed.Configure(); seed.Init(); float samplerate = seed.AudioSampleRate(); // Use pin 16 to read the voltage of the knob AdcChannelConfig adcConfig; adcConfig.InitSingle(seed.GetPin(16)); seed.adc.Init(&adcConfig, 1); synth.Init(samplerate, plkbuff, BUFSIZE, PLUCK_MODE_RECURSIVE); // Use pin 15 to read button to trigger the synth button1.Init(seed.GetPin(15), samplerate / 48.f); seed.adc.Start(); seed.StartAudio(AudioCallback); for (;;) { } }
ピン15にボタンが、ピン16に可変抵抗がつながっていて、可変抵抗でdecayと音程を両方コントロールしている。
ボタン
ボタン、というかトリガ・あるいはゲートを出すタイプの入力を扱うにはSwitch
クラスを使う。main()
で
button1.Init(seed.GetPin(15), samplerate / 48.f);
と、どのピンの入力かとupdate rateを指定(48という数字で割っているのはなぜなのか理解していない...)して初期化する。
AudioCallback()
の中では
button1.Debounce(); if (button1.RisingEdge()) { trig = 1.0f; }
とするとボタンを押したタイミングが取れる。同様にbutton1.FallingEdge()
とするとボタンを離したタイミングが取れる。
button1.Debounce()
は、前回の変化からあまりにも短時間で状態が変化していれば無視する、みたいな処理らしい。要はチャタリングを防ぐために呼んでいる。
可変抵抗
連続的な電圧の入力を扱うには、DaisySeed.adc
を使う。まずmain()
で
AdcChannelConfig adcConfig; adcConfig.InitSingle(seed.GetPin(16)); seed.adc.Init(&adcConfig, 1); seed.adc.Start();
と、どのピンの入力かを指定して初期化する。ちなみにInitSingle()
じゃなくてInitMux()
を使うとマルチプレクサIC(CD405X
)を勝手に制御してくれるらしい。便利そう...
AudioCallback()
の中では、
seed.adc.GetFloat(0)
とすれば0〜1の値が取れる(0
はチャンネル名。マルチプレクサを使ってない場合は常に0
)
Pluck
肝心の Pluck
は、まずは初期化には
synth.Init(<サンプリングレート>, <Pluck内部で使うバッファ>, <バッファのサイズ>, <モード>);
が指定できる。バッファの長さを増やすとより金属っぽい音?になる気がするけど理論がよくわからない。モードもPLUCK_MODE_RECURSIVE
, PLUCK_MODE_WEIGHTED_AVERAGE
, PLUCK_LAST
の3種類があるけどどういう意味なのかよくわからない。とりあえずPLUCK_MODE_RECURSIVE
を選んでおく。
あとはそんな特殊なところはなくて、decayや音程は以下のように指定できる(ちなみにmtof()
はMIDIのノート番号から周波数に変換する関数)。
synth.SetDecay(seed.adc.GetFloat(0));
synth.SetFreq(mtof(seed.adc.GetFloat(0) * 127));
あとSetDamp()
で減衰率?を指定できるのでそこも適切に指定する方がよさそうだけど、とりあえず今回は無視。
結果
なんか低音が謎のローファイさだけど、おおむね弦っぽい音が鳴ってる気がする。
pluck pic.twitter.com/XDPIqTD2qh
— Hiroaki Yutani (@yutannihilation) 2020年12月13日