製作モジュール一覧

2026年10月10日土曜日

NuVCOの波形モーフィング ― 位相を合わせて基本波形を変化させる

ここ数回AIに記事を書いていてもらったのですが、どうも癖が強すぎてわかりやすいようでいてわかりにくいような。うまく言語化できないのですが、これ「AIやん」とすぐにわかる独特の癖があると思います。自分の考えをまとめるためにも今回は自分で書くことにしました。

今回は波形モーフィング機能についてです。

デジタルでやりやすいのは足し算とか掛け算などの計算です。アナログでもOPアンプの演算回路を駆使すればやれないこともないと思います。アナログでも出来るかもしれないけれどデジタルの単純さに比べるとかなりハードルが高い世界です。

モーフィングは2つの波形にそれぞれ係数を掛け算して、足し合わせるという意外と単純なものです。掛けて足すのが得意なのはGPUやDSPですがそこまで高性能でなくても小数が扱えればマイコンでもリアルタイム演算が可能です。

実際にやってみると、Sine波を生成する計算などと比べてかなり低負荷で実行できました。

基本波形をモーフィングする


基本波形は、Sine、Triangle、Saw、Squareです。アナログVCOではSineが無いことが多いですが、NuVCOでは苦労した末Sineも出せるようになりました。この基本波形の間をなめらかにつなぐのがモーフィングです。

まず、順番を考えないといけません。Sine→Triangleは素直です。Triangle→SquareにするかTriangle→Sawにするかは悩みどころです。今回はSine→Triangle→Square→Sawの順番にしました。これは絶対的なものではなくSine→Squareや、Sine→Sawなどのモーフィングも考えられます。いずれモーフィングの順番を選択できるようにしたいと思っています。

DDSでは位相が一定時間で進むようにプログラミングします。大体の場合Timerを使って一定時間で割り込みを掛けて、次の割り込みまでの間に波形の値を計算をします。NuVCOではI2Sがお知らせしてくれる割り込み毎に128フレームをまとめて計算しています。


計算と言ってもWaveTable形式なら波形テーブルから位相に対応するアドレスの値を取ってくるだけです。SineのLUTはこの形式です。

Triangle、Square、Sawなどの波形はテーブルを使わずに、位相から直接計算した方が速いしメモリも使わないです。一番簡単なのはSawです。位相値をそのまま出力すればよい。Squareは閾値でHigh / Low に振り分けます。Triangleはほんの少し面倒ですが簡単な計算で出せます。

このように求めたA波形の値とB波形の値に係数を掛けてやって足し算します。係数はAとBの割合であって、足すと1になるようにします。

モーフィングの位置をα ( 0.0 ~ 1.0 )、Aの波形の値をa、Bの波形の値をbとすると

( 1.0 - α ) * a + α * b

計算した結果 0 ~ 1 の範囲に収まらなかった場合丸めるなどの後処理も必要ですが、モーフィングの基本はこれだけです。

位相と向きをそろえる


位相というと難しそうに思えますが、プログラミングでは0.0 ~ 1.0の間の値です。1サンプルごとにデルタ分だけ加算します。1.0に達したら0.0に戻り、これを繰り返します。

同じ位相でSine、Triangle、Saw、Squareの一般的な生成式でグラフ化したのですが、出来上がった波形の「形」が今一つしっくりこない結果になりました。


まずTriangleのピークを見るとSineより1/4周期遅れています。Sineをベースとすると、Triangleの計算に使う位相から0.25を引いて、左にずらします。1.0を超えた分は0.0側から持ってきます。

SawはSquareとプラス・マイナスが逆です。Squareがプラスの間でSawはマイナスになっているので、足し合わせると打ち消し合います。Sawを上昇する形から下降する形に反転しました。


これでSineとTriangleは形の山・谷が揃い、SquareとSawtoothはプラス・マイナスの関係が揃います。

モーフィング途中の波形も描いてみました。

SineからTriangleは、丸い部分が徐々に直線状になる変化です。

SquareからSawへは、水平だった部分が徐々に斜めに傾き、中央の段差が小さくなっていき、最後は下降するSawになります。この変化は時間軸の形としてはあまりきれいではない感じがしますが、音として聴いたりFFTすると高調波が増減するのがわかります。

製作中のNuVCOのWaveモーフィング


実機で波形モーフィングしている様子を動画にしました。ぜひ見てください。



2026年9月10日木曜日

NuVCOでPolyBLEPを試す ― SawtoothのAliasingを減らす

YouTubeの動画の音声でも分かる「ギュルギュル」した音に気付いた人は、私の仲間です。

NuVCOの最初の記事では、STM32G431とPCM5102Aを使ってVCOを動かし、実際の音もYoutubeへアップしました。

最初の記事:
https://dad8893.blogspot.com/2026/07/nuvco-stm32vco.html

最初の記事の動画では、Sawtoothの周波数を上げていくと、音程とは別に動くような「ギュルギュル」した音が聞こえます。

固定した周波数で聞いているより、Tune POTを回して周波数を変化させたときの方が違和感 が出ます。

Sawtooth本来の音程とは別の音が、妙な動き方をしています。

SawtoothとAliasing

Sawtoothは波形の端で、

+1 → -1

のように急激に変化します。

この不連続部分には非常に高い周波数まで高調波が含まれます。

NuVCOのSampling Rateは48kHzなので、Nyquist周波数は24kHzです。

24kHzを超えた高調波はそのまま再生できず、可聴帯域へ折り返してきます。

これがaliasingです。

Sawtoothの音程を変えると元の高調波も動きますが、折り返してきた成分は本来の高調波とは違う動きをします。

Tune POTを回したときに「ギュルギュル」というノイズは、最初はUSB接続で現れがちなノイズ、GNDループによって拾ってしまうデジタル機器のノイズなど疑いまくりました。

最終的にはAliasing(折り返しノイズ)だと突き止めました。

FFTで見てみる

約1725HzのSawtoothをFFTで見ると、本来の高調波とは別に多くの成分が現れます。

周波数を変化させると、これらの成分も本来の音程とは違う動きをします。

耳で聞いていた「ギュルギュル」と対応しているように見えます。

PolyBLEPを入れてみる

Sawtoothに PolyBLEP を入れた場合です。

PolyBLEPは、Sawtooth全体へフィルターをかけるのではなく、波形が不連続になる部分の近くだけを補正する方法です。

今回使った一般的なPolyBLEPでは、正規化した位置を x とすると、

-x² + 2x - 1

のような2次関数を使って、不連続点付近の波形を少しなめらかにつなぎます。

実装では、現在の位相が不連続点から1 sample分程度の範囲にあるときだけ補正値を加えています。

つまり、Sawtooth全体の形を大きく変えるのではなく、問題になる急峻な部分だけを局所的に修正しています。

もともとどこで急峻な部分が現れるかがわかっているからできるのがPolyBLEPです。リアルタイムに検知しているわけではないのでご承知ください。

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で使うことにしています。

Aliasingは音で分かる

今回面白かったのは、FFTを見る前にまず耳で違和感に気付いたことです。

単純なSawtoothでも、高い周波数へ上げていくとaliasingはかなり目立ちます。

特に周波数を連続して変化させると、本来の音程とは別に動く成分として聞こえるので分かりやすいです。

PolyBLEPという比較的軽い補正を加えるだけでも、その違和感はかなり減りました。

最初のNuVCO動画に入っていた「ギュルギュル」が、後になってこういう形で改善できたのは面白い結果でした。

2026年8月24日月曜日

NuVCOに実験用UIを作る ― 増えてきた音作りのアイデアを試したい

前回までで、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の操作系

最初の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資源にも制約があります。

実験用UIを作る

まだNuVCOに搭載する音作り機能は絞れていません。

そこで、最終形のUIを決める前に、いろいろな機能を試すための実験用UIを用意しました。

現在の状態を確認するためにOLEDを使い、必要な設定値も表示します。

値の変更にはRotary Encoderを使います。

1個のPOTで複数のパラメータを切り替えて操作する方法もありますが、POTには物理的な位置があります。

例えば、あるパラメータを大きな値に設定してPOTが右側にある状態で別のパラメータへ切り替えると、POTの位置と新しいパラメータの値が一致しません。

Rotary Encoderなら絶対的な位置を持たないので、表示している現在値を基準に増減できます。

OLEDで現在の項目と値を表示し、Encoderで変更する方式なら、操作するパラメータが増えても主にFirmware側の変更で対応できます。

さらにMCP23017を使い、SwitchやLEDも追加できるようにしています。

NuVCO UI Expander

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へ組み込んでいったかを書こうと思います。