DeckGhostで学ぶ、判断エンジンとリアルタイム処理の分け方
はじめに
個人開発で、DeckGhostというアルゴリズムDJアプリを作っています。
このアプリは、初期実装からAUTO MIX、楽曲解析、DJ技法データベース、MIDI、録音・リプレイ、拍位置補正などの主要機能までを、AIコーディングエージェントを使いながら最初の5日間で集中的に開発しました。
その後も、曲構成のSSM解析、局所テンポ、コード列、実際に重なる区間の調性評価、自動テストを追加しています。5日間で完成したというより、5日間で動く芯を作り、観測できた失敗を材料に育て続けています。
ただし、この5日間は、整然としたプロジェクト管理が成功した結果ではありません。
別の長期開発プロジェクトでは、AI開発向けにタスクの正本、READY条件、WIP制限、完了証跡、ドキュメント同期などの仕組みを先に作っていました。それにもかかわらずDeckGhostでは、ある事情から、そのルールをほぼ無視して実装を始めました。
この裏話は、第10章で触れます。
実際に動作する公開版はこちらです。
DeckGhost
https://deckghost.nulltan.dev/
曲を順番に再生するだけのオートDJではありません。楽曲を解析し、入り口となるCUEポイントを作り、相性のよい曲とDJ技法を選び、2台のデッキ、ミキサー、エフェクトを自動操作します。
最初に考えていたのは、かなり素朴なアイデアでした。
曲の終わりでフェードするだけではなく、曲の途中でも、DJらしく自然にMIXできないか。
ところが、実際に作り始めると、難しかったのは「AIにどの曲を選ばせるか」だけではありませんでした。
- 音楽のテンポをどう測るか
- どの位置から次の曲を入れるか
- 低音同士の衝突をどう避けるか
- ブラウザが一瞬止まっても音を破綻させない方法
- AIの判断が外れたとき、どう安全に逃げるか
- 同じ演奏を後から再現できるか
作っていくうちに、一番大切な設計原則が見えてきました。
AIは演奏計画を作る。DSPは決定論的に演奏する。
この記事では、DeckGhostの開発で実際に起きた失敗を紹介しながら、リアルタイム処理へAIを組み込むときの設計を説明します。
DJや音声処理に触れたことがなくても読み進められるよう、専門用語はその場で補足します。
DeckGhostとは
🔰簡単に言うと
2台の音楽プレイヤーとミキサーをブラウザ上に作り、曲の解析、選曲、MIX計画、演奏、評価までを自動化するアプリです。
DeckGhostは、Node.jsとブラウザのWeb Audio APIで動きます。
Node.js側は画面や楽曲ライブラリ、技法データを配信します。実際の音声解析と再生はブラウザ内で行う、Local-firstの構成です。読み込んだ音声ファイルを解析のためにサーバへ送信しません。
現在の主な機能は次のとおりです。
- 2デッキでの再生、CUE、ループ、テンポ変更、SYNC
- 3バンドEQ、クロスフェーダー、Limiter、Safety Gain
- ECHO、REVERB、FILTER、ROLL、GATE、DISTORTION、NOISE、BACKSPINなど12種のBEAT FX
- BPM、拍位置、キー、スペクトル、ラウドネス、無音区間、局所テンポの推定
- SSMと複数特徴量によるIntro、Verse、Build、Chorus、Drop、Break、Outroの構成解析
- 小節単位のコード列と、実際に重なる区間の調性評価
- 全体波形へのセクション表示
- 役割付きCUEの自動生成
- 67技法の知識ベースと、60技法のAUTO MIX候補
- AUTO MIXによる決定論的な自動演奏
- 演奏履歴の記録、リプレイ、Mix Criticによる評価
- MIDIコントローラー入出力
- Node.js標準テストによる解析・判断ロジックの検証
🔰まず、DJ MIXを1分で理解する
図はスマートフォンでも文字が潰れにくいよう、原則として上から下へ読みます。複雑な接続図は複数枚に分けています。
この記事では音楽理論そのものを詳しく覚える必要はありません。
最低限、次の流れだけ分かれば読み進められます。
- Deck Aで現在の曲を流す
- Deck Bへ次の曲を読み込む
- ヘッドホンだけでDeck Bを事前確認
- 2曲の速さと拍の位置を合わせる
- Deck Bを少しずつ表へ出す
- 低音や音量の主導権をDeck AからDeck Bへ渡す
- Deck Aを消し、次の曲へ移る
DeckGhostが自動化しているのは、この一連の判断と操作です。
音楽用語が出てきたら、「時間の目盛り」「音の帯域」「現在の曲と次の曲の切り替え」のどれかとして読むと、だいたい迷いません。
🔰音楽用語とシステム用語の対応表
| 用語 | 音楽・DJでの意味 | エンジニア向けに言うと |
|---|---|---|
| 拍(Beat) | メトロノームの「カチッ」1回に相当する時間単位 | 最小のスケジューリング単位 |
| 小節(Bar) | 多くのダンス曲では4拍をまとめた単位 | 4ティックを束ねた固定フレーム |
| フレーズ | 8小節や16小節など、意味のある音楽上のまとまり | 処理ブロック、トランザクション境界 |
| BPM | 1分間に何拍あるか。曲の速さ | クロック周波数、処理周期 |
| ダウンビート | 小節の1拍目 | フレーム先頭、同期基準点 |
| ビートグリッド | 各拍が曲のどの時刻にあるかを並べた目盛り | タイムスタンプ付きスケジュール |
| 位相 | 同じBPMの2曲で、拍の山がどれだけずれているか | 同周期タスク間のオフセット |
| SYNC | BPMと拍位置を近づける操作 | クロック同期、時刻同期 |
| デッキ | 曲を再生するプレイヤー | 独立したワーカー、ストリーム入力 |
| ミキサー | 複数の音を合流させ、音量や帯域を制御する装置 | ルーター、集約パイプライン |
| CUE | 再生や切り替えを始める目印 | ブックマーク、チェックポイント |
| PFL / CUEモニター | 表の音へ出さず、次の曲をヘッドホンだけで確認する経路 | 本番へ流さないプレビュー環境 |
| チャンネルフェーダー | 1曲単位の音量を上下するつまみ | 入力ごとのゲイン、流量制御 |
| クロスフェーダー | Deck AとDeck Bの割合を一つのつまみで切り替える | 2系統間の重み付きルーティング |
| EQ | 周波数帯ごとの音量を調整する処理 | 帯域別フィルター、特徴量ごとの重み調整 |
| LOW | キックやベースが多い低い音域 | 低周波帯。2系統を重ねると飽和しやすい資源 |
| MID | ボーカルやメロディが集まりやすい中音域 | 情報密度が高く、競合を解消しにくい帯域 |
| HIGH | ハイハットなど明るく細い音が多い高音域 | 高周波成分、輪郭情報 |
| キック | 「ドン」と鳴る低い打楽器。拍位置の基準になりやすい | クロックパルス、同期用ピーク |
| ベース | 曲の低域を支える音 | 低周波の持続成分 |
| キー | 曲全体がどの音階を中心にしているか | 全体の互換性ラベル |
| コード | ある瞬間に同時に鳴る複数の音の組み合わせ | 時間区間ごとの局所状態 |
| Camelot | DJがキー相性を素早く見るための番号表記 | 互換性を比較しやすく正規化したID |
| Intro | 曲の導入部 | 初期化・ウォームアップ区間 |
| Verse | 主題を展開する比較的安定した区間 | 定常処理区間 |
| Build | ドロップへ向けて盛り上がる区間 | 負荷・状態が上昇する準備フェーズ |
| Chorus | サビ。曲の中心的で印象の強い区間 | 主要機能、ピークとなる処理区間 |
| Drop | ためた後にキックやベースが強く戻る区間 | 大きな状態遷移、主処理の開始点 |
| Break | キックや低音が減り、静かになる区間 | アイドル区間、低負荷フェーズ |
| Outro | 曲の終盤。次の曲を重ねやすいことが多い | 終了処理、ハンドオフ区間 |
| FX | ECHO、REVERB、FILTERなどの音響効果 | 変換ミドルウェア、エフェクト処理 |
| トランジション | ある曲から次の曲へ移る手順 | データ移行、フェイルオーバー、ローリング切替 |
| DSP | 音量、EQ、再生速度、FXを実時間で処理する部分 | データプレーン、低レイテンシ実行層 |
🔰この記事の読み方
音楽用語が多い章でも、次の三つへ読み替えれば大丈夫です。
| 記事で扱うもの | 読み替え |
|---|---|
| BPM、拍、フレーズ | 時間軸とスケジューリング |
| LOW、MID、HIGH、コード | 複数入力の競合と互換性 |
| CUE、PFL、フェーダー、FX | 切り替え手順と安全なリリース |
音楽理論の正解を覚える記事ではなく、不確実な解析結果から、時間制約のある処理を安全に実行する設計の記事として読んでください。
1. 「AIにDJをさせる」を二つの問題へ分ける
🔰簡単に言うと
「どう演奏するかを考える仕事」と「音を時間どおりに動かす仕事」は、別の問題です。
🔰この章で出てくる用語
| 用語 | この記事での意味 | エンジニア向けに言うと |
|---|---|---|
| リアルタイム処理 | 拍や音声を止めず、期限内に処理し続ける | 応答期限を破ると結果自体が無効になる処理 |
| 決定論的 | 同じ計画なら同じ時刻に同じ操作を行う | 同じ入力・状態から同じ出力を得られる実装 |
| Brain | 次の曲、CUE、技法、長さを決める判断層 | 制御プレーン、オーケストレーター |
| Planner | 演奏計画を拍・小節・時刻ごとの操作へ分解する | ジョブを実行ステップへ展開するスケジューラー |
| DSP | 再生、EQ、フェーダー、FXを実際に動かす | 低レイテンシのデータプレーン |
| レイテンシ | 判断や操作が届くまでの遅れ | 要求から応答までの遅延 |
| ジッター | 毎回の遅延が一定でなく揺れること | 処理時間のばらつき |
| 制御プレーン | 方針や経路を決める部分 | 設定・意思決定を担当する管理系 |
| データプレーン | 音を途切れさせず処理する部分 | 高速で実データを流す実行系 |
自動DJと聞くと、AIがリアルタイムに音を聴きながら、フェーダーやエフェクトを自在に動かす姿を想像するかもしれません。
しかし、LLMや複雑な判断処理には、応答時間のばらつきがあります。
100ミリ秒で返ることもあれば、数秒かかることもあります。ネットワーク接続が必要なAIなら、通信障害も起こります。
一方、音楽の拍は待ってくれません。
128BPMの曲では、メトロノームの「カチッ」1回に相当する1拍は約469ミリ秒です。16分音符なら約117ミリ秒しかありません。判断が少し遅れるだけで、拍の基準になりやすい低い打音であるキック同士がずれたり、フェーダー操作が8・16小節の処理ブロックに相当するフレーズから外れたりします。
そこで、DeckGhostを二つの層へ分けました。
Brainが決めること
- 次に使う曲
- 入り口と出口のCUE
- 使うDJ技法
- MIXの長さ
- 調性やスペクトルのリスク
- 拍同期に失敗した場合の代替手段
DSPが実行すること
- 指定時刻から再生
- テンポと位相の調整
- LOW、MID、HIGHの操作
- チャンネルフェーダーとクロスフェーダー
- ECHOやREVERBの適用
- 終了時の状態復元
Brainは「16小節かけて低音を入れ替え、最後はフェーダーで抜く」と計画します。
Plannerは、その計画を時間軸上の操作へ変換します。
DSPは、計画済みの操作を同じ入力なら同じように実行します。
インフラの言葉へ置き換える
インフラエンジニアには、制御プレーンとデータプレーンの分離に近い構造です。
- Brainは制御プレーン
- DSPはデータプレーン
制御プレーンは、方針を考えたり、経路を決めたりします。多少時間がかかっても構いません。
データプレーンは、実際のパケットや音声を止めずに処理します。毎回考え直していては間に合いません。
DJアプリを作っていましたが、設計としてはインフラで馴染みのある考え方がかなり役立ちました。
リアルタイム系へAIを入れるとき、AIを時間制約の厳しい処理経路から外すことが最初の安全策になります。
2. 解析結果を「事実」として扱わない
🔰簡単に言うと
BPMやキーは自動解析できますが、常に正しいわけではありません。推定値には信頼度と手動修正を用意します。
🔰この章で出てくる用語
| 用語 | この記事での意味 | エンジニア向けに言うと |
|---|---|---|
| ヒューリスティック | 音の特徴から現実的な候補を求める経験則 | 厳密解より速度と実用性を優先した近似アルゴリズム |
| 自己相関 | 波形をずらし、似る周期からBPM候補を探す | 時系列データの周期検出 |
| SSM | 各小節同士の似ている度合いを行列化する | 全区間ペアの類似度マトリクス |
| Time-lag Novelty | 反復パターンが切り替わる位置を探す | 時系列の構造変化点検出 |
| HPSS | 持続音寄りと打楽器寄りの特徴へ分ける | 異なる性質の信号をマスクで分離して特徴抽出 |
| クロマ | 高さの違うオクターブをまとめ、12音の強さで表す | キーを変えても比較しやすい12次元特徴量 |
| MFCC系特徴 | 音色の違いを少数の数値へ圧縮する | スペクトル形状の低次元特徴ベクトル |
| スペクトル重心 | 音が明るいか暗いかを表す目安 | 周波数分布の重心 |
| 局所テンポ | 短い区間ごとのBPM変化 | スライディングウィンドウで測る局所レート |
| 信頼度 | 推定結果をどの程度信用できるか | モデル出力に付けるconfidence |
| 推定値 | 解析から得た候補で、確定値ではない | 観測ノイズを含む暫定値 |
| 手動補正 | 利用者が解析値を直し、自動値より優先する | オペレーターオーバーライド |
DeckGhostは、曲を読み込むと次の情報を解析します。
- 曲全体のBPMとビートグリッド
- 8秒窓で追跡する局所テンポとテンポ安定度
- 曲頭、曲末、曲中の無音区間
- キーとCamelot表記
- 小節単位のコード列
- LOW、MID、HIGHの割合
- ラウドネス、エネルギー、スペクトル重心
- 打楽器寄りか、持続音寄りかを表す特徴
- Intro、Verse、Build、Chorus、Drop、Break、Outroなどの曲構成
- セクションごとの推定信頼度
- 8、16、32小節周期とSSMから得たフレーズ境界
ここで重要なのは、解析結果を「正解」ではなく、候補として扱うことです。
実際にあった失敗: 96BPMが193BPMになった
BPM検出では、曲の音量変化から周期を探します。
最初の実装では、ある周期だけでなく、その2倍、3倍、4倍の位置にも似たピークがあるかを加点していました。理屈としては、多くの周期で一致する候補を選べば精度が上がりそうです。
ところが、実音源を入れると約96BPMの曲が193BPMと判定されました。ほぼ正確に2倍です。
原因は、偶数倍のピークを加点したことで、速いテンポ候補が遅いテンポの強さまで横取りしていたことでした。
そこで、偶数倍を評価から外し、競合しにくい奇数倍だけを使うよう修正しました。
この経験から、次の設計へ変えました。
自動解析値
+ 解析元
+ 信頼度
+ 手動修正値
たとえばBPMなら、値だけでなく次の情報を持たせます。
{
bpm: 128.0,
bpmSource: "estimated",
bpmConfidence: 0.72
}
ユーザーが手動で修正した場合は、自動解析より優先します。
- BPMを半分または2倍へ補正
- BPMを直接入力
- TAPで測定
- ビートグリッドを1ms、10ms、50ms単位で移動
- 現在位置を1拍目へ設定
固定ブロック分類からSSMへ
初期の曲構成解析は、8小節ごとの平均エネルギーを見て、intro、groove、break、dropなどへ分類していました。
この方法は軽量ですが、境界が8小節単位へ丸められます。音色やコード、打楽器の変化が小節の途中で起きても、実際の変化位置を捉えられません。
現在は、次の流れへ更新しています。
構造解析の流れ
並行して取得する特徴
SSMは、曲の各小節が別の小節とどの程度似ているかを行列にしたものです。ログ区間同士の類似度を総当たりで並べ、繰り返しパターンと変化点を探すイメージに近いです。繰り返されるサビや、似た音色のVerseを見つけやすくなります。
HPSSは、ここではボーカルやドラムの音源を取り出すStem分離ではありません。一定時間続く成分と、瞬間的に立ち上がる成分を、特徴抽出のために分けて見る前処理です。時間方向と周波数方向の中央値を使って、解析特徴を持続音寄りと打楽器寄りへ分けるマスクとして使っています。
ブラウザで曲全体の巨大なスペクトログラムを保持すると重いため、小節中央付近の特徴へ集約しています。精度だけでなく、実行環境の計算量も設計条件です。
自動テストでは、同じ音名でも220Hzから880Hzへ音色が変わる合成波形を作り、その実際の変化位置へフレーズ境界を置けるかを確認しています。
🔰この章のポイント
機械学習やヒューリスティックの出力は、値だけ返すと後段で事実化します。
次も一緒に持たせると、安全に扱いやすくなります。
- どの方法で得たか
- どの程度信用できるか
- 人間が修正したか
- 修正前の値
- どの処理が依存しているか
推定値を使うシステムでは、「正しい値を出すこと」と同じくらい、「間違っている可能性を表現すること」が重要です。
3. 数式が正しくても、全部「下手なDJ」になる
🔰簡単に言うと
評価式がそれらしくても、重み付けを間違えると、すべての場面で同じ判断をするシステムになります。
🔰この章で出てくる用語
| 用語 | この記事での意味 | エンジニア向けに言うと |
|---|---|---|
| 評価関数 | 曲ペアや技法へ点数を付ける計算 | 候補選択用のスコアリング関数 |
| 重み付け | LOW、MID、キーなどの重要度を調整する | 特徴量ごとの係数設定 |
| スペクトル | 音を周波数帯ごとの強さとして見る | 信号の周波数ドメイン表現 |
| Bass Swap | 2曲のLOWを同時に鳴らさず主導権を交換する | 競合資源を一方ずつ所有させるハンドオフ |
| キー | 曲全体の音階上の中心 | 全体へ付く互換性メタデータ |
| コード列 | 小節ごとの和音の並び | 時間窓ごとの局所状態列 |
| N.C. | 確信を持ってコードを決められない状態 | unknown / nullを明示し、推測で埋めない |
| 重なり区間 | 2曲が同時に鳴る実際のMIX範囲 | 互換性を評価すべき本当のトランザクション区間 |
| 飽和 | 多くの候補が上限へ張り付き差が消える | スコア分布が潰れ、ランキング不能になる状態 |
| キャリブレーション | 実データに合わせて重みを調整する | 評価式を観測結果で較正する作業 |
DeckGhostは、2曲を重ねたときの音域衝突を評価します。
最初は、LOW同士の重なりを最も危険だと考え、低域へ大きな重みを付けていました。
LOWの衝突: とても重い
MIDの衝突: 中程度
HIGHの衝突: 軽い
一見すると正しそうです。キックとベースが2曲分重なると、確かに音は濁ります。
実際にあった失敗: 20組中20組がスラム系
実音源5曲から全20ペアを評価すると、ほぼすべてが「ブレンド禁止、短く切り替えるべき」と判定されました。
どの曲を入れても、同じようなハードカットやスラム系のMIXになります。
原因は、一般的なダンスミュージック同士なら、LOWの値が高いのが普通だからです。LOW同士を掛けて大きく加点すると、ほぼすべてのペアが上限へ張り付きました。
しかし実際のDJでは、低音の重なりはEQのLOWキルやBass Swapで処理できます。
むしろ処理しにくいのは、ボーカルやメロディが集まるMIDの衝突です。
そこで、評価軸を次のように変えました。
- LOW: 技法で処理できるため重みを下げる
- MID: 混雑すると逃がしにくいため重みを上げる
- HIGH: 補助的に評価
- 技法側にLOWを片方だけ残す制約を追加
グローバルキーが合っていても、重なるコードは衝突する
曲全体へ付けた互換性ラベルであるキーが同じでも、実際にMIXする16小節で鳴っている局所状態であるコードまで同じとは限りません。
そこで、現在のBrainは曲全体のCamelot互換性だけでなく、候補CUEから実際に重なる最大16小節のコード列を比較します。
曲Aの全体キー: 8B
曲Bの全体キー: 8B
↓
全体だけなら高得点
実際の重なり区間:
曲A: 8B相当
曲B: 1B相当
↓
長尺ブレンドの評価を下げる
コード推定の確信度が低い小節はN.C.として扱います。分からないものを無理にコード名へ決めず、判断材料から弱めるためです。
自動テストでも、全体キーが一致している二曲へ、重なり区間だけ衝突するコード列を与え、調性スコアとは別に重なり区間の点数が低下することを確認しています。
この失敗から学んだこと
評価関数を作るとき、対象の特徴量だけ見ても足りません。
後段の制御で何を解決できるかまで含めて重みを決める必要があります。
これは音声以外でも同じです。
たとえば障害のリスク評価で、復旧手段がある故障と、データを失う故障を同じ重みで扱うべきではありません。
入力の危険度
- 後段で吸収できる範囲
= 実際に判断へ使うべきリスク
評価式は、現実の運用や制御手段を知らないと、数学的に整っていても使えません。
4. 局所的な正解が、演奏全体を壊す
🔰簡単に言うと
良いCUEポイントを選んでも、そこからMIXする時間が残っていなければ、良い演奏にはなりません。
🔰この章で出てくる用語
| 用語 | この記事での意味 | エンジニア向けに言うと |
|---|---|---|
| CUE | MIXを開始・終了する候補位置 | 処理開始点、チェックポイント |
| フレーズ境界 | 8・16小節など、音楽上のまとまりの切れ目 | 安全なトランザクション境界 |
| 局所最適 | CUE単体では良いが、MIX全体では悪い選択 | コンポーネント最適が全体最適を壊す状態 |
| runway | 計画を最後まで実行するための残り時間 | 後続工程へ必要な容量・期限の余白 |
| プリロール | 本番前に無音で次曲を走らせ同期する | ウォームアップ、シャドートラフィック |
| PFL | 次曲を表へ出さずヘッドホンで確認する | 本番系から隔離したプレビュー経路 |
| キック位相補正 | 2曲の低音ピークを同じ拍へ寄せる | 周期信号のオフセット補正 |
| 結合テスト | 解析、計画、実行を組み合わせて確認する | 複数モジュールの統合動作検証 |
DeckGhostは、フレーズ境界や曲構成からCUE候補を作ります。
初期の評価では、出口として音楽的にきれいな位置を高く評価していました。
ところが、曲のかなり後半にあるサビ終わりが選ばれることがありました。
実際にあった失敗: 16小節MIXが1、2小節へ縮んだ
Brainは16小節のブレンドを計画していました。
しかし、選ばれた出口から曲末までの残り時間が短く、実行時には1、2小節へ圧縮されました。次の曲を入れた直後に、すぐ切り替わる不自然なMIXになります。
CUE単体では「きれいな出口」でしたが、プラン全体では使えませんでした。
そこでCUE評価へ、**runway(滑走路)**という考え方を追加しました。
- 計画した小節数を実行できる残り時間
- プリロールに必要な時間
- 拍同期を安定させる時間
- フェードアウト後の余白
局所評価だけでなく、後続処理が最後まで完走できるかを確認します。
良い開始位置
+ 必要な実行時間
+ 安全な終了余白
= 実際に使えるCUE
通常MIXを手順として固定する
現在の標準AUTO MIXは、単にクロスフェーダーを左から右へ動かす処理ではありません。
Brainはこの流れを選び、Plannerが小節と拍へ展開し、DSPが時間どおりに実行します。
通常MIXを明示的な手順へしたことで、派手な技法を増やしても、基本動作へ戻る場所ができます。高度な技法が成立しない場合は、短いカットやセーフフェードへ縮退します。
システム設計としての学び
一つの関数が正しい値を返しても、システム全体で正しいとは限りません。
- API単体では正しい
- データ形式も正しい
- テストも通る
- しかし後工程の時間や容量が足りない
こうした問題は、ローカルな単体テストだけでは見つけにくいものです。
候補の品質だけでなく、その候補を選んだ後にシステムが完走できるかを評価します。
5. 音声処理は「少しの間違い」がすぐ聞こえる
🔰簡単に言うと
音声では、信号経路やタイミングの小さなミスが、音量の急増、濁り、ズレとしてすぐ表面化します。
🔰この章で出てくる用語
| 用語 | この記事での意味 | エンジニア向けに言うと |
|---|---|---|
| Audio Node | 音源、EQ、FX、出力を表すWeb Audio APIの部品 | 接続可能な処理ノード |
| Audio Graph | Audio Node同士を接続した信号経路 | データフローグラフ、ネットワークトポロジー |
| dry | FXを通していない原音 | 未加工の入力ストリーム |
| wet | FX処理後の音 | 変換後の出力ストリーム |
| PFL | フェーダー前の音を別出力へ送るモニター経路 | 本番出力と分離した観測用タップ |
| Limiter | 瞬間的に大きすぎる音を抑える | 出力上限を守るレートリミッターに近い安全弁 |
| Safety Gain | 過負荷が続くと全体ゲインを自動で下げる | 継続異常時の自動スロットリング |
| Bypass | FXを通さず信号を素通しする | ミドルウェアを迂回するフォールバック経路 |
| 縮退運転 | 高度な技法を諦め、安全なフェードへ逃げる | 機能を落としてサービス継続するgraceful degradation |
| クリッピング | 音量が上限を超え、波形が潰れる | バッファや数値レンジのオーバーフローに近い破綻 |
Web Audio APIでは、音をノードとして接続します。
横長にならないよう、接続を三つに分けて示します。
1チャンネル分の処理
Deck AとDeck Bは、それぞれ同じ経路を持ちます。
2チャンネルの合流とメイン出力
ヘッドホン用CUE経路
PFLはチャンネルフェーダーの影響を受ける前に分岐します。
現在はECHO、DELAY、REVERB、FLANGER、PHASER、FILTER、CRUSH、ROLL、GATE、DISTORTION、NOISE、BACKSPINの12種を扱います。
FXを追加する場合は、種類を増やす前に、原音とエフェクト音の混ぜ方を設計する必要があります。
実際にあった失敗: 原音とFX音が二重になった
ECHO、DELAY、REVERBで、原音を100パーセント残したまま、エフェクト音も足していました。
結果として、FXをONにすると音が太くなるのではなく、単純に音量が増えました。
ユーザーから見ると、「CUEモニターの音まで混ざっているのでは」と疑いたくなる症状です。しかし原因は、dryとwetの経路設計でした。
そこで、equal-powerのdry/wetクロスフェードへ変更しました。
- dryを下げながらwetを上げる
- send系FXでは最低限のdryを残す
- insert系FXは深い設定で原音を置き換える
- 最終段にLimiterとSafety Gainを置く
セーフティを演奏の一部にする
DeckGhostは、理想的なMIXだけでなく、失敗したときの逃げ方も計画します。
- 拍ロックできない場合はセーフフェード
- 入ってくる曲を無音で先行再生するプリロール
- キックの位置を推定して開始位置を微調整
- 信頼度が低ければ、細かな補正を諦めてグリッド同期へ縮退
- 曲末の残り時間が足りない場合だけプリロールを短縮
- 継続的なピークを検知した場合は自動でゲインを下げる
正常系の精度を上げるだけでなく、失敗時の音を「事故」から「自然なフェード」へ変えます。
これはリアルタイムシステムのgraceful degradation、縮退運転に近い考え方です。
完璧に同期できないとき、無理に高度な技法を続けるより、地味でも破綻しない技法へ逃げます。
6. 描画ループにも副作用を入れすぎない
🔰簡単に言うと
毎フレーム呼ばれる処理へ、サイズ変更や初期化のような重い副作用を混ぜると、画面が自己増殖することがあります。
🔰この章で出てくる用語
| 用語 | この記事での意味 | エンジニア向けに言うと |
|---|---|---|
| Canvas | 波形、ジョグ、VUメーターを描く領域 | 低レベル描画バッファ |
| devicePixelRatio | CSSの1pxに対応する物理画素数 | 論理サイズと物理解像度の倍率 |
| バッキングストア | Canvas内部の実ピクセル領域 | 表示とは別に確保される描画バッファ |
| 描画ループ | 画面更新のため毎秒何十回も呼ばれる処理 | 高頻度ポーリング/イベントループ |
| requestAnimationFrame | ブラウザ描画に合わせて処理を呼ぶAPI | フレーム同期コールバック |
| 副作用 | 戻り値以外にDOMや状態を変更する処理 | 外部状態を書き換える操作 |
| 再確保 | Canvasや配列のメモリを作り直す | ホットパス内の再アロケーション |
| 冪等性 | 何度呼んでも状態が不要に膨らまない性質 | 再実行安全性 |
音声とは別に、UIでも印象的な不具合がありました。
DeckGhostは、波形、ジョグ、VUメーターをCanvasへ描画します。高解像度ディスプレイに対応するため、devicePixelRatioに合わせてCanvasの内部解像度を調整していました。
実際にあった失敗: Canvasが無限に膨張した
毎フレーム、次のような調整をしていました。
canvas.width = clientWidth × devicePixelRatio
ところが、一部のCanvasではCSS上の表示サイズが固定されていませんでした。
内部サイズを大きくする
→ clientWidthも大きくなる
→ 次のフレームでさらに大きくする
このループが発生し、操作していないのに画面が拡大し続けました。
対策は派手ではありません。
- CSSで表示サイズを固定
- 内部解像度と表示サイズを分離
- ゼロサイズのガード
- 同じサイズなら再確保しない
- 初期化と毎フレーム描画を分離
🔰この章のポイント
requestAnimationFrameや監視ループへ、何でも置くのは危険です。
ループ内では、できるだけ次を避けます。
- DOM構造の作り直し
- Canvasの再確保
- ネットワークアクセス
- 音声ノードの再接続
- 大きな配列の生成
- 状態の初期化
頻繁に呼ばれる処理ほど、読み取りと描画に寄せ、副作用を小さくします。
7. 技法を「知っている」と「演奏できる」を分ける
🔰簡単に言うと
AIが名前を知っている技法と、現在のプログラムが安全に実行できる技法は別です。
🔰この章で出てくる用語
| 用語 | この記事での意味 | エンジニア向けに言うと |
|---|---|---|
| 知識ベース | 技法名、適用条件、注意点を保存したデータ | ポリシー・ルール・カタログ |
| 実行可能サブセット | 現在のDSPで再現できる技法だけの集合 | 実装済みCapability一覧 |
| ガード条件 | 技法を選んでよいか判定する条件 | precondition、ポリシーゲート |
| Stem分離 | 曲をボーカル、ドラム、ベースなどへ分ける | 混合入力を独立チャネルへ分解する処理 |
| 歌詞タイムコード | どの時刻にどの言葉が鳴るかの情報 | イベントの時刻付きメタデータ |
| AudioWorklet | 音声スレッド寄りで独自DSPを動かす仕組み | メインスレッドから分離した低遅延ワーカー |
| Capability | システムが技術的に実行できる操作 | 実装済み能力 |
| 権限 | 能力のうち、実際に使ってよい範囲 | 認可された操作範囲 |
| フォールバック技法 | 高度な技法が無理なとき使う安全な切替 | 代替実装、縮退パス |
DeckGhostには、DJ技法の知識ベースがあります。
Phrase Blend、Bass Swap、Echo Out、Double Drop、Loop Exit、Wordplayなど、67技法をデータとして収録しています。
一方、現在のDSPとデッキ制御でAUTO MIX候補として扱えるのは、60技法です。
新たに、Mid Swap、High Swap、V-Curve Fade、Constant Power Fade、Ghost Drop、Noise Sweep、Gate Transition、Distortion Fade、Snare Drop、Break Drop、Intro to Outro、Chorus to Chorus、Half / Double BPM、Semitone Shiftなどの実演カーブも加わりました。
Stem分離、歌詞タイムコード、音階MIDI、サンプラーなど、まだ存在しない入力や機能が必要な技法は、知識ベースへ残しつつ実行候補から外しています。
知識ベース
67技法
↓ 実行条件を満たすか
AUTO MIX候補
60技法
↓ 現在の曲ペア・解析信頼度へ適用可能か
今回の候補
これは機能不足を隠すためではありません。
知識と能力を分離すると、次の利点があります。
- 将来実装する技法の設計を先に整理
- 現在できないことを明示
- Brainが存在しない能力を選ばない
- DSP実装後に実行可能フラグを切り替え
- 技法の選択理由を人間へ説明
AIシステムとしての学び
LLMは、実装されていない操作についても自然に説明できます。
そのため、ツール利用型AIでは、知識と実行権限を明確に分ける必要があります。
知っている
≠
実行できる
≠
実行してよい
DeckGhostでは、技法データに実行可能性とガード条件を持たせ、現在のDSPが保証できる操作だけをPlannerへ渡します。
AIへ能力を説明させるのではなく、機械判定できる能力一覧をシステム側で持ちます。
8. 再現できなければ改善できない
🔰簡単に言うと
「なんとなく変だった演奏」を修正するには、そのとき何を判断し、どの操作をしたかを再現できる必要があります。
🔰この章で出てくる用語
| 用語 | この記事での意味 | エンジニア向けに言うと |
|---|---|---|
| 可観測性 | 判断、演奏、失敗理由を外から確認できる性質 | ログ・メトリクス・トレースで内部状態を推測できること |
| Plan Timeline | どの拍で何を操作するかを並べた計画 | 実行計画、ジョブタイムライン |
| セッション記録 | デッキ位置、EQ、フェーダー、イベントの履歴 | 状態スナップショット+イベントログ |
| リプレイ | 記録した操作を同じ曲で再実行する | イベントソーシングからの再生 |
| Critic | 演奏結果を評価し改善候補を出す層 | 事後評価・品質分析サービス |
| トレース | 一連の処理がどの順番で進んだかを追う | 分散トレーシングに近い実行履歴 |
| 回帰テスト | 修正前に直した不具合が再発しないか確認する | 既知の故障条件を固定した自動テスト |
| 合成テストデータ | サイン波やクリックをコードで生成する | Fixtureをプログラムで生成する方式 |
リアルタイム演奏のバグは再現が難しいものです。
- 曲の組み合わせ
- 開始位置
- BPM解析結果
- 選んだ技法
- フェーダーの進行
- FXのタイミング
- ブラウザの負荷
条件が少し変わると、同じ症状が出ません。
そこでDeckGhostには、観測と再現のための仕組みを入れました。
Plan Timeline
Brainが作った計画を、人間が読める形で表示します。
- どのCUEを使うか
- 何小節MIXするか
- どこでLOWを入れ替えるか
- どのFXを使うか
- どの条件をリスクと判断したか
セッション記録とリプレイ
250ミリ秒間隔で次を記録します。
- デッキの再生位置
- 再生状態
- EQ
- チャンネルフェーダー
- クロスフェーダー
- Plannerイベント
同じ楽曲ライブラリ上で、操作履歴を再実行できます。
Mix Critic
演奏後に次を評価します。
- 同じ技法の使いすぎ
- キー互換
- FX過多
- 計画の信頼度
- リスクと改善案
ここで大切なのは、Criticを「正解を決めるAI」にしないことです。
演奏を観測し、次の改善材料を作る役割に留めます。
計画
↓
決定論的な演奏
↓
記録
↓
評価
↓
技法やスコアを改善
これは、メトリクス、ログ、トレースを使って運用改善する流れと同じです。
解析と判断ロジックの自動テスト
リプレイは、実際の演奏全体を再現する仕組みです。
それとは別に、解析やBrainの小さな部品にはNode.js標準のテストランナーを追加しました。
npm test
現在は、次のような条件を合成データで確認しています。
- 曲頭、曲末、曲中の無音区間
- 一定120BPMの局所テンポ安定性
- 120BPMから132BPMへ変化する曲の検出
- 無音を「安定したテンポ」と誤判定しないこと
- FFTスペクトル重心
- HPSSマスクによる持続音と瞬間的な打音の分離
- SSMによる実際の音色変化位置の検出
- メジャーコードと
N.C.の識別 - 実際の重なり区間におけるコード互換性
テスト音源はリポジトリへ大きな音楽ファイルを置かず、サイン波、クリック、クロマベクトルなどをコードで合成します。
これにより、実音源での聴感確認とは別に、解析アルゴリズムの前提が壊れていないかを短時間で確認できます。
リアルタイム処理では、賢い判断を作る前に、何が起きたかを再現できる状態を作ります。
9. デモデータは「おまけ」ではない
🔰簡単に言うと
手元に音源がなくても、開発者や新しいメンバーがすぐ試せるよう、アプリ内でテスト用の曲を生成します。
🔰この章で出てくる用語
| 用語 | この記事での意味 | エンジニア向けに言うと |
|---|---|---|
| デモデータ | 音源なしでも機能を試せる人工的な5曲 | 自己完結したFixture |
| OfflineAudioContext | 実時間再生せず、高速にAudioBufferを生成する | オフラインバッチ実行環境 |
| OscillatorNode | サイン波やノコギリ波などを生成する | 決まった波形を出す信号ジェネレーター |
| ホワイトノイズ | 全周波数を広く含むノイズ | フィルター試験に使える広帯域入力 |
| エンベロープ | 音量や周波数を時間に沿って変化させる形 | 時系列パラメータカーブ |
| AudioBuffer | ブラウザ内に保持するPCM音声データ | メモリ上の時系列サンプル配列 |
| 統合テスト | 解析からAUTO MIXまで通しで確認する | 複数モジュールのE2Eに近い確認 |
| 再現性 | 別環境でも同じ条件を作れる | 決定論的Fixtureによる再実行可能性 |
DeckGhostは、ブラウザ内で5曲のデモトラックを合成できます。
実装ではOfflineAudioContext上で8小節のintro、full、outroパターンを先に生成し、それらを並べて56〜64小節の曲へ組み立てています。キックはOscillatorNodeの周波数を短時間で150Hzから44Hzへ下げ、ハイハットやクラップはホワイトノイズをフィルターへ通して合成します。
const kick = ctx.createOscillator();
kick.frequency.setValueAtTime(150, t);
kick.frequency.exponentialRampToValueAtTime(44, t + 0.11);
コードは記事用に変数名を簡略化しています。実装では、ベース、コード、アルペジオ、ノイズスイープも組み合わせています。
デモ曲には、BPM、キー、曲構成、エネルギー変化を意図的に持たせています。
これにより、著作権のある楽曲をリポジトリへ入れなくても、次を確認できます。
- 曲の解析
- Camelot互換の選曲
- CUE生成
- AUTO MIX
- FX
- 録音
- セッションリプレイ
デモデータがあると、新しい開発者もnpm installとnpm startの後、すぐ動作を確認できます。
これは、初めてプロジェクトへ触れる人が試すための環境としても重要です。
入力データを各自で用意しないと動かないプロジェクトは、環境構築の時点で学習が止まりやすくなります。
サンプルデータは説明資料ではなく、最小の統合テスト環境です。
10. 5日間の短期スパイクで、管理ルールを外した
🔰簡単に言うと
DeckGhostは、よく管理されたAI開発の成功例ではありません。すでに持っていた管理ルールを、時間への焦りから意図的に外し、実装を先に走らせた短期スパイクです。
🔰この章で出てくる用語
| 用語 | この記事での意味 | エンジニア向けに言うと |
|---|---|---|
| スパイク | 正式実装前に、短期間で可能性を確かめる試作 | 技術検証用のタイムボックス開発 |
| 正本 | タスクや仕様について最終的に基準とする場所 | Single Source of Truth |
| READY | 追加質問なしで、安全に着手できるタスク状態 | 実行可能なジョブ |
| WIP制限 | 同時に着手する仕事の数を制限する | キュー流量と並列度の制御 |
| 完了証跡 | テスト結果や確認内容など、DONEを裏付ける記録 | 検証ログ、監査証跡 |
| ドキュメントドリフト | 実装とREADME、設計書などが食い違うこと | 構成情報・仕様情報の不整合 |
| 技術的負債 | 速度を優先した結果、後で必要になる整理や修正 | 将来へ繰り延べた保守コスト |
| タイムボックス | 作業時間を先に決め、その範囲で検証する | 期限固定の実験枠 |
裏話: 管理ルールは、すでに別プロジェクトで作っていた
DeckGhostを作る前に、別の長期開発プロジェクトでは、AIエージェントを継続運用するための管理構造を実装していました。
たとえば次のような仕組みです。
- Markdownをタスク定義の正本にする
-
BACKLOGとREADYを分ける - 受け入れ条件と対象外を先に書く
- 同時進行数へWIP上限を置く
- 実装、テスト、証跡がそろうまでDONEにしない
- 実装前に影響する設計文書を更新する
- GitHub Issue、PR、コミットを日別に要約する
- 人間確認が必要なら途中で停止する
つまり、DeckGhostを始めた時点で「AIへ勢いだけで実装させると、あとで整理が必要になる」ことは理解していました。
それでも、このプロジェクトではルールをほぼ使いませんでした。
きっかけは、Fableが使えなくなるという話だった
DeckGhostは、長期計画から生まれたプロジェクトではありません。
当時使っていたFableが今後使えなくなるという話を聞き、「自分が欲しかったアルゴリズムDJの操作感を、使えるうちに形へしておきたい」と焦ったことがきっかけでした。
ここで重要なのは、Fableが実際にいつ、どの範囲で使えなくなるかというサービス情報ではありません。
自分が期限だと感じた瞬間、すでに持っていた管理ルールより、実装速度を優先したことです。
本来の判断
目的を整理
↓
タスクを分解
↓
READY条件を整える
↓
WIP上限内で実装
DeckGhostでの判断
使えなくなるかもしれない
↓
まず動くものを作る
↓
聴いて気になった部分をその場で直す
↓
思いついた機能も続けて追加
これは、管理を知らなかった失敗ではありません。
ルールを知ったうえで、緊急性を理由に破った開発です。
先行プロジェクトの通常運用と、DeckGhostで実際に行ったこと
| 観点 | 先行プロジェクトでの通常ルール | DeckGhostで実際に行ったこと |
|---|---|---|
| タスクの入口 | 正本へ背景、範囲、完了条件を記録 | アイデアから直接実装を依頼 |
| 着手条件 |
READYだけを開始 |
気になった機能をその場で開始 |
| 同時進行 | WIP上限を設定 | 実装、解析、UI、FXを連続追加 |
| 設計文書 | 影響文書を先に更新 | 実装を先行し、READMEと設計書を後追い |
| 完了判定 | テスト、証跡、実機確認 | まず聴いて動かし、テストは後から追加 |
| 優先順位 | Issueと現在目標から選択 | 聴感上の違和感と興味を優先 |
| 状態の観測 | 日別サマリーとメトリクス | コミット履歴と実際の演奏結果から把握 |
| 停止条件 | 人間判断や実機確認で停止 | 興味が続く限り実装を継続 |
ルールを外したことで得られたもの
管理を省いたことには、短期的な利点もありました。
- 発想から実装までの距離が短い
- 音を聴いた直後に修正へ入れる
- 技術的な可能性を広く試せる
- 仕様を固定する前に、触って学べる
- 「作れるか分からない」状態から、公開版まで一気に到達できる
特にDeckGhostのように、正解を文章だけで決めにくいプロジェクトでは、実際に音を出さなければ分からないことが多くあります。
- BPM検出は実音源でどのように間違うか
- どのMIXが「下手」に聞こえるか
- EQ交換のどこが不自然か
- Canvasやタッチ操作が実機でどう壊れるか
- どのDJ技法を現在のDSPで表現できるか
短期スパイクとして見るなら、先に動くものを作った判断には意味がありました。
代わりに発生した負債
ただし、速度は無料ではありませんでした。
1. 設計書と実装の数値がずれた
実装とREADMEでは、知識ベース67技法、AUTO MIX候補60技法まで増えています。
一方、詳細設計書の一部には、以前の40技法、34技法という説明が残りました。
記事を書く際にも、README、設計書、実装、テストを照合しなければ、現在値を判断できませんでした。
2. テストが後から追加された
BPM、無音区間、局所テンポ、SSM、HPSS、コード互換性の自動テストは、主要機能が動いた後に追加しています。
先に実装したことで、実際に起きた失敗をテストケースへ変換できた一方、修正前は聴感と手動確認への依存が大きい状態でした。
3. 機能の優先順位が実装中に変化した
当初は「曲の途中でも自然にMIXする」ことが中心でした。
しかし実装中に、MIDI、録音、リプレイ、技法データベース、セクション解析、コード列評価などが増えました。
どれも価値のある機能ですが、管理上はスコープが拡大しています。
4. 現在地をコードから復元する必要が生まれた
正式なタスク正本を作らなかったため、現在の能力境界や残課題を知るには、README、設計書、コミット、実装を読む必要があります。
この状態は、一人で勢いよく開発している間は回せます。
時間が空いた後や、別の人へ引き継ぐ場面では、復元コストとして返ってきます。
これは「管理しない方が速い」という話ではない
ここだけ切り取ると、管理ルールを捨てたから5日で作れたように見えるかもしれません。
実際には、もう少し複雑です。
- 管理を省いたことで、探索速度は上がった
- 既に別の長期開発で設計原則を考えていたため、コード内部の責務分離はある程度保てた
- 実音をすぐ確認できたため、曖昧な要求を短時間で具体化できた
- 一方で、文書同期、優先順位、完了条件、引き継ぎ可能性には負債が残った
つまり、完全な無管理ではありません。
プロジェクト管理は外したが、過去に身につけた設計判断までは消えていなかったという状態です。
BrainとDSPを分けたこと、推定値へ信頼度を持たせたこと、手動補正を優先したこと、安全なフォールバックを用意したことは、思いつきだけではなく、別プロジェクトで積み上げた設計経験の流用です。
スパイクとして始め、後から製品へ近づける
この開発をやり直すとしても、最初からすべての管理ルールを適用するとは限りません。
ただし、最初に次を明示します。
目的:
5日間でアルゴリズムDJの技術成立性を確認する
期間:
5日間のタイムボックス
完了条件:
デモ曲で2デッキAUTO MIXを再生できる
公開URLで第三者が触れる
既知の限界を記録する
終了後:
続けるなら、タスク正本、テスト、文書同期を整備する
続けないなら、検証結果と再実行方法だけを残す
これなら、ルールを無視した暴走ではなく、意図的に制約した探索になります。
今回のDeckGhostは、結果としてそこまで明文化せず走りました。
そのため、この記事自体が後から作った設計記録でもあります。
次の記事へ続く話
DeckGhostの記事を先に公開するのは、順番として意味があります。
この記事では、AIを使って5日間で何を作れたか、どのように壊れ、どう修正したかを扱いました。
次の記事では、その裏側として次を扱います。
- なぜ先行プロジェクトでは管理の仕組みを先に作っていたのか
- AIで増えるのは完成品ではなく変更案であること
-
BACKLOGとREADYをなぜ分けるのか - WIP制限が必要になった理由
- Markdown正本、GitHub同期、完了証跡
- 人間確認が必要な場所でAIを停止させる方法
- そのルールをDeckGhostで破った結果、何が起きたか
DeckGhostは、管理ルールを守って速く作った話ではありません。
管理ルールの必要性を知りながら、期限への焦りで破り、それでも設計経験に支えられて公開まで走った話です。
この章の結論
DeckGhostの最初の5日間は、管理を不要だと証明した期間ではありません。
探索速度を優先した結果、短期間で触れるものを作れた一方、文書同期、テスト、優先順位、再開可能性の負債が残った期間でした。
次章では、作ったものをローカル環境だけに閉じず、Cloudflare WorkersとCI/CDで公開環境へ運んだ部分を分けて扱います。
11. 作ったものを公開環境へ運ぶ
🔰簡単に言うと
AIがコードを速く書いても、利用者が触れる場所へ安全に届けられなければ価値にはなりません。公開手順を自動化し、人間は音や操作の確認へ集中します。
AIでコードを速く書けても、ブラウザで動き、変更のたびに安全に配布されるところまで届かなければ、利用者へ価値を渡せません。
🔰この章で出てくる用語
| 用語 | この記事での意味 | エンジニア向けに言うと |
|---|---|---|
| CI | 変更後にテストやビルドを自動実行する | 継続的インテグレーション |
| CD | 確認済みの変更を公開環境へ配布する | 継続的デリバリー/デプロイ |
| デプロイ | アプリを利用可能な環境へ配置する | リリース成果物の反映 |
| プレビュー環境 | 本番前に変更後の画面を確認する | PR単位の一時環境 |
| Cloudflare Workers | Cloudflareのエッジ上でアプリを動かす | サーバ管理を減らせるエッジ実行基盤 |
| ステータスチェック | PRやコミットが条件を満たしたかをGitHubへ返す | マージ前ゲート |
| 必須ゲート | 失敗していれば公開やマージを止める条件 | 強制的な品質ポリシー |
| ロールバック | 問題がある変更を以前の状態へ戻す | 直前リリースへの復旧 |
DeckGhostは、最初の大規模な実装から公開可能な主要機能までを、最初の5日間に集中して進めました。
この期間には、次のような変更が含まれます。
- 2デッキ、ミキサー、FX、楽曲解析、Brain、AUTO MIXの初期実装
- ブラウザの意図しないズームと高解像度Canvasの不具合修正
- 「下手なDJ」になるMIX判断の改善
- BPMとビートグリッドの手動補正
- キーロック、キー補正、ラウドネス正規化
- EQ主導のトランジション
- DJ技法データベースの拡充
- Web MIDI入出力
- フレーズを考慮したMIX、録音、履歴、リプレイ
- サイレント・プリロールとキック位置補正
その後の更新では、固定ブロック型だった曲構成解析をSSMとHPSSの複合特徴へ置き換え、局所テンポ、無音区間、コード列、重なり区間の調性評価を追加しました。技法データも67件、AUTO MIX候補も60件へ増えています。
最初の5日間は「完成までの期間」ではなく、利用者が触って失敗を観測できるところまで到達した期間です。
AIコーディングエージェントを使うと、設計案、コード、修正候補を短時間で作れます。
一方で、音が出るだけでは完成ではありません。
- 実ブラウザで操作できるか
- 実音源で解析が破綻しないか
- 高解像度画面やタッチ操作で壊れないか
- 自動MIXを聴いて不自然ではないか
- デプロイ後の公開環境でも同じように動くか
AIが実装速度を上げても、最後の品質確認は実際に触り、聴き、観測する必要がありました。
Cloudflare Workersによる公開
DeckGhostはCloudflare Workers上で動かしています。
公開URLはこちらです。
構成はシンプルです。
GitHubで変更をマージすると、Cloudflare側のビルドとデプロイが自動実行されます。
つまり、開発者が毎回サーバへログインし、ファイルを手作業でコピーする必要はありません。
コードを変更
↓
GitHubへPush
↓
PRで変更を確認
↓
mainへMerge
↓
Cloudflare Workersへ自動デプロイ
↓
公開URLで動作確認
この流れによって、短期間のAI開発でも、ローカル環境だけに成果を閉じず、継続的に確認できる公開環境を保てました。
CI/CDで自動化できる範囲とできない範囲
Cloudflare Workersへのデプロイ成功は、公開処理が完了したことを示します。
また、解析とBrainのテストはnpm testで実行できるようになりました。ただし、現時点では最新コミットにGitHubの必須ステータスチェックは設定していません。Cloudflareのビルド・デプロイと、Node.jsの自動テストは存在しますが、テスト成功をデプロイ前の必須ゲートにする作業は残っています。
DJアプリの品質すべてを自動化できるわけでもありません。
自動化しやすい確認:
- Node.js標準テストによる解析・判断ロジックの検証
- Cloudflare上でのビルド
- 静的ファイルとサーバ処理の配置
- 公開URLへの反映
- デプロイ履歴
- 変更単位ごとのプレビュー
人間による確認が残る項目:
- 実際に音が再生されるか
- AUTO MIXが音楽的に自然か
- BPMや拍位置の解析が妥当か
- MIDI機器との接続
- ヘッドホン出力の切り替え
- タッチ操作や長時間再生
CI/CDは、品質確認を不要にする仕組みではありません。
毎回同じ公開手順を自動化し、人間が音や操作の確認へ集中するための仕組みです。
AIで開発速度を上げ、CI/CDで配布作業を安定させ、人間は音楽的な品質と実機挙動を確認します。
DeckGhostという名前にはAIとAlgorithmicの両方を入れています。
実際の中身は、すべてをLLMへ任せる設計ではありません。
- 音声解析は信号処理
- 相性評価はスコアリング
- 技法選択はルールと重み付け
- 演奏計画は構造化データ
- 実行は決定論的なDSP
- 人間の手動補正を最優先
- 失敗時は安全なフェードへ縮退
AIの価値は、何でも自由に操作することではありません。
不確実な情報から候補を作り、理由を説明し、実行可能な計画へ変換する部分にあります。
時間制約が厳しい実行は、AIから切り離します。
12. 「AIらしさ」より、壊れ方を設計する
🔰この章で出てくる用語
| 用語 | この記事での意味 | エンジニア向けに言うと |
|---|---|---|
| Local-first | 音源解析と再生を利用者のブラウザ内で完結する | 機密データをクライアント側へ閉じる設計 |
| 責務分離 | 解析、判断、計画、実行、描画を分ける | 単一責任とレイヤ分離 |
| 安全境界 | 自動で進める範囲と、人へ戻す範囲の境目 | 権限・承認・リスクの境界 |
| 決定論的実行 | 同じ計画を同じ順序で再生する | 再現可能な実行エンジン |
| フォールバック | 高度なMIXが無理なら安全な技法へ切り替える | 代替経路、縮退パス |
| オペレーターオーバーライド | 手動BPMやCUEを自動推定より優先する | 人間の明示設定を自動値より強くすること |
| graceful degradation | 完全な品質を保てなくても破綻を避ける | 機能縮退しながらサービス継続 |
| 能力境界 | 知っている技法と実行可能な技法を分ける | 宣言Capabilityと実装Capabilityの一致 |
🔰ここまでのポイント
DeckGhostで得た学びを、音声以外にも使える形へまとめると次のようになります。
1. 判断と実行を分ける
不確実で時間のかかる判断と、時間どおりに動く必要がある実行を同じ処理へ入れない。
2. 推定値へ出所と信頼度を付ける
値だけを渡さず、自動推定か、手動確定か、どの程度信用できるかを残す。
3. 手動修正を正式な機能にする
自動化の失敗を「例外」として隠さず、人間が直せるUIと保存形式を用意する。
4. 局所評価だけで決めない
候補そのものの点数だけでなく、その後の処理が完走できるかを見る。
5. 正常系より先に逃げ道を考える
同期できない、信頼度が低い、時間が足りない場合の縮退動作を決める。
6. 観測と再現を組み込む
計画、実行、ログ、リプレイを用意し、「なんとなく変」を調査可能にする。
7. 知識、能力、権限を分ける
AIが知っていること、システムが実行できること、実行してよいことを同一視しない。
付録 迷ったときの用語クイックリファレンス
記事の途中で用語を忘れた場合は、次の対応だけ見直せば十分です。
| 音楽側 | システム設計側 |
|---|---|
| 拍、BPM、ビートグリッド | クロック、周期、時刻表 |
| フレーズ、セクション | 処理ブロック、状態フェーズ |
| CUE、ダウンビート | 開始点、同期基準 |
| PFL | 本番へ出さないプレビュー環境 |
| Deck A / Deck B | 現行系と切替先 |
| フェーダー | 流量・ゲイン制御 |
| EQのLOW / MID / HIGH | 帯域別の資源競合 |
| キー | 全体の互換性ラベル |
| コード列 | 時間窓ごとの局所状態 |
| トランジション | ローリング切替、フェイルオーバー |
| DSP | データプレーン |
| Brain | 制御プレーン |
| Planner | 実行スケジューラー |
| セーフフェード | 縮退運転 |
| 手動BPM・CUE | オペレーターオーバーライド |
この記事の音楽用語は、ほとんどが「時間」「競合」「切り替え」のどれかです。
現在の限界と今後の課題
DeckGhostは動作するプロトタイプですが、まだ完成ではありません。
現在のBPM、キー、曲構成の解析は推定です。ジャンルやマスタリングによって誤判定します。
現在は局所テンポの変化を検出できますが、可変テンポ曲へ追従するBeat Gridはまだありません。HPSSも解析特徴を分けるマスクであり、ボーカルやドラムを独立音源として取り出すStem分離ではありません。
今後の課題には、次があります。
- ボーカル区間の検出
- Stem分離
- 可変テンポへ追従するBeat Grid
- MLを使ったサビやドロップの検出
- 楽曲解析のWorker化
- 解析キャッシュ
- 長時間運転時の性能検証
- Brainロジックのブラウザ、Node.js、Worker間での共有
- 実音源の回帰テストとブラウザE2Eテスト
-
npm testをデプロイ前の必須ゲートへ組み込むCI - MIDI Clockの先読みスケジューリング
- 描画ループと演奏制御のさらなる分離
すでに多くの機能がありますが、リアルタイム処理では、機能数より再現性と検証可能性が大切です。
今回の更新でも、技法数を増やすだけでなく、SSM、局所テンポ、コード列、信頼度、自動テストという「判断の根拠を観測する仕組み」を増やしました。
今後は技法を増やすだけでなく、長時間動かしても同じ品質を保てるかを優先して改善します。
おわりに
最初は、「曲の途中でも上手にMIXする自動DJを作りたい」という発想でした。
作ってみると、重要だったのは派手なAIモデルより、次のような地味な設計でした。
- 判断と実行の分離
- 推定値の信頼度
- 手動補正
- 決定論的なタイムライン
- 安全な縮退
- ログとリプレイ
- 知識と実行能力の分離
インフラ所属の自分がDJアプリを作ることに、最初は少し分野違いの感覚もありました。
しかし、制御プレーンとデータプレーン、可観測性、縮退運転、Local-first、責務分離など、普段の仕事で触れている考え方が、そのまま音声アプリの設計へつながりました。
技術分野が変わっても、壊れにくいシステムを作る基本は意外と似ています。
良いAIシステムとは、すべてをAIに任せたシステムではありません。
AIが考える場所、正確に実行する場所、人間へ戻す場所を分けたシステムです。
そして、良いAI開発とは、常にルールを厳格に守ることだけでもありません。
探索のためにルールを緩めるなら、何を捨て、いつ通常運用へ戻すかを明示する必要があります。DeckGhostでは、それを明示しないまま勢いで走りました。
次の記事では、今回ほぼ使わなかった先行プロジェクト側の管理構造と、未来の自分とAIが迷わず再開するためのプロジェクト管理について掘り下げます。