Delphi で gRPC を使う Google Cloud Speech-to-Text

· コンポーネント

Speech-to-Text は、話された音声を書き起こされたテキストに変換します。Google Cloud はこれを gRPC サービスとして公開しており、sgcWebSockets Enterprise には汎用の TsgcGRPCClient の上に構築された型付き Speech クライアントが付属しているため、Delphi や C++Builder から直接音声を書き起こせます。いくつかのプロパティで認識リクエストを組み立て、gRPC 経由で送信し、文字起こしを読み取るだけです。外部ランタイムや手書きの protobuf は必要ありません。

仕組み

gRPC は HTTP/2 上でフレーミングされた Protocol Buffers メッセージなので、Speech クライアントはライブラリの他の部分と同じトランスポート上で動作します。TsgcHTTP2Clientspeech.googleapis.com:443 への TLS 接続を開き、その上で TsgcGRPCClient が gRPC のフレーミングとトレーラーを処理し、sgcGRPC_Google_Speech 内の型付き Speech メッセージがリクエストとレスポンスのシリアライズと解析を代行します。

Google Cloud API には認証が必要です。サービスアカウントで認証し、その JSON キーを短命なベアラートークンと交換して、そのトークンをすべての呼び出しで gRPC メタデータとして送信します。リクエスト自体は RecognitionConfig(言語、エンコーディング、サンプルレート)と音声(インラインのバイト列または Cloud Storage の URI のいずれか)で構成されます。サービスは 1 つ以上の結果を返し、各結果には信頼度スコア付きでランク付けされた文字起こし候補が含まれます。

サービスアカウントによる認証

Google Cloud クライアントは、サービスアカウントの JSON キーをベアラートークンに変換します。キーを読み込み、JWT プロパティを設定し、自己署名トークンが受け入れられるようにオーディエンスを Speech エンドポイントにバインドします。トークンが届いたら、それを gRPC クライアントの DefaultMetadataauthorization ヘッダーとして追加すると、すべての呼び出しに付随するようになります。

uses
  sgcHTTP2_Client, sgcGRPC_Client, sgcGRPC_Types,
  sgcHTTP_Google_Cloud, sgcGRPC_Google_Speech;

// service-account JWT authentication
Cloud.GoogleCloudOptions.Authentication := gcaJWT;
Cloud.GoogleCloudOptions.JWT.ClientEmail := ClientEmail;
Cloud.GoogleCloudOptions.JWT.PrivateKeyId := PrivateKeyId;
Cloud.GoogleCloudOptions.JWT.PrivateKey.Text := PrivateKey;
Cloud.GoogleCloudOptions.JWT.ProjectId := ProjectId;
// self-signed service-account JWT is audience-bound to the Speech endpoint
Cloud.GoogleCloudOptions.JWT.API_Endpoint := 'https://speech.googleapis.com/';

// once the token is acquired, send it on every gRPC call
GRPC.DefaultMetadata.Clear;
GRPC.DefaultMetadata.Add('authorization', 'Bearer ' + Token);

gRPC クライアントの接続

チャネルは HTTP/2 接続です。TsgcHTTP2Client を TLS 有効で Speech ホストに向け、それを gRPC コンポーネントの Client プロパティに割り当て、ワイヤのコンテンツタイプを選択します。Speech サービスは Protocol Buffers を話すため、圧縮なしの grpcProto を使用します。

HTTP2 := TsgcHTTP2Client.Create(nil);
HTTP2.Host := 'speech.googleapis.com';
HTTP2.Port := 443;
HTTP2.TLS  := True;

GRPC := TsgcGRPCClient.Create(nil);
GRPC.Client := HTTP2;
GRPC.ChannelOptions.ContentType := grpcProto;
GRPC.ChannelOptions.Compression := grpcNoCompression;

HTTP2.Active := True;

音声の認識と文字起こしの読み取り

