[Angular] 使用 AWS Transcribe Streaming 实现文字转录的示例代码
首先
如题所述,这是一个使用Angular实现实时转录的样例,使用了AWS Transcribe Streaming。
经过多次试验和错误,我查找并尝试了一些内容,比如“开关麦克风”、“音频流的创建方法”、“AWS SDK 的使用方法”等等。我想把它们记录下来,作为一篇文章留存下来。如果能够对遇到类似问题的人提供一些帮助,我将感到非常幸运。
如果有更好的方法或者对文章中的解释、代码有错误的地方,请您提出指正,将不胜感激。
环境
在Angular v14上进行确认
$ ng version
Angularv14.2.12同上TypeScriptv4.7.4同上Node.jsv16.19.0$ node --version
npmv8.19.3$ npm --version
@aws-sdk/client-transcribe-streamingv3.226.0package.json で確認microphone-streamv6.0.1同上processv0.11.10同上bufferv6.0.3同上$ ng version_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | ‘_ \ / _` | | | | |/ _` | ‘__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 14.2.10
Node: 16.19.0
包管理器: npm 8.19.3
操作系统: darwin x64
Angular: 14.2.12
… 动画,通用,编译器,编译器-cli,核心,表单
… 平台浏览器,平台浏览器动态,路由器
包 版本
———————————————————
@angular-devkit/architect 0.1402.10
@angular-devkit/build-angular 14.2.10
@angular-devkit/core 14.2.10
@angular-devkit/schematics 14.2.10
@angular/cdk 14.2.7
@angular/cli 14.2.10
@angular/material 14.2.7
@schematics/angular 14.2.10
rxjs 6.6.7
typescript 4.7.4
在Angular v13进行确认
$ ng --version
Angularv13.2.3同上TypeScriptv4.5.5同上Node.jsv14.17.0$ node --version
npmv6.14.13$ npm --version
@aws-sdk/client-transcribe-streamingv3.95.0package.json で確認microphone-streamv6.0.1同上processv0.11.10同上bufferv6.0.3同上$ ng version_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | ‘_ \ / _` | | | | |/ _` | ‘__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 13.2.4
Node: 14.17.0
包管理器: npm 6.14.13
操作系统: darwin x64
Angular: 13.2.3
… animations, common, compiler, compiler-cli, core, forms
… platform-browser, platform-browser-dynamic, router
包 版本
———————————————————
@angular-devkit/architect 0.1302.4
@angular-devkit/build-angular 13.2.4
@angular-devkit/core 13.2.4
@angular-devkit/schematics 13.2.4
@angular/cli 13.2.4
@schematics/angular 13.2.4
rxjs 6.6.0
typescript 4.5.5
先决条件
使用AWS Transcribe Streaming进行语音识别和文字转录时,我们将使用以下的SDK和库。
@aws-sdk/client-transcribe-streaming
npm は こちら
microphone-stream
npm は こちら
process
npm は こちら
buffer
npm は こちら
事前准备
为了后续组件的实现,我们将在此之前进行以下操作。
-
- 以下是对上述内容的中文本地化翻译:
安装 @aws-sdk/client-transcribe-streaming
(从 npm 页面复制并粘贴)
$ npm i @aws-sdk/client-transcribe-streaming
安装 microphone-stream
(从 npm 页面复制并粘贴)
$ npm i microphone-stream
安装 process
(从 npm 页面复制并粘贴)
$ npm i process
安装 buffer
(从 npm 页面复制并粘贴)
$ npm i buffer
编辑 polyfills.ts
// 解决 “global is not defined” 的问题
(window as any).global = window;
// 参考:https://stackoverflow.com/questions/50313745/angular-6-process-is-not-defined-when-trying-to-serve-application
// 参考:https://www.npmjs.com/package/process
import * as process from ‘process’;
window.process = process;
// 参考:https://github.com/isaacs/core-util-is/issues/27
// 参考:https://www.npmjs.com/package/buffer
import * as buffer from ‘buffer’;
(window as any).Buffer = buffer.Buffer;
实施
模板
这只是一个简单的结构,可以控制音频识别的开始和结束,并输出转录的信息。
<div>
<div class="title">
<h2 class="h2-style">{{title}}</h2>
</div>
<div>
<button type="button" class="event-button event-button-w-100" (click)="startVoiceRecognition($event)">音声認識-開始</button>
<button type="button" class="event-button event-button-w-100" (click)="stopVoiceRecognition($event)">音声認識-終了</button>
</div>
<div class="output-area">
<textarea class="output-text" readonly placeholder="AWS Transcribe Streaming で文字変換された情報が出力されます."></textarea>
</div>
</div>
主要部分
使用AWS SDK进行语音识别和转录的示例代码。
请参考代码中的注释以获取有关每个操作的详细说明。
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable prefer-arrow/prefer-arrow-functions */
/* eslint-disable @typescript-eslint/naming-convention */
import { Component, ElementRef, OnInit } from '@angular/core';
// AWS Transcribe Streaming を使った文字起こしに必要なライブラリ群
// https://github.com/aws/aws-sdk-js-v3/tree/d8475f8d972d28fbc15cd7e23abfe18f9eab0644/clients/client-transcribe-streaming
import {
TranscribeStreamingClient,
StartStreamTranscriptionCommand,
StartStreamTranscriptionCommandInput,
LanguageCode,
MediaEncoding,
StartStreamTranscriptionCommandOutput,
} from '@aws-sdk/client-transcribe-streaming';
// AWS Transcribe Streaming に流す audio データを作るのに必要
// https://github.com/microphone-stream/microphone-stream#readme
const MicrophoneStream = require('microphone-stream').default;
let micStream: any = null;
@Component({
selector: 'app-use-aws-transcribe-streaming',
templateUrl: './use-aws-transcribe-streaming.component.html',
styleUrls: ['../../../style/common.css', './use-aws-transcribe-streaming.component.css'],
})
export class UseAwsTranscribeStreamingComponent implements OnInit {
title = 'AWS Transcribe Streaming を使ったサンプル';
private outputArea: any = null;
// AWS Transcribe Streaming を使うための準備
// この処理でクライアントインスタンスが生成される
// ここで生成したインスタンスは後述の処理で AWS Transcribe Streaming にコマンドを送る際に使用する
//
// サンプルコードなので credentials をハードコーディングしているがセキュリティ推奨されない
// Cognito 認証と絡める等、別の手段で認証を通すことを検討するべき
private client = new TranscribeStreamingClient({
region: 'ap-northeast-1',
credentials: {
accessKeyId: 'hogehoge',
secretAccessKey: 'hogehoge',
// sessionToken: 'hogehoge', // ここは必要に応じて設定する
},
});
constructor(private elementRef: ElementRef) {}
ngOnInit() {
this.outputArea = this.elementRef.nativeElement.querySelector('.output-text');
}
/**
* html テンプレートの「音声認識-開始」がクリックされたら実行されるメソッド
* 音声認識処理の起点
*/
async startVoiceRecognition(event: any) {
// micStream は後で出てくる `stopVoiceRecognition` で `stop()` を実行して音声認識を停止する
// このとき実際には AudioContext.close() が実行されているのだが、これを実行すると micStream の再利用ができなくなる
// なので、一度停止した場合は micStream のインスタンスを新規に生成してやる必要がある
//
// AudioContext.close() については下記を参照
// https://developer.mozilla.org/ja/docs/Web/API/AudioContext/close
if (!micStream) {
micStream = new MicrophoneStream();
}
await this.setStream();
// AWS Transcribe Streaming に流す音声データのパラメータ
// 肝は `AudioStream: this.audioStream()` の部分。ここで音声データを作っている
const params: StartStreamTranscriptionCommandInput = {
// https://docs.aws.amazon.com/ja_jp/transcribe/latest/dg/API_streaming_StartStreamTranscription.html#API_streaming_StartStreamTranscription_RequestSyntax
LanguageCode: LanguageCode.JA_JP,
MediaSampleRateHertz: 44_100, // 有効範囲: 最小値は 8,000. 最大値は 48,000
MediaEncoding: MediaEncoding.PCM,
AudioStream: this.audioStream(),
// VocabularyName: 'custom_vocabulary' // カスタム語彙を指定する場合はここを設定する
};
// 音声自動文字起こし機能
const command = new StartStreamTranscriptionCommand(params);
let response: StartStreamTranscriptionCommandOutput;
try {
// ここまでの処理で作成したクライアントインスタンスと音声データのパラメータから AWS Transcribe Streaming を実行する
// `handleResponse()` はレスポンスから文字起こしをするための処理
response = await this.client.send(command);
await this.handleResponse(response);
} catch (error: any) {
console.dir(error);
}
}
/**
* AWS Transcribe Streaming からのレスポンスを解析し文字起こしを行う
*
* 実装の大まかな部分は AWS Transcribe Streaming SDK のサンプルコードをコピーしたもの
* -> https://github.com/aws/aws-sdk-js-v3/tree/d8475f8d972d28fbc15cd7e23abfe18f9eab0644/clients/client-transcribe-streaming#handling-text-stream
*
* レスポンスの構成については下記を参照
* -> https://docs.aws.amazon.com/ja_jp/transcribe/latest/dg/API_streaming_StartStreamTranscription.html#API_streaming_StartStreamTranscription_ResponseSyntax
* */
async handleResponse(response: StartStreamTranscriptionCommandOutput) {
for await (const event of response.TranscriptResultStream!) {
if (event.TranscriptEvent) {
const results = event.TranscriptEvent.Transcript!.Results;
let transcript = '';
results!
.filter((result) => !result.IsPartial) // 変換途中 は処理対象外とする
.map((result) => {
(result.Alternatives || []).map((alternative) => {
transcript = alternative.Items!.map((item) => item.Content).join(' ');
// 変換したデータは html のテキストエリアに出力する
this.outputArea.innerHTML += transcript;
});
});
}
}
}
/**
* html テンプレートの「音声認識-終了」がクリックされたら実行されるメソッド
*
* 前掲の startVoiceRecognition() の説明でも触れたとおり、
* micStream.stop() を実行することで実際には AudioContext.close() が実行される
* これによって、これまで使用していた micStream のインスタンスは役目を終える
* 新たにストリームを流すためにはインスタンスを作り直す必要があるので、これを明示的に示すために
* micStream = null; を行い、startVoiceRecognition() では null 判定を行った上でインスタンスを生成している
*/
stopVoiceRecognition(event: any) {
micStream.stop();
micStream = null;
}
/**
* 前掲の startVoiceRecognition() で使用されているメソッド
* getUserMedia() では音声データのみを使用する設定でメディアの使用を要求し、
* マイクに入力された音声をストリームにセットしている
*/
private async setStream() {
micStream.setStream(
await window.navigator.mediaDevices.getUserMedia({
video: false,
audio: true,
})
);
}
/**
* こちらも前掲の startVoiceRecognition() で使用されているメソッド
*
* ストリームに流れてくる音声データを PCM にエンコードする処理で、
* 実装は AWS Transcribe Streaming SDK の サンプルコード をコピーした
* -> https://github.com/aws/aws-sdk-js-v3/tree/d8475f8d972d28fbc15cd7e23abfe18f9eab0644/clients/client-transcribe-streaming#acquire-from-browsers
*/
private audioStream = async function* () {
for await (const chunk of micStream) {
yield {
AudioEvent: { AudioChunk: pcmEncodeChunk(chunk) /* pcm Encoding is optional depending on the source */ },
};
}
};
}
/**
* PCM エンコード処理の実態
*
* 実装は AWS Transcribe Streaming SDK の サンプルコード をコピーしたもの
* -> https://github.com/aws/aws-sdk-js-v3/tree/d8475f8d972d28fbc15cd7e23abfe18f9eab0644/clients/client-transcribe-streaming#pcm-encoding
*/
function pcmEncodeChunk(chunk: Buffer) {
const input = MicrophoneStream.toRaw(chunk);
let offset = 0;
const buffer = new ArrayBuffer(input.length * 2);
const view = new DataView(buffer);
for (let i = 0; i < input.length; i++, offset += 2) {
const s = Math.max(-1, Math.min(1, input[i]));
view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
}
return Buffer.from(buffer);
}
处理的流程
下面的顺序进行处理,进行语音识别和转录成文字。
1. setStream
2. pcmEncodeChunk
3. audioStream
4. handleResponse
每个处理的具体步骤如下所示。
1. setStream で audio から流れる音声を stream に流し続けるための設定を行う
2. 以降は延々と stream に音声が流れ続ける
3. 流れてくる stream は pcmEncodeChunk で解析して
4. auditoStream で AWS Transcribe Streaming にわたす
5. 返ってきた情報を handleResponse で html に出力する
关于代码的补充说明
本文旨在以示例代码的形式,通过组件来使处理流程更易于理解。
-
- 「認証」
-
- 「音声ストリームの生成」
-
- 「SDKの実行」
-
- 「レスポンスの解析」
- 「HTMLへの出力」
我们正在进行一次性处理,但对于每个处理,有些最好委托给服务进行。如果您参考本代码,请根据需要进行评估。
在实际应用中,我们应该考虑使用另一种认证机制。
在上述代码示例中,硬编码传递给 SDK 的凭据是不安全的。考虑使用其他方法,例如与 Cognito 认证相关联来通过 SDK 进行身份验证,会更好。
以下是与Cognito身份验证相关的文档和教程。
-
- 認証されたユーザーで文字起こしアプリを構築する
- ブラウザスクリプトを準備します。
IAM角色的权限
如果通过SDK进行身份验证但权限未设置,将出现错误(AccessDeniedException)。
AccessDeniedException: {
"Message":"User: ${user情報} is not authorized to perform: transcribe:StartStreamTranscriptionWebSocket because no identity-based policy allows the transcribe:StartStreamTranscriptionWebSocket action"
}
因此,需要授予访问权限,但 AWS 提供的 Transcribe 策略只有以下两个选项。(截至2022年06月08日确认)

