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 키를 단기 bearer 토큰으로 교환하고, 그 토큰을 모든 호출에 gRPC 메타데이터로 전송합니다. 요청 자체는 RecognitionConfig(언어, 인코딩, 샘플 레이트)와 오디오로 구성되며, 오디오는 인라인 바이트이거나 Cloud Storage URI입니다. 서비스는 하나 이상의 결과로 응답하며, 각 결과는 신뢰도 점수가 매겨진 순위화된 전사 대안을 담고 있습니다.

서비스 계정으로 인증하기

Google Cloud 클라이언트는 서비스 계정 JSON 키를 bearer 토큰으로 변환합니다. 키를 로드하고, JWT 속성을 설정하고, 자체 서명된 토큰이 수락되도록 audience를 Speech 엔드포인트에 바인딩합니다. 토큰이 도착하면 authorization 헤더로 gRPC 클라이언트의 DefaultMetadata에 추가하면, 이후 모든 호출에 따라갑니다.

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 연결입니다. TLS가 활성화된 TsgcHTTP2Client를 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)

요청의 Config 객체는 Google의 RecognitionConfig 메시지에 직접 매핑됩니다. 언어와 샘플 레이트 외에도, 순위화된 변형을 요청하는 MaxAlternatives, 불쾌한 단어를 가리는 ProfanityFilter, 다중 채널 오디오를 위한 AudioChannelCount, 읽기 쉬운 출력을 위한 EnableAutomaticPunctuation, 튜닝된 인식 모델을 선택하는 Model을 설정할 수 있습니다. 각 속성은 선택 사항이며 설정된 경우에만 와이어에 전송되므로, 필요한 것만 보내게 됩니다.

인라인 오디오 또는 Cloud Storage URI

짧은 클립은 요청 안에 담아 보낼 수 있습니다. 원시 오디오 바이트를 Audio.Content에 할당하면 클라이언트가 이를 protobuf에 임베드합니다. 더 긴 파일의 경우 오디오를 버킷에 업로드하고 대신 Audio.Urigs:// 경로로 설정하면, 요청이 작게 유지되고 Google이 객체를 직접 읽을 수 있습니다. 둘은 상호 배타적이므로 주어진 요청에서 둘 중 하나만 설정합니다.

결과와 대안

응답은 인식된 오디오 세그먼트당 하나씩, 결과의 목록입니다. 각 결과는 가능성에 따라 정렬된 하나 이상의 대안을 담고 있으며, 가장 가능성 높은 전사가 먼저 오고 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 제품 페이지에 있습니다.

질문이나 의견이 있으신가요? 문의하기. 코드를 작성한 사람들로부터 답변을 받게 됩니다.