# 機能設計書 92-キー音ライブラリ管理

## 概要

本ドキュメントは、K-Shoot MANIAにおけるキー音（ボタン押下時に再生される効果音）をハッシュマップベースのライブラリで管理する機能の設計を記述する。

### 本機能の処理概要

本機能は、譜面ファイルで定義されたキー音ファイルを一元管理するライブラリ機能である。キー音の読み込み、存在確認、取得、クリアの各操作をハッシュマップ（辞書）データ構造を使って提供する。

**業務上の目的・背景**：K-Shoot MANIAの譜面にはBT/FXノートに対応するキー音（clap、sample等の効果音）が定義されている。同一のキー音が譜面中で繰り返し参照されるため、重複読み込みを防ぎメモリを効率的に使用するライブラリ管理が必要である。

**機能の利用シーン**：プレイ画面の初期化時にinitKeySoundModuleで初期化し、譜面読み込み時にloadKeySoundToLibraryでキー音をライブラリに登録する。プレイ中はgetKeySoundFromLibraryでキー音のポインタを取得して再生する。プレイ終了後はclearKeySoundLibraryでクリーンアップする。

**主要な処理内容**：
1. initKeySoundModule: ライブラリの初期化（ハッシュマップ生成）
2. loadKeySoundToLibrary: キー音ファイルの読み込みとライブラリへの登録
3. keySoundExistsInLibrary: キー音の存在確認
4. getKeySoundFromLibrary: キー音ポインタの取得
5. clearKeySoundLibrary: 全キー音の破棄とライブラリクリア

**関連システム・外部連携**：ksmcore.dllのCreateKeySound/DestroyKeySound/PlayKeySound関数を使用してキー音の生成・破棄・再生を行う。alib_hash.asのハッシュマップ実装を使用してキー音名をキーとした辞書管理を行う。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 5 | ゲームプレイ画面 | 主画面 | initKeySoundModuleによるハッシュマップベースのキー音ファイル読み込み・管理 |

## 機能種別

データ管理 / リソース管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| p1 (initKeySoundModule) | str | Yes | 実行ファイルのあるディレクトリパス(dir_default) | なし |
| p1 (loadKeySoundToLibrary) | str | Yes | キー音の名前(例: "clap") | なし |
| p2 (loadKeySoundToLibrary) | str | No | 音声ファイルのディレクトリ相対パス | 拡張子なしの場合はse\noteディレクトリを使用 |
| p3 (loadKeySoundToLibrary) | int | Yes | ksh譜面の"ver"フィールドの整数値 | なし |
| p1 (getKeySoundFromLibrary) | str | Yes | キー音の名前 | ライブラリに存在しない場合はエラーダイアログ |

### 入力データソース

- キー音名: 譜面ファイル(.ksh)のchipfx/fx定義から取得
- キー音ファイル: se\note\ディレクトリまたは譜面ディレクトリ内の音声ファイル(.wav)

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| s_keySoundLibrary | HashMapInt | キー音名→KeySoundポインタの辞書 |
| pKeySound (返値) | int | KeySoundオブジェクトへのポインタ |

### 出力先

メモリ上のハッシュマップ(s_keySoundLibrary)。アプリケーション終了時にonexit修飾子でDestroyKeySoundが呼ばれ解放される。

## 処理フロー

### 処理シーケンス

```
1. initKeySoundModule: ライブラリ初期化
   └─ HashInitInt/HashClearでハッシュマップを初期化
2. loadKeySoundToLibrary: キー音登録
   └─ HashCheckKeyで重複確認
   └─ 未登録の場合のみCreateKeySoundで生成しハッシュマップに格納
3. getKeySoundFromLibrary: キー音取得
   └─ HashCheckKeyで存在確認後、ポインタを返却
4. clearKeySoundLibrary: ライブラリクリア
   └─ 全KeySoundをDestroyKeySoundで破棄
   └─ HashClearでハッシュマップをクリア
```

### フローチャート

