body method

  1. @override
String body(
  1. String baseName,
  2. String className
)
override

Defines the actual body code. path is passed relative to lib, baseName is the filename, and className is the filename converted to Pascal case.

実際の本体コードを定義します。pathlibからの相対パス、baseNameにファイル名が渡され、classNameにファイル名をパスカルケースに変換した値が渡されます。

Implementation

@override
String body(String baseName, String className) {
  return """
`Text-To-Speech`は下記のように利用する。

## 概要

$excerpt

## 設定方法

### katana.yamlを使用する場合(推奨)

1. `katana.yaml`に下記の設定を追加。

  ```yaml
  # katana.yaml

  # Describe the settings for using speech synthesis.
  # 音声合成による発話を利用するための設定を記述します。
  text_to_speech:
    enable: true
  ```

2. 下記のコマンドを実行して設定を適用。

  ```bash
  katana apply
  ```

3. `lib/adapter.dart`の`masamuneAdapters`に`TextToSpeechMasamuneAdapter`を追加。

  ```dart
  // lib/adapter.dart

  /// Masamune adapter.
  ///
  /// The Masamune framework plugin functions can be defined together.
  // TODO: Add the adapters.
  final masamuneAdapters = <MasamuneAdapter>[
      const UniversalMasamuneAdapter(),

      // Text-To-Speechのアダプターを追加。
      const TextToSpeechMasamuneAdapter(
        defaultLocale: Locale('ja', 'JP'),                         // デフォルト言語(必須)
        defaultSpeechRate: 0.5,                                    // 話速(0.0-1.0、デフォルト: 1.0)
        defaultVolume: 1.0,                                        // 音量(0.0-1.0、デフォルト: 1.0)
        defaultPitch: 1.0,                                         // ピッチ(0.0-1.0、デフォルト: 1.0)
        defaultIosAudioCategory: TextToSpeechIosAudioCategory.playback,  // iOSオーディオカテゴリ
        defaultIosAudioCategoryOptions: [
          TextToSpeechIosAudioCategoryOptions.mixWithOthers,       // 他のオーディオとミックス
        ],
      ),
  ];
  ```

### 手動でパッケージを追加する場合

1. パッケージをプロジェクトに追加。

  ```bash
  flutter pub add masamune_text_to_speech
  ```

2. `lib/adapter.dart`の`masamuneAdapters`に`TextToSpeechMasamuneAdapter`を追加。

  ```dart
  // lib/adapter.dart

  /// Masamune adapter.
  ///
  /// The Masamune framework plugin functions can be defined together.
  // TODO: Add the adapters.
  final masamuneAdapters = <MasamuneAdapter>[
      const UniversalMasamuneAdapter(),

      // Text-To-Speechのアダプターを追加。
      const TextToSpeechMasamuneAdapter(
        defaultLocale: Locale('ja', 'JP'),                         // デフォルト言語(必須)
        defaultSpeechRate: 0.5,                                    // 話速(0.0-1.0、デフォルト: 1.0)
        defaultVolume: 1.0,                                        // 音量(0.0-1.0、デフォルト: 1.0)
        defaultPitch: 1.0,                                         // ピッチ(0.0-1.0、デフォルト: 1.0)
        defaultIosAudioCategory: TextToSpeechIosAudioCategory.playback,  // iOSオーディオカテゴリ
        defaultIosAudioCategoryOptions: [
          TextToSpeechIosAudioCategoryOptions.mixWithOthers,       // 他のオーディオとミックス
        ],
      ),
  ];
  ```

**英語を使用する場合**:

```dart
const TextToSpeechMasamuneAdapter(
defaultLocale: Locale('en', 'US'),
)
```

## 利用方法

### 基本的な使い方

`TextToSpeechController`を使用してテキストを読み上げ、再生を停止し、音声を照会します。

```dart
class TextReaderPage extends PageScopedWidget {
@override
Widget build(BuildContext context, PageRef ref) {
  final tts = ref.page.controller(TextToSpeechController.query());

  // ページ読み込み時に初期化
  ref.page.on(
    initOrUpdate: () {
      tts.initialize();
    },
  );

  return Scaffold(
    appBar: AppBar(title: const Text("Text to Speech")),
    body: Column(
      children: [
        TextField(
          onChanged: (text) {
            // 読み上げるテキストを保存
          },
        ),

        ElevatedButton(
          onPressed: () async {
            await tts.speak("こんにちは、Masamune!");
          },
          child: const Text("読み上げ"),
        ),

        ElevatedButton(
          onPressed: () async {
            await tts.stop();
          },
          child: const Text("停止"),
        ),
      ],
    ),
  );
}
}
```

### 音声パラメータの設定

ピッチ、話速、言語を動的に変更できます。

```dart
await tts.setLanguage("ja-JP");
await tts.setSpeechRate(0.6);
await tts.setPitch(1.2);
await tts.speak("こんにちは");
```

`availableLanguages()`と`availableVoices()`を使用して、ユーザーに選択UIを提供できます。

```dart
// 利用可能な言語を取得
final languages = await tts.getAvailableLanguages();
print("利用可能な言語: \$languages");

// 利用可能な音声を取得
final voices = await tts.getAvailableVoices();
for (final voice in voices) {
print("音声: \${voice.name}, 言語: \${voice.locale}");
}
```

### キューイングと完了

- `await tts.speak()`は再生が完了すると解決されます
- `tts.setQueueMode(QueueMode.queue)`を使用して複数の文をキューに入れます
- 音声が終了したときに反応するために`onComplete`コールバックを登録します

```dart
// キューモードで複数のテキストを読み上げ
await tts.setQueueMode(QueueMode.queue);
await tts.speak("最初の文章です。");
await tts.speak("次の文章です。");
await tts.speak("最後の文章です。");
```

### 再生状態の監視

コントローラーの状態を監視してUIを更新します:

```dart
// 読み上げ中かどうかを確認
if (tts.speaking) {
// 読み上げ中のUIを表示
}

// 初期化済みかどうかを確認
if (tts.initialized) {
// 初期化済みの場合の処理
}

// 変更を監視
tts.addListener(() {
// 状態が変更されたときの処理
});
```

### iOSのオーディオカテゴリ設定

iOSでは、他のオーディオとの混合を制御するためにオーディオカテゴリを設定できます:

```dart
const TextToSpeechMasamuneAdapter(
defaultLocale: Locale('ja', 'JP'),
defaultIosAudioCategory: TextToSpeechIosAudioCategory.playback,  // 再生カテゴリ
defaultIosAudioCategoryOptions: [
  TextToSpeechIosAudioCategoryOptions.mixWithOthers,  // 他のオーディオとミックス
  TextToSpeechIosAudioCategoryOptions.duckOthers,     // 他のオーディオを下げる
],
)
```

利用可能なカテゴリ:
- `playback`: バックグラウンド再生対応
- `ambient`: 他のオーディオとミックス(システム音声を消音しない)
- `playAndRecord`: 録音と再生の両方

### 実用的な例:テキストリーダー

```dart
class ArticleReaderPage extends PageScopedWidget {
@override
Widget build(BuildContext context, PageRef ref) {
  final tts = ref.page.controller(TextToSpeechController.query());
  final article = "長い記事のテキスト...";

  return Scaffold(
    appBar: AppBar(
      title: const Text("記事リーダー"),
      actions: [
        IconButton(
          icon: Icon(tts.speaking ? Icons.pause : Icons.play_arrow),
          onPressed: () async {
            if (tts.speaking) {
              await tts.stop();
            } else {
              await tts.speak(article);
            }
          },
        ),
      ],
    ),
    body: SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: Text(article),
    ),
    floatingActionButton: Column(
      mainAxisAlignment: MainAxisAlignment.end,
      children: [
        FloatingActionButton(
          heroTag: "speed_up",
          mini: true,
          child: const Icon(Icons.fast_forward),
          onPressed: () async {
            final currentRate = await tts.getSpeechRate();
            await tts.setSpeechRate((currentRate + 0.1).clamp(0.0, 2.0));
          },
        ),
        const SizedBox(height: 8),
        FloatingActionButton(
          heroTag: "speed_down",
          mini: true,
          child: const Icon(Icons.fast_rewind),
          onPressed: () async {
            final currentRate = await tts.getSpeechRate();
            await tts.setSpeechRate((currentRate - 0.1).clamp(0.0, 2.0));
          },
        ),
      ],
    ),
  );
}
}
```

### Tips

- iOSのオーディオカテゴリ(`playback`、`ambient`など)を設定して、他のオーディオとの混合を制御します
- `tts.initialize()`のエラーを適切に処理してください。特にWebでは権限が異なります
- 連続呼び出しで低レイテンシが必要な場合は、頻繁に使用するフレーズをキャッシュします
- `masamune_speech_to_text`と組み合わせて会話型インターフェースを構築できます
- 読み上げ中はユーザーに視覚的なフィードバック(進行状況バー、ハイライトなど)を提供してください
""";
}