書き起こすには TsgcGRPCSpeechRecognizeRequest を構築します。Config に言語コード、エンコーディング、サンプルレートを設定し、Audio.Uri を Cloud Storage オブジェクトに向ける(またはインラインのバイト列で Audio.Content を設定する)か、そして google.cloud.speech.v1.Speech サービスの Recognize を呼び出します。リクエストは ToBytes で自身をシリアライズし、レスポンスは型付きレスポンスに解析されるので、結果と候補を順に処理できます。

var
  oRequest: TsgcGRPCSpeechRecognizeRequest;
  oResponse: TsgcGRPCResponse;
  oSpeech: TsgcGRPCSpeechRecognizeResponse;
  oResult: TsgcGRPCSpeechRecognitionResult;
  oAlt: TsgcGRPCSpeechRecognitionAlternative;
  i, j: Integer;
begin
  oRequest := TsgcGRPCSpeechRecognizeRequest.Create;
  try
    oRequest.Config.Encoding := 0;             // 0 = ENCODING_UNSPECIFIED, let the API detect
    oRequest.Config.SampleRateHertz := 16000;
    oRequest.Config.LanguageCode := 'en-US';
    oRequest.Config.EnableAutomaticPunctuation := True;
    oRequest.Audio.Uri := 'gs://my-bucket/audio.flac';

    oResponse := GRPC.Call('google.cloud.speech.v1.Speech', 'Recognize',
      oRequest.ToBytes);
  finally
    oRequest.Free;
  end;

  if oResponse.StatusCode <> grpcOK then
  begin
    ShowMessage('gRPC error: ' + oResponse.StatusMessage);
    Exit;
  end;

  oSpeech := TsgcGRPCSpeechRecognizeResponse.Create;
  try
    oSpeech.LoadFromBytes(oResponse.Data);
    for i := 0 to oSpeech.ResultCount - 1 do
    begin
      oResult := oSpeech.ResultItem(i);
      for j := 0 to oResult.AlternativeCount - 1 do
      begin
        oAlt := oResult.Alternative(j);
        Memo1.Lines.Add('Transcript: ' + oAlt.Transcript);
        Memo1.Lines.Add('Confidence: ' + FloatToStr(oAlt.Confidence));
      end;
    end;
  finally
    oSpeech.Free;
  end;
end;

認識の設定

リクエストの Config オブジェクトは、Google の RecognitionConfig メッセージに直接対応します。言語とサンプルレートに加えて、ランク付けされた候補を要求する MaxAlternatives、不適切な単語をマスクする ProfanityFilter、マルチチャンネル音声用の AudioChannelCount、読みやすい出力にする EnableAutomaticPunctuation、調整済みの認識モデルを選ぶ Model を設定できます。各プロパティは省略可能で、設定された場合にのみワイヤ上に出力されるため、必要なものだけを送信できます。

インライン音声または Cloud Storage URI

短いクリップはリクエスト内に含めて送れます。生の音声バイト列を Audio.Content に割り当てると、クライアントがそれを protobuf に埋め込みます。長いファイルの場合は、代わりに音声をバケットにアップロードし、Audio.Urigs:// パスを設定します。これによりリクエストを小さく保ち、Google がオブジェクトを直接読み取れます。両者は排他的で、特定のリクエストでは一方のみを設定します。

結果と候補

レスポンスは結果のリストで、認識された音声のセグメントごとに 1 つずつあります。各結果には尤度順に並べられた 1 つ以上の候補が含まれ、最も可能性の高い文字起こしが先頭に、0 から 1 の間の Confidence スコアが付きます。ResultCountAlternativeCount を反復してすべてを読み取るか、最良の推定として最初の結果の最初の候補を単純に取得します。型付きヘルパーが protobuf の解析を行うので、ふつうの Delphi の文字列や浮動小数点数を扱えます。

提供状況

型付き Speech-to-Text gRPC クライアントは sgcWebSockets Enterprise エディションの一部で、Windows、macOS、Linux、iOS、Android で動作します。この記事のもとになった、すぐに実行できるサンプルは Demos\21.GRPC\11.Speech_to_Text にあり、完全なリファレンスは gRPC Client 製品ページにあります。

ご質問やご意見はありますか? お問い合わせください。コードを書いた本人から返信いたします。