Skip to content

Instantly share code, notes, and snippets.

@masakielastic
Last active June 20, 2025 14:22
Show Gist options
  • Save masakielastic/bc9e10a201b669632b272b3a7aba6d2f to your computer and use it in GitHub Desktop.
Save masakielastic/bc9e10a201b669632b272b3a7aba6d2f to your computer and use it in GitHub Desktop.
VOICEVOX による読み上げコマンド(HTTP API 前提)

VOICEVOX による読み上げコマンド (HTTP API 前提)

HTTP API 前提の VOICEVOX によるテキスト読み上げコマンドです。デフォルトのポート番号は 50021 です。

使い方

# 基本的な使用
./say "こんにちは"

# スピーカーとスタイル指定
./say "あまあまです" ずんだもん あまあま

# パラメータ調整
./say "速く高い声で" --speedScale 1.5 --pitchScale 0.1

# ファイル出力
./say "保存します" --output greeting.wav --quiet

# 利用可能なスピーカー確認
./say --list-speakers

ポート番号を変更できます。

./say "こんにちは" -p 50029
#!/bin/bash
# VOICEVOX say コマンド
# 使用例: say "こんにちは" [スピーカー名] [スタイル名] [オプション]
# デバッグモードでない場合のみset -euo pipefailを有効化
if [[ "${DEBUG_MODE:-false}" != true ]]; then
set -euo pipefail
fi
# デフォルト設定
readonly DEFAULT_PORT=50021
readonly DEFAULT_SPEAKER="ずんだもん"
readonly DEFAULT_STYLE="ノーマル"
# キャッシュ設定
readonly CACHE_DIR="$HOME/.cache/voicevox_say"
readonly SPEAKERS_CACHE="$CACHE_DIR/speakers.json"
readonly SPEAKERS_MAP="$CACHE_DIR/speakers_map.sh"
# 一時ファイル
readonly TEMP_DIR="/tmp/voicevox_say_$$"
readonly TEXT_FILE="$TEMP_DIR/text.txt"
readonly QUERY_FILE="$TEMP_DIR/query.json"
readonly AUDIO_FILE="$TEMP_DIR/audio.wav"
# グローバル変数
declare -gA SPEAKER_STYLES
# エラーハンドリング
cleanup() {
rm -rf "$TEMP_DIR" 2>/dev/null || true
}
trap cleanup EXIT
error_exit() {
echo "エラー: $1" >&2
if [[ "${DEBUG:-false}" == true ]]; then
echo "デバッグ情報:" >&2
echo " 現在のディレクトリ: $(pwd)" >&2
echo " 一時ディレクトリ: $TEMP_DIR" >&2
echo " 一時ファイル存在確認:" >&2
echo " TEXT_FILE: $(ls -la "$TEXT_FILE" 2>/dev/null || echo '存在しない')" >&2
echo " QUERY_FILE: $(ls -la "$QUERY_FILE" 2>/dev/null || echo '存在しない')" >&2
echo " AUDIO_FILE: $(ls -la "$AUDIO_FILE" 2>/dev/null || echo '存在しない')" >&2
fi
exit 1
}
# ログ出力(クワイエットモード対応)
log() {
if [[ "${QUIET:-false}" == "false" ]]; then
echo "$1"
fi
}
# 必要なコマンドのチェック
check_dependencies() {
local -a required_commands=(jq curl aplay bc)
for cmd in "${required_commands[@]}"; do
command -v "$cmd" > /dev/null || error_exit "$cmd コマンドが見つかりません"
done
}
# キャッシュディレクトリ作成
init_cache_dir() {
mkdir -p "$CACHE_DIR" || error_exit "キャッシュディレクトリの作成に失敗しました: $CACHE_DIR"
}
# VOICEVOXサーバー接続確認
check_voicevox_server() {
local port=${1:-$DEFAULT_PORT}
if curl -s --connect-timeout 3 "127.0.0.1:$port/version" > /dev/null 2>&1; then
return 0
else
return 1
fi
}
# スピーカー情報をキャッシュから読み込み
load_speakers_cache() {
if [[ ! -f "$SPEAKERS_MAP" || ! -s "$SPEAKERS_MAP" ]]; then
return 1
fi
# グローバル変数として宣言
declare -gA SPEAKER_STYLES
# sourceコマンドの結果を確実にキャプチャ
if source "$SPEAKERS_MAP" 2>/dev/null; then
if [[ ${#SPEAKER_STYLES[@]} -gt 0 ]]; then
return 0
else
return 1
fi
else
return 1
fi
}
# スピーカー情報を取得してキャッシュ更新
update_speakers_cache() {
local port=${1:-$DEFAULT_PORT}
# 古いファイルを削除
rm -f "$SPEAKERS_CACHE" "$SPEAKERS_MAP" 2>/dev/null || true
# VOICEVOXサーバーの接続確認
if ! check_voicevox_server "$port"; then
return 1
fi
# スピーカー情報取得
if ! curl -s --connect-timeout 10 "127.0.0.1:$port/speakers" > "$SPEAKERS_CACHE" 2>/dev/null; then
return 1
fi
# ファイルサイズチェック
if [[ ! -s "$SPEAKERS_CACHE" ]]; then
return 1
fi
# JSON妥当性チェック
if ! jq empty "$SPEAKERS_CACHE" 2>/dev/null; then
return 1
fi
# speakers_map生成
echo "# Generated speaker map - $(date)" > "$SPEAKERS_MAP"
echo "# SPEAKER_STYLES配列はグローバルで宣言済み" >> "$SPEAKERS_MAP"
# jqコマンドでデータを抽出し、Bashでフォーマット
if jq -r '.[] | .name as $speaker | .styles[] | select(.type == "talk") | "\($speaker),\(.name) \(.id)"' "$SPEAKERS_CACHE" 2>/dev/null | \
while IFS=' ' read -r speaker_style_name speaker_id; do
echo "SPEAKER_STYLES[\"$speaker_style_name\"]=$speaker_id" >> "$SPEAKERS_MAP"
done; then
if [[ -s "$SPEAKERS_MAP" ]]; then
return 0
else
return 1
fi
else
return 1
fi
}
# スピーカー情報の初期化
init_speakers() {
local port=${1:-$DEFAULT_PORT}
# キャッシュから読み込み試行
if load_speakers_cache; then
return 0
fi
# キャッシュ更新
log "スピーカー情報を更新しています..."
if update_speakers_cache "$port" && load_speakers_cache; then
log "スピーカー情報を更新しました"
return 0
fi
error_exit "スピーカー情報の取得に失敗しました。VOICEVOXサーバー(ポート$port)が起動しているか確認してください"
}
# 利用可能なスピーカーを表示
list_speakers() {
echo "利用可能なスピーカーとスタイル:"
if [[ ${#SPEAKER_STYLES[@]} -eq 0 ]]; then
echo " スピーカー情報が読み込まれていません"
return 1
fi
# 連想配列のキーを配列に変換してソート
local keys=()
for key in "${!SPEAKER_STYLES[@]}"; do
keys+=("$key")
done
# ソート
IFS=$'\n'
local sorted_keys=($(sort <<<"${keys[*]}"))
unset IFS
local current_speaker=""
for key in "${sorted_keys[@]}"; do
local speaker="${key%,*}"
local style="${key#*,}"
if [[ "$speaker" != "$current_speaker" ]]; then
echo " $speaker:"
current_speaker="$speaker"
fi
echo " $style (ID: ${SPEAKER_STYLES[$key]})"
done
}
# スピーカー名が有効かチェック
is_valid_speaker() {
local speaker="$1"
local found=false
for key in "${!SPEAKER_STYLES[@]}"; do
if [[ "$key" =~ ^"$speaker", ]]; then
found=true
break
fi
done
if [[ "$found" == true ]]; then
return 0
else
return 1
fi
}
# スタイル名が有効かチェック
is_valid_style() {
local speaker="$1"
local style="$2"
if [[ -n "${SPEAKER_STYLES["$speaker,$style"]:-}" ]]; then
return 0
else
return 1
fi
}
# スピーカーの最初のスタイルを取得(ノーマル優先)
get_first_style() {
local speaker="$1"
# まず「ノーマル」スタイルがあるかチェック
if [[ -n "${SPEAKER_STYLES["$speaker,ノーマル"]:-}" ]]; then
echo "ノーマル"
return 0
fi
# ノーマルがない場合は最初に見つかったスタイルを返す
for key in "${!SPEAKER_STYLES[@]}"; do
if [[ "$key" =~ ^"$speaker",(.+)$ ]]; then
echo "${BASH_REMATCH[1]}"
return 0
fi
done
return 1
}
# 数値パラメータの範囲チェック
validate_range() {
local value="$1"
local min="$2"
local max="$3"
local param_name="$4"
# 数値形式チェック
local pattern="^-?[0-9]*\.?[0-9]+$"
[[ "$value" =~ $pattern ]] || error_exit "${param_name}には数値を指定してください: $value"
# 範囲チェック(bcを使用)
if [[ $(echo "$value < $min" | bc -l) -eq 1 ]] || [[ $(echo "$value > $max" | bc -l) -eq 1 ]]; then
error_exit "${param_name}は${min}から${max}の範囲で指定してください: $value"
fi
}
# 音声パラメータの調整
adjust_audio_parameter() {
local param_name="$1"
local param_value="$2"
local default_value="$3"
local speaker_id="$4"
local port="$5"
if [[ "$param_value" != "$default_value" ]]; then
# パラメータ値を更新
local pattern
case "$param_name" in
speedScale) pattern="\"speedScale\":[0-9.]+" ;;
pitchScale) pattern="\"pitchScale\":-?[0-9.]+" ;;
volumeScale) pattern="\"volumeScale\":[0-9.]+" ;;
esac
sed -i -r "s/$pattern/\"$param_name\":$param_value/" "$QUERY_FILE" || error_exit "${param_name}の変更に失敗しました"
# 再合成
curl -s \
-H "Content-Type: application/json" \
-X POST \
-d @"$QUERY_FILE" \
"127.0.0.1:$port/synthesis?speaker=$speaker_id" \
> "$AUDIO_FILE" || error_exit "音声合成に失敗しました"
fi
}
# ヘルプ表示
show_help() {
cat << 'EOF'
使用法: say <テキスト> [スピーカー名] [スタイル名] [オプション]
引数:
<テキスト> 読み上げるテキスト
[スピーカー名] 話者名(デフォルト: ずんだもん)
[スタイル名] スタイル名(デフォルト: ノーマル)
オプション:
-p, --port PORT ポート番号を指定(デフォルト: 50021)
-o, --output [FILE] 音声ファイルを保存(デフォルト: output.wav)
-q, --quiet ログメッセージを表示しない
--speedScale SCALE 話す速度を指定(デフォルト: 1.0、範囲: 0.1-3.0)
--pitchScale SCALE 声の高さを指定(デフォルト: 0.0、範囲: -0.15-0.15)
--volumeScale SCALE 音量を指定(デフォルト: 1.0、範囲: 0.1-3.0)
--list-speakers 利用可能なスピーカーとスタイルを表示
--update-cache スピーカー情報のキャッシュを強制更新
-h, --help このヘルプを表示
使用例:
say "こんにちは"
say "こんにちは" ずんだもん あまあま
say "こんにちは" --speedScale 1.5 --pitchScale 0.1
say "こんにちは" --output greeting.wav --quiet
say --list-speakers
EOF
}
# メイン処理
main() {
# 引数解析
local port=$DEFAULT_PORT
local text=""
local speaker_arg=""
local style_arg=""
local output_file=""
local save_to_file=false
local quiet=false
local speed_scale="1.0"
local pitch_scale="0.0"
local volume_scale="1.0"
local list_speakers_flag=false
local update_cache_flag=false
while [[ $# -gt 0 ]]; do
case $1 in
-p|--port)
[[ -z "${2:-}" ]] && error_exit "ポート番号を指定してください"
port="$2"
shift 2
;;
-o|--output)
save_to_file=true
if [[ $# -gt 1 && ! "$2" =~ ^- ]]; then
output_file="$2"
[[ "$output_file" == *.wav ]] || error_exit "出力ファイル名には .wav 拡張子が必要です: $output_file"
shift 2
else
output_file="output.wav"
shift
fi
;;
--speedScale)
[[ -z "${2:-}" ]] && error_exit "speedScaleの値を指定してください"
validate_range "$2" 0.1 3.0 "speedScale"
speed_scale="$2"
shift 2
;;
--pitchScale)
[[ -z "${2:-}" ]] && error_exit "pitchScaleの値を指定してください"
validate_range "$2" -0.15 0.15 "pitchScale"
pitch_scale="$2"
shift 2
;;
--volumeScale)
[[ -z "${2:-}" ]] && error_exit "volumeScaleの値を指定してください"
validate_range "$2" 0.1 3.0 "volumeScale"
volume_scale="$2"
shift 2
;;
--list-speakers)
list_speakers_flag=true
shift
;;
--update-cache)
update_cache_flag=true
shift
;;
-q|--quiet)
quiet=true
shift
;;
-h|--help)
show_help
exit 0
;;
-*)
error_exit "不明なオプション: $1"
;;
*)
if [[ -z "$text" ]]; then
text="$1"
elif [[ -z "$speaker_arg" ]]; then
speaker_arg="$1"
else
style_arg="$1"
fi
shift
;;
esac
done
# グローバル変数設定
readonly QUIET=$quiet
# 依存関係チェック
check_dependencies
init_cache_dir
# 特別なオプションの処理
if [[ "$update_cache_flag" == true ]]; then
echo "スピーカー情報のキャッシュを更新中..."
if update_speakers_cache "$port"; then
echo "キャッシュを更新しました"
else
error_exit "キャッシュの更新に失敗しました。VOICEVOXサーバー(ポート$port)が起動しているか確認してください"
fi
exit 0
fi
if [[ "$list_speakers_flag" == true ]]; then
init_speakers "$port"
list_speakers
exit 0
fi
# 通常の音声合成処理
[[ -z "$text" ]] && error_exit "読み上げるテキストを指定してください"
# スピーカー情報を初期化
init_speakers "$port"
# スピーカーとスタイルの決定
local final_speaker=$DEFAULT_SPEAKER
local final_style=$DEFAULT_STYLE
if [[ -n "$speaker_arg" ]]; then
if is_valid_speaker "$speaker_arg"; then
final_speaker="$speaker_arg"
if [[ -z "$style_arg" ]]; then
final_style=$(get_first_style "$final_speaker")
else
final_style="$style_arg"
fi
else
error_exit "サポートされていないスピーカーです: $speaker_arg (利用可能なスピーカーは --list-speakers で確認してください)"
fi
fi
[[ -n "$style_arg" ]] && final_style="$style_arg"
# スピーカーとスタイルの組み合わせチェック
if ! is_valid_style "$final_speaker" "$final_style"; then
error_exit "サポートされていないスピーカー/スタイルの組み合わせです: $final_speaker / $final_style (利用可能な組み合わせは --list-speakers で確認してください)"
fi
# スピーカーID取得
local speaker_id=${SPEAKER_STYLES["$final_speaker,$final_style"]}
# 一時ディレクトリ作成
mkdir -p "$TEMP_DIR" || error_exit "一時ディレクトリの作成に失敗しました"
# VOICEVOXサーバーの接続確認
check_voicevox_server "$port" || error_exit "VOICEVOXサーバー(ポート$port)に接続できません"
log "読み上げ中: \"$text\" ($final_speaker / $final_style)"
# テキストファイル作成
echo -n "$text" > "$TEXT_FILE" || error_exit "テキストファイルの作成に失敗しました"
# 音声クエリ作成
curl -s \
-X POST \
"127.0.0.1:$port/audio_query?speaker=$speaker_id" \
--get --data-urlencode text@"$TEXT_FILE" \
> "$QUERY_FILE" || error_exit "音声クエリの作成に失敗しました"
# 基本音声合成
curl -s \
-H "Content-Type: application/json" \
-X POST \
-d @"$QUERY_FILE" \
"127.0.0.1:$port/synthesis?speaker=$speaker_id" \
> "$AUDIO_FILE" || error_exit "音声合成に失敗しました"
# 音声パラメータ調整
adjust_audio_parameter "speedScale" "$speed_scale" "1.0" "$speaker_id" "$port"
adjust_audio_parameter "pitchScale" "$pitch_scale" "0.0" "$speaker_id" "$port"
adjust_audio_parameter "volumeScale" "$volume_scale" "1.0" "$speaker_id" "$port"
# 音声ファイルのサイズチェック
[[ -s "$AUDIO_FILE" ]] || error_exit "音声ファイルの生成に失敗しました"
# ファイル保存または音声再生
if [[ "$save_to_file" == true ]]; then
log "DEBUG: ファイル保存を開始: $output_file"
log "DEBUG: 音声ファイルサイズ: $(wc -c < "$AUDIO_FILE" 2>/dev/null || echo 0) bytes"
if cp "$AUDIO_FILE" "$output_file" 2>/dev/null; then
log "音声ファイルを保存しました: $output_file"
log "DEBUG: 保存後のファイルサイズ: $(wc -c < "$output_file" 2>/dev/null || echo 0) bytes"
else
error_exit "音声ファイルの保存に失敗しました: $output_file"
fi
else
# 音声再生(set -e を一時的に無効化)
set +e
aplay_result=0
if aplay -q "$AUDIO_FILE" 2>/dev/null; then
aplay_result=0
else
aplay_result=1
fi
set -e
if [[ $aplay_result -eq 0 ]]; then
log "音声を再生しました"
else
log "警告: 音声の再生に失敗しましたが、音声ファイルは正常に生成されました"
fi
fi
}
# スクリプトが直接実行された場合のみmain関数を呼び出し
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
#!/bin/bash
# sayコマンドのテストスイート
# 使用法: ./test_say.sh [--debug] [ポート番号]
# 引数処理
if [[ "${1:-}" == "--debug" ]]; then
TEST_DEBUG_MODE=true
TEST_PORT=${2:-50021}
else
TEST_DEBUG_MODE=false
TEST_PORT=${1:-50021}
fi
# sayスクリプトの絶対パス取得
SAY_SCRIPT="$(realpath ./say)"
TEST_AUDIO_DIR="/tmp/say_test_$$"
FAILED_TESTS=0
TOTAL_TESTS=0
# テスト用関数
test_log() {
echo "[TEST] $1"
}
test_success() {
echo "✓ $1"
}
test_failure() {
echo "✗ $1"
((FAILED_TESTS++))
}
run_test() {
local test_name="$1"
local command="$2"
local expected_exit_code="${3:-0}"
((TOTAL_TESTS++))
test_log "実行中: $test_name"
local output
local actual_exit_code=0
set +e
output=$(eval "$command" 2>&1)
actual_exit_code=$?
set -e
if [[ $actual_exit_code -eq $expected_exit_code ]]; then
test_success "$test_name"
else
test_failure "$test_name (終了コード: 期待値=$expected_exit_code, 実際=$actual_exit_code)"
fi
}
run_test_with_exit_code() {
local test_name="$1"
local command="$2"
local expected_exit_code="$3"
((TOTAL_TESTS++))
test_log "実行中: $test_name"
local actual_exit_code=0
# set -e を一時的に無効化してエラーをキャプチャ
set +e
bash -c "$command" >/dev/null 2>&1
actual_exit_code=$?
set -e
if [[ $actual_exit_code -eq $expected_exit_code ]]; then
test_success "$test_name"
else
test_failure "$test_name (終了コード: 期待値=$expected_exit_code, 実際=$actual_exit_code)"
fi
}
debug_single_test() {
local test_name="$1"
local command="$2"
local expected_exit_code="${3:-0}"
echo "=== デバッグ: $test_name ==="
echo "コマンド: $command"
echo "期待終了コード: $expected_exit_code"
local output
local actual_exit_code=0
set +e
output=$(eval "$command" 2>&1)
actual_exit_code=$?
set -e
echo "実際の終了コード: $actual_exit_code"
echo "出力: $output"
echo "結果: $([[ $actual_exit_code -eq $expected_exit_code ]] && echo "成功" || echo "失敗")"
echo ""
}
# メイン処理開始
echo "=== sayコマンド テストスイート ==="
echo "テストポート: $TEST_PORT"
echo "sayスクリプト: $SAY_SCRIPT"
echo "デバッグモード: $TEST_DEBUG_MODE"
echo ""
# デバッグモードの場合は単体テストを実行してすぐに終了
if [[ "$TEST_DEBUG_MODE" == true ]]; then
echo "=== デバッグモード: 失敗テストの詳細 ==="
# VOICEVOXサーバーの接続確認
if ! curl -s --connect-timeout 3 "127.0.0.1:$TEST_PORT/version" > /dev/null 2>&1; then
echo "エラー: VOICEVOXサーバー(ポート$TEST_PORT)に接続できません"
exit 1
fi
debug_single_test "不正なvolumeScale(小さすぎ)" "$SAY_SCRIPT 'テスト' --volumeScale 0.05 -p $TEST_PORT -q" 1
debug_single_test "不正なvolumeScale(大きすぎ)" "$SAY_SCRIPT 'テスト' --volumeScale 5.0 -p $TEST_PORT -q" 1
debug_single_test "不正なポート" "$SAY_SCRIPT 'テスト' -p 99999 -q" 1
exit 0
fi
# セットアップ
test_log "テスト環境をセットアップ中..."
mkdir -p "$TEST_AUDIO_DIR"
# キャッシュをクリア
rm -rf ~/.cache/voicevox_say/
# sayスクリプトが存在するかチェック
if [[ ! -f "$SAY_SCRIPT" ]]; then
echo "エラー: $SAY_SCRIPT が見つかりません"
exit 1
fi
# VOICEVOXサーバーが起動しているかチェック
if ! curl -s --connect-timeout 3 "127.0.0.1:$TEST_PORT/version" > /dev/null 2>&1; then
echo "エラー: VOICEVOXサーバー(ポート$TEST_PORT)に接続できません"
echo "テストを実行する前にVOICEVOXサーバーを起動してください"
exit 1
fi
test_success "テスト環境セットアップ完了"
# 1. 基本機能テスト
echo ""
echo "=== 基本機能テスト ==="
run_test "ヘルプ表示" "$SAY_SCRIPT --help" 0
run_test "バージョン表示(存在しないオプション)" "$SAY_SCRIPT --version" 1
run_test "キャッシュ更新" "$SAY_SCRIPT --update-cache -p $TEST_PORT" 0
run_test "スピーカー一覧表示" "$SAY_SCRIPT --list-speakers -p $TEST_PORT" 0
# 2. 音声合成テスト
echo ""
echo "=== 音声合成テスト ==="
run_test "基本的な音声合成" "$SAY_SCRIPT 'テスト音声です' -p $TEST_PORT -q" 0
run_test "スピーカー指定" "$SAY_SCRIPT 'ずんだもんです' ずんだもん -p $TEST_PORT -q" 0
run_test "スピーカーとスタイル指定" "$SAY_SCRIPT 'あまあまです' ずんだもん あまあま -p $TEST_PORT -q" 0
run_test "四国めたん" "$SAY_SCRIPT 'めたんです' 四国めたん -p $TEST_PORT -q" 0
run_test "中部つるぎ" "$SAY_SCRIPT 'つるぎです' 中部つるぎ -p $TEST_PORT -q" 0
# 3. 音声パラメータテスト
echo ""
echo "=== 音声パラメータテスト ==="
run_test "話速変更(速い)" "$SAY_SCRIPT '速く話します' --speedScale 1.5 -p $TEST_PORT -q" 0
run_test "話速変更(遅い)" "$SAY_SCRIPT 'ゆっくり話します' --speedScale 0.7 -p $TEST_PORT -q" 0
run_test "音の高さ変更(高い)" "$SAY_SCRIPT '高い声です' --pitchScale 0.1 -p $TEST_PORT -q" 0
run_test "音の高さ変更(低い)" "$SAY_SCRIPT '低い声です' --pitchScale -0.1 -p $TEST_PORT -q" 0
run_test "音量変更(大きい)" "$SAY_SCRIPT '大きな声です' --volumeScale 1.5 -p $TEST_PORT -q" 0
run_test "音量変更(小さい)" "$SAY_SCRIPT '小さな声です' --volumeScale 0.7 -p $TEST_PORT -q" 0
run_test "複合パラメータ" "$SAY_SCRIPT 'すべて変更' --speedScale 1.2 --pitchScale 0.05 --volumeScale 1.1 -p $TEST_PORT -q" 0
# 4. ファイル出力テスト
echo ""
echo "=== ファイル出力テスト ==="
# 現在のディレクトリを記録
ORIGINAL_DIR="$(pwd)"
cd "$TEST_AUDIO_DIR"
rm -f output.wav custom.wav quiet.wav 2>/dev/null
run_test "デフォルトファイル出力" "$SAY_SCRIPT 'ファイル出力テスト' -o -p $TEST_PORT -q && test -f output.wav" 0
run_test "指定ファイル出力" "$SAY_SCRIPT 'カスタムファイル' -o custom.wav -p $TEST_PORT -q && test -f custom.wav" 0
run_test "クワイエット+ファイル出力" "$SAY_SCRIPT 'クワイエットテスト' -o quiet.wav -q -p $TEST_PORT && test -f quiet.wav" 0
# 元のディレクトリに戻る
cd "$ORIGINAL_DIR"
# 5. エラーケーステスト
echo ""
echo "=== エラーケーステスト ==="
run_test "テキストなし" "$SAY_SCRIPT -p $TEST_PORT" 1
run_test "不正なスピーカー" "$SAY_SCRIPT 'テスト' 存在しないスピーカー -p $TEST_PORT" 1
run_test "不正なスタイル" "$SAY_SCRIPT 'テスト' ずんだもん 存在しないスタイル -p $TEST_PORT" 1
run_test "不正な拡張子" "$SAY_SCRIPT 'テスト' -o test.mp3 -p $TEST_PORT" 1
run_test_with_exit_code "不正なspeedScale(小さすぎ)" "$SAY_SCRIPT 'テスト' --speedScale 0.05 -p $TEST_PORT -q" 1
run_test_with_exit_code "不正なspeedScale(大きすぎ)" "$SAY_SCRIPT 'テスト' --speedScale 5.0 -p $TEST_PORT -q" 1
run_test_with_exit_code "不正なpitchScale(小さすぎ)" "$SAY_SCRIPT 'テスト' --pitchScale -0.2 -p $TEST_PORT -q" 1
run_test_with_exit_code "不正なpitchScale(大きすぎ)" "$SAY_SCRIPT 'テスト' --pitchScale 0.2 -p $TEST_PORT -q" 1
run_test_with_exit_code "不正なvolumeScale(小さすぎ)" "$SAY_SCRIPT 'テスト' --volumeScale 0.05 -p $TEST_PORT -q" 1
run_test_with_exit_code "不正なvolumeScale(大きすぎ)" "$SAY_SCRIPT 'テスト' --volumeScale 5.0 -p $TEST_PORT -q" 1
run_test_with_exit_code "不正なポート" "$SAY_SCRIPT 'テスト' -p 99999 -q" 1
# 6. 引数順序テスト
echo ""
echo "=== 引数順序テスト ==="
run_test "オプション後にテキスト" "$SAY_SCRIPT -p $TEST_PORT -q 'テスト'" 0
run_test "スピーカー後にオプション" "$SAY_SCRIPT 'テスト' ずんだもん -p $TEST_PORT -q" 0
run_test "混在順序" "$SAY_SCRIPT -p $TEST_PORT 'テスト' ずんだもん あまあま --speedScale 1.2 -q" 0
# テスト結果サマリー
echo ""
echo "=== テスト結果サマリー ==="
echo "実行テスト数: $TOTAL_TESTS"
echo "成功: $((TOTAL_TESTS - FAILED_TESTS))"
echo "失敗: $FAILED_TESTS"
# クリーンアップ
test_log "テスト環境をクリーンアップ中..."
rm -rf "$TEST_AUDIO_DIR"
test_success "クリーンアップ完了"
if [[ $FAILED_TESTS -eq 0 ]]; then
echo ""
echo "✓ すべてのテストが成功しました!"
exit 0
else
echo ""
echo "✗ $FAILED_TESTS 個のテストが失敗しました"
exit 1
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment