PCを音声でコントロールする②2026-01-01
前の記事では、PCキーボードのUp(上矢印)/Down(下矢印)キーを音声で操作する方法を紹介しましたが。今回は、音声でマウスポインターを任意の場所に移動させクリックする方法を紹介します。

上図のように、Up/Downキーでプリセットを切り替えられない音源の場合、プリセット名の右にある極小の上下矢印をマウスでクリックする必要があります。これはUp/Downキーを押すより遥かに面倒な操作です。
そこで「次」と唱えるだけで、下矢印の上にマウスポインタを移動させクリックもしようという話です。しかも、プラグインのウィンドが何処にあっても、その中のターゲットに確実にヒットしなければなりません。そんな事が出来るのか?と思いましたが、結果的には出来ましたw
マウスポインタの座標を計測する
このアクロバティックな機能を実装する為には、先ずプラグイン画面の左上の隅からクリックしたい位置までの相対座標を計測する必要があります。やり方は、マウスポインタの現在位置を表示する下のスクリプトを実行します。
import pyautogui
import time
print("マウスを移動させてください。5秒後に現在の座標を表示します。")
time.sleep(5)
# マウスカーソルの現在のX, Y座標を表示する
print(pyautogui.displayMousePosition()) Pythonそして、プラグインウィンド(タイトルが書かれた白い帯を含む)の左上隅にマウスポインタを持っていき、その時Thonnyに表示された座標(下図の黄色いマーカー部分)を書き留めます。次に、プリセット名の左にある矢印(とりあえず下矢印)にポインタを合わせてその時の座標を書き留めます。するとこの2つの座標の差が、ウィンド上の下矢印の座標という訳です。

音声でマウスポインタを飛ばす
次はいよいよ音声でマウスポインタを移動させます。以下は完成したコードですが、先ず冒頭で読み込まれているライブラリをThonnyで予めインストールします。
そして27行目付近の”OFFSET_X = 585 OFFSET_Y = 50”の部分に先程計測したX,Y座標の相対値を入れます(Y座標は数値が増える程下に移動します)。尚、このスクリプトではその下にReaktor用のオフセット座標(但しアンサンブルによって全然違う)も設定してあるので、この値とプラグイン名を変えれば好きなプラグインを操作できます。
つまりスクリプトがウィンドのタイトル(OberhausenなりReaktorなり)を探し、そのプラグインの指定された座標にマウスポインタを飛ばすわけです。もし何れのタイトルも見当たらない場合は、PCキーボードのUp/Downキーを押すという流れです。
import json
import queue
import sounddevice as sd
from vosk import Model, KaldiRecognizer
import pyautogui
import time
import pygetwindow as gw
# マウス暴走時のフェイルセーフ
pyautogui.FAILSAFE = True
# --- 設定 ---
model = Model("model")
device_info = sd.query_devices(None, 'input')
samplerate = int(device_info['default_samplerate'])
q = queue.Queue()
def callback(indata, frames, time, status):
q.put(bytes(indata))
words_limit = '["次", "前", "[unk]"]'
INTERVAL = 1.0
last_action_time = 0
# Oberhausen用のオフセット座標
OFFSET_X = 585
OFFSET_Y = 50
PREV_OFFSET_X = 585
PREV_OFFSET_Y = 40
# Reaktor用のオフセット座標
ReOFFSET_X = 752
ReOFFSET_Y = 55
RePREV_OFFSET_X = 752
RePREV_OFFSET_Y = 45
def click_on_window(window_title, off_x, off_y):
"""指定されたタイトルのウィンドウがあれば座標をクリック"""
target_windows = gw.getWindowsWithTitle(window_title)
if target_windows:
win = target_windows[0]
click_x = win.left + off_x
click_y = win.top + off_y
pyautogui.click(click_x, click_y)
return True
return False
# --- メイン実行ループ ---
with sd.RawInputStream(samplerate=samplerate, blocksize=8000, device=None,
dtype='int16', channels=1, callback=callback):
rec = KaldiRecognizer(model, samplerate, words_limit)
print("音声操作実行中(ハイブリッド・上下キーモード)...")
while True:
data = q.get()
current_time = time.time()
if rec.AcceptWaveform(data):
rec.Result()
else:
partial = json.loads(rec.PartialResult())
p_text = partial.get("partial", "").replace(" ", "")
if ("次" in p_text or "前" in p_text) and (current_time - last_action_time > INTERVAL):
if "次" in p_text:
if click_on_window('oberhausen', OFFSET_X, OFFSET_Y):
print("Oberhausen: 次の音色をクリック")
elif click_on_window('Reaktor', ReOFFSET_X, ReOFFSET_Y):
print("Reaktor: 次の音色をクリック")
else:
# --- キーボード操作を [下] に変更 ---
pyautogui.press('down')
print("キーボード操作: [下] (Next) を送信")
elif "前" in p_text:
if click_on_window('oberhausen', PREV_OFFSET_X, PREV_OFFSET_Y):
print("Oberhausen: 前の音色をクリック")
elif click_on_window('Reaktor', RePREV_OFFSET_X, RePREV_OFFSET_Y):
print("Reaktor: 前の音色をクリック")
else:
# --- キーボード操作を [上] に変更 ---
pyautogui.press('up')
print("キーボード操作: [上] (Prev) を送信")
last_action_time = current_time
time.sleep(0.1)
rec.Reset()Pythonでは、指定したプラグインを表示させた状態で、上のスクリプトを実行してみてください。「次」の声でポインタは目的の場所に飛んだでしょうか?…多分、最初は多少ズレると思いますが、そこまで来たらしめたもの。後はピクセル単位でオフセット値を微調整していけば良いのです。そうして下矢印にヒットしたら次は上矢印。X座標(PREV_OFFSET_X)は同じなので、Y座標(PREV_OFFSET_Y)の値を少し引いていけばそのうちヒットするでしょうw