[2026-02-21 追記] 登録するコマンドと起動フレーズを実行時に JSON で渡す方法を「より便利に使うための Tips」に追記しました。これで実行場所ごとに音声起動するコマンドをカスタマイズできます。
作業していると、エディタでの編集から離れずにビルドやテストやアップロードなどをしたいことがあると思います。「ビルドして」と声を出したらそれでビルドしてほしいです。それで調べると、Windows の音声認識 に「限定した候補フレーズ (GrammarBuilder で登録) のみ認識する」機能があり、それで実現できたので (下図)、スクリプトを以下に記します。
ユースケースとしては、例えば開発と並行して資料作成するときに、デスクトップの状態を妨げず「ビルドして」で TeX ファイルをビルドして 1、「アップロード」で保管場所にアップロードするのに便利だと思いました。が、長期的にも便利かはまだ不明です。
前提条件
Windows の音声認識を利用するので、マイクがある Windows 環境である必要があります。Windows 環境なら、シェルはコマンドプロンプトでも PowerShell でも Git Bash でも使用できます。
スクリプトと使い方
- 下記の
VUI.ps1を「BOM 付き UTF-8」か「SJIS」で適当な場所に配置します。 - コマンド
powershell -ExecutionPolicy Bypass -File VUI.ps1を実行すれば音声認識の待機が開始します。「こんにちは」と言って「こんにちは」と標準出力されたら成功しています。「止めて」と言うか Ctrl+C で終了します。- Ctrl+C で終了する場合、終了するまでに最大 3 秒程度ラグがあります (Ctrl+C が通るように音声待機を 3 秒でタイムアウトすることを繰り返しているため)。
動作確認できたら、「ここをカスタマイズ」に好きなコマンドと起動フレーズを登録して利用ください (実行時に JSON で渡すようにもできます; 後述)。
# VUI.ps1
# Windows標準の音声認識を使った音声コマンドランチャー
# 使い方: powershell -ExecutionPolicy Bypass -File VUI.ps1
param(
[double]$Confidence = 0.7 # 信頼度の閾値 (0.0-1.0)
)
Add-Type -AssemblyName System.Speech
# ============================================================
# コマンド定義 (ここをカスタマイズ)
# "起動フレーズ" = "実行コマンド"
# ============================================================
$commands = [ordered]@{
"ビルドして" = "bash build.sh"
"アップロードして" = "bash upload.sh"
"ステータス" = "git status"
"こんにちは" = "echo 'こんにちは'"
"止めて" = "__EXIT__"
}
# ============================================================
# 認識エンジン初期化
# ============================================================
$recognizer = New-Object System.Speech.Recognition.SpeechRecognitionEngine
try {
$recognizer.SetInputToDefaultAudioDevice()
} catch {
Write-Host "[エラー] マイクが見つかりません。" -ForegroundColor Red
exit 1
}
# 認識対象フレーズを登録
$choices = New-Object System.Speech.Recognition.Choices
$commands.Keys | ForEach-Object { $choices.Add($_) }
$builder = New-Object System.Speech.Recognition.GrammarBuilder
$builder.Culture = [System.Globalization.CultureInfo]::new("ja-JP")
$builder.Append($choices)
$grammar = New-Object System.Speech.Recognition.Grammar($builder)
$recognizer.LoadGrammar($grammar)
# ============================================================
# メインループ
# ============================================================
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Voice Command Launcher" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "設定:" -ForegroundColor Yellow
Write-Host " 信頼度閾値: $Confidence"
Write-Host ""
Write-Host "登録コマンド:" -ForegroundColor Yellow
foreach ($key in $commands.Keys) {
$val = $commands[$key]
if ($val -ne "__EXIT__") {
Write-Host " '$key' -> $val"
}
}
Write-Host ""
$waitMsg = "音声を待機中... (Ctrl+C または '止めて' で音声認識終了)"
Write-Host $waitMsg -ForegroundColor Green
Write-Host ""
try {
while ($true) {
# 音声を待機 (3 秒ごとタイムアウトは Ctrl+C で終了できるようにするため)
$result = $recognizer.Recognize([System.TimeSpan]::FromSeconds(3))
if (-not $result) {
continue
}
$text = $result.Text
$conf = $result.Confidence
$confR = [math]::Round($conf, 2)
$timestamp = Get-Date -Format "HH:mm:ss"
if ($conf -lt $Confidence) {
Write-Host ("[$timestamp] 却下: '$text' (信頼度:$confR < 閾値:$Confidence)") `
-ForegroundColor DarkMagenta
continue
}
Write-Host ("[$timestamp] 認識: '$text' (信頼度:$confR)") -ForegroundColor White
$cmd = $commands[$text]
if ($cmd -eq "__EXIT__") {
Write-Host "音声認識を終了します。" -ForegroundColor Green
break
}
Write-Host "[$timestamp] 実行: $cmd" -ForegroundColor Cyan
try {
Invoke-Expression $cmd
} catch {
Write-Host "[$timestamp] コマンドエラー: $_" -ForegroundColor Red
}
$timestampEnd = Get-Date -Format "HH:mm:ss"
Write-Host "[$timestampEnd] 実行が終わりました。" -ForegroundColor Cyan
Write-Host $waitMsg -ForegroundColor Green
Write-Host ""
}
} finally {
$recognizer.Dispose()
Write-Host "音声認識エンジンを解放しました。" -ForegroundColor DarkGray
}
より便利に使うための Tips
信頼度について
- 信頼度の閾値は、お手元の環境で誤認識・誤分類が多く発生するならより高めに、却下が多く発生するならより低めに調整してください。
- 誤分類を避けるには起動フレーズどうしが似ていないようにするとよいです。
PowerShell スクリプト配置場所について
- スクリプト配置場所と実行場所が異なっても構いません。ただし、スクリプトのパスを Windows 形式で指定する必要があります (POSIX パスだと PowerShell に解釈してもらえません)。
-
cygpathが通るなら、$(cygpath -w ~/.local/bin/VUI.ps1)で POSIX パスを Windows 形式に変換できます。 - Git Bash ユーザなら
~/.bashrcにalias vui='powershell -ExecutionPolicy Bypass -File "$(cygpath -w ~/.local/bin/VUI.ps1)"'のようにエイリアスしておくとvuiでどこからでも実行できます。
-
コマンドと起動フレーズを実行時に JSON で渡す方法
コマンドと起動フレーズを実行時に JSON で渡すようにもできます。例えば実行場所にある .vui.json に追加したいコマンドと起動フレーズを定義しておきます (この JSON も「BOM 付き UTF-8」か「SJIS」にしてください)。
{
"こんばんは": "echo 'こんばんは'"
}
そして PowerShell スクリプトの冒頭を以下のように変更すれば、JSON で定義したコマンドが追加されます。これで実行場所ごとに音声起動するコマンドをカスタマイズできます。
# VUI.ps1
# Windows標準の音声認識を使った音声コマンドランチャー
# 使い方: powershell -ExecutionPolicy Bypass -File VUI.ps1
param(
[double]$Confidence = 0.5, # 信頼度の閾値 (0.0-1.0)
+ [string]$JsonPath = ".vui.json" # 実行場所にこのファイルがあればコマンド追加
)
Add-Type -AssemblyName System.Speech
# ============================================================
# コマンド定義 ("起動フレーズ" = "実行コマンド")
# ============================================================
$commands = [ordered]@{
"ビルドして" = "bash build.sh"
"アップロードして" = "bash upload.sh"
"ステータス" = "git status"
"こんにちは" = "echo 'こんにちは'"
"止めて" = "__EXIT__"
}
+if ($JsonPath -and (Test-Path $JsonPath)) {
+ try {
+ $json = Get-Content $JsonPath -Raw | ConvertFrom-Json
+ foreach ($p in $json.PSObject.Properties) {
+ $commands[$p.Name] = $p.Value
+ }
+ }
+ catch {
+ # JSON を読み込めない場合は無視
+ }
+}
-
Latexmk で保存の度に自動リビルドもできますが、それだと高頻度すぎるものとします。 ↩