# 機能設計書 41-デュアルトラック再生

## 概要

本ドキュメントは、K-ShootMania のプレイ画面におけるデュアルトラック再生機能の設計を記述する。エフェクトON/OFFの2チャンネルの音楽ストリームを管理し、レーザー操作やFXロングノートに応じてリアルタイムに音声トラックを切り替える機能である。

### 本機能の処理概要

**業務上の目的・背景**：音楽ゲームにおいて、プレイヤーのレーザー操作やFXロングノートの押下に連動して、エフェクト適用済みの音声トラックとオリジナルの音声トラックをリアルタイムに切り替えることで、ゲームプレイと音楽演出の一体感を実現する。これにより、プレイヤーが楽曲のエフェクト変化を体感できるインタラクティブな音楽体験を提供する。

**機能の利用シーン**：ゲームプレイ画面において、譜面にエフェクト付きの音声ファイルが指定されている場合に利用される。プレイヤーがレーザーノブを操作した際やFXロングノートを押下した際に、ノーマルトラックからエフェクトトラックへ、またはその逆へ音声が切り替わる。

**主要な処理内容**：
1. 譜面ファイルから音楽ファイルを最大4トラック（mid配列）として読み込み、BASS_StreamCreateFileでストリームを生成する
2. mid.0（メイントラック）とmid.1以降のトラックをBASS_ChannelSetLinkで再生同期リンクする
3. レーザー操作やFXロングノートの状態（leffstat/peffstat）に基づいてアクティブなトラックを判定する
4. BASS_ChannelSetAttributeでボリューム属性を制御し、アクティブなトラックのみ音量を反映、非アクティブなトラックは0.0にミュートする
5. リンク失敗時（mlinkfailed!=0）は各トラックを個別に再生制御するフォールバック処理を行う

**関連システム・外部連携**：BASS Audio Library（bass.dll）によるストリーム再生制御、BASS_ChannelSetLink APIによるチャンネル同期

**権限による制御**：特になし。譜面ファイルに複数音声ファイルが指定されている場合に自動的に有効化される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 5 | ゲームプレイ画面 | 主画面 | エフェクトON/OFFの2チャンネル(mid配列)によるデュアルトラック切替再生 |

## 機能種別

リアルタイムオーディオ制御処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| csongfile | 文字列配列 | Yes | 譜面で指定された音楽ファイルパス（最大4トラック） | ファイル存在確認（exist命令） |
| peffect | 整数 | Yes | レーザーエフェクト有効フラグ（1=有効） | 0または1 |
| feffect | 整数 | Yes | FXエフェクト有効フラグ（1=有効） | 0または1 |
| leffstat | 整数 | No | レーザーエフェクト状態（0=無効/1=有効） | - |
| peffstat3 | 整数 | No | レーザーフィルターエフェクト状態 | - |
| csongvol | 整数 | Yes | 楽曲ボリューム（0-100） | 0以上 |
| mastervol | 整数 | Yes | マスターボリューム（0-100） | 0-100 |

### 入力データソース

- 譜面ファイル（.ksh）の`m=`パラメータから音楽ファイルパスを取得
- config.iniからマスターボリューム設定を取得
- ゲームロジック（play_game_logic.hsp）からレーザー/FXエフェクト状態を取得

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| mid | 整数配列 | BASSチャンネルハンドル（最大4トラック） |
| mnow | 整数 | 現在再生中のトラックインデックス |
| mlinkfailed | 整数 | リンク失敗フラグ（0=成功, 1=失敗） |

### 出力先

- BASSオーディオエンジンへのリアルタイム出力（スピーカー出力）

## 処理フロー

### 処理シーケンス

```
1. 譜面読み込み時の音楽ファイルロード（play_chart_load.hsp）
   └─ csongfileに指定された各音声ファイルをBASS_StreamCreateFileでストリーム生成し、mid配列に格納
2. チャンネルリンク設定
   └─ mid.0とmid.1以降をBASS_ChannelSetLinkで同期再生リンク
      └─ リンク失敗時はmlinkfailed=1を設定
3. 毎フレームのトラック切替処理（processAudioEffects）
   └─ leffstat（レーザーFXエフェクト）とpeffstat3（レーザーフィルター）の状態を判定
4. アクティブトラック決定
   └─ feffect=1の場合: FXエフェクト処理経由でSwitchAudioを含む制御
   └─ feffect=0の場合: stat1にアクティブトラックインデックスを設定
5. ボリューム制御
   └─ アクティブトラック: BASS_ChannelSetAttribute(mid.stat1, 2, csongvol/100*mastervol/100)
   └─ 非アクティブトラック: BASS_ChannelSetAttribute(mid.cnt, 2, 0.0)
```

### フローチャート

```mermaid
flowchart TD
    A[譜面ファイル読み込み] --> B{音楽ファイル数}
    B -->|1ファイル| C[mid.0のみ生成]
    B -->|2-4ファイル| D[mid.0-mid.3を生成]
    D --> E[BASS_ChannelSetLinkで同期リンク]
    E --> F{リンク成功?}
    F -->|Yes| G[mlinkfailed=0]
    F -->|No| H[mlinkfailed=1]
    G --> I[毎フレーム: エフェクト状態判定]
    H --> I
    I --> J{feffect=1?}
    J -->|Yes| K[FXエフェクト経由トラック制御]
    J -->|No| L{レーザー/エフェクト状態判定}
    L --> M[アクティブトラック決定]
    M --> N[アクティブトラック音量ON]
    N --> O[非アクティブトラック音量OFF]
    K --> O
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-41-01 | トラック最大数 | 音楽トラックは最大4チャンネルまで | 譜面ファイルの`m=`パラメータに複数ファイルが指定される場合 |
| BR-41-02 | リンク失敗フォールバック | リンク失敗時は各トラックを個別にPlay/Pause制御する | mlinkfailed!=0 の場合 |
| BR-41-03 | ミュート再生方式 | 非アクティブトラックはボリューム0.0で同期再生し続ける | 常時（リンク成功時） |
| BR-41-04 | エフェクトモード優先 | feffect=1の場合はFXロングノートによるトラック制御が優先される | feffect=1の場合 |

### 計算ロジック

- アクティブトラックのボリューム: `double(csongvol) / 100 * mastervol / 100`
- 非アクティブトラックのボリューム: `0.0`
- stat1（アクティブトラック）の決定: `leffstat + peffstat3 * 2` の計算結果に基づく

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

### 操作別データベース影響一覧

本機能ではデータベース操作は発生しない。すべてインメモリでのオーディオストリーム制御である。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| BASS_ErrorGetCode()!=0 | チャンネルリンクエラー | BASS_ChannelSetLinkが失敗した場合 | mlinkfailed=1を設定し、個別再生制御にフォールバック |
| strsize<=0 | ファイル不在 | 音楽ファイルが存在しない場合 | 該当トラックをスキップ |

### リトライ仕様

リトライは行わない。リンク失敗時はmlinkfailedフラグによるフォールバック動作に切り替える。

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

該当なし（インメモリリアルタイム処理）

## パフォーマンス要件

- トラック切替は1フレーム（約16ms@60fps）以内に完了すること
- BASS_ChannelSetAttributeによるボリューム変更はレイテンシなしで即座に反映されること
- updateperiod設定値に基づくBASSエンジンの更新周期内での制御

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

特になし。ローカルファイルの音声再生のみ。

## 備考

- mid配列のインデックス0が常にメイントラック（ノーマル音声）となる
- peffect（レーザーフィルター）とfeffect（FXエフェクト）は排他的に使用されることが多いが、コード上は両方有効な場合の処理も含まれる
- autoモード（auto=1）の場合、stat1の計算結果が2で割られるため、常にトラック0が選択される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | bass.as | `src/bass.as` | BASS APIのHSP用バインディング定義。BASS_StreamCreateFile, BASS_ChannelSetAttribute, BASS_ChannelSetLink等の関数宣言を確認する |

**読解のコツ**: HSPの`#cfunc`は戻り値ありの関数、`#func`は戻り値なしの手続き。`sptr`は文字列ポインタ型、`float`は浮動小数点型を示す。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | play_chart_load.hsp | `src/scene/play/play_chart_load.hsp` | 音楽ファイルの読み込みとmid配列への格納処理 |

**主要処理フロー**:
1. **560-607行目**: 音楽ファイルをBASS_StreamCreateFileでストリーム生成し、mid.cnt配列に格納する。cnt=0がメイントラック、1以降がエフェクトトラック
2. **584-589行目**: mid.0（メイントラック）の長さ取得と、BASS_ChannelSetLinkによるトラック間リンク設定。リンク失敗時にmlinkfailed=1を設定
3. **592-606行目**: peffect=1の場合、各トラックにPeaking Filter、BitCrusher等のエフェクトハンドルを設定

#### Step 3: トラック切替処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | play_audio_effects.hsp | `src/scene/play/play_audio_effects.hsp` | processAudioEffects関数でのトラック切替ロジック |

**主要処理フロー**:
- **843-870行目**: leffstat（レーザーFXエフェクト状態）とpeffstat3（レーザーフィルター状態）を組み合わせてstat1（アクティブトラック）を決定。`stat1 = leffstat + peffstat3*2` で算出し、foreachループで非アクティブトラックをミュート、アクティブトラックにボリュームを設定
- **861-870行目**: feffect=1の場合とfeffect=0の場合の分岐。feffect=0の場合にstat1に基づくデュアルトラック切替を実行

#### Step 4: 再生開始処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | play_audio.hsp | `src/scene/play/play_audio.hsp` | playSongIfTimeReachesOffset関数での同期再生開始処理 |

**主要処理フロー**:
- **96-138行目**: 再生開始タイミングに達した時、foreachでmid配列の各トラックを再生開始。mlinkfailed=0の場合はmid.0のみPlayし、リンクにより他トラックも自動再生。mlinkfailed!=0の場合は全トラックを個別にPlay

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

```
start_play (play_init.hsp)
    |
    +-- loadKSHChartFile (play_chart_load.hsp)
    |       |
    |       +-- BASS_StreamCreateFile -> mid.0, mid.1, ... mid.3
    |       +-- BASS_ChannelSetLink(mid.0, mid.1) ... mid.3
    |
    +-- on_update_play (毎フレームループ)
            |
            +-- processPlayTiming (play_audio.hsp)
            +-- playSongIfTimeReachesOffset (play_audio.hsp)
            |       +-- BASS_ChannelPlay mid.0 (リンク時は連動再生)
            |
            +-- processAudioEffects (play_audio_effects.hsp)
                    +-- leffstat/peffstat判定
                    +-- BASS_ChannelSetAttribute(mid.stat1, VOL, csongvol*mastervol)
                    +-- BASS_ChannelSetAttribute(mid.other, VOL, 0.0)
```

### データフロー図

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

譜面ファイル(.ksh)          loadKSHChartFile                 mid配列(BASSチャンネルハンドル)
  m=song.ogg;song_f.ogg --> BASS_StreamCreateFile -->        mid.0, mid.1
                            BASS_ChannelSetLink

レーザー/FX状態             processAudioEffects              スピーカー出力
  leffstat, peffstat3  -->  stat1算出 -->                    アクティブトラックのみ
                            BASS_ChannelSetAttribute         音量反映
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| play_chart_load.hsp | `src/scene/play/play_chart_load.hsp` | ソース | 音楽ファイルの読み込みとmid配列生成、チャンネルリンク設定 |
| play_audio.hsp | `src/scene/play/play_audio.hsp` | ソース | 再生タイミング管理、再生位置同期、再生開始処理 |
| play_audio_effects.hsp | `src/scene/play/play_audio_effects.hsp` | ソース | トラック切替ロジック、エフェクト状態に基づくボリューム制御 |
| play_init.hsp | `src/scene/play/play_init.hsp` | ソース | プレイシーン初期化、ボリュームFX設定 |
| bass.as | `src/bass.as` | ソース | BASS Audio Library APIバインディング |
| bass_vst.as | `src/bass_vst.as` | ソース | BASS VST APIバインディング |
| play_key_input.hsp | `src/scene/play/play_key_input.hsp` | ソース | 一時停止時のトラック制御 |
