# 機能設計書 22-オーディオ正規化

## 概要

本ドキュメントは、StaxRipのオーディオ正規化機能について、その処理内容、入出力仕様、および実装詳細を記載した設計書である。

### 本機能の処理概要

オーディオ正規化機能は、音声トラックの音量レベルを分析し、指定されたターゲット値に調整する機能である。ffmpegのloudnorm（EBU R128準拠）またはdynaudnorm（動的正規化）フィルタを使用して、一貫した音量レベルを実現する。

**業務上の目的・背景**：異なるソースからの音声トラックは音量レベルが不均一なことが多い。特に映画、テレビ番組、ユーザー作成コンテンツを混在させる場合、視聴体験を統一するために音量正規化が必要となる。EBU R128規格に準拠したloudnorm処理により、放送品質の音量レベルを達成できる。

**機能の利用シーン**：
- 映画ソースの音声が小さすぎる/大きすぎる場合の調整
- 複数エピソードを一貫した音量でエンコードする場合
- 放送規格（EBU R128）に準拠した音量レベルを達成する場合
- ダイナミックレンジ圧縮により夜間視聴向けに調整する場合

**主要な処理内容**：
1. 2パス処理（分析パス + 適用パス）による音量測定と正規化
2. volumedetect: ピーク音量検出とゲイン調整
3. loudnorm: EBU R128準拠の統合ラウドネス正規化
4. dynaudnorm: フレームベースの動的音量正規化

**関連システム・外部連携**：
- FFmpeg: 正規化処理エンジン（loudnorm/dynaudnormフィルタ）
- eac3to: 代替正規化オプション（-normalize）
- qaac: ネイティブ正規化オプション（--normalize）

**権限による制御**：本機能に権限による制御はない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 6 | AudioForm | 主画面 | 音量レベル正規化設定チェックボックス |

## 機能種別

計算処理 / 信号処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| Normalize | Boolean | Yes | 正規化の有効/無効 | - |
| ffmpegNormalizeMode | ffmpegNormalizeMode | No | 正規化モード | Enum値チェック |
| ffmpegLoudnormIntegrated | Double | No | ターゲット統合ラウドネス（LUFS） | デフォルト: -24 |
| ffmpegLoudnormTruePeak | Double | No | ターゲットトゥルーピーク（dBTP） | デフォルト: -2 |
| ffmpegLoudnormLRA | Double | No | ターゲットラウドネスレンジ（LU） | デフォルト: 7 |
| ffmpegDynaudnormF | Integer | No | フレーム長（サンプル数） | デフォルト: 500 |
| ffmpegDynaudnormG | Integer | No | ガウシアンフィルタサイズ | デフォルト: 31（奇数のみ） |
| ffmpegDynaudnormP | Double | No | ターゲットピーク値 | デフォルト: 0.95 |
| ffmpegDynaudnormM | Double | No | 最大ゲイン係数 | デフォルト: 10 |

### 入力データソース

- 画面入力（AudioForm）
- GUIAudioProfile.Parameters

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Gain | Single | 検出されたゲイン値（dB） |
| ffmpegLoudnormIntegratedMeasured | Double | 測定された統合ラウドネス |
| ffmpegLoudnormTruePeakMeasured | Double | 測定されたトゥルーピーク |
| ffmpegLoudnormLraMeasured | Double | 測定されたラウドネスレンジ |
| ffmpegLoudnormThresholdMeasured | Double | 測定されたスレッショルド |

### 出力先

- AudioProfile: 測定値を保存
- 出力オーディオファイル: 正規化適用済み

## 処理フロー

### 処理シーケンス

```
1. 正規化モード判定
   └─ volumedetect / loudnorm / dynaudnorm の選択

2. 分析パス実行（volumedetect/loudnorm）
   └─ ffmpeg -af volumedetect/loudnorm ... -f null NUL

3. 測定値抽出
   └─ ログからmax_volume/Input Integrated等を解析

4. 適用パス実行
   └─ エンコード時に-afパラメータで正規化フィルタ適用

5. 結果保存
   └─ Gain値またはloudnormパラメータをプロファイルに保存
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{Normalize有効?}
    B -->|No| Z[終了]
    B -->|Yes| C{NormalizeMode?}
    C -->|volumedetect| D[volumedetect分析]
    C -->|loudnorm| E[loudnorm分析]
    C -->|dynaudnorm| F[dynaudnorm適用]
    D --> G[max_volume抽出]
    E --> H[Input Integrated等抽出]
    G --> I[Gain値設定]
    H --> J[loudnormパラメータ設定]
    I --> K[エンコード時にvolume適用]
    J --> L[エンコード時にloudnorm適用]
    F --> M[エンコード時にdynaudnorm適用]
    K --> Z
    L --> Z
    M --> Z
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-22-01 | 2パス処理 | volumedetect/loudnormは分析パス後に適用パスを実行 | ffmpegNormalizeMode=volumedetect/loudnorm |
| BR-22-02 | ゲイン正値適用 | volumedetect検出のmax_volumeが負の場合のみゲイン適用 | max_volume < 0 |
| BR-22-03 | DeeZy非対応 | DeeZyエンコーダー使用時は正規化スキップ | Encoder=deezy |
| BR-22-04 | ExtractCore非対応 | DTS Core抽出時は正規化スキップ | ExtractCore=True |

### 計算ロジック

**loudnormフィルタパラメータ**:
```
-af loudnorm=I={Integrated}:TP={TruePeak}:LRA={LRA}
    :measured_I={IntegratedMeasured}
    :measured_TP={TruePeakMeasured}
    :measured_LRA={LraMeasured}
    :measured_thresh={ThresholdMeasured}
    :print_format=summary
