PyAudioとPyWorldで音声を逐次分析合成しつづけるPythonスクリプト

PyAudio & PyWorld。Numpyも。
合成するまでには、ある程度の遅延は発生するけども、まぁ許容範囲でしょう。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

# Copyright (c) 2020 Akira TAMAMORI
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import numpy as np
import pyaudio
import pyworld as pw


sample_rate = 16000
frame_length = 1024 * 8


def audio_trans(input):

    signal = np.frombuffer(input, dtype='int16')
    signal = signal.astype(np.float64)
    f0, t = pw.harvest(signal, sample_rate)  # 基本周波数の抽出
    sp = pw.cheaptrick(signal, f0, t, sample_rate)  # スペクトル包絡の抽出
    ap = pw.d4c(signal, f0, t, sample_rate)  # 非周期性指標の抽出

    synth = pw.synthesize(modified_f0, modified_sp, ap, sample_rate)

    return synth.astype(np.int16).tobytes()


if __name__ == "__main__":

    audio = pyaudio.PyAudio()

    stream = audio.open(format=pyaudio.paInt16,
                        channels=1,
                        rate=sample_rate,
                        frames_per_buffer=frame_length,
                        input=True,
                        output=True)
    try:
        while stream.is_active():
            input = stream.read(frame_length, exception_on_overflow=False)
            input = audio_trans(input)
            stream.write(input)

    except KeyboardInterrupt:
        stream.stop_stream()
        stream.close()
        audio.terminate()
        print("Stop Streaming")

声の高さと声色を調整してボイスチェンジャー的に遊んでみたい方は以下のコード。
gist.github.com

Juliusの音声認識結果をソケット通信で受け取って表示・保存するPythonプログラム

前回の記事の関連として。
Juliusのdictation kitをダウンロードしておく。

以下のコマンドによりJuliusをmoduleモードで立ち上げる。

julius -n 5 -output 1 -rejectshort 800 -C main.jconf -C am-dnn.jconf -dnnconf julius.dnnconf -module &

ポート番号10500におけるTCP/IP経由でクライアントからの接続を待つ状態になる。一種のサーバーモードである。

クライアント側はJuliusと接続後、上記のポートを介して認識結果を受け取る。音声認識結果はログに保存される(example.log)。

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import socket
import logging

# create logger
logger = logging.getLogger('simple_example')
logging.basicConfig(filename='example.log', level=logging.INFO,
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

host = '127.0.0.1'   # IPアドレス
port = 10500         # Juliusとの通信用ポート番号

# Juliusにソケット通信で接続
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))

data = ""
try:
    while True:
        # 音声認識結果のみをXMLで取得
        while (data.find("</RECOGOUT>\n.") == -1):
            soc = sock.recv(1024)
            data = data + soc.decode('utf-8')

        # 音声認識結果のXMLから単語部分のみを抜き出して連結
        recog_text = ""
        for line in data.split('\n'):
            index = line.find('WORD="')
            if index != -1:
                line = line[index+6:line.find('"', index+6)]
                recog_text = recog_text + line

        logging.info("認識結果: " + recog_text)
        print("認識結果: " + recog_text)
        data = ""

# Command + C などで強制終了した場合、JuliusサーバーをDIEコマンドにより落とす
# https://julius.osdn.jp/juliusbook/ja/desc_module.html
except KeyboardInterrupt:
    print('finished')
    sock.send("DIE".encode('utf-8'))
    sock.close()

後ほど記事は追記・更新予定。

オウム返しなPythonプログラム(オンラインで音声認識→オフラインで音声合成)

音声認識して、その内容を音声合成で返すという、単純なもの。

Mac 10.13.6上での動作確認。
Pythonのバージョンは3.7.3。

PythonのSpeechRecognitionライブラリを利用。
Google音声認識のサービスを用いるのでオンライン環境。

pip3 install SpeechRecognition
brew install open-jtalk

の後に

#!/usr/bin/env python3

import speech_recognition as sr
import subprocess
import tempfile


def jtalk_run(message, out_wav='/tmp/voice.wav', speed=1.0):

    jtalk_dir = "/usr/local/Cellar/open-jtalk/1.10_1/"
    dic_path = jtalk_dir + "/dic"
    voice_path = jtalk_dir + "/voice/mei/mei_normal.htsvoice"

    with tempfile.NamedTemporaryFile(mode='w+') as tmp:
        tmp.write(message)
        tmp.seek(0)
        command = 'open_jtalk -x {} -m {} -r {} -ow {} {}'.format(
            dic_path, voice_path, speed, out_wav, tmp.name)
        command = command + '; afplay ' + out_wav
        # print(command)
        subprocess.run(command, shell=True)


