---
title: RSocketを使ってCIのビルドの結果をlaptop上の音声合成で喋らす
tags: ["RSocket", "Spring Boot", "VoiceText4J", "Concourse"]
categories: ["Programming", "Java", "am", "ik", "voicetext4j"]
date: 2020-03-01T09:32:13Z
updated: 2020-04-03T14:19:55Z
---

CIの結果を音声合成で喋らせたいということがあったので実現方法を共有します。

音声合成ソフトはいろいろあるけど、今回はWeb APIで使える[VoiceText](https://cloud.voicetext.jp/webapi)を使いました。
そして、昔作った[VoiceText4J](/entries/278)を引っ張り出してきました。

まずはシンプルにこのVoiceText4JをSpring Boot経由で呼ぶだけの[syaberu](https://github.com/making/syaberu)というアプリを作りました。


![image](https://user-images.githubusercontent.com/106908/75622480-14ed7980-5be4-11ea-81b8-3cec8059dfc8.png)

### ローカルでSyaberuを実行
[こちら](https://oss.sonatype.org/content/repositories/snapshots/am/ik/lab/syaberu-server/0.0.1-SNAPSHOT/)から最新のjarファイルをダウンロードしてください。

```
java -jar syaberu-0.0.1*.jar
```

API Keyを[こちら](https://cloud.voicetext.jp/webapi/api_keys/new)から取得してください。

```
API_KEY=...
curl http://localhost:8080 -H "X-Api-Key: ${API_KEY}" -d text=こんにちは
```

`speaker`パラメータで話者を変えることもできます。

```
curl http://localhost:8080 -H "X-Api-Key: ${API_KEY}" -d text=こんにちは -d speaker=haruka
curl http://localhost:8080 -H "X-Api-Key: ${API_KEY}" -d text=こんにちは -d speaker=hikari
```

### インターネット経由でローカルのSyaberuを実行

ローカルで音声合成を鳴らすのは簡単にできました。しかし、任意のCIサーバーからビルド結果を鳴らすにはインターネットを経由してSyaberuにリクエストを送る必要があります。
このSyaberuをクラウドにデプロイしても意味がありません。音声が鳴るのはスピーカーがついている端末上で、かつ聞こえないと意味がないです。
なので、ローカルで起動しているSyaberuにインターネット経由でFirewallを超えてアクセスしてもらう必要があります。

まず思いつくのは[ngrok](https://ngrok.com/)を使う方法です。Syaberuの隣にngrokサーバーを立てて、ngrokが払い出すport/hostnameを経由して`localhost:8080`にアクセスさせることは可能です。
無料プランだとngrokのport/hostnameがrestartするたびに変わるのでCIに設定するには都合が悪いです。そこで、同じような仕組みを作りました。

ここで役に立つのが双方向プロトコルである[RSocket](https://rsocket.io/)です。RSocketはSpring Boot 2.2から簡単に利用できるようになっています。興味のある人はこちらの[資料](https://docs.google.com/presentation/d/1ygSM85-RQ3NZjCg6RaZ52mGzxbWiItVwzlCpr1vaWBw/edit)を見てください。

RSocketを使って、SyaberuのProxy Serverを作成し、これをクラウド上におき、CIからはクラウド上のProxyサーバーにアクセスしてもらいます。Syaberuは起動時にProxyとRSocketでつなぎに行きます。ProxyはHTTPでリクエストをもらったらボディをそのままRSocketでつながっているSyaberuに送信し、このリクエストをSyaberuはVoiceTextに送ることで音声合成を鳴らすことができます。

図に示すと次のようになります。

![image](https://user-images.githubusercontent.com/106908/75622753-33a13f80-5be7-11ea-8557-f6600097b13f.png)

ソースは[こちら](https://github.com/making/syaberu-rsocket-proxy)。

### Syaberu RSocket Proxyをローカルで実行

[こちら](https://oss.sonatype.org/content/repositories/snapshots/am/ik/lab/syaberu-rsocket-proxy/0.0.1-SNAPSHOT/)から最新のjarファイルをダウンロードしてください。

```
java -jar syaberu-rsocket-proxy-0.0.1*.jar
```

Syaberuの起動時に`syaberu.proxy-uri`にRSocket ProxyのURLを指定します。`syaberu.proxy-subscription-id`はチャネル名みたいなものです。

```
java -jar syaberu-0.0.1*.jar --syaberu.proxy-uri=http://localhost:8082/rsocket --syaberu.proxy-subscription-id=test 
```

これで次のようにProxy経由でSyaberuにリクエストを送れます。

```
curl http://localhost:8082/proxy/test -H "X-Api-Key: ${API_KEY}" -d text=こんにちは
```

### Syaberu RSocket ProxyをCloud Foundryで実行

Syaberu RSocket ProxyをCloud Foundryにデプロイする場合は`cf push`するだけです。

```
cf push syaberu-rsocket-proxy -p syaberu-rsocket-proxy-0.0.1*.jar
```

Syaberu RSocket ProxyはトランスポートレイヤにWebSocketを使っているため、[Pivotal Web Services](https://run.pivotal.io)を使う場合は8443ポートを経由してアクセスする必要があります。

お試し用として[https://syaberu-rsocket-proxy.cfapps.io:8443](https://syaberu-rsocket-proxy.cfapps.io:8443)にデプロイしてあるので、以下のように使えます。`syaberu.proxy-subscription-id`は被らないようにした方が良いです。

```
java -jar syaberu-0.0.1*.jar --syaberu.proxy-uri=https://syaberu-rsocket-proxy.cfapps.io:8443/rsocket --syaberu.proxy-subscription-id=test 
```

CIからは次のようにアクセスすれば良いです。

```
curl https://syaberu-rsocket-proxy.cfapps.io:8443/proxy/test -H "X-Api-Key: ${API_KEY}" -d text=こんにちは
```


### Concourseからビルド結果を喋らせる

Concourseから鳴らしたい場合、は`task`で`curl`を叩けば十分なのですが、`put`で鳴らしたい場合は、以前[Line Notify](/entries/411)に通知したのと同じような方法で実現できます。

サンプルパイプラインを貼っておきます。

```yaml
resource_types:
- name: http-api
  type: docker-image
  source:
    repository: aequitas/http-api-resource
resources:
- name: syaberu
  type: http-api
  source:
    uri: https://syaberu-rsocket-proxy.cfapps.io:8443/proxy/((syaberu_subscription_id))
    method: POST
    headers:
      X-Api-Key: ((voicetext_api_token)) 
    form_data:
      text: "{text}"
      speaker: "{speaker}"
      emotion: "{emotion}"

jobs:
- name: hello
  plan:
  - task: say-hello
    config:
      platform: linux
      image_resource:
        type: docker-image
        source:
          repository: alpine
      run:
        path: sh
        args:
        - -c
        - |
          echo "Hello"
          # わざと失敗させたいとき
          # exit 1
    on_success:
      put: syaberu
      params:
        speaker: hikari
        text: タスクが成功しました
        emotion: happiness
    on_failure:
      put: syaberu
      params:
        speaker: hikari
        text: タスクが失敗しました
        emotion: sadness
```
