2020 Prototype — from first sound to Dirichlet kernel, and the wrong turn that taught me everything
Before describing the current architecture in detail, I want to walk through what was actually built and running five years ago — in four stages, in the order they happened.
This is the proof-of-concept record. It is also the record of how the design philosophy of the current engine was forged through a sequence of experiments, two of which succeeded for the wrong reasons and one of which succeeded for reasons I did not appreciate until much later.
Stage 1 — First sound (November 2020)
[Tektronix display with 1 kHz sine, C5G board on the workbench]
This photograph captures the moment the C5G prototype produced its first sound. The Tektronix oscilloscope shows a clean 1 kHz sine wave; the small Terasic Cyclone V GX board on the cutting mat is the source. Behind the soldering equipment, the parts bins, and the everyday clutter of an electronics bench, something unprecedented was happening: 10,240 independent oscillators, all set to the same frequency, were summing into a single tone.
The test setup, in full, was the following:
- All 10,240 bins set to 1 kHz
- All bins set to phase = 0
- All bins set to maximum amplitude
- Reset held high; output muted
I will admit that I held my breath when I released reset. Mathematically, the output should have been a single 1 kHz sine at full scale — 10,240 unit-amplitude sinusoids, all coherent, all at the same frequency, summing to a single coherent tone scaled by the count. But "mathematically should" and "actually does, on real silicon, on the first try" are two different statements. There was a real possibility that I would hear a click, a burst of broadband noise, or nothing at all — any of which would have meant a fundamental error somewhere in the pipeline that would have taken weeks to find.
What I heard instead was a clean, sustained "piiiiiiiii——" at 1 kHz. The oscilloscope confirmed it visually. No transient. No artifact. Just the tone the mathematics predicted.
It took me a moment to register what had just happened. Then it took me longer to register what it meant. If 10,240 phase-coherent oscillators all sum cleanly into one tone, then the entire compute pipeline — every Maclaurin evaluation, every NCO accumulator, every adder in the 10,240-input adder tree — is working correctly, simultaneously, in real time. The single coherent output was, paradoxically, the strongest possible test signal: any error anywhere in any of the 10,240 bins would have shown up immediately as decoherence.
The boring tone on the oscilloscope was a complete proof of life.
Stage 2 — Dirichlet kernel (November 18, 2020)
Once the basic compute pipeline was confirmed, the obvious next test was to break the coherence on purpose, and see whether the bins behaved as 10,240 independent oscillators rather than as one big oscillator pretending to be many.
I picked a single 2,048-bin pipeline module (one of the five that make up the full engine) and detuned its 2,048 oscillators across a narrow frequency range:
- 2,048 sinusoidal oscillators, all phase-coherent, all equal amplitude
- Frequencies spaced from 996 Hz to 1004 Hz, in steps of 1/256 Hz
- All bins at maximum amplitude that did not clip the sum (97% of full scale)
What you hear is not a synthesizer "patch." It is a direct physical realization of a Dirichlet kernel — the closed-form sum of 2,048 equal-amplitude phase-coherent sinusoids spaced uniformly in frequency. The mathematics predicts a sharp main lobe at the geometric center, followed by gradually decaying side lobes whose mutual beating creates a slow envelope at frequencies determined by the detune step.
What it sounds like
I had expected a click. A burst. Possibly something noisy.
It is none of those.
It begins with a clear "piiiiinnnn——————" — a sustained tone at the centroid frequency, with no transient onset whatsoever. Then, over several seconds, it gently undulates a few times per second, the side-lobe beating pattern slowly walking through its own envelope. And then it descends, gradually and unhurriedly, into silence — like something sinking through clear water.
It is, unexpectedly, beautiful.
The waveform
[ The stereo capture showing main lobe and decaying side lobes]
Stereo capture via analog audio interface. The original FPGA output is bit-perfect mono; the small left/right asymmetry visible above is from the analog re-capture path, not from the synthesis itself.
The shape is the textbook Dirichlet envelope: a tall narrow main lobe at t=0, followed by exponentially-decreasing side lobes whose zero-crossings sit exactly where the mathematics predicts them. 2,048 phase-coherent oscillators, generating their own envelope through interference alone. No amplitude modulation. No envelope generator. Just the sum of 2,048 sine waves placed at the right frequencies.
The video
[Insert YouTube embed: "FPGA Spectrum Engine — 2,048 oscillators detuned by 1/256 Hz (2020 prototype)"]
The video shows the static waveform display; the audio is the actual recording. Headphones recommended for the descent into silence.
What this proved (that the first-sound test could not)
The first-sound test confirmed that the compute pipeline was internally correct. The Dirichlet test confirmed something else: that the bin parameters — frequency, phase, amplitude — could be set independently per bin, with the precision required to place each oscillator on a 1/256 Hz grid. The main lobe arrived exactly at the centroid; the side-lobe zeros arrived exactly where the mathematics put them. The frequency resolution claim of the engine was, at this point, no longer theoretical.
Together, the two tests — first sound and Dirichlet — bound the architecture from both directions. Coherent sum: every bin computes the same tone the same way. Coherent detune: every bin computes its own tone independently. The compute substrate of the engine was, by these two experiments, validated.
Stage 3 — The wrong turn (80-voice polyphony)
The Dirichlet test was, in retrospect, a diagnostic experiment. It was not the original goal of the C5G prototype. The original goal was something quite different and considerably less interesting: a conventional 80-voice polyphonic additive synthesizer, with 128 partials per voice (80 × 128 ≈ 10,240 — the same total bin budget, organized differently).
I built it. It worked. There’s even a video of me trying to control the keyboard using an RTL-implemented MIDI interface and pressing as many keys as possible at once, only to end up knocking the MIDI keyboard off the desk.
["FPGA Additive Synth — 80-voice polyphonic stress test (2020, C5G prototype)"]
The keyboard itself was somehow unharmed, but although I happened to press several keys at once, due to a fault with the MIDI interface, the notes weren’t being turned off and just kept playing. You can probably guess how many notes were sounding simultaneously from that rather silly video of me—someone with no perfect pitch—trying to find each key one by one to send a Note Off signal.
A video showing efforts to press as many keys as possible simultaneously with my elbow, whilst fixing a very basic issue inherent in the MIDI interface implemented in RTL on an FPGA. Although the sound is stable across the entire range from high to low notes, I’ve gradually begun to suspect that 10,240 pins might not be the best use of resources for this particular sound.
[ "80-voice polyphonic Stress testing following MIDI I/F debugging (2020, 17 December C5G prototype)"]
Then while playing it, I gradually realized something uncomfortable: the voice-allocation abstraction was the wrong abstraction. It made the system act like a conventional polyphonic synth, which means it could only do what conventional polyphonic synths do. The 10,240 bins were organized into 80 boxes of 128, each box behaving like one note. Beautiful, but artificially constrained.
["Vivaldi 'Smmer' Allegro non molto on FPGA additive synth — Performance from a DAW (2020)"]
(This piece is the first movement of Vivaldi’s ‘Summer’ (RV 315, ‘L’estate’, Allegro non molto))
What the Dirichlet test had shown — almost by accident — was the alternative: don't allocate voices. Address bins directly. A "note" is just a particular bin pattern. A "chord" is just a different bin pattern. A "noisy texture," a "comb-filtered impulse," an "evolving spectral cloud" — all the same hardware, all just bin patterns.
The 2020 architecture had the compute power. It just had the wrong API on top of it.
Stage 4 — The Vivaldi clue
Around the same time, I made a different recording: playing the third movement of Vivaldi's Winter (RV 297, "L'inverno", Allegro non molto) on the C5G prototype, controlled from MAX/MSP via MIDI, while moving slider faders to add and subtract individual harmonics in real time during the performance.
["Vivaldi 'Winter' Allegro non molto on FPGA additive synth — harmonic slider performance (2020)"]
This was meant as a musical demo. In retrospect, it was the second clue. If I could move harmonics with sliders during a Vivaldi performance, then those harmonics weren't really "harmonics" — they were just bins I happened to have placed at integer multiples of the fundamental. Nothing in the hardware required them to be there. I could have placed them anywhere.
The implication, once I let it land: every synthesis paradigm is a bin-placement problem. FM, additive, polygonal, fractal, Cantor-spectrum, Shepard tones, physical models — all of them. The current project is what happens when you take that observation seriously.
What I'm building now, and why these old proofs matter
The DE10-nano build now in progress is not a successor of the 2020 polyphonic synth. It is a deliberate abandonment of the voice-allocation abstraction, replaced by a 3-layer architecture in which the FPGA does nothing but address bins, the ARM HPS expands abstract scene descriptions into bin assignments, and the PC layer holds the high-level musical/sensing language.
The four 2020 experiments are the foundation:
- First sound validates the compute pipeline's internal correctness.
- Dirichlet kernel validates the per-bin parameter independence and frequency resolution.
- 80-voice polyphony documents the failed abstraction layer that taught me what not to do.
- Vivaldi with sliders is the moment the right abstraction first became visible, even if I did not yet know how to name it.
The compute substrate is real. Only the abstraction layer is being rebuilt.
A note on Open Prompt
This Build Log is descriptive, not prescriptive — I am documenting what was built, not handing over a blueprint to copy. The full architectural and mathematical description, sufficient for any capable engineer (with or without an LLM as a collaborator) to regenerate their own implementation, will appear in Build Log #4 as the Open Prompt declaration.
For now: Open Prompt does not exclude open source. It includes it, and reframes it. I will publish sample source code as I produce it — Verilog skeletons, MAX patches, bin-pattern generators — as reference implementations. Anyone who regenerates their own implementation from the architecture is free to publish or not publish their version, by their own judgment. The architecture is the commons; each implementation is its author's own. More on this in #4.
A small but illustrative example of how this works in practice: the C5G prototype used a particular polynomial evaluation strategy that suited its constraints at the time. The Build Log #1 description of an 11th-order Maclaurin series evaluated by Horner's method is a different implementation of the same mathematics, suited to a different set of constraints. Both are correct. Both are derivable from the same architectural specification. This is what Open Prompt looks like when it works: the same specification regenerating into different implementations, each owned by its author.
Coming next
- Build Log #1 (already posted) — Why 11th-order Maclaurin, not CORDIC and not LUT: the mathematical and pipeline-architectural reasoning.
- Build Log #3 — Synthesis paradigm lineage: from Chowning's 1973 FM equation to the spectral fractal direction, and where this engine sits on that timeline.
- Build Log #4 — Open Prompt: a knowledge-sharing paradigm for the LLM era.
Companion interactive page: https://dsohnaka.github.io/FPGA_Spectrum_Engine/
▼ Build Log 本文(日本語版・併記用)
2020年プロトタイプ — 初出音から Dirichlet カーネル、そしてすべてを教えてくれた誤った方向
現在のアーキテクチャを詳述する前に、5年前に実際に作って動いていたものを4つの段階で——起こった順序で——辿りたい。
これは実証記録である。同時に、現エンジンの設計思想がどのように一連の実験を通じて鍛えられたかの記録でもある——そのうち2つの実験は誤った理由で成功し、1つの実験は私が後になるまで気づかなかった理由で成功した。
第1段階 — 初出音(2020年11月)
[ここにオシロスコープ写真挿入 — Tektronix の表示に1kHz正弦波、作業台上のC5Gボード]
この写真は、C5Gプロトタイプが最初の音を出した瞬間を捉えている。Tektronix オシロスコープには綺麗な1kHz正弦波;カッティングマット上の小さなTerasic Cyclone V GXボードがその発生源。ハンダ付け機材、部品箱、電子工作机の日常的な雑然さの背後で、何か前例のないことが起きていた:10,240本の独立したオシレータが、すべて同一周波数に設定された状態で、単一のトーンに総和されていたのである。
完全なテスト構成は以下の通り:
- 全10,240ビンを 1 kHz に設定
- 全ビンを 位相 = 0 に設定
- 全ビンを 最大振幅 に設定
- リセット High 維持、出力ミュート
リセットを解除する瞬間、息を止めていたことを認めなければならない。数学的には、出力はフルスケールの単一1kHz正弦波になるはず——10,240本の単位振幅正弦波がすべてコヒーレントに、すべて同一周波数で、本数倍のスケールで単一のコヒーレントトーンに総和される。だが「数学的にはなるはず」と「実シリコン上で初回試行で実際になる」は別の主張である。クリック音、広帯域ノイズ、あるいは何も聞こえない可能性は現実的にあった——いずれもパイプラインのどこかにある根本的なエラーを意味し、発見に何週間もかかる類のものになる。
代わりに聞こえたのは、清潔で持続する1kHzの「ピーーーーー——」だった。オシロスコープが視覚的に確認した。過渡なし。アーチファクトなし。ただ数学が予測したそのままのトーン。
何が起きたかを認識するのに少し時間がかかった。それが何を意味するかを認識するにはもっと時間がかかった。10,240本の位相コヒーレントなオシレータが綺麗に1つのトーンに総和されるなら、計算パイプライン全体——全マクローリン評価、全NCOアキュムレータ、10,240入力加算木の全加算器——は同時に、実時間で、正しく動作している。 単一のコヒーレント出力は、逆説的に、最も強力なテスト信号だった:10,240ビンのいずれかにエラーがあれば、即座にデコヒーレンスとして現れたはずである。
オシロスコープに映る退屈なトーンが、完全なる生命の証だった。
第2段階 — Dirichlet カーネル(2020年11月18日)
基本的な計算パイプラインが確認された後、次の明白なテストは意図的にコヒーレンスを破壊し、ビンが10,240本の独立したオシレータとして振る舞うか——それとも多数のフリをした1つの巨大オシレータとして振る舞うのか——を見ることだった。
5つのパイプラインモジュール(フルエンジンを構成する5つのうちの1つ)の単一2,048ビンモジュールを選び、その2,048本のオシレータを狭い周波数範囲に分散させた:
- 2,048本の正弦波オシレータ、すべて位相コヒーレント、すべて同振幅
- 周波数を996 Hz から 1004 Hz まで、1/256 Hz 刻みで分散
- 全ビンを総和でクリップしない最大振幅(フルスケールの97%)に設定
聴こえるのは「シンセのパッチ」ではない。これは Dirichlet カーネル——周波数軸上に等間隔配置された2,048本の同振幅・位相コヒーレント正弦波の閉形式総和——の直接的な物理実現である。数学は予測する:幾何中心に鋭い主ローブ、続いて徐々に減衰する副ローブ、それらが互いにビートしてデチューン間隔で決まる周波数のゆっくりした包絡線を作る。
実際の音
私はクリック音を予想していた。バーストか、ノイズめいた何か。
そのいずれでもなかった。
「ピィンーーーーーーー………」という、立ち上がり過渡をまったく持たない、中心周波数の持続音から始まる。それが秒に数回、副ローブのビートパターンが自身の包絡線をゆっくり歩きながら、穏やかにうねる。そして緩やかに、急がず、静寂の中へ降りていく——透明な水の中に何かが沈んでいくように。
意外にも、美しい音だった。
波形
[ここに波形スクリーンショット挿入 — 主ローブと減衰副ローブを示すステレオ録音]
アナログオーディオインターフェース経由のステレオ録音。FPGA の元出力はビット完全モノラルであり、上の波形に見える左右の微妙な差はアナログ再録音経路に由来する。合成自体には差はない。
形状は教科書通りの Dirichlet 包絡:t=0 に高く狭い主ローブ、続いて指数的に減衰する副ローブ群、ゼロ交差点はすべて数学が予測する位置にある。2,048本の位相コヒーレントなオシレータが、干渉だけで自身の包絡線を生成している。 振幅変調なし。エンベロープジェネレータなし。ただ正しい周波数に配置された2,048本の正弦波の総和のみ。
動画
[YouTube埋め込み挿入:「FPGA Spectrum Engine — 2,048 oscillators detuned by 1/256 Hz (2020 prototype)」]
動画は静止波形表示だが、音声は実録音である。終盤の静寂への降下はヘッドホンを推奨。
これが証明したこと(初出音テストでは証明できなかったこと)
初出音テストは計算パイプラインが内部的に正しいことを確認した。Dirichlet テストは別のことを確認した:ビンパラメータ——周波数、位相、振幅——をビンごとに独立して、各オシレータを 1/256 Hz グリッドに配置できる精度で、設定できることを。主ローブは中心に正確に到達した;副ローブのゼロは数学が置いた場所に正確に到達した。本エンジンの周波数分解能の主張は、この時点で、もはや理論ではなくなった。
両者を併せて——初出音と Dirichlet——アーキテクチャを両側から境界した。コヒーレント総和:全ビンが同じトーンを同じ方法で計算する。コヒーレントデチューン:全ビンが各自のトーンを独立に計算する。 本エンジンの計算基盤は、この2つの実験により、検証された。
第3段階 — 誤った方向(80音ポリフォニー)
Dirichlet テストは、振り返ると診断実験だった。それは C5G プロトタイプの本来の目標ではなかった。本来の目標はかなり別物で、しかも遥かに興味の薄いもの——標準的な80音ポリフォニック加算合成シンセサイザー、1音あたり128倍音(80 × 128 ≈ 10,240——現在と同じ総ビン予算を異なる仕方で組織したもの)。
作ってみた。ちゃんと動いた。RTLで実装したMIDIインターフェースを使ってキーボードを操作し、できるだけ多くのキーを同時に押そうとしたところ、結局MIDIキーボードを机から落としてしまったという動画まである。
[YouTube埋め込み挿入:「FPGA Additive Synth — 80-voice polyphonic stress test (2020, C5G prototype)」]
キーボード本体はなぜか無傷だったが、たまたま複数のキーを同時に押してしまったところ、MIDIインターフェースの不具合により、音符がオフにならず、ただ鳴り続けてしまった。絶対音感のない私が、一つずつキーを探してノートオフ信号を送ろうとしている、あの少々滑稽な動画を見れば、どれだけの音が同時に鳴っていたか推論していただけるだろう。
FPGA上のRTLで実装されたMIDIインターフェースに内在するごく基本的な問題を修正しつつ、肘を使ってできるだけ多くのキーを同時に押そうとする様子を収めた動画である。高音から低音までの全音域で音は安定してるが、この特定の音作りに10,240本ものピンを割くのは、リソースの最適な活用とは言えないのではないかと、次第に疑い始めていた。
[YouTube埋め込み挿入:「80-voice polyphonic Stress testing following MIDI I/F debugging (2020, 17 December C5G prototype)」]
そして演奏しているうちに、居心地の悪い気づきが徐々に育っていった:ボイスアロケーションという抽象が、間違った抽象だった。 これはシステムを従来型ポリシンセのように振る舞わせる——つまり、従来型ポリシンセができることしかできなくさせる。10,240ビンが80個の128ビンの箱に組織され、各箱が1音のように振る舞う。美しいが、人為的に拘束されている。
[YouTube埋め込み挿入:「Vivaldi 'Smmer' Allegro non molto on FPGA additive synth — Performance from a DAW (2020)」]
(This piece is the first movement of Vivaldi’s ‘Summer’ (RV 315, ‘L’estate’, Allegro non molto))
Dirichlet テストが——ほとんど偶然に——示したのは別の道筋だった:ボイスを割り当てるな。ビンを直接アドレスせよ。 「音」は単に特定のビンパターン。「和音」は別のビンパターン。「ノイジーなテクスチャ」「櫛形フィルタインパルス」「進化するスペクトル雲」——すべて同じハードウェア、すべて単なるビンパターン。
2020年のアーキテクチャは計算能力を持っていた。間違っていたのはその上に乗っていた API だった。
第4段階 — ヴィヴァルディの手がかり
ほぼ同時期に別の録音を作っている:MAX/MSP から MIDI 制御で C5G プロトタイプ上にヴィヴァルディ「冬」第三楽章(RV 297, "L'inverno", Allegro non molto)を演奏させながら、スライダーフェーダーで個々の倍音を演奏中にリアルタイムに加減した記録。
[YouTube埋め込み挿入:「Vivaldi 'Winter' Allegro non molto on FPGA additive synth — harmonic slider performance (2020)」]
これは音楽デモのつもりだった。後から振り返ると、第二の手がかりだった。もしヴィヴァルディ演奏中に倍音をスライダーで動かせるなら、それらの倍音は本当の意味で「倍音」ではない——たまたま基音の整数倍位置に置いたビンに過ぎない。ハードウェア上、そこに置く必然性はない。どこにでも置けたはずなのだ。
その含意を引き受けたとき:あらゆる合成パラダイムはビン配置問題である。 FM、加算、ポリゴナル、フラクタル、Cantor スペクトル、Shepard トーン、物理モデル——すべて。現プロジェクトはこの観察を真剣に引き受けたときに何が起きるかである。
いま作っているもの、そしてこれらの古い証拠が重要である理由
進行中の DE10-nano ビルドは、2020年ポリシンセの後継機ではない。ボイスアロケーション抽象を意図的に放棄し、3層アーキテクチャに置き換えたもの——FPGA はビンをアドレスすること以外何もせず、ARM HPS は抽象シーン記述をビン割当に展開し、PC 層が高水準の音楽的・センシング的言語を保持する。
2020年の4つの実験が基礎である:
- 初出音は計算パイプラインの内部的正しさを検証する
- Dirichlet カーネルはビンごとのパラメータ独立性と周波数分解能を検証する
- 80音ポリフォニーは何をすべきでないかを教えてくれた失敗した抽象層を文書化する
- スライダー付きヴィヴァルディは正しい抽象が初めて見えた瞬間である——まだ名付け方を知らなかったとしても
計算基盤は実在する。再構築されているのは抽象層だけだ。
オープンプロンプトについての注
この Build Log は記述的であって規範的ではない——作ったものを記録しているのであり、コピー用設計図を渡しているのではない。完全なアーキテクチャ的・数学的記述——LLM を協働者として(あるいは伴わずに)有能なエンジニアが各自の実装を再生成するに足るもの——は Build Log #4 にてオープンプロンプト宣言として登場する予定である。
差し当たり:オープンプロンプトはオープンソースを排除しない。包含し、その位置づけを変える。 私自身が生み出すサンプルソースコード——Verilog スケルトン、MAX パッチ、ビンパターン生成器——はリファレンス実装として公開していく。アーキテクチャから自身の実装を再生成した者は、その版を公開するもしないも、自身の判断で自由に選択できる。アーキテクチャは共有財産、各実装はその作者自身のもの。詳細は #4 にて。
オープンプロンプトが実際にどう機能するかを示す小さくも示唆的な例:C5G プロトタイプは当時の制約に適した特定の多項式評価戦略を用いていた。Build Log #1 で記述された Horner 法による11次マクローリン級数の評価は、同じ数学の異なる実装であり、異なる制約集合に適している。両者とも正しい。両者とも同じアーキテクチャ仕様から導出可能である。これがオープンプロンプトが機能するときの姿である:同一仕様が異なる実装に再生成され、各々が作者に所有される。
次回予告
- Build Log #1(既掲載) — なぜ11次マクローリンか、CORDIC でなく LUT でもない理由:数学的・パイプラインアーキテクチャ的根拠
- Build Log #3 — 合成パラダイムの系譜:Chowning の1973年 FM 方程式から、スペクトラルフラクタルへの方向性、そしてこのエンジンがそのタイムライン上のどこに位置するか
- Build Log #4 — オープンプロンプト:LLM 時代の知識共有パラダイム
Companion interactive page: https://dsohnaka.github.io/FPGA_Spectrum_Engine/
Tsuneo.Ohnaka
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.