💬

Google Text-to-Speech APIのPCMフォーマット

に公開

Google Cloud Platform (GCP)のText-to-Speech (TTS) APIを使用して、DartでPCMフォーマットの音声ファイルを生成する方法を紹介します。

特に新規性は無いのですが、LINEAR16(PCM)を要求したときに送られてくるコンテナフォーマットはWAVである、という点は落とし穴です。

pubspec.yaml

空のプロジェクトを作成し、以下の依存関係を追加してください。

pubspec.yaml
dependencies:
  googleapis_auth: ^1.4.1
  http: ^1.1.0

追加したら、dart pub getを実行して依存関係を取得します。

main.dart

プログラムはシンプルです。サービス・クレデンシャルの保存場所を環境変GOOGLE_APPLICATION_CREDENTIALSから読み込み、APIにリクエストを送信し、レスポンスをファイルに保存します。

main.dart
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:googleapis_auth/auth_io.dart';
import 'dart:convert';

// GCP Text-to-Speech API Endpoint
const String _ttsApiEndpoint =
    'https://texttospeech.googleapis.com/v1/text:synthesize';

// Scope of the auth platform
const List<String> _scopes = ['https://www.googleapis.com/auth/cloud-platform'];

Future<void> synthesizeTextToPcm(String text, String outputFilename) async {
  try {
    // Get service account credentials from environment variable
    // Make sure to set the environment variable before running the script
    // Example: export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/service-account-file.json"

    late String serviceAccountJson;
    try {
      serviceAccountJson = File(
        Platform.environment['GOOGLE_APPLICATION_CREDENTIALS']!,
      ).readAsStringSync();
    } catch (e) {
      print('Error reading service credential file from local file system: $e');
      print('Check the environment variable GOOGLE_APPLICATION_CREDENTIALS.');
      return;
    }

    late AccessCredentials credentials;
    try {
      credentials = await obtainAccessCredentialsViaServiceAccount(
      ServiceAccountCredentials.fromJson(jsonDecode(serviceAccountJson)),
      _scopes,
      http.Client(),
      );
    } catch (e) {
      print('Error obtaining access privilege from server: $e');
      print('Check whether the credential file is valid or not.');
      return;
    }

    // Request body for the TTS API
    final Map<String, dynamic> requestBody = {
      'input': {'text': text},
      'voice': {
        'languageCode': 'ja_JP', 'ssmlGender': 'MALE',
      },
      'audioConfig': {
        'audioEncoding': 'LINEAR16', // PCM
        'sampleRateHertz': 24000,
        'speakingRate': 1.0,
      },
    };

    // HTTP POST request to the TTS API
    final response = await http.post(
      Uri.parse(_ttsApiEndpoint),
      headers: {
        'Authorization': 'Bearer ${credentials.accessToken.data}',
        'Content-Type': 'application/json; charset=utf-8',
      },
      body: jsonEncode(requestBody),
    );

    if (response.statusCode == 200) {
      // Parse the response
      final Map<String, dynamic> responseData = jsonDecode(response.body);
      final String audioContentBase64 = responseData['audioContent'];

      // Decode the base64 audio content and write to file
      final file = File(outputFilename);
      await file.writeAsBytes(base64Decode(audioContentBase64));

      print('Audio file is saved in $outputFilename.');
    } else {
      print('Failed to request API : ${response.statusCode}');
      print('Error response: ${response.body}');
    }
  } catch (e) {
    print('Unhandled Error: $e');
  }
}

void main() async {
  final outputFileName = 'dart_output.wav';
  final textToConvert = 'こんにちは。';

  await synthesizeTextToPcm(textToConvert, outputFileName);
}

サービス・クレデンシャルのJSONファイルを作成し、環境変数GOOGLE_APPLICATION_CREDENTIALSにそのパスを設定してください。

export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/service-account-file.json"

プログラムを実行すると、dart_output.wavというファイルが生成されます。オーディオ信号のエンコード形式はPCMです。しかしWAVコンテナに格納されているため、そのままWAVファイルとして再生できます。

LINEAR16(PCM)のコンテナ形式がWAVであることは、公式ドキュメントであるCloud Text-to-Speech basicsの冒頭に明記されています。

しかしながら、提供されるサンプル・コードはデータ形式がMP3であること、GeminiにPCMの例を訪ねると、拡張子を.pcmとして保存するコードを生成することから、「英語ドキュメントを最初から最後まで読んでからコーディングする人」以外は足を取られる可能性があります。

Discussion