Help us understand the problem. What is going on with this article?

Text To Speech (Open Jtalk) を、Kubernetes Cronjob からホストのオーディオインターフェイスを使って喋らせる

TORICOでは、社内にツール用サーバが何台かあり、社内ツールはすべて Kubernetes + Docker で起動しています。

オフィスでは、決まった時間に Text To Speech (Open Jtalk)で、「換気しましょう」といったアナウンスをするようにしています。

以前は Ubuntu にそのまま Open JTalk をインストールし、生cron で実行していたのですが、最近そのサーバマシンを停止してすべて Kubernetes運用になりましたので、Text To Speech していたタスクを生 cron から Kubernetes Cronjob に移行しました。

Dockerイメージを作る

Dockerfile

まず、Open-JTalk がインストールされた Docker イメージを作ります。

apt install open-jtalk でもインストールできますが、バージョンが古いのでソースからビルドしたほうが使えるオプションが増えていて良いです。音量設定など。

mei ボイスを 1.7, 1.8 の2バージョン入れているのは、切り替えて使えるようにするためです。1.8では1.7からけっこう変化があり、、声のトーンが落ち着いてます。1.7の方がよく通る声なのでそっちを多く使っています。

マルチステージビルドしない場合、1GBほどのイメージサイズになります。この Dockerfile のようにマルチステージビルドした場合、130MBほどで済みました。Alpineは試していません。

Dockerfile
FROM ubuntu:18.04 as builder

WORKDIR /var/speech

