基本波形をモーフィングする
( 1.0 - α ) * a + α * b
ソフト屋がハードにはまっていく軌跡
( 1.0 - α ) * a + α * b
YouTubeの動画の音声でも分かる「ギュルギュル」した音に気付いた人は、私の仲間です。
NuVCOの最初の記事では、STM32G431とPCM5102Aを使ってVCOを動かし、実際の音もYoutubeへアップしました。
最初の記事:
https://dad8893.blogspot.com/2026/07/nuvco-stm32vco.html
最初の記事の動画では、Sawtoothの周波数を上げていくと、音程とは別に動くような「ギュルギュル」した音が聞こえます。
固定した周波数で聞いているより、Tune POTを回して周波数を変化させたときの方が違和感 が出ます。
Sawtooth本来の音程とは別の音が、妙な動き方をしています。
Sawtoothは波形の端で、
+1 → -1
のように急激に変化します。
この不連続部分には非常に高い周波数まで高調波が含まれます。
NuVCOのSampling Rateは48kHzなので、Nyquist周波数は24kHzです。
24kHzを超えた高調波はそのまま再生できず、可聴帯域へ折り返してきます。
これがaliasingです。
Sawtoothの音程を変えると元の高調波も動きますが、折り返してきた成分は本来の高調波とは違う動きをします。
Tune POTを回したときに「ギュルギュル」というノイズは、最初はUSB接続で現れがちなノイズ、GNDループによって拾ってしまうデジタル機器のノイズなど疑いまくりました。
約1725HzのSawtoothをFFTで見ると、本来の高調波とは別に多くの成分が現れます。
周波数を変化させると、これらの成分も本来の音程とは違う動きをします。
耳で聞いていた「ギュルギュル」と対応しているように見えます。
Sawtoothに PolyBLEP を入れた場合です。
PolyBLEPは、Sawtooth全体へフィルターをかけるのではなく、波形が不連続になる部分の近くだけを補正する方法です。
今回使った一般的なPolyBLEPでは、正規化した位置を x とすると、
-x² + 2x - 1
のような2次関数を使って、不連続点付近の波形を少しなめらかにつなぎます。
実装では、現在の位相が不連続点から1 sample分程度の範囲にあるときだけ補正値を加えています。
つまり、Sawtooth全体の形を大きく変えるのではなく、問題になる急峻な部分だけを局所的に修正しています。
もともとどこで急峻な部分が現れるかがわかっているからできるのがPolyBLEPです。リアルタイムに検知しているわけではないのでご承知ください。
PolyBLEP適用後のFFTです。
初期状態で見えていた、本来の高調波とは異なる成分がかなり減りました。
聴感上の変化も分かりやすいです。
特にTune POTを回して周波数を連続的に変えたとき、PolyBLEP OFFでは「ギュルギュル」とした成分が目立ちます。
PolyBLEPをONにすると、その成分がかなり減ります。
一方で、Sawtoothらしい明るい音まで大きく失われた感じはありませんでした。
私の場合、固定した1音を聞き比べるよりも、周波数を動かしたときの方がPolyBLEPの効果はずっと分かりやすく感じました。
PolyBLEPをON / OFFしながら、Sawtoothの周波数を変化させた様子を動画でも撮影しました。
OFFのときは、Sawtoothの音程とは別に動く「ギュルギュル」した音に注目すると違いが分かりやすいと思います。
ONにすると、この成分がかなり小さくなります。
当然、PolyBLEPを入れると計算量は増えます。
NuVCOではCycle Counterを使って11 Voice動作時の処理時間も確認しました。
PolyBLEPを有効にしてもaudio生成のdeadline超過は発生せず、まだ十分な余裕がありました。
そのためNuVCOでは、Sawtooth系の波形についてPolyBLEPを基本的にONで使うことにしています。
今回面白かったのは、FFTを見る前にまず耳で違和感に気付いたことです。
単純なSawtoothでも、高い周波数へ上げていくとaliasingはかなり目立ちます。
特に周波数を連続して変化させると、本来の音程とは別に動く成分として聞こえるので分かりやすいです。
PolyBLEPという比較的軽い補正を加えるだけでも、その違和感はかなり減りました。
最初のNuVCO動画に入っていた「ギュルギュル」が、後になってこういう形で改善できたのは面白い結果でした。
前回までで、NuVCOは11 Voiceまで動作するようになり、Sine生成の処理時間についてもCycle Counterで測定し、LUT化まで行いました。
基本的なVCOを動かすこと以外にも、いろいろ試してみたいことが増えてきました。
例えば、現在考えているものには次のようなものがあります。
Detune Range
Detuneの可変域を設定する。
Voice Spread
Detuneの広がり方を設定する。
Pair Morph
Sine → Triangle → Square → Sawtoothという一続きのMorphだけでなく、Sine → Square、Triangle → Sawtoothなど、2つの波形を選んでMorphする。
Voiceの初期phase
複数Voiceを同じphaseから始めるか、Voiceごとにphaseをずらすかなどを設定する。波形の重なり方が変わるので、音の立ち上がりや厚みなどに違いが出る可能性がある。
Stereo / Pan
MorphingやDetuneしたVoiceをL/Rへ振り分け、Stereoの広がりを持たせる。
Sub
1 Octave下の波形を生成してMixする。
これらは私にとって未知の世界です。
名前や考え方は分かっていても、NuVCOで実際に音を出したときにどういう効果になるのかは、試してみないと分かりません。
最初のNuVCOの設計では、
Tune POT
Wave POT
Detune POT
USER1 SW
USER2 SW
を使っていました。
Tune POTは基準周波数、Wave POTはSine → Triangle → Square → SawtoothのMorph、Detune POTは複数VoiceのDetune量を操作します。
USER1 SWではVoice数を、
1 / 2 / 3 / 5 / 7 / 9 / 11
と切り替え、USER2 SWではTune Rangeを切り替えるようにしました。
この方法で機能を増やしていく場合、機能ごとにPOTやSWを追加する必要があります。
部品や配線が増えるだけでなく、Nucleo-G431KB側もADCやGPIOとして自由に使えるpinには限りがあります。
UI用のI2Cを追加したときにも、pinの制約が実際に出てきました。
当初はI2C2を、
SCL: PA9
SDA: PF0
で使う予定でした。
ところがPF0が実機でうまく使えなかったため、SDAをPA8へ変更しました。
PA8は、それまでUSER1 SWのVoice数切り替えに割り当てていたpinです。
そのためI2Cを組み込んだ直後は、USER1 SWによるVoice数切り替えが使えなくなりました。
今はVoice切り替えをMCP23017側へ移しています。
このように、POTやSWを単純に増やしていく方法では、部品数だけでなくMCUのpin資源にも制約があります。
まだNuVCOに搭載する音作り機能は絞れていません。
そこで、最終形のUIを決める前に、いろいろな機能を試すための実験用UIを用意しました。
現在の状態を確認するためにOLEDを使い、必要な設定値も表示します。
値の変更にはRotary Encoderを使います。
1個のPOTで複数のパラメータを切り替えて操作する方法もありますが、POTには物理的な位置があります。
例えば、あるパラメータを大きな値に設定してPOTが右側にある状態で別のパラメータへ切り替えると、POTの位置と新しいパラメータの値が一致しません。
Rotary Encoderなら絶対的な位置を持たないので、表示している現在値を基準に増減できます。
OLEDで現在の項目と値を表示し、Encoderで変更する方式なら、操作するパラメータが増えても主にFirmware側の変更で対応できます。
さらにMCP23017を使い、SwitchやLEDも追加できるようにしています。
NuVCOの音作りを試すために作ったUI Expander Proto-A。OLED、Rotary Encoder、MCP23017、5個のSwitchとLEDを搭載しています。
現在は、OLED、Rotary Encoder、MCP23017、Switch、LEDを NuVCO UI Expander Proto-A にまとめています。
NuVCO本体とはAdapter基板を介して接続します。
このUIは最終形ではなく、Detune RangeやVoice Spreadなど、これから試してみたい機能を操作するための実験用です。
機能を増やしても、すぐに専用POTや専用SWを追加する必要はなく、Firmware上で項目を追加し、OLEDで状態を確認しながらEncoderやSwitchで操作できます。
次回は、この実験用UIで使っているOLED、Rotary Encoder、MCP23017をどのように試し、NuVCOへ組み込んでいったかを書こうと思います。