# 機能設計書 118-Audio

## 概要

本ドキュメントは、Three.jsにおける`Audio`クラスの機能設計を記述する。Audioは、非位置依存（グローバル）なオーディオオブジェクトを表現し、Web Audio APIを利用した音声再生機能を提供する。

### 本機能の処理概要

Audioクラスは、3Dシーン内で位置に依存しない（どこでも同じ音量で聞こえる）BGMやUI効果音などのオーディオ再生を実現する。Web Audio APIを活用し、再生制御、音量調整、ループ設定、オーディオフィルターの適用など、高度なオーディオ機能を提供する。

**業務上の目的・背景**：WebGLベースの3Dアプリケーションやゲームにおいて、BGM、ナレーション、UI効果音などの非位置依存オーディオは必須の要素である。Audioクラスは、Web Audio APIの複雑さを抽象化し、Three.jsのシーングラフと統合されたオーディオ管理を可能にする。

**機能の利用シーン**：
- バックグラウンドミュージック（BGM）の再生
- ナレーション音声の再生
- UI効果音の再生
- 環境音の再生
- オーディオビジュアライゼーション

**主要な処理内容**：
1. オーディオバッファの設定と再生制御（play, pause, stop）
2. 音量制御（setVolume）とピッチ調整（setDetune）
3. ループ設定とループ範囲指定
4. オーディオフィルター（BiquadFilterNode等）の適用
5. 再生速度制御（setPlaybackRate）

**関連システム・外部連携**：AudioListenerと連携してオーディオグラフを構築し、AudioLoaderでロードしたオーディオバッファを再生する。Object3Dを継承しているため、シーングラフに追加可能。

**権限による制御**：特になし（ブラウザのオートプレイポリシーに従う）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 25 | WebAudioサンプル | 主機能 | 3Dオーディオの再生 |

## 機能種別

オーディオ再生 / 再生制御 / フィルター処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| listener | AudioListener | Yes | グローバルオーディオリスナー | - |

### 入力データソース

- AudioLoaderでロードされたAudioBuffer
- MediaElement（HTMLAudioElement等）
- MediaStream
- AudioNode

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| listener | AudioListener | オーディオリスナー参照 |
| context | AudioContext | Web Audioコンテキスト |
| gain | GainNode | 音量制御ノード |
| buffer | AudioBuffer | オーディオバッファ |
| source | AudioNode | オーディオソースノード |
| isPlaying | boolean | 再生中フラグ |

### 出力先

- AudioListenerを介してスピーカー出力

## 処理フロー

### 処理シーケンス

```
1. コンストラクタ
   └─ AudioListenerからcontextを取得
   └─ GainNodeを作成しlistenerに接続

2. setBuffer() - バッファ設定
   └─ bufferプロパティに格納
   └─ autoplayならplay()呼び出し

3. play() - 再生開始
   └─ BufferSourceNodeを作成
   └─ ループ設定を適用
   └─ source.start()で再生開始
   └─ フィルターチェーンに接続

4. pause() - 一時停止
   └─ 現在位置を計算して保存
   └─ source.stop()

5. stop() - 停止
   └─ 位置を0にリセット
   └─ source.stop()
```

### フローチャート

