AzureのSpeechサービスのREST APIで音声ファイルの文字起こしをする

船井総研デジタルのよもぎたです。

Azure Cognitive ServicesのうちのSpeech ServiceのSpeech to Text REST APIで音声ファイルから文字起こしを試してみたので概要を共有します。

Speech ServiceのAPIで出来ることはたくさんあるのですが、まずは1つの音声ファイルを文字起こしすることをゴールといたします。
この記事ではAPIとのRESTのやり取りをまとめます。APIを利用して文字起こしするPythonのコードは次の記事にまとめるようにいたします。あわせてご覧いただけると幸いです。

:::note
この記事は2023年4月現在最新のv3.1 APIに基づいています。ご了承ください。
:::

# Speech ServiceのプランとAPIの選択について
文字起こしするAPIは``Speech to Text REST API``と``Speech to Text REST API for short audio``の2つがあります。このうち、``for short audio``は無料のF0プランで使えるのが魅力ですが、60秒までしか文字起こしできません。そこで、今回は従量課金のS0プランを選択します。

Speech Serviceのドキュメントは[こちら](https://learn.microsoft.com/ja-jp/azure/cognitive-services/Speech-Service/)、APIのリファレンスは[こちら](https://learn.microsoft.com/ja-JP/azure/cognitive-services/speech-service/rest-speech-to-text)になります。

# まず用意するもの
まず用意するものは、次の2つになります。

## Speech Serviceのリソース
Azure PortalからでもCLIからでも作成できます。ただ、CLIから行うには、[Speech CLI](https://learn.microsoft.com/ja-jp/azure/cognitive-services/speech-service/spx-overview)を追加インストールする必要があるそうなので、今回私はAzure Portalから作成しました。前述の通り、S0プランを選択します。

Azure Portalの検索バーで「音声サービス」で検索すればヒットします。

![speech-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822136/baa9371d-3e83-d049-45a3-9310ba45d4ba.png)

## 文字起こしする音声ファイル
私は今回256kbpsのモノラルのWAVEファイルを用意しました。
文字起こしはチャネルごとに行われるので、ひとつにまとめておいた方がラク、という考えからです。

# いざ、実際に文字起こししてみる
## APIを使って文字起こしをする大まかな流れ
1. 文字起こし対象のファイルのURL＋アルファをAPIにPOSTする。⇒UUIDが返されます。これ以降の進捗確認・文字起こし結果の取得をこのUUIDを基に行います。
1. 進捗（Status）を確認する。
    1. 正常系のステータスは``Running``か``Succeeded``になります。
    1. ``Succeeded``になると、あわせて文字起こし結果のJSONファイルのURLが返されるようになります。
1. 文字起こし結果のJSONファイルを取得します。
1. JSONファイルをパースして、あとは煮るなり焼くなり・・・。

## 文字起こしを開始する
[Create Transcription](https://eastus.dev.cognitive.microsoft.com/docs/services/speech-to-text-api-v3-1/operations/Transcriptions_Create) APIを使います。

指定できるパラメータは多数ありますが、1ファイルを文字起こしするならば下記の要領でOKです。

```
POST https://<region>.api.cognitive.microsoft.com/speechtotext/v3.1/transcriptions
Content-Type: application/json
Ocp-Apim-Subscription-Key: <API Key>

{
  "locale": "<日本語を指定するなら ja-JP>",
  "displayName": "<任意の displayName>",
  "contentUrls": [
    "<文字起こししたいファイルのURL>"
  ]
}
```

レスポンスで期待するステータスコードは200です。レスポンスボディのJSONで着目すべきポイントは次の2点です。

```
{
    "self": "https://<region>.api.cognitive.microsoft.com/speechtotext/v3.1/transcriptions/<UUID>",
---snip---
    "status": "Running or Succeeded",
---snip---
}
```

```"self": "https://.../<UUID>"``` のUUIDは、これ以降文字起こしの進捗や結果を問い合わせるのに必要になります。
```"status":...``` は文字通りステータスで、この値が ```Succeeded``` になるまで待つ必要があります。

## 文字起こしの進捗（ステータス）確認

~~進捗どうですか？~~

文字起こしの進捗確認には、[Get Transcription](https://eastus.dev.cognitive.microsoft.com/docs/services/speech-to-text-api-v3-1/operations/Transcriptions_Get) APIを使います。
リクエストはエンドポイントに先ほどのUUIDをつけてGETするだけです。

```
GET https://<region>.api.cognitive.microsoft.com/speechtotext/v3.1/transcriptions/<UUID>
Ocp-Apim-Subscription-Key: <API Key>
```

期待するステータスコードは200です。レスポンスボディのJSONで着目すべきポイントは次の1つです。

```
{
---snip---
    "status": "Running or Succeeded",
---snip---
}
```

ステータスが```Running```ならしばし待ち、```Succeeded```なら結果が生成されています。わかりやすいですね。

## 文字起こしの結果ファイルを取得する
めでたくステータスが```Succeeded```になったら、結果のJSONファイルが生成されているので、そのURLを取得します。[Get Transcription Files](https://eastus.dev.cognitive.microsoft.com/docs/services/speech-to-text-api-v3-1/operations/Transcriptions_ListFiles) APIを使います。

リクエストは次の通りです。

```
GET https://<region>.api.cognitive.microsoft.com/speechtotext/v3.1/transcriptions/<UUID>/files
Ocp-Apim-Subscription-Key: <API Key>
```

期待するステータスコードは200です。
レスポンスボディのJSONで着目するポイントは下記のとおりです。

```
{
    "values": [
        {
---snip---
            "kind": "Transcription",
---snip---
            "links": {
                "contentUrl": "https://<foo>.blob.core.windows.net/<bar>/TranscriptionData/<FileWithSAS>"
            }
        },
---snip---
    ]
}
```

```"values"``` の配列に最低2つの要素が入っています。このうち、 ```"kind": "TranscriptionReport"``` とある方は、文字通り文字起こしの結果のレポートで、文字起こしの結果本文は入っていません。 ```"kind": "Transcription"```となっている方の ```"contentUrl"``` でポイントされているファイルのほうに文字起こしの結果本文が入っているので、こちらを取得します。このURLはBLOBのSAS付きのURLですので、URLを取得するところからファイルの実体を取得するところまで、一連のアクションとして行うのがよさそうです。ファイルの取得は```contentUrl```で示されたURLをGETするだけです。ここに限り、API Keyは不要です。

```
GET https://<foo>.blob.core.windows.net/<bar>/TranscriptionData/<FileWithSAS>
```

## 文字起こしの結果をパースする
文字起こしの結果はJSONファイルで提供されます。ここで着目すべきポイントは次の通りです。

```
{
---snip---
  "combinedRecognizedPhrases": [
    {
      "channel": 0,
      "lexical": "実際に認識された単語が列挙されます",
      "itn": "lexicalの結果から逆正規化（Doctor -> Dr.など）した結果です",
      "maskedITN": "itnから不適切表現を除いた結果です",
      "display": "認識されたテキストの表示形式。 追加された句読点と大文字化が含まれます。"
    }
  ],
---snip---
}
```

この中で一番扱いやすいのは、```"display"``` で提供されている結果です。特にこだわりが無ければ、この結果を採用すればよいと思います。

## 文字起こしのタスクを削除する
あと始末です。[Delete Transcription](https://eastus.dev.cognitive.microsoft.com/docs/services/speech-to-text-api-v3-1/operations/Transcriptions_Delete) APIを使います。

```
DELETE https://<region>.api.cognitive.microsoft.com/speechtotext/v3.1/transcriptions/<UUID>
Ocp-Apim-Subscription-Key: <API KEY>
```

期待するステータスコードは204です。レスポンスボディはありません。

たまったタスクをまとめて削除するワンライナーもご紹介しておきます。これが生まれた背景は察してください。詮索はしないでください。

```bash
$ export APIKEY="<API KEY>"
$ curl -X GET -H "Ocp-Apim-Subscription-Key: ${APIKEY}" https://<region>.api.cognitive.microsoft.com/speechtotext/v3.1/transcriptions/ | jq '.values[].self' | tr -d '"' | awk -F'/' '$0=$NF' | while read TID; do curl -I -X DELETE -H "Ocp-Apim-Subscription-Key: ${APIKEY}" https://<region>.api.cognitive.microsoft.com/speechtotext/v3.1/transcriptions/${TID}; done
```

# 文字起こし、実際のところどうなの
ここまでREST APIの初歩的な使い方をご紹介してきましたが、皆さん気になるのは「使い物になるの？」というところだと思います。
日本語の音声の文字起こしを試してみましたが、「音⇒カナの文字起こしはいい感じだけれど、カナ⇒漢字の変換はダメ」というのが私の正直な感想です。どのような感じかというと、「カナ⇒漢字変換の際、前後の単語も考慮されておらず、無関係な単語に変換されてしまう」という状況でした。ニホンゴッテムズカシイ・・・。英語は割とよさそうだったのですが（自分のヒアリング能力に自信ないのであまりつっこみません）。

~~ネタとして~~ ためしに津軽弁の音声を試してみましたが、全く認識されませんでした。私も理解できませんでした。

日本語をターゲットに文字起こしサービス/機能を提供している会社さんはすごいんだな～と感じました。

まあ、Cognitive Serviceもこれから進歩していくでしょうし、なによりOpenAIがあります。Whisperに期待してます。

このAPIを利用したPythonのコードについては、連休明けに別記事にまとめるようにいたします。

最後までお読みいただきありがとうございました。