# Transport 同期 v2（親 BGM マスター）

[work-master-time.md](./work-master-time.md) の transport 実装。

## 役割

| レイヤ | 役割 |
|--------|------|
| **Platform `player.html`** | マスター `<audio>`（`assets.masterAudio.url`）または **サイレントクロック**（URL なし時 · `durationSec` のみ） |
| **Karaoke embed** | 親の `muvic:transport` に追従。`masterBgmActive` 時は伴奏 BGM をミュート |
| **CDS3D embed** | 同左 · タイムライン / 振付 |

## postMessage

### 8780 → 8790 / 8765 · `muvic:session`（拡張）

既存フィールドに加え:

| フィールド | 意味 |
|------------|------|
| `transportAuthority` | `'parent'` — 親 transport バーが正 |
| `durationSec` | タイムライン終端（秒） |
| `masterBgmUrl` | 親が再生する BGM URL（任意） |
| `masterBgmActive` | `true` なら子はローカル BGM を鳴らさない |

### 8780 → 8790 / 8765 · `muvic:transport`

```json
{
  "type": "muvic:transport",
  "workId": "work_demo_001",
  "action": "play" | "pause" | "seek",
  "positionSec": 1.25,
  "source": "parent"
}
```

- **play** — `positionSec` から再生（既に再生中ならシークして継続）
- **pause** — 停止（位置は維持）
- **seek** — シークのみ（再生状態は変えない）

### 8780 → 8790 / 8765 · `muvic:clockSync`（BGM マスター時）

親 transport **再生中**（マスター BGM またはサイレントクロック）、約 **350ms** ごとに表示中 embed へ:

```json
{ "type": "muvic:clockSync", "workId", "positionSec", "isPlaying": true, "source": "parent" }
```

子はローカル BGM を鳴らさず、カーソル / タイムラインのみ更新。

**CDS3D（8765）· `muvicParentMasterBgm === false` でも** `clockSync` を受け取り `syncPositionFromParent` する（非表示タブの時計が親から遅れない · タブ戻り時の裏 BGM 二重を防ぐ）。マスター BGM **オン**のときだけ `isPlaying === false` で toggle を呼ぶ。

**CDS3D（8765）:** `clockSync` は **`syncPositionFromParent`**（`muvicApplyingParentTransport` 中 · transport リレーなし）。`jumpToTime` 経由だと v3 の seek リレーが連打され同期が壊れるため。

**8780 → iframe の origin:** `js/embed-post-message.js` — iframe の `src` から origin を取り、**未ロード（about:blank · 親と同じ 8780）には送らない**（`postMessage` 8765/8780 mismatch 防止）。

## UI

`player.html` ビューポート下 **マスター transport**（再生 / シーク / 時刻表示）。

**BGM あり（PoC）:** `bgmFallback` → 親から 8s WAV 再生 · [work-master-time.md](./work-master-time.md)。  
**BGM なし:** サイレントクロックで位置のみ揃える。

## アクティブビューと BGM（v2.1）

親 transport **再生中**は **表示中タブの embed だけ** `play`、もう一方は `pause`（同じ `positionSec`）。  
3D → 楽譜に戻したとき **CDS3D の BGM が裏で鳴り続けない** ようにする。

## 確認

1. 8780 / 8782 / 8790 / 8765（**embedVersion** 更新後は 8790/8765 ハードリロード）
2. `player.html?work=work_demo_001&api=1`
3. 親 transport で再生 → 楽譜カーソル / 3D タイムラインが同じ秒付近で動く
4. `GET /works/:id` で `durationSec` · `assets.masterAudio.url` を確認

## embed postMessage（8780 → iframe）

`js/embed-post-message.js` — iframe **`src` の origin と一致**し、**`onload` 後**（`data-muvic-embed-loaded`）のみ送信。未ロード時の `configuredOrigin` フォールバックは使わない（Console の origin mismatch 防止）。Player は **`onload` 登録 → `assignEmbedIframeSrc`** の順。

## 遅延ロード / 振付 fetch 後の再同期

- CDS3D iframe は **3D タブ初回**で load。親が先に再生していた場合、`onload` と **3D タブ切替**で `resyncEmbeds('dance')`（リトライ付き）。
- 振付 JSON 適用後 `applyDanceScoreData` が `jumpToTime(0)` するため、8765 → 8780 **`muvic:embedReady`** で再度 `muvic:session` + transport を送る。

## Transport v3（embed → 親 · 実装済）

**ゴール:** embed 内 transport も親が正本 — 親バーと他 embed が追従。

### 8765 / 8790 → 8780 · `muvic:transportRequest`

```json
{
  "type": "muvic:transportRequest",
  "workId": "work_demo_001",
  "view": "score" | "dance",
  "action": "play" | "pause" | "seek",
  "positionSec": 1.25,
  "source": "embed"
}
```

`transportAuthority: 'parent'` の `muvic:session` 受信後、embed の再生/停止/シークは **親へリレー**（`muvicApplyingParentTransport` 中は再送しない）。

### 8780 親

`player-transport.js` · **`handleEmbedTransportRequest`** — 自クロック + 親バー UI 更新 → **`muvic:transport`** / **`muvic:clockSync`**（BGM 時）で再送。

### 1 音源ルール（V3-4）

- **BGM あり:** 親 `<audio>` のみ audible · 子 BGM ミュート
- **サイレントクロック:** 表示中 embed のみ `play` · 非表示は `pause`（v2.1）
- **embed 起点の play:** 親 `<audio>` は iframe 内クリックでは autoplay できないことがある → 親は **`masterAudioPlaying` が false の間はサイレントクロック**で秒を進め、`clockSync` で子を追従（子 BGM は引き続きミュート）

### 確認

1. `embedVersion` **6** → **8780 を Ctrl+Shift+R**（Player 3D タブ / **Studio**）— iframe `src` の `v=6.xxxxx`。8765 **`muvic_embed.js?v=3`** — [README.md](../../README.md)
2. 楽譜 embed の ▶ → **親バーも再生** · 3D タブで同じ秒付近
3. 3D embed の transport → 親バー + 楽譜（非表示側は pause）
4. 再生中 Console に `postMessage` origin mismatch が **雪だるma式に増えない**

### Studio embed（`studio.html` · PoC）

- 8780 **`studio.html?work=&api=1`** — 親 transport バー + **`bindPlayerTransport`**（CDS3D iframe のみ · `getActiveView: 'dance'`）
- **`muvic:session`** / **`muvic:transport`** / **`muvic:clockSync`** / v3 **`muvic:transportRequest`** — CDS3D **`muvicMode=studio`**（`muvic-embed-studio`）でも player と同型
- 確認: 親 ▶ → 8765 タイムライン追従 · embed ▶ → 親バー連動 · Console postMessage 増殖なし

### コンクール S3（`go.startAtMs`）

Contest WS の `go` → `player-transport.schedulePlayAtMs` → 親 `playFrom` + 既存 `muvic:transport`。詳細は [contest-sync.md](./contest-sync.md)。

### 未着手

- 親 BGM と譜面 MIDI の完全ロック（Player 楽譜タブ）