AmazonTranscribeFullAccess(フルアクセス)是权限过多的,并且AmazonTranscribeReadOnlyAccess(只读权限)没有用于转录流的权限。因此,在这里,我们创建了一个专用策略。(省略了策略创建的步骤)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "transcribe:StartStreamTranscriptionWebSocket",
"Resource": "*"
}
]
}
通过将此策略附加到使用 SDK 的 IAM 角色上,将授予执行 Transcribe Streaming 的权限。
自定义词汇
AWS Transcribe Streaming 是一个非常方便的实时转录服务,但是并不总是能够准确识别全部内容。
如果希望提高语音识别的准确度,可以事先在自定义词汇表中注册词汇进行处理。
我将从前述的代码中提取出指定自定义词汇的部分。
// AWS Transcribe Streaming に流す音声データのパラメータ
// 肝は `AudioStream: this.audioStream()` の部分。ここで音声データを作っている
const params: StartStreamTranscriptionCommandInput = {
// https://docs.aws.amazon.com/ja_jp/transcribe/latest/dg/API_streaming_StartStreamTranscription.html#API_streaming_StartStreamTranscription_RequestSyntax
LanguageCode: LanguageCode.JA_JP,
MediaSampleRateHertz: 44_100, // 有効範囲: 最小値は 8,000. 最大値は 48,000
MediaEncoding: MediaEncoding.PCM,
AudioStream: this.audioStream(),
// ★★★↓このパラメータを指定します★★★
VocabularyName: 'custom_vocabulary' // カスタム語彙を指定する場合はここを設定する
};
根据阅读,AWS似乎推荐使用表格格式而不是列表格式,尽管在自定义词汇中有表格和列表两种格式可供选择。
(节选)
词汇表与列表相比
强烈推荐使用词汇表。
关于自定义词汇的注册流程,我们将不再赘述,请参考以下内容。
- カスタム語彙の作成
请注意,正如上述文章中所提到的,如果您从控制台注册Table格式的文件,会导致错误。所以请确保通过S3进行注册Table格式的文件。
源代码
本文中所使用的代码是用于本次文章的操作确认。
请提供更多的上下文和要求,以便为您提供准确的中文表达。
-
- Amazon Transcribe が、日本語、韓国語、ブラジルポルトガル語のストリーミング機能に対応
-
- @aws-sdk/client-transcribe-streaming のブランチ README
-
- transcribe-streaming-app
-
- Angular 6 – process is not defined when trying to serve application
-
- Buffer is not defined #27
-
- AudioContext.close
StartStreamTranscription
リクエストの構文
レスポンスの構文
Custom vocabularies
Creating a custom vocabulary using a table
Creating a custom vocabulary using a list