# 画面設計書 4-音声認識画面

## 概要

本ドキュメントは、TF DemoアプリにおけるSpeechActivity（音声認識画面）の画面設計書である。マイクから音声を取得し、音声コマンド認識モデルで音声ラベルを検出・表示する画面について記述する。

### 本画面の処理概要

**業務上の目的・背景**：TensorFlowの音声認識モデルのオンデバイス推論をデモンストレーションするために本画面が必要である。TensorFlowの音声認識チュートリアル（https://www.tensorflow.org/tutorials/audio_training）で訓練されたモデルを使用し、マイクからリアルタイムで音声コマンドを認識する。認識結果はラベルリスト上でハイライトアニメーションにより表示される。

**画面へのアクセス方法**：Androidランチャーから直接起動される。AndroidManifest.xmlにてランチャーActivityとして登録されている。

**主要な操作・処理内容**：
1. マイクからの音声入力の取得（AudioRecord API、16kHz、モノラル、PCM16ビット）
2. 録音データのリングバッファへの格納（16000サンプル=1秒分）
3. TensorFlowInferenceInterfaceを通じたconv_actions_frozen.pbモデルによる音声コマンド認識推論
4. RecognizeCommandsによる認識結果の平滑化（直近500msの結果を平均化）
5. 認識されたラベルのListView上でのハイライトアニメーション表示
6. 終了ボタンによるアプリケーション終了

**画面遷移**：Androidランチャーから起動される。他の画面への遷移機能は持たない。終了ボタン押下時はプロセスを強制終了する。CameraActivityを継承しない独自のActivityである。

**権限による表示制御**：マイク権限（RECORD_AUDIO）が必要。Android M以上ではランタイム権限リクエストが実行される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 53 | 音声操作（Audio Operations） | 主機能 | マイクからの音声入力をAudioRecordで取得しPCM16ビットデータとして録音バッファに格納 |
| 81 | TFLite推論エンジン | 主機能 | TensorFlowInferenceInterfaceを通じてconv_actions_frozen.pbモデルによる音声コマンド認識推論を実行 |
| 62 | SavedModel保存 | 補助機能 | conv_actions_frozen.pbモデルファイルの読み込み（事前保存済みモデルの利用） |
| 17 | 活性化関数（Activation Functions） | 補助機能 | labels_softmaxノードによるSoftmax活性化関数で音声ラベルの確率分布を算出 |

## 画面種別

リアルタイム処理画面（音声入力＋認識結果表示）

## URL/ルーティング

Androidネイティブアプリのため、URLベースのルーティングは存在しない。パッケージ: `org.tensorflow.demo`、クラス: `SpeechActivity`。

## 入出力項目

| 項目名 | 入力/出力 | データ型 | 説明 |
|--------|----------|---------|------|
| 音声入力データ | 入力 | short[] (PCM16) | マイクから取得される16kHz/モノラル/PCM16ビットの音声データ |
| recordingBuffer | 中間 | short[16000] | 1秒分のリングバッファ |
| floatInputBuffer | 中間 | float[16000] | -1.0~1.0に正規化された浮動小数点音声データ |
| outputScores | 出力 | float[] | 各ラベルのSoftmaxスコア |
| RecognitionResult | 出力 | RecognizeCommands.RecognitionResult | 認識されたコマンド名と新規認識フラグ |

## 表示項目

| 項目名 | データ型 | 説明 |
|--------|---------|------|
| ラベルリスト | ListView (list_view) | 認識可能な音声ラベルのリスト（アンダースコアで始まるラベルを除外して表示） |
| 終了ボタン | Button (quit) | アプリケーション終了ボタン |

## イベント仕様

### 1-音声録音イベント（バックグラウンドスレッド）

onCreate時にrecordingThreadが開始され、マイクからの音声を継続的に録音する。

処理フロー:
1. AudioRecord.getMinBufferSize()でバッファサイズを推定
2. AudioRecordインスタンスを生成（DEFAULT音源、16kHz、MONO、PCM_16BIT）
3. record.startRecording()で録音開始
4. ループ内でrecord.read()によりaudioBufferにデータ取得
5. recordingBufferLockで排他制御しながらリングバッファ（recordingBuffer）にコピー
6. shouldContinue=falseになるまで継続

### 2-音声認識イベント（バックグラウンドスレッド）

onCreate時にrecognitionThreadが開始され、リングバッファのデータに対して定期的に推論を実行する。

処理フロー:
1. recordingBufferLockで排他制御しながらinputBufferにリングバッファの内容をコピー
2. short値を32767.0fで除算して-1.0~1.0のfloat値に変換
3. inferenceInterface.feed()でSAMPLE_RATE_NAMEとINPUT_DATA_NAMEにデータ入力
4. inferenceInterface.run()で推論実行（出力: labels_softmax）
5. inferenceInterface.fetch()でoutputScoresを取得
6. recognizeCommands.processLatestResults()で結果を平滑化
7. 新規コマンド認識時、UIスレッドでListViewの対応ラベルにハイライトアニメーションを適用
8. MINIMUM_TIME_BETWEEN_SAMPLES_MS（30ms）間スリープ

### 3-終了ボタン押下イベント

終了ボタン（quitButton）押下時にmoveTaskToBack(true)でアプリをバックグラウンドに移動し、Process.killProcess()とSystem.exit(1)でプロセスを強制終了する。

## データベース更新仕様

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

本画面はデータベースを使用しない。

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| なし | なし | なし | データベース操作なし |

## メッセージ仕様

| メッセージID | メッセージ内容 | 種別 | 表示条件 |
|-------------|--------------|------|---------|
| MSG-01 | "Audio Record can't initialize!" | エラー | AudioRecordの初期化に失敗した場合（ログ出力のみ） |
| MSG-02 | "Problem reading label file!" | エラー | ラベルファイルの読み込みに失敗した場合（RuntimeException） |

