Skip to content

The Bach Generator That Stopped Repairing

バッハの器楽曲を生成するプログラムを書いている。オルガンのフーガ、無伴奏チェロ組曲、シャコンヌ。譜面ではなくMIDIを吐く。最初のアーキテクチャは、いま思えば素直すぎた。まず音を生成して、ルール違反をあとから直す。 これで動くと思っていた。

動いた。並行5度は消えた。声部交差も直った。跳躍も収まった。テストは緑になった。

でも、鳴らすと音楽ではなかった。

直せば直すほど音楽でなくなる、という奇妙な手応えがあった。リペアをいくら足しても、吐き出されるのは不協和音の嵐だ。子どもがオルガンをでたらめに叩いているような、聞くに堪えないデータ。それが延々と出てくるのを見て、この先にゴールはないと悟った。わたしはエンジンを丸ごと捨てた。リペアという発想そのものを捨てた。この記事は、その方針転換の記録だ。

対位法を、まず3分で

先に最低限の言葉を用意しておく。これを読んでいるエンジニアの多くは、たぶん対位法とは縁がなかっただろう。読み飛ばしてもいいが、ここを通しておくと後半が一気に読めるようになる。

対位法(counterpoint)とは、独立した複数の旋律を同時に鳴らして、それでも調和させる書法だ。メロディに伴奏のコードを付けるのとは違う。主旋律と和音、ではなく、どの線もそれ自体が旋律として成立していて、しかも重ねて鳴らしても破綻しない。各々の線を声部(voice)と呼ぶ。フーガなら3声、4声と重なる。

軸は二つある。——一本の線が、それ単独で歌える旋律になっていること。——ある瞬間に同時に鳴っている音どうしが、濁らずに響くこと。作曲とは、この縦と横を同時に満たし続ける作業だ。

たとえるなら、数人が同時に喋る会話に近い。各人が筋の通った話をしていて(横)、かつ全員が声をそろえて同じ言葉を唱和したりはしない(縦の独立)。誰か一人に全員が飲み込まれた瞬間、それは会話ではなく斉唱になる。

協和と不協和

二つの音の隔たりを音程(interval)という。隔たりによって、響きは安定したり緊張したりする。ここから先の譜面はどれも再生できる。再生ボタンを押して、耳で確かめてほしい。

まず完全協和音程——完全5度や完全8度。

完全協和音程完全5度・完全8度・完全12度
澄んでいて安定しているが、空虚でもある。出だしや終わりの支点には向く。ただし二声がこの響きに並行して着地し続けると、二声には聞こえなくなる。
上声のどの音も、保続された C の完全5度・8度・12度上にある。

なぜ「完全」と呼ぶのか

「完全」は出来栄えの話ではない。ドからソまでが完全5度、ドから1オクターヴ上のドまでが完全8度——どちらも溶け合う力が強く、二つの音がほとんど一つに聞こえる。3度や6度には長・短の二種類があって響きが揺れるのに、5度と8度にはその揺れがなく一種類しかない。だから「完全」と呼ぶ。そしてこの溶け合いの強さこそが、のちに「並行させてはいけない」と禁じられる理由になる。

次に不完全協和音程——3度・6度・10度。

不完全協和音程3度・6度・10度
二声書法の主力だ。十分に豊かで、なお動き続けたがる。そして完全音程と違い、3度や6度の並行はいくらでも使える。
保続された C に対する3度と6度。豊かで、なお前へ傾いている。

2度・7度・三全音(トライトーン)は不協和音程で、緊張を生む。

不協和音程2度・7度・三全音
不協和は敵ではない。対位法はこの緊張を燃料にして進む。壊れるのは「管理されていない」不協和だ——準備なく現れ、解決せずに去る衝突。これを覚えておいてほしい。記事全体の蝶番になる。
vertical_dissonance赤い音程——2度・7度・三全音——はいずれも不協和で、出口を欲しがる。

運動

二つの声部が動くとき、関係は三通りだ。同じ方向に動く並行(parallel)、逆方向に動く反行(contrary)、片方が止まってもう片方だけが動く斜行(oblique)。声部の独立は、常に並行しないことから生まれる。