RUN apt update && \
    apt install -y \
    unzip \
    curl \
    build-essential \
    && apt clean \
    && rm -rf /var/lib/apt/lists/*

RUN curl -L -O https://downloads.sourceforge.net/hts-engine/hts_engine_API-1.10.tar.gz
RUN tar zxvf hts_engine_API-1.10.tar.gz
RUN cd hts_engine_API-1.10 && ./configure && make  && cd -

RUN curl -L -O https://downloads.sourceforge.net/open-jtalk/open_jtalk-1.11.tar.gz
RUN tar zxvf open_jtalk-1.11.tar.gz
RUN cd open_jtalk-1.11 && ./configure --with-charset=UTF-8 \
    --with-hts-engine-header-path=/var/speech/hts_engine_API-1.10/include \
    --with-hts-engine-library-path=/var/speech/hts_engine_API-1.10/lib \
    && make && make install

RUN mkdir /usr/share/hts-voice
RUN curl -L -O https://downloads.sourceforge.net/mmdagent/MMDAgent_Example-1.7.zip
RUN unzip MMDAgent_Example-1.7.zip

RUN curl -L -O https://downloads.sourceforge.net/mmdagent/MMDAgent_Example-1.8.zip
RUN unzip MMDAgent_Example-1.8.zip

FROM ubuntu:18.04

WORKDIR /var/speech

RUN apt update && \
    apt install -y \
    open-jtalk-mecab-naist-jdic \
    alsa-utils \
    && apt clean \
    && rm -rf /var/lib/apt/lists/*


COPY --from=builder /usr/local/bin/open_jtalk /usr/local/bin/open_jtalk
COPY --from=builder /var/speech/MMDAgent_Example-1.7/Voice/mei /usr/share/hts-voice/mei17
COPY --from=builder /var/speech/MMDAgent_Example-1.8/Voice/mei /usr/share/hts-voice/mei18

COPY wav ./wav
COPY sh ./sh

TTS再生スクリプト

sh ディレクトリの中身はこのようになっています。

sh/chime-speech.sh
#!/usr/bin/env bash

if [ ! "$1" ]; then
  echo "Usage: $0 <speech-text>" >2
  exit 1
fi

cd "$(dirname $0)" || exit

./ring-chime.sh
./jsay-female.sh "$1"
sh/jsay-female.sh
#!/bin/sh
TMP=/tmp/jsay.wav
echo "$1" | open_jtalk \
    -m /usr/share/hts-voice/mei/mei_normal.htsvoice \
    -x /var/lib/mecab/dic/open-jtalk/naist-jdic \
    -r 0.7 \
    -g 6 \
    -ow $TMP && \
./play-wav.sh $TMP
sh/play-wav.sh
#!/usr/bin/env bash
aplay -D plughw:1 ${1}
ring-chime.sh
#!/usr/bin/env bash
cd "$(dirname $0)" || exit
./play-wav.sh ../wav/chime.wav

./chime-speech.sh こんにちは というコマンドで、チャイム効果音を鳴らした後、「こんにちは」とTTSで発声するようになっています。

なお、sh/play-wav.shplughw:1 の「1」はサウンドカード番号であり、Dockerホストの環境に依存すると思います。詳細は後ほど書きます。

ビルドスクリプト

docker-compose でも良いのですが、私は下記のような bash スクリプトで Docker イメージのビルドとサーバへのデプロイをしています。

config.sh
#!/usr/bin/env bash
image_name=torico/torico-speech
container_name=torico-speech
deploy_host=user@deploy-server.example.com
build.sh
#!/usr/bin/env bash
cd "$(dirname $0)" || exit
. config.sh
docker build . -t ${image_name}
docker-image-copy-to-remote.sh
#!/usr/bin/env bash
. config.sh
docker save ${image_name} | bzip2 | ssh ${deploy_host} 'bunzip2 | sudo docker load'

↑手元でビルドした Docker イメージをリモートサーバにコピーします

login.sh
#!/usr/bin/env bash
./run.sh /bin/bash
run.sh
#!/usr/bin/env bash
cd "$(dirname $0)" || exit
. config.sh
docker run \
    --rm -it \
    --device /dev/snd \
    --name=${container_name} ${image_name} \
    "$@"

スクリプトファイルをたくさん作るのは、エディタ (IntelliJなど) で右クリックからの実行を簡単にするためです。

Screenshot 2019-12-12 17.57.33.png

build.sh でビルドしたら、docker-image-copy-to-remote.sh で SSH でサーバに送りつけます。今回は Docker リポジトリは使っていません。

./run.sh は動作確認のために作っています。Ubuntu用です。Mac では /dev/snd が無いので動きません。

サウンドインターフェイスの確認

--device /dev/snd をつけて docker コンテナを起動し、サウンドカードの様子を見ます。 --privileged はなくても動きました

sudo docker run --rm -it --device /dev/snd --name=torico-speech torico/torico-speech /bin/bash
# alsamixer

Screenshot 2019-12-12 17.37.00.png

root@f3a4dc37f277:/var/speech# amixer
Simple mixer control 'IEC958',0
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [on]
Simple mixer control 'IEC958',1
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [on]
Simple mixer control 'IEC958',2
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [on]
Simple mixer control 'IEC958',3
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [on]
Simple mixer control 'IEC958',4
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [on]

root@f3a4dc37f277:/var/speech# amixer -c 1
Simple mixer control 'Master',0
  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
  Playback channels: Mono
  Limits: Playback 0 - 87
  Mono: Playback 87 [100%] [0.00dB] [on]
Simple mixer control 'Headphone',0
  Capabilities: pvolume pswitch
  Playback channels: Front Left - Front Right
  Limits: Playback 0 - 87
  Mono:
  Front Left: Playback 87 [100%] [0.00dB] [on]
  Front Right: Playback 87 [100%] [0.00dB] [on]
Simple mixer control 'Speaker',0
  Capabilities: pswitch
  Playback channels: Front Left - Front Right
  Mono:
  Front Left: Playback [on]
  Front Right: Playback [on]
Simple mixer control 'Speaker+LO',0
  Capabilities: pvolume
  Playback channels: Front Left - Front Right
  Limits: Playback 0 - 87
  Mono:
  Front Left: Playback 87 [100%] [0.00dB]
  Front Right: Playback 87 [100%] [0.00dB]
Simple mixer control 'Front Mic',0
  Capabilities: pvolume pswitch
  Playback channels: Front Left - Front Right
  Limits: Playback 0 - 31
  Mono:
  Front Left: Playback 0 [0%] [-34.50dB] [off]
  Front Right: Playback 0 [0%] [-34.50dB] [off]
...
root@f3a4dc37f277:/var/speech# aplay wav/chime.wav
ALSA lib pcm_dmix.c:1052:(snd_pcm_dmix_open) unable to open slave
aplay: main:788: audio open error: No such file or directory

root@f3a4dc37f277:/var/speech# aplay -D plughw:1 wav/chime.wav
Playing WAVE 'wav/chime.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
(再生される)
root@f3a4dc37f277:/var/speech#

サウンドカードは2つ認識されていて、使いたいカード番号は 1 でした。
なので、aplay の引数に -D plughw:1 をつけます。

Kubernetes cronjob

ローカルにある Docker イメージを使って Cronjob を起動します。

ローカルの Docker イメージを使うので、imagePullPolicy は Never です。

cronjob.yml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: speech-12-open
  namespace: torico
  labels:
    cronjob: speech-12-open
spec:
  concurrencyPolicy: Replace
  schedule: "0 12 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: torico-speech
              image: torico/torico-speech
              imagePullPolicy: Never
              args:
                - "/var/speech/sh/chime-speech.sh"
                - "12時になりました。…窓を開けて、5分間換気をしてください。"
              volumeMounts:
              - mountPath: /dev/snd
                name: dev-snd
              securityContext:
                privileged: true
          volumes:
            - name: dev-snd
              hostPath:
                path: /dev/snd

docker run の --device オプションの代わりに、volumeMounts を使います。privileged が必要です。

ちなみに私は、上記ファイルと同じディレクトリに apply.sh とか作って、エディタから右クリックでkubectl を実行するようにしていまします。

apply.sh
#!/usr/bin/env bash

export KUBECONFIG=${HOME}/.kube/remote-server-kubeconfig

kubectl apply -f cronjob.yml

Screenshot 2019-12-12 19.30.23.png

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした