at backyard

Color my life with the chaos of trouble.

音声合成エンジンのVOICEVOXをMac上のDocker Desktopで試す

音声合成エンジンのVOICEVOXをMac上のDocker Desktopで試す

商用・非商用問わず無料で利用できる(※)という、音声合成エンジンのVOICEVOXをMac上で試すために、OSS版を手元で動かしてみることにした。
(※詳しくは各キャラクターの利用規約を参照とのこと。また、下に貼ったVOICEBVOXの全体的な構成についても合わせてまずは読んでおくと製品とOSS版の違いなどもわかり、良い)

voicevox.hiroshiba.jp

上にも書いたが、VOICEBVOXの全体的な構成については下記のページに詳細に記載されているので、私のように初めて触る方はまずこちらを読んでおくと良い。

github.com

このVOICEVOXのソフトウェアの製品版のほうはMacには対応していないようだったが、OSS版を用いて自身のローカルで動かせば動かすことはできるので、今回は音声合成エンジンをMac上で動くDocker Desktop上で動かし実行してみることにした。

なお、VOICEVOXのエンジン部分はWebサーバとなっており、curlでリクエストすることで音声ファイルを生成することが可能となっている。

リポジトリはこちら。

github.com

mac上で音声を喋らせるまでの備忘録

以下、備忘録となる。
(といってもREADMEに書かれていることを実行しているだけ...)

まずはDockerを使ってエンジンを立ち上げる。

# dockerでサーバを立ち上げる
docker pull hiroshiba/voicevox_engine:cpu-ubuntu20.04-latest
docker run --rm -it -p '127.0.0.1:50021:50021' hiroshiba/voicevox_engine:cpu-ubuntu20.04-latest

次にcurlを使って立ち上げたサーバに対して音声を喋らせる。
(基本的にコマンドはREADMEのものを記載しているが、最後の 音声ファイルを作成する 箇所のみ、こちらでURLにダブルクォートを付与している。(コマンドでエラーとなったため)

# しゃべるテキストをtxtに書き込む
echo -n "こんにちは、音声合成の世界へようこそ" >text.txt

# 音声を喋らせるためのquery?ファイルが作られる
curl -s \
    -X POST \
    "localhost:50021/audio_query?speaker=1"\
    --get --data-urlencode text@text.txt \
    > query.json
    
# 音声ファイルを作成する。
curl -s \
    -H "Content-Type: application/json" \
    -X POST \
    -d @query.json \
    "localhost:50021/synthesis?speaker=1" \
    > audio.wav

以上の手順を実行後、カレントに生成された audio.wavを再生することで生成された音声を聞くことができる。

特に詰まる箇所もなく喋らせることができたので良かった。

まだ触りたてなので、Hello Worldレベルのことしかできていないが、もう少し色々触ってみたいと考えている。

mac環境で利用できる音声生成について

以前、こちらのブログでmac環境で利用できる音声生成について書いた。

shinshin86.hateblo.jp

Macでこのようなことを試された方なら頷いていただけると思うが、Windowsに比べてMac環境は音声生成の選択肢が弱い。

今回のVOICEVOXのようにmac環境でも生成ができるというのは心強いので、もう少しこちらのツールを触っていけたらと考えている。

Seleniumで現在利用しているChromeのUser Agentを取得する方法

Seleniumで現在動かしているChromeのUser Agentを取得する方法について調べたので、備忘録がてら書き残しておく。

といっても仕組みは単純で、例えばChromeの場合、下記のようにConsoleを開いてnavigator.userAgentと打つことでUser Agentは取得することができる。

f:id:shinshin86:20220121075356p:plain
Chromeの開発者コンソールでUser Agentを表示させたところ

これと同じ操作をSelenium上で行うだけ。

コードサンプルは下記となる。
(なお、最近は webdriver_manager を利用しているので、これを併用した前提のコードとなっている。webdriver_manager、便利です。)

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager


def main():
    s=Service(ChromeDriverManager().install())
    driver = webdriver.Chrome(service=s)

    # このブログにアクセス
    # (注: 最初はlocalhost:3000にアクセスしようとしたが、それだとWebDriverException: Message: unknown error: net::ERR_CONNECTION_REFUSEDになるので、実際のサイトにアクセス)
    driver.get("https://shinshin86.hateblo.jp")

    user_agent = driver.execute_script("return navigator.userAgent")
    print(user_agent)


if __name__ == "__main__":
    main()

このコードを実行すると、User Agentが表示されるのが分かるかと思う。

以上、備忘録でした。

Seleniumとwebdriver-managerを使っていてDeprecationWarning: executable_path has been deprecated, please pass in a Service objectが出たとき

Selenium + webdriver-managerを使っていて、下記のようなwarningが出た。

DeprecationWarning: executable_path has been deprecated, please pass in a Service object

コードとしては下記のように書いていたが、

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager


def main():
    driver = webdriver.Chrome(ChromeDriverManager().install())
    # 以下省略


if __name__ == "__main__":
    main()

下記のように Service を使うよう修正する必要がある。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager


def main():
    s=Service(ChromeDriverManager().install())
    driver = webdriver.Chrome(service=s)
    # 以下省略

if __name__ == "__main__":
    main()

これで警告は出なくなった。

参照したポスト

下記のstackoverflowを参照している。
Seleniumの4系から出るようになった警告のようだ。

stackoverflow.com

Pythonのsubprocess.Popenでファイルを開こうとすると【[WinError 2] 指定されたファイルが見つかりません】

Windows環境でsubprocess.Popenでファイルを開こうとすると【[WinError 2] 指定されたファイルが見つかりません】というエラーがでる

MacWindowsどちらにも対応したアプリを書いていて、とある処理でPythonのsubprocess.Popenを使って指定したファイルを開く処理を書いた際に躓いたこととその解決法に関するメモとなる。

import subprocess
import os
from os import path
・
・
・
file_path = path.join(os.getcwd(), 'foo.txt')

if os_name == "windows":
  subprocess.Popen(["start", file_path])
elif os_name == "mac":
  subprocess.Popen(["start", file_path])
・
・
・

実際のコードとは異なるが、だいたいこんな感じの内容のコードを書いた。

このコードを手元のMacで実行すると、対応するファイルで foo.txt を開いてくれる。

windowsの場合、open ではなく start を使うということだったので、このように記述してみたが、実際にWindows環境で動かしてみると、

[WinError 2] 指定されたファイルが見つかりません

というエラーが出てしまう。

どうやらsubprocess.Popen の引数に shell=True を与える必要があるようだった。

というわけで、下記のようにwindowsの場合のみ shell=True を与えることでWindowsMacどちらの環境でも対応するアプリでファイルが開かれるようになった。

import subprocess
import os
from os import path
・
・
・
file_path = path.join(os.getcwd(), 'foo.txt')

if os_name == "windows":
  subprocess.Popen(["start", file_path], shell=True)
elif os_name == "mac":
  subprocess.Popen(["start", file_path])
・
・
・

Pythonのsubprocess.Popenの公式ドキュメント

ちなみにこの内容に関する公式ドキュメントは下記となる。

docs.python.org

こちらのページにWindows環境で shell=True を動かす際のことについても記載がある。

Windows で shell=True とすると、COMSPEC 環境変数がデフォルトシェルを指定します。Windows で shell=True を指定する必要があるのは、実行したいコマンドがシェルに組み込みの場合だけです (例えば dir や copy)。バッチファイルやコンソールベースの実行ファイルを実行するために shell=True は必要ありません。

他にもセキュリティ的に考慮すべきことなども書かれているので、そちらについても読んでおいたほうが良いだろう。

Wordle、面白い

最近海外のTwitter界隈で流行っているらしい?Wordleを今更ながらプレイしてみた。

www.powerlanguage.co.uk

Wordleのルール

ルールは簡単

  1. ゲームとしては単純な5文字の単語当てゲーム
  2. 6回まで試せる
  3. 正解した文字を入力すると、その文字は緑色で表示される
  4. 文字は間違えたものの、5文字の単語の中に含まれる文字の場合、その文字は黄色で表示される
  5. ハズレた場合はグレーになる

6回という試行の間に色々と考えながら単語を当てるだけのゲームだが、個人的には英語を考える良いきっかけになるし、普段あまり使わない思考回路を使ったせいか、結構息抜き的な楽しさを感じた。

ちなみにゲームは1日1回までとなっており、一度プレイしたあとは日付が変わって問題が更新されるまではプレイできない仕様となっている。

が、ブラウザのシークレットウィンドウでアクセスすれば同じ問題に何度でも挑戦できるので、どうしても答えが知りたいという人は少々野暮かもしれないが、そのようにして力づくで答えにたどり着くことも可能となっている。
(日付が変わるまで同じ問題が出題され続ける)

初挑戦の結果

初チャレンジだったものの、いきなりあと一文字当てればクリア的な状況になって、思いの外白熱した。

それにしても5文字の英単語って全然思い浮かばないなと、ゲームをプレイしながら思った次第。
(語彙がなさすぎるのよ...)

Pythonのsqlite3を用いてDBからの取得結果をdictで返すサンプルコード

タイトルのようなことをしたかったのだが、検索してみると理解が間違えている(?)日本語の記事が普通に検索結果上位に出てきたので、自分の方でも改めて記載しておくことにした。

fetchoneを使って指定したデータをdict形式で取得する方法

サンプルコードを下記に載せる。

# DBの取得結果をdict形式で返すサンプル
def get_item(id):
    conn = sqlite3.connect("foo.db")
    conn.row_factory = sqlite3.Row
    cur = conn.cursor()
    sql_statement = "select * from item where id=?"
    cur.execute(sql_statement, (str(id))) # idが数値型の場合、エラーになるので文字列に変換する
    
    # この時点では `sqlite3.Row object` として値が返ってきている。これはdictではないのでもうひと手間必要
    result = cur.fetchone()

    conn.close()

    # dict形式に変換してreturn
    return dict(result)

fetchallを使って取得したすべてのデータをdict形式で取得する方法

サンプルコードは下記。

# DBの取得結果すべてをdict形式で返すサンプル
def get_all_item():
    conn = sqlite3.connect("foo.db")
    conn.row_factory = sqlite3.Row
    cur = conn.cursor()
    cur.execute("select * from item")
    
    # dictの配列として格納
    result = [dict(row) for row in cur.fetchall()]
    
    conn.close()
    return result

以上となる。

Python3で最大の整数を取得する関数 - sys.maxsizeについて

Python3 では表示できる整数の上限というのはないらしい。
(逆に言うとPython2ではあったようで、そちらの値は sys.maxint という関数で値は取得できた。だが、Python3ではこちらの関数自体がなくなっている)

整数の上限はないようだが、最大値を示す整数を取得する関数というのはあるらしい。

docs.python.org

Py_ssize_t 型の変数が取りうる最大値を示す整数です。通常、32 ビットプラットフォームでは 231 - 1、64 ビットプラットフォームでは 263 - 1 になります。

私の環境(M1 mac)で実施すると下記のようになる。

sys.maxsize
# => 9223372036854775807

なお、こちらの整数に整数を足しても特にエラーにはならない。

sys.maxsize + 1
# => 9223372036854775808

私の場合、とある数値計算の比較処理で、比較対象の数値よりも必ず大きい整数を返す場合の処理として、この sys.maxsize を利用することにしたが、こういう関数は他にどういうユースケースがあるのかちょっと気になる。

あとで時間を見つけて調べてみようと思う。