```

**dynaudnormフィルタパラメータ**:
```
-af dynaudnorm=f={F}:g={G}:p={P}:m={M}:r={R}:s={S}:n={N}:c={C}:b={B}
```

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

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| E-22-01 | 分析失敗 | ffmpeg分析パスがmax_volumeを検出できない | Gain=0のまま続行 |
| E-22-02 | 測定値解析失敗 | ログからloudnorm測定値を抽出できない | デフォルト値で続行 |

### リトライ仕様

リトライ処理なし。

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

トランザクション制御なし。

## パフォーマンス要件

- 分析パス: 入力ファイル全体のデコードが必要（再生時間と同程度）
- 適用パス: エンコード処理と統合されるため追加オーバーヘッドは軽微

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

外部プロセス（ffmpeg）の実行はPackage経由で制御。

## 備考

正規化モードの選択指針:
- **volumedetect**: シンプルなピークベース正規化。処理が軽量。
- **loudnorm**: EBU R128準拠。放送品質の音量調整。2パス処理推奨。
- **dynaudnorm**: 動的正規化。ダイナミックレンジ圧縮効果。1パス処理。

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | AudioProfile.vb | `Source/General/AudioProfile.vb` | Parameters内の正規化関連プロパティ（1835-1856行目） |
| 1-2 | AudioProfile.vb | `Source/General/AudioProfile.vb` | ffmpegNormalizeMode Enum（2834-2838行目） |

**読解のコツ**: ffmpegLoudnorm*とffmpegDynaudnorm*のプロパティ群がそれぞれのモードのパラメータを保持している。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AudioProfile.vb | `Source/General/AudioProfile.vb` | NormalizeFF()（963-1011行目）が正規化処理のメイン |

**主要処理フロー**:
1. **964行目**: 正規化条件チェック（Normalize、ExtractCore、Encoder）
2. **966-982行目**: ffmpeg分析コマンド構築
3. **984-1008行目**: Proc実行とログ解析

#### Step 3: パラメータ生成を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Audio.vb | `Source/General/Audio.vb` | GetLoudNormArgs()（448-458行目） |
| 3-2 | Audio.vb | `Source/General/Audio.vb` | GetDynAudNormArgs()（460-474行目） |

**読解のコツ**: 測定値（Measured）が0の場合は分析パス、測定値がある場合は適用パスとなる。

#### Step 4: エンコード時の適用を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | AudioProfile.vb | `Source/General/AudioProfile.vb` | GetffmpegCommandLine()内の正規化適用（1624-1630行目） |
| 4-2 | AudioProfile.vb | `Source/General/AudioProfile.vb` | GetPipeCommandLine()内の正規化適用（1452-1458行目） |

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

```
GUIAudioProfile.NormalizeFF()
    │
    ├─ ffmpegNormalizeMode判定
    │
    ├─ 分析コマンド構築
    │      ├─ volumedetect: "-af volumedetect"
    │      └─ loudnorm: "-af loudnorm=I=...:TP=...:LRA=..."
    │
    ├─ Proc.Start() [分析実行]
    │
    └─ ログ解析
           ├─ Regex.Match("max_volume: -(\\d+\\.\\d+) dB")
           │      └─ Gain += 検出値
           │
           └─ Regex.Match("Input Integrated/TruePeak/LRA/Threshold")
                  └─ ffmpegLoudnorm*Measured に保存

エンコード時:
GUIAudioProfile.GetffmpegCommandLine()
    │
    ├─ Params.Normalize && loudnorm
    │      └─ Audio.GetLoudNormArgs(Params)
    │
    └─ Params.Normalize && dynaudnorm
           └─ Audio.GetDynAudNormArgs(Params)
```

### データフロー図

```
[入力]                [分析パス]              [適用パス]              [出力]

オーディオ    ───▶   ffmpeg             ───▶  測定値抽出    ───▶   Gain/
ファイル              -af volumedetect         max_volume           loudnormパラメータ
                      -f null NUL              解析                  保存
                                                  │
                                                  ▼
                      ffmpeg             ───▶  統合ラウドネス  ───▶   loudnorm
                      -af loudnorm             TruePeak等            Measured値
                      -f null NUL              解析                  保存
                                                  │
                                                  ▼
                                              エンコード時
                                              -af volume={Gain}dB
                                              または
                                              -af loudnorm=...measured_*=...
                                              または
                                              -af dynaudnorm=...
                                                  │
                                                  ▼
                                              正規化済み
                                              オーディオファイル
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AudioProfile.vb | `Source/General/AudioProfile.vb` | ソース | NormalizeFF()、正規化パラメータ |
| Audio.vb | `Source/General/Audio.vb` | ソース | GetLoudNormArgs()、GetDynAudNormArgs() |
| AudioForm.vb | `Source/Forms/AudioForm.vb` | ソース | 正規化設定UI（cbNormalize） |
