生成パイプライン
generator.generate(config) を呼び出すと、作曲エンジンはリクエストを完全かつ決定論的な楽曲へと変換する固定のパイプラインを実行します。
このパイプラインでの音楽用語
このパイプラインでは、音楽を構造化データとして扱います。形式が声部と作譜済みノートキャリアを割り当て、和声が和音ターゲットを与え、検証が不正な声部関係を弾きます。短い用語集はエンジニアのための音楽用語入門にあります。
パイプライン全体は決定論的です。同じ設定とシードは常にバイト単位で同一の出力を生成します。
ステップ1: 作曲リクエスト
生の設定が作曲リクエストに解決されます。既定が補完され、シードが解決され(seed が 0 の場合は具体的な非ゼロ値となり、getInfo().seedUsed で報告)、リクエストが検証されます。
検証は厳格です。不明な form/character/instrument/scale の値や範囲外の bpm、および禁止された性格と形式の組み合わせ(chorale_prelude は playful/restless を、toccata_and_fugue は noble を拒否)は却下されます。form は声部数・拍子・基準長を固定し、scale/targetBars が最終的な小節数を解決します(形式の刻みにスナップし [最小, 128] に丸め込み)。
ステップ2: 形式ディレクター
形式ディレクターが楽曲をレイアウトします。選択された形式に対し、ボイスインテントを小節スパンに割り当てます — 主題と応答、グラウンドバス、定旋律、音型、変奏素材を解決された長さにわたって配置します。
小節スパンと素材
小節スパンは、連続する小節の範囲です。素材は、フーガ主題、対旋律、反復するグラウンドバスなど、キャリアがそのまま再生する作譜済みの音楽内容です。
レイアウトはデザイン値のアーク — 提示、展開、スパンの約80%でのクライマックス、解決 — に従い、下流で使用する密度・音域・ベロシティの段階を駆動します。アークは形式により固定され、探索されません。
ステップ3: 候補探索
候補探索は、各ボイスインテントのスパンを処理経路へ振り分けます。出荷時の既定形式では、すべてのスパンがキャリアです。各キャリアは形式ビルダーが組み立てたノートをそのまま再生するため、採点付き探索が通常の出力に加えるノートは0です。
採点付き探索は既定で無効の診断用オプションです。bach_cli --free-counterpoint は、パッサカリア V1(voice == 1)の対旋律だけを、拍ごとに和声音へ固定する探索へ切り替えます。グラウンドと主要変奏はキャリアのままです。ほかの形式には切り替え可能なスパンがないため、自由対位法を要求すると FreeCounterpointUnavailable を返します。
和音、転調、カデンツ
和音は各時点の縦のターゲットです。転調は音楽上の中心を別の調へ移すことです。カデンツ、つまり終止はフレーズの終わり方で、典型的には V から I のような属和音から主和音への到着です。
ステップ4: ルール検証器
検証器は組み上がった声部を対位法・構造ルールに対して検査します。その検証処理で見つかった失敗をまとめて記録し、該当スパンを特定できるようルール識別子とともに報告します。音楽上の考え方は対位法コースで学び、特定のルール ID を調べるときは検証器ルール一覧を使います。
停止させる失敗が見つかったあとの動作
公開生成経路は処理を中止して検証レポートを返します。スコアの修復、探索の再試行、別候補への切り替えは行いません。
ステップ5: レンダラー
Composer::run() の最後に、組み立てた声部が声部ごとのトラックへレンダリングされます。この時点でチャンネルとノートタイミングが決まります。最終検証後、公開生成経路は楽器別のベロシティカーブを適用し、General MIDI プログラムを設定してトラックを再レンダリングします。
source タグはレンダリング後も残ります
レンダリングされた音は由来を保ちます。キャリア素材は "material"、任意の採点付き探索で選ばれた音は "compose"、後から追加された装飾は "ornament" です。検証結果がどの素材に由来するかを調べるときに役立ちます。
ステップ6: 装飾と表現(後処理)
公開生成経路は Composer::run() のあと、装飾の適用、FinalScore 検証、ベロシティの適用と再レンダリング、コントローラーイベントの追加、テンポイベントの追加、という順に処理します。FinalScore 検証で停止させる失敗が見つかると、ベロシティ、コントローラー、テンポ、MIDI、公開イベントの出力には進みません。
- 装飾 — トリル、モルデント、ナッハシュラーク。密度は性格と楽器に依存します。グラウンドバスと定旋律の線は決して装飾されません。
- ベロシティ — 最終検証後に楽器別のフレーズカーブを適用します。MIDI と
getEvents()が更新後の値を共有するよう、トラックを再レンダリングします。 - 表現 — オルガンとチェンバロには CC 7 のレジストレーション段階、ピアノ、ヴァイオリン、チェロには CC 11 の連続表現を加えます。
- テンポ — 形式に応じて終結のリタルダンドを加えます(トリオ・ソナタにはありません)。前奏曲、トッカータ、ファンタジア系の形式では、フーガ入りでセクションテンポも変わります。
これらの処理が追加したノートは source: "ornament" 由来タグを持ちます("material" と "compose" に対して)。
装飾
装飾は、構造上の音の周辺に追加される小さな飾りの音型です。元の主旋律そのものではないため、イベントデータでは source: "ornament" として区別されます。
ステップ7: MIDI ライター
内部表現はすべて C で作曲・検証されます。出力ライターは指定された key と出力オクターヴシフトを適用します。MIDI ライターは拍子記号も書き込み(パッサカリアとシャコンヌは3/4、それ以外は4/4)、Type 1 の標準 MIDI ファイルを生成します。公開イベントライターも同じピッチ変換を適用します。
const midi = generator.getMidi() // Uint8Array(選択した調に移調)
const events = generator.getEvents() // イベントデータ(出力調のピッチ)TIP
getEvents() は出力調のピッチを報告し、すべてのノートに source タグを付与します。内部診断用の generated.v1 は C のピッチを保持します。公開イベントの型定義はJavaScript APIを参照してください。
パイプラインの外側: 品質はどう測られるか
ここまでの7ステップが実行時のすべてです。既定では、パイプラインは実行中に「よりバッハらしい候補」を探索しません。形式ビルダーがキャリア素材を作譜し、検証器が不正な結果を弾きます。では、その設計値が実際にバッハらしい出力を生むことは、どうやって確かめているのでしょうか。
答えは開発側の品質ゲートです。エンジンの形式や音型に手が入るたびに、生成結果を2系統の自動チェックにかけます。どちらもエンジンリポジトリの Python ツール(bach_tools.py の texture-gate / closure コマンド)として公開されています。
コーパス統計モデル
生成された楽曲のイベントデータから5つの分布を取り、実曲コーパスから推定した参照分布との距離(負の対数尤度)で採点します。距離が校正済みの閾値を超えると不合格です。
| 特徴量 | 見ているもの |
|---|---|
| ピッチクラス | どの音名がどれだけ使われるか |
| 旋律音程 | 声部内の隣接音間の距離の分布 |
| 音価 | ノート長の分布 |
| 拍位置 | ノートが小節内のどこで発音されるか |
| 縦の音程クラス | 同時に鳴る音同士の音程の分布 |
このうち最も重みが大きいのが旋律音程です。バッハの実曲では旋律音程の最頻値が圧倒的に順次進行(隣のスケール音への半音1〜2個分の移動 — 入門)なので、跳躍の多い線はこの距離をすぐに押し広げます。
横の順次性と縦の協和のせめぎ合い
ここに音型設計の核心的な緊張があります。
- 横方向: 線は順次進行が支配的であるほどコーパスに近づく — 音階的なラン、波形の音型。
- 縦方向: 拍頭はその小節の和音の構成音に固定されるほど、保続するバスに対して協和する(跳躍には回復が要るも参照)。
この二つは素朴に実装すると衝突します。自由に走る音階ランは拍頭で非和声音を踏み、逆に毎拍を最寄りの和声音へ飛ばすと跳躍だらけの線になります。エンジンの音型(音階波、ノコギリ波、分散和音)は、走行方向の前方にある和声音へ着地する・オシレーションの相手はまずバスに協和する隣接音から探すといった選び方で、順次性を保ったまま拍頭を協和させるように書かれています。
テクスチュアゲート
統計モデルと並んで、シード×形式の一括 sweep が構造指標を直接チェックします — 各声部の活動量、沈黙率、同音連打の長さ、平行完全協和音程の数、そして上記モデルスコアの閾値。1ケースでも落ちれば、その変更はマージされません。
これらは generate() の一部ではありません
コーパスモデルもテクスチュアゲートも実行時には走りません。生成は常に決定論的な一発出力で、品質はその出力を生む設計値の側で — 開発時に — 担保されています。