```mermaid
flowchart TD
    A[initKeySoundModule] --> B[HashInitInt/HashClear]
    B --> C{譜面のキー音定義}
    C --> D[loadKeySoundToLibrary]
    D --> E{既にロード済み?}
    E -->|Yes| F[何もしない]
    E -->|No| G[getFilePathByKeySoundName]
    G --> H{ファイル存在?}
    H -->|Yes| I[CreateKeySound]
    H -->|No| J[エラーダイアログ]
    I --> K{成功?}
    K -->|Yes| L[ハッシュマップに格納]
    K -->|No| M[エラーダイアログ, 0を格納]
    L --> N[プレイ中: getKeySoundFromLibrary]
    N --> O[PlayKeySound]
    O --> P[clearKeySoundLibrary]
    P --> Q[全KeySoundをDestroyKeySound]
    Q --> R[HashClear]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-92-01 | 重複読み込み防止 | 同一キー音名のキー音は1度のみ読み込む | loadKeySoundToLibrary呼び出し時 |
| BR-92-02 | パス解決 | 拡張子なしの名前はse\note\ディレクトリの.wavファイルとして解決 | getFilePathByKeySoundName内 |
| BR-92-03 | バージョン別ポリフォニー | ver>=171の場合は最大同時発音数1、それ以外は10 | getMaxPolyphonyForVersion内 |
| BR-92-04 | onexit自動クリア | アプリケーション終了時にclearKeySoundLibraryが自動呼び出し | clearKeySoundLibraryにonexit修飾子 |

### 計算ロジック

- 最大同時発音数の決定: `if (ver >= 171) return 1 else return 10`

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

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

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

| 操作 | 対象 | 操作種別 | 概要 |
|-----|------|---------|------|
| キー音読み込み | se\note\*.wav | READ | 音声ファイルの読み込み |
| キー音読み込み | 譜面ディレクトリ内の音声ファイル | READ | カスタムキー音の読み込み |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| N/A | ファイル未発見 | soundFilePathのファイルが存在しない(strsize<=0) | エラーダイアログ表示 |
| N/A | 読み込みエラー | CreateKeySoundが失敗(false返却) | エラーダイアログ表示、ポインタ0を格納 |
| N/A | 未登録参照 | getKeySoundFromLibraryで未登録のキー音名を参照 | エラーダイアログ表示、0を返却 |

### リトライ仕様

特になし。

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

特になし。

## パフォーマンス要件

ハッシュマップによるO(1)のキー音検索を実現し、プレイ中のリアルタイム再生に対応する。

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

特になし。

## 備考

- key_soundモジュールはHSP3の#moduleディレクティブでモジュール化されており、内部変数(s_keySoundLibrary等)はカプセル化されている
- alib_hash.asのHashInitInt/HashCheckKey/HashClearを使用した辞書実装

---

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

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

### 推奨読解順序

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

キー音ライブラリの内部データ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | key_sound.as | `src/audio/key_sound.as` | s_keySoundLibraryハッシュマップの構造(6行目: HashElemマクロ定義) |
| 1-2 | alib_hash.as | `third_party/alib_hash.as` | ハッシュマップの基盤実装(HashInitInt/HashCheckKey/HashClear) |
| 1-3 | ksmcore.as | `src/ksmcore.as` | CreateKeySound/DestroyKeySound/PlayKeySoundのDLL関数定義(9-11行目) |

**読解のコツ**: HSP3の`#define ctype`はC言語のマクロに類似した型付きマクロ定義。`s_keySoundLibrary(%1)`は`HashElem(s_keySoundLibrary, %1)`に展開される。

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

モジュール初期化がエントリーポイントとなる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | key_sound.as | `src/audio/key_sound.as` | initKeySoundModule(13-20行目): dir_defaultの保存とハッシュマップ初期化 |

**主要処理フロー**:
1. **14行目**: s_dirDefaultにdir_defaultを保存
2. **18行目**: HashInitIntでハッシュマップを整数値型で初期化
3. **19行目**: HashClearでハッシュマップをクリア

#### Step 3: キー音読み込み処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | key_sound.as | `src/audio/key_sound.as` | getFilePathByKeySoundName(29-34行目): 名前からファイルパスを解決 |
| 3-2 | key_sound.as | `src/audio/key_sound.as` | getMaxPolyphonyForVersion(41-46行目): バージョン別同時発音数 |
| 3-3 | key_sound.as | `src/audio/key_sound.as` | loadKeySoundToLibrary(56-75行目): メイン読み込み処理 |

**主要処理フロー**:
- **30-31行目**: 拡張子がない場合は`se\note\{name}.wav`パスを構築
- **58行目**: HashCheckKeyで重複チェック（0=未登録）
- **60-61行目**: ファイルパス解決と存在チェック
- **68行目**: CreateKeySoundでKeySoundオブジェクト生成
- **69行目**: ハッシュマップに格納

#### Step 4: キー音取得・クリア処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | key_sound.as | `src/audio/key_sound.as` | keySoundExistsInLibrary(82-83行目): 存在確認 |
| 4-2 | key_sound.as | `src/audio/key_sound.as` | getKeySoundFromLibrary(90-96行目): ポインタ取得 |
| 4-3 | key_sound.as | `src/audio/key_sound.as` | clearKeySoundLibrary(101-108行目): 全クリア(onexit) |

**主要処理フロー**:
- **91行目**: HashCheckKeyで存在確認(0=未登録→エラー)
- **96行目**: ハッシュマップからポインタを返却
- **102-106行目**: 全エントリをループし、DestroyKeySoundで破棄

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

```
プレイ画面初期化
    |
    +-- initKeySoundModule(dir_default)
    |       └── HashInitInt / HashClear
    |
    +-- loadKeySoundToLibrary(name, dir, ver)  ※譜面のキー音定義ごと
    |       +-- getFilePathByKeySoundName(name, dir)
    |       +-- getMaxPolyphonyForVersion(ver)
    |       └── CreateKeySound(path, maxPoly, &ptr)
    |
    +-- getKeySoundFromLibrary(name)  ※プレイ中のキー音再生時
    |       └── HashCheckKey → ポインタ返却
    |
    +-- clearKeySoundLibrary()  ※onexit時
            +-- DestroyKeySound(ptr)  ※各エントリ
            └── HashClear
```

### データフロー図

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

譜面のキー音名 ──────> getFilePathByKeySoundName ───> ファイルパス
                            |
se\note\*.wav  ──────> loadKeySoundToLibrary ──────> s_keySoundLibrary
(音声ファイル)              |                        (ハッシュマップ)
                            |
                      CreateKeySound(ksmcore.dll)
                            |
                            v
プレイ中の入力 ──────> getKeySoundFromLibrary ────> KeySoundポインタ
                            |
                      PlayKeySound(ksmcore.dll) ──> スピーカー出力
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| key_sound.as | `src/audio/key_sound.as` | ソース | キー音ライブラリ管理モジュール本体 |
| alib_hash.as | `third_party/alib_hash.as` | ソース | ハッシュマップデータ構造の実装 |
| ksmcore.as | `src/ksmcore.as` | ソース | CreateKeySound/DestroyKeySound/PlayKeySoundのDLL定義 |
| ksmcore.dll | `ksmcore.dll` | DLL | キー音オブジェクトの生成・破棄・再生の実装 |
| se\note\*.wav | `se/note/` | 音声ファイル | デフォルトのキー音ファイル群 |
