タイピング速度に連動して音楽の速度が変化するやつ

今回はやってみた系

今回は普段と違い、エンターテイナー色強めの記事にしています。
きっかけは友人にハッカソンに誘われたときに見た(未参加)、ハッカソンのダイジェスト動画に感化されたからです。

今回作りたいものは、「キーボード入力の速度に連動して、音楽の再生速度が変化するプログラム」です。

開発環境

  • Linux(CentOS7)
  • Python3.6
  • VLC
# yum install vlc
# pip install 足りないモジュールを随時インストール

大したものは使いません。ただしPythonの中でOS依存のメソッドを使用します(Linux系なら大丈夫)。VLCはコマンド操作で音楽を再生するために使用します。
※VLCは動画再生ソフトとしても非常に強力なソフトです

CLIで音楽再生操作するモジュール

まずはVLC部分のモジュールから。
vlcの操作は以下の通り。

# 引数に音楽ファイルやフォルダを指定して再生
$ vlc ファイル名
# 速度を10%速める・遅める
$ key key-rate-faster-fine
$ key key-rate-slower-fine

だけです。

次に上記のコマンドの内、速度操作のコマンドを記述するファイルを作成します。

echo "\n" > expect.txt
  1. タイピング速度によってこのファイルの内容を書き換える
  2. 定期的にこのファイルの内容を読み込んでvlcに流す

今回作成するプログラムを要約すると上の様な流れになります。(このセクションでは2.を実装)
今回は対話処理自動化でお馴染みのexpectを使って実装します。

#!/bin/bash expect -c "
set timeout -1
set OUTFILE_PATH \"expect.txt\"
spawn vlc /home/user/音楽/
sleep 1
send \n

while {True} {
        #2秒毎に速度チェックしコマンド決定
        set OUTFILE [exec cat \"\${OUTFILE_PATH}\"]
        sleep 2
        expect -re \">.*\"
        send \$OUTFILE
        send \n
}
expect eof exit 0
"

言うまでもなく2秒毎にexpect.txtのコマンドを、vlcに垂れ流しています。

タイピング速度検出モジュール

まずはキーボードのデバイスファイルを調べます。
自分の場合だと

$ cat /proc/bus/input/devices
~略~
I: Bus=0003 Vendor=046d Product=c31c Version=0110
N: Name="Logitech USB Keyboard"
P: Phys=usb-0000:00:10.0-1.3/input0
S: Sysfs=/devices/pci0000:00/0000:00:10.0/usb2/2-1/2-1.3/2-1.3:1.0/input/input197
U: Uniq=
H: Handlers=sysrq kbd leds event4
B: PROP=0
B: EV=120013
B: KEY=1000000000007 ff9f207ac14057ff febeffdfffefffff fffffffffffffffe
B: MSC=10
B: LED=1f

上のH:行から、次のデバイスファイルが作成されることが分かります。
/dev/input/event4
こいつにキー入力の度に書き込みが行われています。
プログラムを実行するときは、許可を求めらるのがうざいのと、vlcがrootで実行出来ないという都合上、誰にでも参照できるように権限を変更します。

# chmod o+r /dev/input/event4

参照権限なので、大丈夫だとは思うが、念のため遊び終わったら元に戻すことを推奨します。

# バイナリファイル構造体
# struct input_event {
#       struct timeval time;
#       unsigned short type;
#       unsigned short code;
#       unsigned int value;
# };

infile_path = "/dev/input/event4"
V_ZERO = 4
count = 0

# thread_1
# キーイベント処理部分
#
import struct
import re
EVENT_FORMAT = "llHHI";
EVENT_SIZE = struct.calcsize(EVENT_FORMAT)

def keyfunc():
        global count
        with open(infile_path, "rb") as file:
                while True:
                        event = file.read(EVENT_SIZE)
                        strtmp = struct.unpack(EVENT_FORMAT, event)[3:]
                        listtmp = strtmp
                        if listtmp[0] != 0 and listtmp[1] == 0:
                                count = count + 1

# thread_2
# ラップ処理部分
#
import time
expfile_path = 'expect.txtのパス'

def timerfunc():
        global count
        btime = time.time()
        while True:
                ntime = time.time()
                if (ntime - btime >= 1):
                        vel = (count / round(ntime - btime))
                        if (vel < 3):
                                with open(expfile_path, mode='w') as f:
                                        f.write('key key-rate-slower-fine\n')
                        elif (3 <= vel and vel <= 5):
                                with open(expfile_path, mode='w') as f:
                                        f.write('\n')
                        else:
                                with open(expfile_path, mode='w') as f:
                                        f.write('key key-rate-faster-fine\n')
                        btime = time.time()
                        count = 0

# main
import threading
if __name__ == "__main__":
        thread_1 = threading.Thread(target=keyfunc)
        thread_2 = threading.Thread(target=timerfunc)

        thread_1.start()
        thread_2.start()

スレッド1はキー入力のイベントの際にカウントする処理。スレッド2は1秒毎にタイピング速度(1秒間当たりのタイピング回数)を算出し、vlcコマンドを決定する処理。(expect.txtを書き換えている)
2つのスレッドをそれぞれ、並列に実行しています。

スレッド1について補足すると、キーイベントがデバイスファイルにバイナリとして書き込まれ、それをCの構造体からpython用にパースしています。それらを解析して、特定のキーコード、特定のキーイベント(押す・離す)で評価する。そんなイメージです。

結合

あとは2つのモジュールを繋げます。
key.pyはそのままで、music_expect.shを編集。

#!/bin/bash
# trap処理
trap 'KLPS=`ps -ef | grep key.py | grep -v grep | tr -s " " | cut -d " " -f 2`;kill $KLPS;echo "" > expect.txt' 2 15

python3.6 key.py &

expect -c "
set timeout -1
set OUTFILE_PATH \"expect.txt\"
spawn vlc -I rc /home/user/音楽/
sleep 1
send \n

while {true} {
        #2秒毎に速度チェックしてコマンド決定
        set OUTFILE [exec cat \"\${OUTFILE_PATH}\"]
        sleep 2
        expect -re \">.*\"
        send \$OUTFILE
        send \n
}
expect eof exit 0
"

このままだと、一瞬で再生速度が偏ってしまい、腱鞘炎誘発プログラムとなってしまうので、タイピング速度を検出する式を見直したり、速度の下限を設定するなどして調整する必要がありますが、今回はこれでよしとします。

何はともあれ完成しました。長くなってしまい申し訳ございません。

※下記ファイル構成

.
|-- expect.txt
|-- key.py
`-- music_exprect.sh

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA