クーの自由研究

最下層からまた一段ずつ螺旋階段を登り始めます

フォルマント実験装置(ソフト)の製作(Qt5編)

今年はゆっくり進みます(ブログ駆動がボクのスタイル?)

 

こんにちわ、こんばんわ、かえるのクーです。 

 

ある程度記事がまとまってから公開するのが礼儀とわかってはおりますが、
実験や、制作と同時進行で2~3日や1週間程度かけて一つの記事にしていきます。
そうでもしないと、ずぼらな性格のボクはなかなか先へすすみません。

なので、書き始めの状態で公開してしまい、2~3日で順次追記、更新していくスタイルが自分にあっている感じがしています。
成功しても失敗しても、そして構想や妄想で終わってしまっても「ネタ」にはなるので、あしからず。
これをブログ駆動開発と称します。「ブログ・ファースト」です!!
このブログこそが開発の「エンジン」・「モチベーション」となります。

また、ボクは技術的な事を説明することが苦手です。

助手がたまに補足してくれていますが、自分が理解できていないことを説明できる訳もなく、いったん分かってしまうと「一体何がわからなかったのかが分からなくなる。」というひどいものです。(なので、わかるポイントや要を説明できません)
ボクが説明するより、諸先輩の参考のリンクや、これかな?という論文や解説を貼っていきますのでご了承ください。

参考

フォルマントとは

 声についてのえとせとら

声の印象を決める『整数次倍音』と『非整数次倍音』とは? | ナレーターを目指そう

 

フォルマントの実験装置を作ります

まずは母音「あいうえお」のフォルマント合成装置をつくってみます。

目的:

フォルマントの音の「形」は波形編集ソフトなどで簡単に表示できますが、波形を自分で合成して目的の母音を出すにはどうすればいいかを実験します。

環境:

Python 3.x (anaconda 3.x) + PyCharm

Pycharmをいれてみたい方はこちら

GUIはQt(キュート)を使ってみたいと思います。
Qt5のデザイナチュートリアルはこちら(インドのかたかな?)

Qt5がはいっていない人、最新版をいれたい人はAnacondaのプロンプトを管理者権限で起動して

pip install PyQt5

pip install PyQt5-tools

をいれましょう。

Qtデザイナのインストール後のありかはこちら参照(anacondaにはいっているものです。日本語メニューでした)

[Python] AnacondaとPyQt5でQt Designerについて · GitHub


「pip install PyQt5-tools」でインストールした最新版は
Anaconda3\Lib\site-packages\pyqt5-tools
にインストールされていました。(こちらのデザイナはメニューが英語版)

ちなみにこのサイトでも助手が「変拍子練習ソフト」をQtを使って書いてました。

う~。UIがあるとプログラム長くなります。

材料:

Qt5(GUIとして)
pyqtgraph(リアルタイム波形表示用のGUIとして)
PyAudio(音声入出力として)


母音の発声をまねる方法として、正弦波を合成する方法と、本当の声のように
倍音をたくさん含む「声帯音」をノドや口の音響調整を模してフィルタリングや強調を
行う方法があると思います。
今回は単純に正弦波合成でいきます。10倍音くらいまでにしてみます。

フォルマントを視覚的と聴覚的に理解することを目的としていますので、
「あいうえお」を発音するだけの単機能なものとします。
調整点がでてくれば、その都度考えます。

画面UIの試作

訳も分からず、こんな画面をQt5のデザイナで画面をデザインしました。f:id:np2LKoo:20180512155110p:plain


細部はつくりながら調整します。
保存すると、拡張子が.uiのファイルができます。(例:FormantTest01.ui)
中身はxmlです。

.uiのファイルを pythonのソースに変換して実行するのが普通かもしれませんが、
.uiファイルのままLoadしてオブジェクトを生成することもできるので、そちらでやります。(上記チュートリアルでもそのようにやってました)
(ちなみにQtはまったく知りません。動けばあたかも知っていたかのように書くことをご容赦ください。だいたいは分からず書いてます。)

Qtは海外サイトだとチュートリアルのページや動画は山ほどあるんですが、日本にはあまりないですね。。。
人気そんなにないのかなぁ。。。
英語はわかりませんが、操作やコードはなんとなくわかるので、英語とかコイサン諸語とかのチュートリアルはそれほど苦になりません。