if __name__ == "__main__":

    # マイクからの音声入力
    r = sr.Recognizer()
    with sr.Microphone() as source:
        print("話しかけてみましょう!")
        audio = r.listen(source)

    try:
        # 日本語でGoogle音声認識
        text = r.recognize_google(audio, language="ja-JP")
    except sr.UnknownValueError:
        print("Google音声認識は音声を理解できませんでした。")
    except sr.RequestError as e:
        print("Google音声認識サービスからの結果を要求できませんでした;"
              " {0}".format(e))
    else:
        print(text)
        jtalk_run(text)

pythonコードを適当な名前で保存して実行したのであった。

以下を参考にさせていただきました。
thr3a.hatenablog.com

Flow系の論文たち

サーベイ論文

  • Normalizing Flows: Introduction and Ideas (2019) URL
  • Normalizing Flows for Probabilistic Modeling and Inference (2019) URL

代表的なもの

  • NICE: Non-linear Independent Components Estimation (2014) URL
  • MADE: Masked Autoencoder for Distribution Estimation (2015) URL
  • Variational Inference with Normalizing Flows (2015) URL
  • NADE: Neural Autoregressive Density Estimator (2016) URL
  • Real NVP : Real-valued non-volume preserving transformations (2016) URL
  • IAF: Inverse Autoregressive Flow (2016) URL
  • Density estimation using Real NVP (2017) URL
  • MAF: Masked Autoregressive Flow (2017) URL
  • NAF: Neural Autoregressive Flows (2018) URL
  • Glow: Generative Flow with Invertible 1x1 Convolutions (2018) URL
  • MintNet : Building Invertible Neural Networks with Masked Convolutions (2019) URL

今後に期待

  • Sylvester Normalizing Flows for Variational Inference (2018) URL
  • Flow-GAN: Combining Maximum Likelihood and Adversarial Learning in Generative Models (2018) URL

音声系

  • WaveGlow: A Flow-based Generative Network for Speech Synthesis (2018) URL
  • FloWaveNet: A Generative Flow for Raw Audio (2018) URL
  • Blow: a single-scale hyperconditioned flow for non-parallel raw-audio voice conversion (2019) URL

各論文の読書メモを記したブログ記事は以下から。

peluigi.hatenablog.com

Flow系の生成モデルをまとめたスライドは以下から。

www.slideshare.net

その他

ブログ記事
akosiorek.github.io

Eric Jang氏によるNormalizing Flowの解説ブログ記事その1
blog.evjang.com

Eric Jang氏によるNormalizing Flowの解説ブログ記事その2
blog.evjang.com

Kingma氏の論文

Kingma氏が第1著者の論文を(独断と偏見で)抜き出してまとめておく。

そして以下がD論。必読。

書籍『統計的パターン認識と判別分析』のレビュー

最近発売された『統計的パターン認識と判別分析』のレビューを書く。
統計的パターン認識と判別分析 (シリーズ 情報科学における確率モデル 1)

各章の概要についてはコロナ社のサイトを参照されたい。
『シリーズ 情報科学における確率モデル』|書籍紹介

本書の「ご利益」を一言で表せば、回帰と識別(分類)という機械学習における2大タスクの定式化に現れる、(最小二乗誤差の意味で)最適な非線形関数の解釈を通じてこれらをより良く理解することに役立つ、と言えよう。本書では、この最適な非線形関数に現れる事後確率を「線形近似」するアプローチにより、解釈が進められる。実際に、線形回帰/識別関数が、非線形回帰/識別関数の「線形近似」となる事実が、丁寧な導出とともに具体的に示されており、読者は理解を大いに深めることができるだろう。後半の章では、主成分分析/線形判別分析→カーネル主成分分析/カーネル判別分析という流れで話が進み、これらは最終章の布石をなしている。そして最終章において、非線形判別写像に現れる事後確率を線形近似することにより、線形判別分析が導出できる、という事実が示されるのであった。事後確率の近似方法として、正規分布を仮定する場合など線形近似以外の方法についても具体的な言及がされていたのは好印象であった。最適な非線形判別分析をカーネル判別分析として再解釈する議論もまた、読者にとって有益であると思われる。

本書を通読して、確かに対象読者はパターン認識の基礎を「一通り」勉強し、微分積分線形代数に「十分に慣れた」大学院生や研究者向け、という印象を持った。本書は早い段階で積分を駆使した変分法や確率計算、行列やベクトル計算、行列の偏微分など、高度な数学的操作が現れるため、それらにある程度習熟していることが望ましい。さもなくば、とても「スラスラ」とは読めないであろう。巻末の付録には線形代数、最適化、確率統計の基礎事項がまとめてあり、読者への配慮は伺える。ただし、それらはあくまで備忘録として参照する程度に済むくらいの読者が対象といえるだろう。本書の「まえがき」では付録の存在・活用法について一切言及がないため、ここは幾分残念な点であった。

いくつかの誤植はあるが、ほとんどは軽微な修正で済む。

著者らの最近の論文(2016) は最終章の理解に役立つ(いくつかの誤植の確認はこの論文を参照しながら行った)
www.jstage.jst.go.jp