Skip to content

生成パイプライン

generator.generate(config) を呼び出すと、作曲エンジンはリクエストを完全かつ決定論的な楽曲へと変換する固定のパイプラインを実行します。

このパイプラインでの音楽用語

このパイプラインでは、音楽を構造化データとして扱います。形式が声部を割り当て、和声が和音ターゲットを与え、候補探索が音を選び、検証が違法な声部関係を弾きます。短い用語集はエンジニアのための音楽用語入門にあります。

パイプライン全体は決定論的です。同じ設定とシードは常にバイト単位で同一の出力を生成します。

ステップ1: 作曲リクエスト

生の設定が作曲リクエストに解決されます。既定が補完され、シードが解決され(seed0 の場合は具体的な非ゼロ値となり、getInfo().seedUsed で報告)、リクエストが検証されます。

検証は厳格です。不明な form/character/instrument/scale の値や範囲外の bpm、および禁止された性格と形式の組み合わせ(chorale_preludeplayful/restless を、toccata_and_fuguenoble を拒否)は却下されます。form は声部数・拍子・基準長を固定し、scale/targetBars が最終的な小節数を解決します(形式の刻みにスナップし [最小, 128] に丸め込み)。

ステップ2: 形式ディレクター

形式ディレクターが楽曲をレイアウトします。選択された形式に対し、ボイスインテントを小節スパンに割り当てます — 主題と応答、グラウンドバス、定旋律、音型、変奏素材を解決された長さにわたって配置します。

小節スパンと素材

小節スパンは、連続する小節の範囲です。素材は、フーガ主題や反復するグラウンドバスのように、候補探索が自由に置き換えるのではなく尊重すべき事前定義の音楽内容です。

レイアウトはデザイン値のアーク — 提示、展開、スパンの約80%でのクライマックス、解決 — に従い、下流で使用する密度・音域・ベロシティの段階を駆動します。アークは形式により固定され、探索されません。

ステップ3: 候補探索

和声プラン(和音、転調、カデンツ)に対し、候補探索が固定でない各声部のノートを選択します。選択は拍ごとに和声音へ固定されます。各拍で、和声と他声部に協和するピッチを優先し、最初の選択が制約に違反する場合はランク付けされた代替候補へ切り替えます。形式ディレクターが割り当てた素材(主題、グラウンド、定旋律)は固定され、ここでは再選択されません。

和音、転調、カデンツ

和音は各時点の縦のターゲットです。転調は音楽上の中心を別の調へ移すことです。カデンツ、つまり終止はフレーズの終わり方で、典型的には V から I のような属和音から主和音への到着です。

ステップ4: ルール検証器

検証器は組み上がった声部を対位法・構造ルールに対して検査し、違反があれば即座に失敗します。該当スパンを特定できるよう、ルール識別子とともに失敗を報告します。音楽上の考え方は対位法コースで学び、特定のルール ID を調べるときは検証器ルール一覧を使います。

ここでの即時失敗とは

エンジンは、不適切な候補に含まれるすべての音楽上の問題を列挙するわけではありません。その処理で最初に違反したルールで停止し、ルール識別子を返し、探索または呼び出し側がその候補を扱います。

ステップ5: レンダラー

検証された声部がトラックへとレンダリングされます — 声部ごとに1トラック — チャンネル、楽器のGeneral MIDI プログラム、ノートのタイミングを伴います。

source タグはレンダリング後も残ります

レンダリングされた音は由来を保ちます。固定素材は "material"、候補探索で選ばれた音は "compose"、後から追加された装飾は "ornament" です。どの音がルールによって書き換え可能だったのかを調べるときに役立ちます。

ステップ6: 装飾と表現(後処理)

決定論的な後処理がレンダリング済みトラックを装飾します。C++ ライブラリの層では装飾処理は独立した関数(applyOrnamentPass)で、意図的に Composer::run() の外に置かれています。JavaScript API・CLI・このデモが使う公開生成経路 bach_generate_from_json は、検証のあとで必ずこの処理を呼び出します。

  • 装飾 — トリル、モルデント、ナッハシュラーク。密度は性格と楽器に依存します。グラウンドバスと定旋律の線は決して装飾されません。
  • 表現 — 形式のエネルギーアークに従うオルガンレジストレーションのCC#7/#11カーブ、および終結のリタルダンドのテンポイベント。