安全な運動反行と斜行
反行(逆方向)と斜行(片方は静止)は、完全音程も含めどんな響きへ向かうにも安全な運動だ。二声に聞こえるのは、二声が別々のことをしているからだ。並行のトラブルの大半は、片方を逆向きに動かした瞬間に消える。
まず二声が逆方向に開く。次に一方が保たれ、もう一方だけが動く。

もう一つだけ。拍には強弱の序列がある。各小節の頭が強拍で、それ以外は弱拍だ。さっきの不協和は、弱拍でなら通り抜けてよいが、強拍に置けば構造の誤りに聞こえる。どこに緊張を置けるかは、拍の位置で決まる。

基礎強拍と弱拍
再生しながら「1、2、3、4。1、2、3」と数えてみてほしい。各小節の1拍目——小節頭——はチェックポイントで、耳はそこで和声が安定していることを期待する。エンジンも同じ二値だ。小節の頭の tick が強拍、それ以外はすべて弱拍。緊張は弱拍なら通り抜けてよい。強拍に置けば、構造の誤りとして聞こえる。
各小節の1拍目が強拍——和声が安定していなければならない場所だ。

これで、声部・協和と不協和・運動・拍の強弱がそろった。道具はこれで足りる。ここからが本題だ——なぜ、リペアでは音楽にならないのか。

学習して、生成して、直す

捨てた側のエンジン——コミットログで「legacy generation system」と呼んでいたもの——は、当時のわたしとしてはよく出来ていたと思う。三段構えだった。

まず学習。本物のバッハを448曲ぶんJSON化したコーパスを用意し、そこからMarkov連鎖の遷移表や5-gramの語彙テーブルを抽出した。「この音型のあとには、この音型が来やすい」という統計だ。

次に生成。語彙テーブルを引きながら音を並べる。声部の動きには制約ソルバを噛ませた。ConstraintStateという三層モデル——果たすべき義務(Obligation)、破ってはならない不変条件(Invariant)、各音の引力(Gravity)——を持ち、FeasibilityEstimatorが「この先まだ解けるか」を見積もる。それなりに大掛かりな仕組みだった。

そして採点。bach-mcpという姉妹プロジェクトのスコアラに生成結果を渡し、本物のコーパスとの近さで点をつける。点が低ければ作り直す。といっても自動のループではなく、スコアを見てわたしが手で回していた。

要するに、データからバッハを学習し、模倣して生成し、おかしいところを直す。今のAI生成の素朴な発想とそう変わらない。問題は「直す」のところに全部詰まっていた。

リペアが積み上がる

生成された音には、必ずルール違反が混じる。そのたびにリペアのパスを足した。用語は流し読みでいい——見てほしいのは、コミットログに残った積み上がりそのものだ。

  • 並行5度・8度を消す(parallel repair
  • 跳躍——音が大きく飛ぶこと——を直す(leap resolution module
  • 同じ音の繰り返しを直す(repeated-note repair
  • 隣り合う声部の音のぶつかりを直す(tritone repaircross-relation fix
  • 外声がうっかり完全音程に着地するのを止める(hidden-perfect outer-voice rule
  • 声部どうしの衝突を時間軸ごと解く(harmonic-timeline-aware collision resolution
  • 曲の終わり(コーダ)の声部進行を作り直す(coda voice-leading search

一つひとつは正しい。並行5度は対位法で禁じられている。直すべきだ。でも、リペアを足すほど、別の問題が顔を出す。並行5度を消そうと1音ずらすと、そのずらしが跳躍を生む。跳躍を直すと同じ音の繰り返しになる。それを直すと、今度はまた別の声部と並行8度になる。モグラ叩きだった。

そして致命的だったのは、リペアは規則違反を消せても、音楽を作れないということだ。

リペアが音楽を壊す

なぜ局所的なリペアでは音楽にならないのか。具体的に、耳で確かめながら見ていく。

並行5度が禁じられるのは、道徳の問題ではない。さっきの会話のたとえで言えば、二人が完全5度のまま声をそろえて動いた瞬間に、二声が一声へ溶けてしまうからだ。

禁則並行5度
同じ方向へ同じ完全音程のまま動く二声は、一本に溶ける。赤い縦線はどちらも完全5度、矢印はどちらの声部も止まっていないことを示す——斜行の逃げ道はない。教科書通りの禁則並行であり、旧リペア群が最も追い回した標的でもある。
parallel_fifth二声がそろって上行し、完全5度のまま離れない——赤い縦線は前後とも同じだ。

リペアの発想だと、これは「片方の音を1個ずらせば消える」問題に見える。実際、消える。でも、ずらした1音は旋律の論理の中にいる。前後の音となめらかに繋がっていた線が、リペアの都合で折れる。違反は消えても、線が死ぬ。

隠伏5度はもっと厄介だ。今その瞬間は5度になっていなくても、外声が同じ方向から完全5度に「到達」すると、耳には並行5度の残響が残る。

禁じられた到達隠伏5度
ここに並行5度はない——5度は一度きりだ。だが外声が同方向に動いてそこへ到達するので、耳には並行の残響が残る。「今この瞬間」だけでは足りない。入り方まで正しくなければならない。
hidden_parallel_fifth不完全音程から同方向に進み、むき出しの完全5度へ直接入る。

ここで効いてくるのが、禁じられているのは完全音程の並行だけという事実だ。3度や6度——10度も含めて——の並行はむしろバッハの常套手段で、二声がきれいに寄り添って動く。

バッハの実例バッハ: 平均律 I 巻 変ホ長調プレリュード — まるまる1小節の並行10度
変ホ長調プレリュード(平均律 I 巻、BWV 852)の第32小節。二本の線が1小節まるごとを10度で並走する——どの16分音符も複合3度だ。音階の都合で長10度と短10度を行き来するが、不完全協和という家族からは一度も出ない。だから並行禁則は何も言わない。同じことを5度やオクターヴでやれば、たちまち崩れる。罪は並行運動ではなく、並行する完全音程のほうだ。(各走句の上で鳴り続ける保持音——B♭ と G、つづいて E♭ と C——は省いた。)
二本の16分音符の線が、小節まるごと厳格な並行10度で走る——どのルールも咎めない。

つまり「並行を消す」リペアは粗すぎる。完全音程か不完全音程かで扱いが正反対なのだから、消すべきものと残すべきものを、音を置く前に判断していなければならない。

掛留は、あとから作れない

リペアの限界がいちばんはっきり出るのが掛留(サスペンション)だ。

掛留は、前の拍で協和していた音をそのまま次の拍へ「保留」し、その拍では不協和になり、続けて2度下行して解決する。準備—保留—解決の三拍子。不協和になる前に、協和として準備されていなければならない。

許容される型7-6の連鎖
7–6 を連ねると、それは推進装置になる。上声はバスの一歩ごとにタイで持ち越され、7度でぶつかり、6度へ解決する——その6度が、もう次の7度の準備音だ。着実に下るバスに対して上声が遅れていくこのシンコペーションは、バロックのゼクエンツで最も耳に残る響きのひとつだ。
suspension_seventh_sixthsuspension_resolution_step_downバスが下りていくあいだ、各解決音がそのまま次の掛留の準備音になる。

問題はここだ。リペアは「いま鳴っている不協和音」を見つけてから動く。でも掛留の準備は、不協和が鳴る一つ前の拍で済んでいなければならない。リペアが違反を見つけた時点で、もう手遅れなのだ。時間を巻き戻して前の拍を協和に書き換えれば、今度は前の拍の整合が崩れる。

準備なしにいきなり不協和を置くと、それはただの濁りであって掛留ではない。

弱拍準備されない不協和
弱拍の非和声音が許されるのは、線が順次でそこへ入り、順次で出て、型が読み取れるときだけだ。跳躍で入る・跳躍で出ると、経過音ではなくただの間違いになる——そして後付けの修正で、間違いが経過音に変わることはない。
unprepared_dissonance弱拍の不協和から跳躍で離れるため、耳は経過音として読めない。

バッハの掛留の連鎖を聴くと、不協和が緊張と解決のリズムを生んでいるのがわかる。これは生成後の修正では絶対に届かない。設計の段階で時間軸ごと組み立てるしかない。

バッハの実例バッハ: 平均律 I 巻 ロ短調プレリュード — 声部を渡り歩く掛留
ロ短調プレリュード(平均律 I 巻、BWV 869)。掛留は一つの声部に留まらず、声部から声部へ手渡され、不協和が途切れずに到来し続ける。聴けば、緊張と解決そのものが脈になっているのがわかる。これは音を後から直して届く場所ではない——バッハは時間軸ごと組み上げてこれをやっている。聴くたびに、なぜリペアを消したのかを思い出す。
suspension_preparationsuspension_resolution_step_down歩くバスの上で、ソプラノとアルトが代わる代わる音を吊り、不協和を作っては順次下行で解く。

転回しても成り立つ、という制約

もう一つ、局所リペアが原理的に届かない例がある。転回対位法だ。

ここでいう「転回」は、下の声部をまるごとオクターヴ上げて、上と下を入れ替えることだ。入れ替えると、二声の音程が変わる。たとえば下が C、上が G なら完全5度。その C を G の上へ持ち上げると、今度は G と C で完全4度になる。5度が4度に化けたわけだ。同じように3度は6度に、6度は3度になる。上下の数字を足すと必ず9になる、と覚えておけばいい。

下のトグルで、それを目で確かめられる。「原型」と「転回」を切り替えると、上の線A は動かないまま、下の線B(青)だけが1オクターヴ上がり、3度が6度に変わる。

転回対位法下の線を1オクターヴ上げても、成り立つ
線Bを1オクターヴ上げる —
転回できるように書いた、短い2本の線。上の線A は両方の表示で凍りついたまま——同じ音が、同じ場所にある。動くのは下の線B(青)だけだ。「転回」に切り替えるとB が丸ごと1オクターヴ上がり、二声の上下が入れ替わって、音程は補数へ反転する——3度は6度に。それでもどちらも協和のままだ。両方を再生してみてほしい。入れ替えても、何も損なわれない。
invertible_at_octaveトグルで切り替えても、上の線A は動かない。下の線B(青)だけが1オクターヴ上がり、すべての3度が6度になる。

ここに罠がある。二声だけで鳴るとき、完全4度は不協和として扱われる——完全5度は協和なのに、だ。つまり、入れ替える前は安定していた5度が、入れ替えた瞬間に不協和の4度へ反転してしまう。

4度が不協和、という違和感

ソからその上のドまでが完全4度。耳には落ち着いて聞こえるのに、二声だけで鳴らすと不協和として扱われる——昔からの対位法の決まりだ。理由は、下のソが「土台」のように聞こえてしまい、上のドが宙ぶらりんに感じられるから。下にもう一声足して三声にすれば、4度はちゃんと協和に戻る。だから困るのは、二声を上下そっくり入れ替えるこの場面だけだ。

転回対位法8度の転回 — 5度は4度になる
下の声部がオクターヴ跳び上がり、上と下が入れ替わる。鳴っている二音は同じなのに、5度が4度に変わる——音は一つも書き換えていないのに、協和が不協和へ転じる。
invertible_at_octave同じ二音が5度。下の音をオクターヴ上げると、4度になる。

これを満たすには、二つの線を「上にあっても下にあっても通用する」よう最初から設計しなければならない。生成してからリペアで近づけられる性質のものではない。バッハのフーガでは、主題と対主題がこの制約を当然のように満たしていて、だからこそ展開部で上下を入れ替えても崩れない。

バッハの実例バッハ: BWV 847 — 同じペアが、上にも下にも
青い対主題を目で追う —
ハ短調フーガ(平均律 I 巻、BWV 847)から、同じ主題と対主題の実際の二つの提示。トグルで切り替えて、青い線を目で追ってほしい。第7小節では対主題が上にあり、第20小節では主唱の下へ落ちている——二つの声部が、音はそのままにオクターヴを交換したのだ。動いても壊れないのは、線がそう作られているからだ。全体がオクターヴの補数で噛み合い、不協和へ化ける強拍の4度も、5度へ化ける8度並行も、仕込まれていない。バッハは最初から転回可能に書いている。そうとしか書きようがないからだ。(各所で中声部は見やすさのため省いた。)
invertible_at_octavefourth_only_on_weak_beat第7小節では対主題(青)が主唱の上にあり、第20小節ではバッハが同じペアを裏返して、対主題が下を走る。それでも全ての音程が成立している。

声部交差も同じ匂いがする。旧エンジンは交差を「検出して直す」対象にしていた。でもバッハは意図的に声部を交差させて、線の独立性をむしろ際立たせる。一律に直してはいけないものを、リペアは一律に直してしまう。

バッハの実例バッハ: ゴルトベルク第3変奏 — 同度カノンは交差せざるを得ない
ゴルトベルク第3変奏は同度のカノン——フォロワーがリーダーの線を1小節遅れで、まったく同じ高さでなぞる。同じ高さは同じ音域だ。リーダーが1小節前に歌った B でフォロワーが入るとき、リーダーはすでに3度下りていて、最初の音から声部は交差している。紙の上なら符尾とスラーが二本の線を読み分けさせる。生成された声部に紙はないので、エンジンはそもそも交差を許さない。バッハは厳格な模倣の正当な代価として、このもつれを引き受ける。(通奏低音は省いた。)
voice_crossing二つのカノン声部が同じ音域を分け合う。フォロワーが入った瞬間、リーダーはもうその下へ滑り込んでいる。

最初から正しく置く

結論はシンプルだった。生成してから直すのをやめて、置く瞬間に正しい音を選ぶ。

新しいエンジン——コミットでは「composer subsystem」と名付けた——は、和声プラン(harmonic plan)と各声部の意図(voice intent)から直接MIDIを組み立てる。中心にあるのはcandidate_searchだ。ある位置に音を置くとき、候補となる音を一つずつ評価する。並行完全音程になっていないか、声部交差を起こさないか、跳躍は適切か、不協和は準備・解決されているか、経過音として正当か、フーガの応唱を運べているか。各ルールでスコアをつけ、最も高い候補を採る。

違反が「出てから」直すのではない。違反になる候補は、そもそも選ばれない。リペアのパスは要らなくなった。生成後に走るのはvalidatorだけ——構造(structural)、横の流れ(horizontal)、縦の響き(vertical)の最終チェックで、これは直す装置ではなく、置き方が正しかったかを確かめる装置だ。

新エンジンが立ったところで、退路を断った。

  • まずバッハ参照コーパス448曲のJSONを、スコアラごとbach-mcp側へ移した。生成エンジンはこのデータに依存していない。本物のバッハを参照するのは、最後の検証のときだけだ。
  • C API・CLI・WASMビルドの全10形式を、新しいcomposer経由に切り替えた。
  • そして旧エンジンを丸ごと削除した。counterpoint、fugue、forms、ornament、instrument、transform、solo-stringの各サブシステムと、それらを束ねていたGeneratorごと。

削除後の検証は、ctest 920件、WASMビルドとJSテスト217件、Pythonテスト146件、すべて緑。捨てたコード量のわりに、壊れたものは何もなかった。リペアの山は、実は本質ではなかったのだ。

規則は、耳で確かめる

candidate_searchが見ているルールを、耳で聞ける教材にしてみたかった。そこで対位法講座をサイトに併設した。この記事に貼った再生可能な譜面は、すべてその講座から持ってきたものだ。

講座はバッハの作例で文法を教える。たとえばフーガの応唱。主題をそのまま属調に移すと「真正応唱(real answer)」、調的なつじつまを保つために一部を調整すると「変応唱(tonal answer)」になる。どちらを選ぶかは局所の問題ではなく、曲全体の調的設計の問題だ。

属調(ドミナント)とは

主調——曲のホームになる調——の5度上の調を属調という。ハ長調がホームなら、属調はト長調。フーガでは主題がまずホームの主調で現れ、応唱が属調でそれに答える。このホームと属調の「呼びかけと応え」が、フーガという形式の背骨になっている。

フーガ技法主唱と変応(トーナルアンサー)声部を順に再生
主唱を丸ごと5度上へ移せば「実応」。冒頭だけを曲げて主音↔属音を入れ替えれば「変応」——提示部を主調に係留しておくための調整だ。どちらが正しいかは局所では決まらない。曲全体の調の設計から導かれる。
tonal_answer_dominant_mapping主唱は主音で、応唱は属音で始まる。冒頭は I→V の写像で、音をそのまま移調したのではない。
バッハの実例バッハ: 平均律 I 巻 ハ短調フーガ — 変応声部を順に再生
現存する最も引用される変応——ハ短調フーガ(平均律 I 巻、BWV 847)。主唱は C–B♮–C と始まり、G——主音から属音——へ落ちる。これを律儀に5度上で応えれば4音目は D になり、提示部は早々に調から滑り落ちる。バッハは G–F♯–G と応え、その一音だけを C へ帰す——主音には属音を、属音には主音を、尾部は正確な移調。たった一音が、楽章全体の調によって決まる。リペアには下せない判断だ。
tonal_answer_dominant_mapping主唱の4音目は属音へ落ちる。応唱はその一音だけを主音へ曲げ戻し、残りは正確に移調する。

主題が入ったら、それに寄り添う対主題が要る。さらに、主題の入りが重なり合うとストレッタになる。

バッハの実例バッハ: 平均律 I 巻 ハ長調フーガ — 1拍差のストレッタ
ハ長調フーガ(平均律 I 巻、BWV 846)の第7〜8小節。ほぼ全編がストレッタでできていることで知られる一曲だ。ソプラノが主題を始めると、わずか1拍後にアルトが4度下で同じ主題を始め、どちらも最後まで走り切る。本物の重なり、正確な移調——入りの上に入りが積まれて、なお何も衝突しない。(この小節のバスは省いた。)
stretto_overlap_validアルトはソプラノの1拍後、4度下で主題を始める——先行声部はまだ4音目だ。

そして終止。完全終止だけでなく、期待を裏切る偽終止、短調の曲を長三和音で閉じるピカルディの三度。終止は和声の文法であって、これも音を置く前に決まっていなければ書けない。

バッハの実例バッハ: ゴルトベルクのアリア — 最後の終止
ゴルトベルクのアリア(BWV 988)の最後の2小節。旋律の16分音符が導音 F♯ へ螺旋を描いて降り、バスは終止の和声を縫って D へ着く。そして契約が支払われる——F♯ は半音上がって G へ、バスは D から G へ落ち、内声は最後の和音のためだけにそっと加わる。完全正格終止とは、外声が交わして守る約束だ。(演奏ではフェルマータで延ばされるが、ここでは複縦線で切り上げた。)
cadence_voice_leading旋律が導音 F♯ に降り立って G へ上がり、バスは属音から主音へ落ちる。
バッハの実例バッハ: BWV 847 — 最後の小節で長調になる
ハ短調フーガ(平均律 I 巻、BWV 847)の最終小節。バスは第29小節から主音 C を保続している。その上で属和音が最後にもう一度集まり、上声は F–G–A♭ と上って戻り、第7音 F が E ナチュラル——半音上げられた長3度——へ解決して、曲の最後の響きの頂に座る。短調のフーガが長調で閉じる——古いピカルディの慣習だ。導音は解決し、最後の和音は光の側へ転じる。(内声を一つ省いた。原典では保続音をさらに1オクターヴ下で重ねる。)
cadence_voice_leading主音の保続の上で、属七の F が E♮ へ落ちる——ハ短調のフーガが、長3度で終わる。

講座の章立ては、そのままcandidate_searchが見ているルールの一覧でもある——音程と協和、運動と禁則並行、不協和の処理、旋律法、調的文法、フーガの技法、形式ごとの制約。記事の制約と講座の章が一対一で対応している。理屈に興味が湧いたら、対位法講座を最初から辿ってもらうのが早い。どの譜面もブラウザでそのまま鳴らせる。

何がわかったか

「バッハは論理だ」と思って始めた。違った。

正確には、論理ではあるのだが、論理をあとから当てはめることはできない。並行5度を消す、跳躍を直す、不協和を解決する——一つずつなら全部できる。でも音楽は、そういう局所的な正しさの寄せ集めではない。掛留は鳴る前に準備されていなければならず、転回対位法は上下を入れ替えても成り立っていなければならず、応唱は曲全体の調的設計の中にいなければならない。どれも「置いたあとで直す」では原理的に届かない。

生成してから直すのではなく、最初から正しく置く。当たり前のことを、エンジンを一つ捨てるまで理解できなかった。そして生成された曲を鳴らすたびに、バッハの偉大さをかみしめている。一音も後から直さずに、これだけのものを書いた人がいる。