張りぼてUIを動作させます。Qt Designerで登録したUIファイルをロードするだけです。


import sys
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.uic import loadUi


class Test(QMainWindow):
def __init__(self):
super(Test, self).__init__()
loadUi('FormantTest01.ui', self)
self.setWindowTitle('Fromant Test')

if __name__ == '__main__':
app = QApplication(sys.argv)
window = Test()
window.show()
sys.exit(app.exec_())


動作させると「張りぼて(モック)」がちゃんと表示されました。Qtチョー便利。

f:id:np2LKoo:20180512155925p:plain

動くかどうかボタン1つだけコーディングして実行してみると、

Pythonが異常終了してしまいました。バージョンは1年以上あげていないので、anacondaとかpythonとか最新にアップします。

ここまでの教訓

 部品配置は適当でもよいですが、名前はあとで変更しないようによく考えて付けよう!!!

Qt5をはじめるときはAnacondaとかPythonとかも最新にしよう。

最新にあげれば落ちなくなりました。

イベントの記述

 イベントの記述をしていきます。

いろいろなコーディング方法があると思いますが、デコレータというのを使った方法でつなぎます。暗黙の接続名でつないだり、シグナルとスロットを書く方法もあるようですが、デコレータを使用すると簡略化できます。

@pyqtSlot()デコレータ」を使用したQtオブジェクトの接続は、日本語記事があまりヒットしないので、ネタとしてはいいかもしれません。

デコレータの説明は以下です。イメージとしては型の明示的マッピングをする感じです。

Support for Signals and Slots — PyQt 5.10.1 Reference Guide

 例えば周波数設定に使用している doubleSpin (上下ボタンの付いた小数点以下まで指定できる画面部品)はQtでは double型ですが、pythonでうけるときはfloat型にします。(たぶん)

Qtのイベントをひろうときは、その部品にどんなシグナル、スロットがあるか知る必要がありますが、これはデザイナーで部品クリックして「シグナル/スロットを編集」操作すれば表示できます。どのようイベントシグナルがあり、それで引数を渡してもらえるのかもわかり、便利です。

f:id:np2LKoo:20180512223138p:plain

 イベントのテストとして、ひととおりイベントが拾えるか確認します。

 追記部分だけだとわかりにくいかもしれないので、冗長ですが、今一度貼ります。


import sys
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.uic import loadUi


class Test(QMainWindow):
def __init__(self):
super(Test, self).__init__()
loadUi('FormantTest01.ui', self)
self.setWindowTitle('Fromant Test')
self.event_mapping()

@pyqtSlot()
def on_pushButton_start_clicked(self):
self.label_debug.setText('pushButton_start clicked')

@pyqtSlot()
def on_pushButton_stop_clicked(self):
self.label_debug.setText('pushButton_stop clicked')

@pyqtSlot(float)
def on_frequency_valueChanged(self, f):
self.label_debug.setText('on_frequency_valueChanged: %.1f' % f)

@pyqtSlot(int)
def on_verticalSlider_1_valueChanged(self, i):
self.label_debug.setText('on_verticalSlider_1_valueChanged: %d' % i)

@pyqtSlot(bool)
def on_radioButton_a_toggled(self, b):
self.label_debug.setText('on_radioButton_a_toggled: %r' % b)

def event_mapping(self):
self.pushButton_start.clicked.connect(self.on_pushButton_start_clicked)
self.pushButton_stop.clicked.connect(self.on_pushButton_stop_clicked)
self.doubleSpin_Frequency.valueChanged.connect(self.on_frequency_valueChanged)
self.verticalSlider_1.valueChanged.connect(self.on_verticalSlider_1_valueChanged)
self.radioButton_a.toggled.connect(self.on_radioButton_a_toggled)

if __name__ == '__main__':
app = QApplication(sys.argv)
window = Test()
window.show()
sys.exit(app.exec_())

 これで、基本的な画面部品のイベント発生テストができました。

スライドバーのデフォルトは0~99ですが、0~999に調整しました。

すこし長くなったので、次のページにします。