これらの処理が追加したノートは source: "ornament" 由来タグを持ちます("material""compose" に対して)。

装飾

装飾は、構造上の音の周辺に追加される小さな飾りの音型です。元の主旋律そのものではないため、イベントデータでは source: "ornament" として区別されます。

ステップ7: MIDI ライター

内部表現はすべて C で作曲されます。MIDI ライターは、指定された key が適用される唯一の場所です — ピッチは出力時に移調され、拍子記号が書き込まれ(パッサカリアとシャコンヌは3/4、それ以外は4/4)、結果はType 1 の標準 MIDI ファイルとなります。

js
const midi = generator.getMidi()       // Uint8Array(選択した調に移調)
const events = generator.getEvents()   // イベントデータ(ピッチは C のまま)

TIP

getEvents() のイベント JSON はピッチを C で報告し、すべてのノートに source タグを付与します。完全な型定義はJavaScript APIを参照してください。

パイプラインの外側: 品質はどう測られるか

ここまでの7ステップが実行時のすべてです。パイプラインは実行中に「よりバッハらしい候補」を探索しません — レイアウトは設計値で固定され、検証器が違法な結果を弾くだけです。では、その設計値が実際にバッハらしい出力を生むことは、どうやって確かめているのでしょうか。

答えは開発側の品質ゲートです。エンジンの形式や音型に手が入るたびに、生成結果を2系統の自動チェックにかけます。どちらもエンジンリポジトリの Python ツール(bach_tools.pytexture-gate / closure コマンド)として公開されています。

コーパス統計モデル

生成された楽曲のイベントデータから5つの分布を取り、実曲コーパスから推定した参照分布との距離(負の対数尤度)で採点します。距離が校正済みの閾値を超えると不合格です。

特徴量見ているもの
ピッチクラスどの音名がどれだけ使われるか
旋律音程声部内の隣接音間の距離の分布
音価ノート長の分布
拍位置ノートが小節内のどこで発音されるか
縦の音程クラス同時に鳴る音同士の音程の分布

このうち最も重みが大きいのが旋律音程です。バッハの実曲では旋律音程の最頻値が圧倒的に順次進行(隣のスケール音への半音1〜2個分の移動 — 入門)なので、跳躍の多い線はこの距離をすぐに押し広げます。

横の順次性と縦の協和のせめぎ合い

ここに音型設計の核心的な緊張があります。

  • 横方向: 線は順次進行が支配的であるほどコーパスに近づく — 音階的なラン、波形の音型。
  • 縦方向: 拍頭はその小節の和音の構成音に固定されるほど、保続するバスに対して協和する(跳躍には回復が要るも参照)。

この二つは素朴に実装すると衝突します。自由に走る音階ランは拍頭で非和声音を踏み、逆に毎拍を最寄りの和声音へ飛ばすと跳躍だらけの線になります。エンジンの音型(音階波、ノコギリ波、分散和音)は、走行方向の前方にある和声音へ着地するオシレーションの相手はまずバスに協和する隣接音から探すといった選び方で、順次性を保ったまま拍頭を協和させるように書かれています。

テクスチュアゲート

統計モデルと並んで、シード×形式の一括 sweep が構造指標を直接チェックします — 各声部の活動量、沈黙率、同音連打の長さ、平行完全協和音程の数、そして上記モデルスコアの閾値。1ケースでも落ちれば、その変更はマージされません。

これらは generate() の一部ではありません

コーパスモデルもテクスチュアゲートも実行時には走りません。生成は常に決定論的な一発出力で、品質はその出力を生む設計値の側で — 開発時に — 担保されています。

Dual-licensed: AGPL-3.0 · commercial licensing available. Generated MIDI is yours to use freely.