## 例外処理

| 例外状態 | 処理内容 |
|---------|---------|
| AudioRecord初期化失敗 | Log.e()でエラー出力し、record()メソッドからreturn |
| ラベルファイル読み込み失敗（IOException） | RuntimeExceptionをスロー |
| マイク権限未付与 | ランタイム権限リクエスト。権限付与後にstartRecording()/startRecognition()を再実行 |

## 備考

- モデルファイル: `conv_actions_frozen.pb`（assetsディレクトリに配置）
- ラベルファイル: `conv_actions_labels.txt`（assetsディレクトリに配置）
- サンプルレート: 16000Hz、録音長: 1000ms（16000サンプル）
- 認識パラメータ: AVERAGE_WINDOW_DURATION_MS=500, DETECTION_THRESHOLD=0.70, SUPPRESSION_MS=1500, MINIMUM_COUNT=3
- TFノード: 入力データ=`decoded_sample_data:0`、サンプルレート=`decoded_sample_data:1`、出力=`labels_softmax`
- レイアウト: `activity_speech.xml`
- CameraActivityを継承しない独自Activity（android.app.Activityを直接継承）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | RecognizeCommands.java | `tensorflow/tools/android/test/src/org/tensorflow/demo/RecognizeCommands.java` | RecognitionResult（foundCommand, isNewCommand）、processLatestResults()の平滑化ロジック |

**読解のコツ**: RecognizeCommandsは直近の認識結果を時間窓で平均化し、閾値以上で新規コマンドと判定する。SUPPRESSION_MSで同じコマンドの連続認識を抑制する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SpeechActivity.java | `tensorflow/tools/android/test/src/org/tensorflow/demo/SpeechActivity.java` | onCreate()（101-158行目）: UI初期化、ラベル読み込み、RecognizeCommands初期化、TFモデル読み込み、スレッド開始 |

**主要処理フロー**:
1. **104行目**: setContentView(R.layout.activity_speech)
2. **105-114行目**: quitButton初期化（終了処理）
3. **115行目**: labelsListView初期化
4. **119-134行目**: ラベルファイル読み込み、displayedLabels構築（_で始まるラベルは除外）
5. **137-139行目**: ArrayAdapterでListView設定
6. **142-149行目**: RecognizeCommands初期化
7. **152行目**: TensorFlowInferenceInterface初期化
8. **155-157行目**: マイク権限リクエスト→録音開始→認識開始

#### Step 3: 録音処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SpeechActivity.java | 同上 | record()（202-253行目）: AudioRecord設定、リングバッファへの書き込みロジック |

#### Step 4: 認識処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | SpeechActivity.java | 同上 | recognize()（279-353行目）: リングバッファ読み出し→正規化→TF推論→RecognizeCommands→UIハイライト |

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

```
SpeechActivity (extends Activity)
    |
    +-- onCreate()
    |       +-- setContentView(activity_speech)
    |       +-- ラベルファイル読み込み
    |       +-- RecognizeCommands()
    |       +-- TensorFlowInferenceInterface()
    |       +-- startRecording()
    |       +-- startRecognition()
    |
    +-- recordingThread: record()
    |       +-- AudioRecord()
    |       +-- record.startRecording()
    |       +-- [ループ] record.read() -> recordingBuffer
    |
    +-- recognitionThread: recognize()
            +-- [ループ]
                +-- recordingBuffer -> inputBuffer (lock)
                +-- short -> float正規化
                +-- inferenceInterface.feed(SAMPLE_RATE_NAME)
                +-- inferenceInterface.feed(INPUT_DATA_NAME)
                +-- inferenceInterface.run(labels_softmax)
                +-- inferenceInterface.fetch(OUTPUT_SCORES_NAME)
                +-- recognizeCommands.processLatestResults()
                +-- runOnUiThread: ハイライトアニメーション
```

### データフロー図

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

マイク音声 -------> AudioRecord -------> リングバッファ -------> 推論スレッド
(16kHz/Mono)       (PCM16ビット)       (short[16000])          |
                                                                v
                                                    short→float正規化
                                                    (-1.0 ~ 1.0)
                                                                |
                                                    TF feed/run/fetch
                                                    (conv_actions_frozen.pb)
                                                                |
                                                    outputScores(Softmax)
                                                                |
                                                    RecognizeCommands
                                                    (平滑化・閾値判定)
                                                                |
                                                    ListView ハイライト
                                                    (AnimatorSet)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SpeechActivity.java | `tensorflow/tools/android/test/src/org/tensorflow/demo/SpeechActivity.java` | ソース | 音声認識画面の主要Activity |
| RecognizeCommands.java | `tensorflow/tools/android/test/src/org/tensorflow/demo/RecognizeCommands.java` | ソース | 認識結果の平滑化処理 |
| TensorFlowInferenceInterface | `org.tensorflow.contrib.android` | ライブラリ | TensorFlow推論インターフェース |
| activity_speech.xml | `tensorflow/tools/android/test/res/layout/activity_speech.xml` | レイアウト | 音声認識画面レイアウト |
| list_text_item.xml | `tensorflow/tools/android/test/res/layout/list_text_item.xml` | レイアウト | ラベルリスト項目レイアウト |
| color_animation.xml | `tensorflow/tools/android/test/res/animator/color_animation.xml` | アニメーション | 認識時のハイライトアニメーション |
| conv_actions_frozen.pb | `assets/conv_actions_frozen.pb` | モデル | 音声コマンド認識モデル |
| conv_actions_labels.txt | `assets/conv_actions_labels.txt` | データ | 音声ラベルファイル |