```mermaid
flowchart TD
    A[Audio生成] --> B[AudioListenerからcontext取得]
    B --> C[GainNode作成]
    C --> D[listenerに接続]
    D --> E{操作}
    E -->|setBuffer| F[buffer設定]
    F --> G{autoplay?}
    G -->|Yes| H[play呼び出し]
    G -->|No| I[待機]
    E -->|play| J{再生中?}
    J -->|Yes| K[警告]
    J -->|No| L[BufferSourceNode作成]
    L --> M[source.start]
    M --> N[connect]
    E -->|pause| O[位置保存]
    O --> P[source.stop]
    E -->|stop| Q[位置リセット]
    Q --> R[source.stop]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-118-01 | 再生制御制限 | hasPlaybackControl=falseの場合、play/pause/stopは使用不可 | NodeSource/MediaSource使用時 |
| BR-118-02 | 二重再生防止 | 再生中にplay()を呼ぶと警告 | isPlaying=true時 |
| BR-118-03 | ソースタイプ | setBuffer→'buffer'、setNodeSource→'audioNode'、setMediaElementSource→'mediaNode' | ソース設定時 |

### 計算ロジック

**一時停止位置計算**:
```javascript
_progress += (currentTime - _startedAt) * playbackRate
if (loop) {
    _progress = _progress % duration
}
```

## データベース操作仕様

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 警告 | 再生中にplay()を呼び出し | isRunningを確認してから呼び出し |
| - | 警告 | playback controlなしでplay/pause/stop | hasPlaybackControlを確認 |
| - | 警告 | bufferソース以外をcopy | bufferソースのみcopy可能 |

### リトライ仕様

該当なし

## トランザクション仕様

該当なし

## パフォーマンス要件

- Web Audio APIのGPU/DSP最適化を活用
- 不要なノード接続を避ける

## セキュリティ考慮事項

- ブラウザのオートプレイポリシーに準拠
- ユーザーインタラクション後に再生を開始する必要がある場合あり

## 備考

- Object3Dを継承しているが、位置は音量に影響しない（PositionalAudioとの違い）
- フィルターチェーンで複数のエフェクトを直列接続可能

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: クラス構造を理解する

Audioクラスの継承関係とプロパティを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Audio.js | `src/audio/Audio.js` | コンストラクタ（29-216行目） |

**読解のコツ**: 主要プロパティ
- `listener`: AudioListener参照
- `context`: AudioContext（listenerから取得）
- `gain`: GainNode（音量制御）
- `buffer`: AudioBuffer
- `source`: AudioNode（再生時に作成）
- `sourceType`: 'empty'/'buffer'/'audioNode'/'mediaNode'/'mediaStreamNode'
- `hasPlaybackControl`: 再生制御可能フラグ
- `isPlaying`: 再生中フラグ

#### Step 2: ソース設定メソッドを理解する

各種ソース設定方法を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Audio.js | `src/audio/Audio.js` | setNodeSource()（238-247行目） |
| 2-2 | Audio.js | `src/audio/Audio.js` | setMediaElementSource()（257-266行目） |
| 2-3 | Audio.js | `src/audio/Audio.js` | setMediaStreamSource()（276-285行目） |
| 2-4 | Audio.js | `src/audio/Audio.js` | setBuffer()（295-304行目） |

**主要処理フロー**:
- **238-247行目**: setNodeSource - hasPlaybackControl=false、sourceType='audioNode'
- **257-266行目**: setMediaElementSource - createMediaElementSource()使用
- **276-285行目**: setMediaStreamSource - createMediaStreamSource()使用
- **295-304行目**: setBuffer - sourceType='buffer'、autoplayならplay()

#### Step 3: 再生制御メソッドを理解する

play/pause/stopの動作を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Audio.js | `src/audio/Audio.js` | play()（314-349行目） |
| 3-2 | Audio.js | `src/audio/Audio.js` | pause()（358-390行目） |
| 3-3 | Audio.js | `src/audio/Audio.js` | stop()（400-422行目） |

**主要処理フロー**:
- **314-349行目**: play() - BufferSourceNode作成、ループ設定、start()、connect()
- **358-390行目**: pause() - _progress計算、source.stop()
- **400-422行目**: stop() - _progress=0、source.stop()

#### Step 4: 接続とフィルターを理解する

オーディオグラフの接続とフィルター適用を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Audio.js | `src/audio/Audio.js` | connect()（430-454行目） |
| 4-2 | Audio.js | `src/audio/Audio.js` | disconnect()（462-492行目） |
| 4-3 | Audio.js | `src/audio/Audio.js` | setFilters()（511-529行目） |

**主要処理フロー**:
- **430-454行目**: connect() - フィルターチェーン接続またはgainに直接接続
- **462-492行目**: disconnect() - 接続の解除
- **511-529行目**: setFilters() - disconnect→フィルター設定→connect

### プログラム呼び出し階層図

```
Audio (extends Object3D)
    │
    ├─ constructor(listener)
    │      ├─ listener.context取得
    │      ├─ createGain()
    │      └─ gain.connect(listener.getInput())
    │
    ├─ setBuffer(audioBuffer)
    │      └─ autoplay → play()
    │
    ├─ setNodeSource(audioNode)
    │      └─ connect()
    │
    ├─ setMediaElementSource(mediaElement)
    │      ├─ createMediaElementSource()
    │      └─ connect()
    │
    ├─ setMediaStreamSource(mediaStream)
    │      ├─ createMediaStreamSource()
    │      └─ connect()
    │
    ├─ play(delay)
    │      ├─ createBufferSource()
    │      ├─ source.start(_startedAt, _progress + offset, duration)
    │      ├─ setDetune()
    │      ├─ setPlaybackRate()
    │      └─ connect()
    │
    ├─ pause()
    │      ├─ _progress計算
    │      └─ source.stop()
    │
    ├─ stop(delay)
    │      ├─ _progress = 0
    │      └─ source.stop()
    │
    ├─ connect()
    │      ├─ filters.length > 0
    │      │      └─ source → filters[0] → ... → filters[n] → getOutput()
    │      └─ else
    │             └─ source → getOutput()
    │
    └─ setFilters(filters)
           ├─ disconnect()
           ├─ this.filters = filters
           └─ connect()
```

### データフロー図

```
[入力]                    [処理]                         [出力]

AudioBuffer ────────────▶ setBuffer() ─────────────────▶ this.buffer
                                │
                                ▼ (autoplay)
                          play()
                                │
                                ▼
                          createBufferSource()
                                │
                                ▼
                          source.start()
                                │
                                ▼
オーディオグラフ:
BufferSource → [Filter1] → [Filter2] → GainNode → AudioListener → destination
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Audio.js | `src/audio/Audio.js` | ソース | メインクラス定義 |
| AudioListener.js | `src/audio/AudioListener.js` | ソース | オーディオリスナー |
| AudioContext.js | `src/audio/AudioContext.js` | ソース | AudioContextシングルトン |
| AudioLoader.js | `src/loaders/AudioLoader.js` | ソース | オーディオファイルローダー |
| PositionalAudio.js | `src/audio/PositionalAudio.js` | ソース | 位置依存オーディオ（派生クラス） |
| Object3D.js | `src/core/Object3D.js` | ソース | 基底クラス |
