自分の声の棒読みちゃんを作る?!Windowsローカル構築で行うQwen3-TTS Base入門編

Qwen3-TTS Base とは?何者?

Qwen3-TTS-12Hz-1.7B-Base / Qwen3-TTS-12Hz-0.6B-Base は、Alibaba が公開している Qwen3-TTS ファミリーの中でも「高品質ボイスクローン用のベースモデル」です。
特徴をざっくり言うと:

  • 参照音声(自分の声)+その文字起こしを渡すと、かなりそれっぽい声質で喋ってくれる
  • マルチリンガル対応で、日本語も自然なイントネーション

という特徴があります。実は声優さんの声でもいけるらしい?です。
好きなセリフを、好きな声で、好きなタイミングで作成できるのが特徴です。

今回はこの Qwen3 TTS Base を Windows 11 上でローカル実行し、PowerShell や既存ツールから「棒読みちゃん互換」で叩ける TTS サーバに仕立てます。
最終的には:

  • http://localhost:50081/Talk に POST でテキストを投げる
    → 棒読みちゃんは 50080 ですが、あえて同時起動できるように +1 しています
  • キューに溜めて、1件ずつ音声生成 → ファイル保存 → 再生
  • 自分の声クローンで、テンポ速め・ピッチ維持・末尾ブツ切れ対策済の音声

という「なんちゃって棒読みちゃん(中身はQwen3)」が動くところまで行きます。
今回はCPUベースで行いますが、GPUでの動作でもできるように随時コメントを追加しています。


1. 環境準備:Windows 11 上に Python + ライブラリを整える

1-1. Python 3.10〜3.12 を用意する

Qwen3-TTS と onnxruntime の組み合わせは、最新すぎる Python(3.13〜)だとホイールがなくてハマります。
安全圏は 3.10〜3.12 なので、以下のどれかで揃えておきます:

  1. 公式サイトから Python 3.12 x64 をインストール
  2. 既に 3.12 が入っているなら、それを使って仮想環境を作ることもできます。

1-2. 作業フォルダと仮想環境の作成

PowerShell を開いて、下記のようなパスを実行します。太字のコマンドを実行してください。

powershellPS C:\> mkdir C:\Qwen3TTS
PS C:\> cd C:\Qwen3TTS

# Python 3.12 のパスが通ていない場合は環境に合わせて変更
PS C:\Qwen3TTS> python.exe -m venv .venv
PS C:\Qwen3TTS> .\.venv\Scripts\Activate.ps1
(.venv) PS C:\Qwen3TTS> python -V

# → 3.12.x を確認

以降はこの仮想環境上で作業します。

1-3. 必要ライブラリのインストール

TTS 本体+音声入出力+Web サーバをまとめて入れます。太字のコマンドを実行してください。

(.venv) PS C:\Qwen3TTS> python -m pip install -U pip

# PyTorch(CPU版)。GPUがあればCUDA版でもOK pip install torch torchaudio --index-url https://download.pytorch.org/whl/cu128

(.venv) PS C:\Qwen3TTS> pip install torch torchaudio --index-url https://download.pytorch.org/whl/cpu

# onnxruntime(qwen-tts が要求)
(.venv) PS C:\Qwen3TTS> pip install onnxruntime

# Qwen3-TTS ラッパー
(.venv) PS C:\Qwen3TTS> pip install qwen-tts

# 音声入出力
(.venv) PS C:\Qwen3TTS> pip install soundfile sounddevice

# Webサーバ(棒読みちゃん互換API用)
(.venv) PS C:\Qwen3TTS> pip install fastapi uvicorn

# ピッチを変えずに話速を変えるためのタイムストレッチ
(.venv) PS C:\Qwen3TTS> pip install --upgrade audiostretchy

ここまで通れば、ライブラリの地獄はほぼ脱出です。


2. 自分の声を録音して「参照音声+スクリプト」を用意する

2-1. 参照音声(my_voice_sample.wav)を作成

  1. Windows の「ボイスレコーダー」などで、10秒程度の音声を録音します
  2. 内容はハッキリ・一定のトーンで、環境ノイズ少なめが理想
    • 例:
      「春の朝、私は7時30分に起き、白いカップで熱いコーヒーを飲みました。今日は2026年1月27日、天気は晴れ、気温は6度です。静かな部屋で深呼吸し、落ち着いた声で一文ずつ、正確に読み上げます。」
  3. 録音を WAV(16bit/24bit, 44.1kHz〜48kHz)として保存し、
    C:\Qwen3TTS\my_voice_sample.wav という名前にします。

2-2. 文字起こし(REF_TEXT)を用意する

録音するときに使用した文を、誤字なしで1行の文字列にします。この分は後でPythonスクリプトに組み込みます。これは Qwen3-TTS が「この音声はこの文章を読んでいる」と認識するためのラベルで、クローン精度を上げるために必要です。


3. まずは単発テスト:0.6B-Base で自分の声クローン

本格的なサーバ構築の前に、スタンドアロンで 1ファイルだけ喋らせてみます。
C:\Qwen3TTS\qwen3_voice_clone_06b.py を作成して、以下の内容を保存します。

# file: qwen3_voice_clone_06b.py
import torch
import soundfile as sf
from qwen_tts import Qwen3TTSModel

MODEL_ID = "Qwen/Qwen3-TTS-12Hz-0.6B-Base" # GPUがあれば "Qwen/Qwen3-TTS-12Hz-1.7B-Base"
DEVICE_MAP = "cpu" # GPUがあれば "cuda:0"
DTYPE = torch.float32 # GPUなら torch.bfloat16 でもOK

REF_AUDIO = "my_voice_sample.wav"

# 録音するときに使用した文ここで組み込み
REF_TEXT = (
"春の朝、私は7時30分に起き、白いカップで熱いコーヒーを飲みました。"
"今日は2026年1月27日、天気は晴れ、気温は6度です。"
"静かな部屋で深呼吸し、落ち着いた声で一文ずつ、正確に読み上げます。"
)


# 出力する文字を指定
TARGET_TEXT = "こんにちは!私はAIです。すごい時代になりましたね。漢字を読むのは苦手です。"
OUTPUT_WAV = "my_clone_voice_06b.wav"

def main():
print("モデル読み込み中...")
model = Qwen3TTSModel.from_pretrained(
MODEL_ID,
device_map=DEVICE_MAP,
dtype=DTYPE,
)

print("ボイスクローン用プロンプト作成中...")
prompt = model.create_voice_clone_prompt(
ref_audio=REF_AUDIO,
ref_text=REF_TEXT,
x_vector_only_mode=False,
)

print("音声生成中...")
wavs, sr = model.generate_voice_clone(
text=TARGET_TEXT,
voice_clone_prompt=prompt,
language="Japanese",
)

print("ファイル保存中:", OUTPUT_WAV)
sf.write(OUTPUT_WAV, wavs[0], sr)

print("完了:", OUTPUT_WAV, "sr:", sr)

if __name__ == "__main__":
main()

実行:

(.venv) PS C:\Qwen3TTS> python qwen3_voice_clone_06b.py

同じフォルダに my_clone_voice_06b.wav ができるので、再生して「自分っぽく聞こえるか」をチェック。
ここまでOKなら、次は「高速化&サーバ化」に進みます。


4. 高速化:プロンプトキャッシュ+ピッチ維持の速度変更

Qwen3 TTS Base はそれなりに重いので、毎回 create_voice_clone_prompt を呼ぶと結構待たされます。
そこで:

  • 参照音声から作った voice_clone_prompt をファイルに保存
  • 起動時・次回以降はキャッシュを読み込むだけ
  • 生成された WAV に対して、ピッチ保持タイムストレッチでテンポだけ速く

というパターンにします。

これは棒読みちゃんサーバ側に最初から組み込むので、ここではイメージだけ押さえておけばOKです。


5. 棒読みちゃん互換サーバを組み立てる

いよいよ本番。C:\Qwen3TTS\bouyomi_qwen3_server.py を作成し、以下を丸ごと入れます。

# file: bouyomi_qwen3_server.py
import os
import pickle
import threading
import queue# file: bouyomi_qwen3_server.py
import os
import pickle
import threading
import queue
import time
from typing import Optional

import numpy as np
import torch
import soundfile as sf
import sounddevice as sd
from fastapi import FastAPI, Form
from fastapi.responses import PlainTextResponse
from qwen_tts import Qwen3TTSModel
from audiostretchy.stretch import stretch_audio # ピッチ保持タイムストレッチ

# ===== Qwen3-TTS 設定 =====

MODEL_ID = "Qwen/Qwen3-TTS-12Hz-1.7B-Base"
DEVICE_MAP = "cpu" # GPUがあれば "cuda:0"
DTYPE = torch.float32 # GPUなら torch.bfloat16

REF_AUDIO = "my_voice_sample.wav"
REF_TEXT = (
"春の朝、私は7時30分に起き、白いカップで熱いコーヒーを飲みました。"
"今日は2026年1月27日、天気は晴れ、気温は6度です。"
"静かな部屋で深呼吸し、落ち着いた声で一文ずつ、正確に読み上げます。"
)

OUTPUT_DIR = "outputs"

MODEL_SAFE_NAME = MODEL_ID.replace("/", "_").replace(":", "_")
PROMPT_CACHE_PATH = f"voice_prompt_{MODEL_SAFE_NAME}.pt"

SPEED_FACTOR = 1.2 # 1.2倍速(テンポだけ速く)
FADE_OUT_SEC = 0.15
TAIL_SILENCE_SEC = 0.25

SERVER_PORT = 50081 # 必要に応じて変更

# ===== グローバル状態 =====

app = FastAPI()

qwen_model: Optional[Qwen3TTSModel] = None
voice_clone_prompt = None

play_queue: "queue.Queue[dict]" = queue.Queue()
player_thread_started = False
player_thread_lock = threading.Lock()


# ===== Qwen3-TTS 関連 =====

def load_model() -> Qwen3TTSModel:
global qwen_model
if qwen_model is None:
print(f"[Qwen3] モデル読み込み中... ({MODEL_ID})")
qwen_model = Qwen3TTSModel.from_pretrained(
MODEL_ID,
device_map=DEVICE_MAP,
dtype=DTYPE,
)
return qwen_model


def build_or_load_voice_prompt(model: Qwen3TTSModel):
global voice_clone_prompt
if voice_clone_prompt is not None:
return voice_clone_prompt

if os.path.exists(PROMPT_CACHE_PATH):
print(f"[Qwen3] 既存プロンプトを読み込み中... ({PROMPT_CACHE_PATH})")
with open(PROMPT_CACHE_PATH, "rb") as f:
voice_clone_prompt = pickle.load(f)
return voice_clone_prompt

print("[Qwen3] ボイスクローン用プロンプト作成中...")
voice_clone_prompt = model.create_voice_clone_prompt(
ref_audio=REF_AUDIO,
ref_text=REF_TEXT,
x_vector_only_mode=False,
)

print(f"[Qwen3] プロンプトをキャッシュに保存中... ({PROMPT_CACHE_PATH})")
with open(PROMPT_CACHE_PATH, "wb") as f:
pickle.dump(voice_clone_prompt, f)

return voice_clone_prompt


def apply_tail_fade_and_silence(wav: np.ndarray, sr: int) -> np.ndarray:
"""末尾フェード+無音で「ブツッ」と切れるのを防ぐ。"""
wav = wav.astype(np.float32)

fade_samples = int(FADE_OUT_SEC * sr)
if fade_samples > 0 and fade_samples < wav.shape[0]:
fade = np.linspace(1.0, 0.0, fade_samples, endpoint=True, dtype=np.float32)
start = wav.shape[0] - fade_samples
wav[start:] *= fade

silence_samples = int(TAIL_SILENCE_SEC * sr)
if silence_samples > 0:
silence = np.zeros(silence_samples, dtype=np.float32)
wav = np.concatenate([wav, silence], axis=0)

return wav


def tts_generate_to_file(text: str, index: int) -> str:
"""テキストからWAVを生成してファイル保存し、そのパスを返す。"""
os.makedirs(OUTPUT_DIR, exist_ok=True)

model = load_model()
prompt = build_or_load_voice_prompt(model)

print(f"[Qwen3] 音声生成中... (#{index}) text={text[:30]}...")
wavs, sr = model.generate_voice_clone(
text=text,
voice_clone_prompt=prompt,
language="Japanese",
)
wav = wavs[0].astype(np.float32)

# 末尾処理
wav = apply_tail_fade_and_silence(wav, sr)

# [-1, 1] を 16bit PCM スケールにクリップ・変換
peak = np.max(np.abs(wav)) if wav.size > 0 else 0.0
if peak > 1.0:
wav = wav / peak
wav_int16 = np.int16(np.clip(wav, -1.0, 1.0) * 32767)

# 等速版を一時ファイルに16bitで保存
base_path = os.path.join(OUTPUT_DIR, f"bouyomi_base_{index:05d}.wav")
sf.write(base_path, wav_int16, sr, subtype="PCM_16")

# ピッチを維持したまま速度変更
if SPEED_FACTOR != 1.0:
ratio = 1.0 / SPEED_FACTOR # 速く → ratio < 1.0
print(f"[Qwen3] ピッチ保持タイムストレッチ中... ratio={ratio:.3f}")
out_path = os.path.join(OUTPUT_DIR, f"bouyomi_{index:05d}.wav")
stretch_audio(
base_path,
out_path,
ratio=ratio,
)
else:
out_path = os.path.join(OUTPUT_DIR, f"bouyomi_{index:05d}.wav")
os.replace(base_path, out_path)

print(f"[Qwen3] ファイル保存完了: {out_path}")
return out_path


def play_wav_file(path: str):
"""WAVファイルを再生して、再生完了までブロック。"""
print(f"[Player] 再生開始: {path}")
# ここも 16bit PCM 前提の読み出しでOK
data, sr = sf.read(path, dtype="int16")
data = data.astype(np.float32) / 32767.0 # sounddevice に float32 で渡す
sd.stop()
sd.play(data, sr)
sd.wait()
sd.stop()
print(f"[Player] 再生終了: {path}")


# ===== 再生スレッド =====

def player_loop():
index = 1
while True:
item = play_queue.get()
try:
text = item.get("text", "")
req_id = item.get("id", index)

if not text:
print("[Player] 空テキストをスキップ")
continue

print(f"[Player] キュー処理開始: id={req_id}, text={text[:30]}...")
wav_path = tts_generate_to_file(text, index=req_id)
play_wav_file(wav_path)
print(f"[Player] キュー処理完了: id={req_id}")
index += 1

except Exception as e:
print("[Player] エラー:", e)
finally:
play_queue.task_done()


def ensure_player_thread_running():
global player_thread_started
with player_thread_lock:
if not player_thread_started:
print("[Player] 再生スレッド起動")
t = threading.Thread(target=player_loop, daemon=True)
t.start()
player_thread_started = True


# ===== FastAPI エンドポイント =====

@app.post("/Talk", response_class=PlainTextResponse)
async def talk(
text: str = Form(...),
speed: int = Form(0),
tone: int = Form(0),
volume: int = Form(0),
voice: int = Form(0),
command: int = Form(0),
):
"""棒読みちゃんの /Talk っぽいインタフェース。"""
if not text:
return "NG"

ensure_player_thread_running()

req_id = int(time.time() * 1000)
item = {"text": text, "id": req_id, "created_at": time.time()}
play_queue.put(item)

print(f"[API] キュー追加: id={req_id}, text={text[:30]}... (queue size={play_queue.qsize()})")
return "OK"


@app.get("/health", response_class=PlainTextResponse)
async def health():
return "OK"


if __name__ == "__main__":
# 起動時にモデルとプロンプトをロード
m = load_model()
build_or_load_voice_prompt(m)

import uvicorn
uvicorn.run(app, host="0.0.0.0", port=SERVER_PORT)
import time
from typing import Optional

import numpy as np
import torch
import soundfile as sf
import sounddevice as sd
from fastapi import FastAPI, Form
from fastapi.responses import PlainTextResponse
from qwen_tts import Qwen3TTSModel
from audiostretchy.stretch import stretch_audio

# ===== Qwen3-TTS 設定 =====

MODEL_ID = "Qwen/Qwen3-TTS-12Hz-0.6B-Base"
DEVICE_MAP = "cpu" # GPUがあれば "cuda:0"
DTYPE = torch.float32 # GPUなら torch.bfloat16

REF_AUDIO = "my_voice_sample.wav"
REF_TEXT = (
"春の朝、私は7時30分に起き、白いカップで熱いコーヒーを飲みました。"
"今日は2026年1月27日、天気は晴れ、気温は6度です。"
"静かな部屋で深呼吸し、落ち着いた声で一文ずつ、正確に読み上げます。"
)

OUTPUT_DIR = "outputs"

MODEL_SAFE_NAME = MODEL_ID.replace("/", "_").replace(":", "_")
PROMPT_CACHE_PATH = f"voice_prompt_{MODEL_SAFE_NAME}.pt"

SPEED_FACTOR = 1.2 # 1.2倍速(テンポだけ速く)
FADE_OUT_SEC = 0.15
TAIL_SILENCE_SEC = 0.25

SERVER_PORT = 50081 # 使用ポート(必要なら変更)

# ===== グローバル状態 =====

app = FastAPI()

qwen_model: Optional[Qwen3TTSModel] = None
voice_clone_prompt = None

play_queue: "queue.Queue[dict]" = queue.Queue()
player_thread_started = False
player_thread_lock = threading.Lock()


# ===== Qwen3-TTS 関連 =====

def load_model() -> Qwen3TTSModel:
global qwen_model
if qwen_model is None:
print(f"[Qwen3] モデル読み込み中... ({MODEL_ID})")
qwen_model = Qwen3TTSModel.from_pretrained(
MODEL_ID,
device_map=DEVICE_MAP,
dtype=DTYPE,
)
return qwen_model


def build_or_load_voice_prompt(model: Qwen3TTSModel):
global voice_clone_prompt
if voice_clone_prompt is not None:
return voice_clone_prompt

if os.path.exists(PROMPT_CACHE_PATH):
print(f"[Qwen3] 既存プロンプトを読み込み中... ({PROMPT_CACHE_PATH})")
with open(PROMPT_CACHE_PATH, "rb") as f:
voice_clone_prompt = pickle.load(f)
return voice_clone_prompt

print("[Qwen3] ボイスクローン用プロンプト作成中...")
voice_clone_prompt = model.create_voice_clone_prompt(
ref_audio=REF_AUDIO,
ref_text=REF_TEXT,
x_vector_only_mode=False,
)

print(f"[Qwen3] プロンプトをキャッシュに保存中... ({PROMPT_CACHE_PATH})")
with open(PROMPT_CACHE_PATH, "wb") as f:
pickle.dump(voice_clone_prompt, f)

return voice_clone_prompt


def apply_tail_fade_and_silence(wav: np.ndarray, sr: int) -> np.ndarray:
"""末尾フェード+無音で「ブツッ」と切れるのを防ぐ。"""
wav = wav.astype(np.float32)

fade_samples = int(FADE_OUT_SEC * sr)
if fade_samples > 0 and fade_samples < wav.shape[0]:
fade = np.linspace(1.0, 0.0, fade_samples, endpoint=True, dtype=np.float32)
start = wav.shape[0] - fade_samples
wav[start:] *= fade

silence_samples = int(TAIL_SILENCE_SEC * sr)
if silence_samples > 0:
silence = np.zeros(silence_samples, dtype=np.float32)
wav = np.concatenate([wav, silence], axis=0)

return wav


def tts_generate_to_file(text: str, index: int) -> str:
"""テキストからWAVを生成してファイル保存し、そのパスを返す。"""
os.makedirs(OUTPUT_DIR, exist_ok=True)

model = load_model()
prompt = build_or_load_voice_prompt(model)

print(f"[Qwen3] 音声生成中... (#{index}) text={text[:30]}...")
wavs, sr = model.generate_voice_clone(
text=text,
voice_clone_prompt=prompt,
language="Japanese",
)
wav = wavs[0].astype(np.float32)

# 末尾処理
wav = apply_tail_fade_and_silence(wav, sr)

# 等速版を一時ファイルに保存
base_path = os.path.join(OUTPUT_DIR, f"bouyomi_base_{index:05d}.wav")
sf.write(base_path, wav, sr, subtype="FLOAT")

# ピッチを維持したまま速度変更
if SPEED_FACTOR != 1.0:
ratio = 1.0 / SPEED_FACTOR # 速く → ratio < 1.0
print(f"[Qwen3] ピッチ保持タイムストレッチ中... ratio={ratio:.3f}")
out_path = os.path.join(OUTPUT_DIR, f"bouyomi_{index:05d}.wav")
stretch_audio(
base_path,
out_path,
ratio=ratio,
)
else:
out_path = os.path.join(OUTPUT_DIR, f"bouyomi_{index:05d}.wav")
os.replace(base_path, out_path)

print(f"[Qwen3] ファイル保存完了: {out_path}")
return out_path


def play_wav_file(path: str):
"""WAVファイルを再生して、再生完了までブロック。"""
print(f"[Player] 再生開始: {path}")
data, sr = sf.read(path, dtype="float32")
sd.stop()
sd.play(data, sr)
sd.wait()
sd.stop()
print(f"[Player] 再生終了: {path}")


# ===== 再生スレッド =====

def player_loop():
index = 1
while True:
item = play_queue.get()
try:
text = item.get("text", "")
req_id = item.get("id", index)

if not text:
print("[Player] 空テキストをスキップ")
continue

print(f"[Player] キュー処理開始: id={req_id}, text={text[:30]}...")
wav_path = tts_generate_to_file(text, index=req_id)
play_wav_file(wav_path)
print(f"[Player] キュー処理完了: id={req_id}")
index += 1

except Exception as e:
print("[Player] エラー:", e)
finally:
play_queue.task_done()


def ensure_player_thread_running():
global player_thread_started
with player_thread_lock:
if not player_thread_started:
print("[Player] 再生スレッド起動")
t = threading.Thread(target=player_loop, daemon=True)
t.start()
player_thread_started = True


# ===== FastAPI エンドポイント =====

@app.post("/Talk", response_class=PlainTextResponse)
async def talk(
text: str = Form(...),
speed: int = Form(0),
tone: int = Form(0),
volume: int = Form(0),
voice: int = Form(0),
command: int = Form(0),
):
"""棒読みちゃんの /Talk っぽいインタフェース。"""
if not text:
return "NG"

ensure_player_thread_running()

req_id = int(time.time() * 1000)
item = {"text": text, "id": req_id, "created_at": time.time()}
play_queue.put(item)

print(f"[API] キュー追加: id={req_id}, text={text[:30]}... (queue size={play_queue.qsize()})")
return "OK"


@app.get("/health", response_class=PlainTextResponse)
async def health():
return "OK"


if __name__ == "__main__":
# 起動時にモデルとプロンプトをロード
m = load_model()
build_or_load_voice_prompt(m)

import uvicorn
uvicorn.run(app, host="0.0.0.0", port=SERVER_PORT)

6. サーバの起動とテスト

6-1. サーバ起動

(.venv) PS C:\Qwen3TTS> python bouyomi_qwen3_server.py
  • 初回はモデルのダウンロード&プロンプト作成が入るので少し待ちます。
  • Application startup complete. 的なメッセージが出たら待ち受け開始です。
  • Windowsファイヤウォールのメッセージが出たら許可してください。

6-2. テストで /Talk を叩く

別の PowerShell から:

C:\Qwen3TTS> Invoke-WebRequest -Uri "http://localhost:50081/Talk" -Method POST -Body @{ text = "テスト1です。Qwen3-TTSで喋っています。" }

PS C:\Qwen3TTS> Invoke-WebRequest -Uri "http://localhost:50081/Talk" -Method POST -Body @{ text = "テスト2です。キューに溜めた2件目です。" }
  • サーバ側コンソールには [API] キュー追加 → [Player] キュー処理開始 → [Qwen3] 音声生成中... → [Player] 再生開始 というログが順番に流れます。
  • 音声は 1件ずつ、前の再生が終わってから次が流れます。
  • WAV は outputs フォルダに bouyomi_*.wav として全部残るので、あとから確認も可能です。

7. 面白いですね、これ

AI(LLM)が出てきて、一気にいろいろなものが進んできており、すごい時代になったものです。

今回の構築では「ローカルの Windows マシンが、自分専用の“喋るエージェント”になった」という点がすごいことだと思います。

Qwen3-TTS-12Hz-1.7B-Base は、クラウドAPIを叩かなくても、自分の声そっくりの音声をかなり高いクオリティで生成してくれます。そこに FastAPI で HTTP サーバをかぶせ、棒読みちゃん互換の /Talk エンドポイントを用意したことで、既存のスクリプトやツールから「棒読みちゃんに送るノリのまま」自分クローンボイスを呼び出せるようになりました。

内部では、参照音声+文字起こしから生成したボイスクローンプロンプトをキャッシュしておき、毎回のリクエストではそれを再利用して低オーバーヘッドで生成する構成にしています。生成後の WAV には末尾のフェードアウトと無音を足し、さらにタイムストレッチでピッチを維持したままテンポだけ速くしているので、「早口だけどブツ切れない、ちゃんと滑らかな読み上げ」になっているのもポイントです。

ニュース読み上げ・チャットログ読み上げ・通知読み上げといった用途に、そのまま流用しやすい小さな音声基盤になりました。ここに「テキスト整形」や「感情プロンプト」を足していけば、もっと人間らしい喋り方や状況に応じたトーンの切り替えも狙えます。今回の構成をベースにして、

  • 別の声(別の参照音声)を増やして複数ボイスに対応する
  • クライアント側から voice や speed パラメータを受けて切り替える
  • OBS や配信ソフトと組み合わせて「自分ボイスの読み上げ配信」をする

といった方向にも簡単に伸ばしていけるはずです。

「棒読みちゃんを自分で作り直した」みたいな感覚で遊べるので、興味のある人はぜひ自分の Windows 環境でも試して、少しずつ好みの“自分ボイスエージェント”に育ててみてください。

Excelでセルを右クリックしてもメニューが表示されない!反応しない時の対処法

「Excelでセルを右クリックしても、メニューが出てこない…」
「コピーや貼り付けをしたいのに、右クリックが反応しなくて困っている」

突然このような状態になると、作業効率がガタ落ちして焦りますよね。再起動しても直らない場合、Excelの設定ファイルが何らかの原因で不具合を起こしている可能性があります。
そんなわけで、今回はExcelの設定ファイル(.xlb)をリセットして、右クリックメニューを復活させる方法です。手順はとても簡単です。


原因:設定ファイル(.xlb)の不具合とは

Excelには、メニューバーやツールバーの表示設定を保存している「.xlbファイル」というものがあります。

このファイルが破損したり不具合を起こしたりすると、「右クリックメニューが出ない」「リボンがおかしい」といったトラブルが発生することがあります。

このファイルを「リネーム(名前の変更)」もしくは「削除」して、Excelに新しい設定ファイルを自動生成させることで解決できます。


対処手順:Excel15.xlb をリセットする

まず作業を行う前に、必ずExcel(および全てのOffice製品、Office 365等)を終了してください。

手順1. 該当のフォルダを開く

エクスプローラーを開き、以下のアドレスへアクセスします。

場所:
C:\Users\%username%\AppData\Roaming\Microsoft\Excel

【簡単な開き方】

  1. キーボードの [Windows]キー + [R]キー を同時に押して「ファイル名を指定して実行」を開きます。
  2. 名前の欄に %AppData%\Microsoft\Excel と入力(コピペ)して「OK」を押します。
  3. 目的のフォルダが一発で開きます。

手順2. 「Excel15.xlb」を探す

開いたフォルダの中に、Excel15.xlb というファイルがあるはずです。
(※PCの設定によっては拡張子が表示されず、単に Excel15 と表示されている場合や、アイコンがExcelのものになっている場合があります)

※補足バージョンによってはファイル名の数字が異なる場合がありますが(例:Excel.xlb など)、拡張子が .xlb のファイルを探してください。

手順3. ファイル名を変更(リネーム)または削除する

見つけたファイルを操作します。安全のため、削除ではなく「名前の変更」をおすすめします。

  • 推奨:名前を変更する
    ファイルの上で右クリックし「名前の変更」を選択。
    Excel15.xlb → @Excel15.xlb
    のように、先頭に@をつけるなどして名前を変えます(名前は何でもOKです)。
    ※もし直らなかった場合に、名前を戻せば元の状態に復元できるため安全です。
  • 削除する場合
    Excel15.xlb を選択して削除(ゴミ箱へ)します。

手順4. Excelを起動して確認する

ファイルの名前変更(または削除)が終わったら、Excelを通常通り起動してください。

起動時に、Excelは「あれ?設定ファイルがないぞ?」と認識し、自動的に正常な新しい設定ファイルを作成します。

セルを右クリックして、メニューが無事に表示されれば解決です!

Microsoft 365 Copilotを起動しても真っ白で表示されない?場合の対処法

Microsoft 365 Copilotは、WordやExcel、Teamsなどの日常的なアプリケーションにAIの力を統合し、生産性を飛躍的に向上させてくれるアシスタントアプリです。しかし起動しようとした際に、画面が真っ白になって何も表示されず、どうにもできない事がまれにあります。

M365 Copilotを起動すると、上の画像のようにアプリケーションのウィンドウは表示されるものの、中身が真っ白で何も操作できない、という症状が発生することがあります。再起動しても改善しない場合がほとんどで、ちょっと途方に暮れてしまいます・・・。

簡単な解決策:タスクバーからCopilotを完全に終了する

複雑な設定変更や再インストールを試す前に、ぜひ以下の手順を試してみてください。多くの場合、この簡単な操作で問題が解決する可能性があります。

手順

  1. タスクバーを確認する
    Windowsの画面下部にあるタスクバーに表示されている、Microsoft 365のアイコン(カラフルなデザインのアイコン)を探します。
  2. アイコンを右クリックする
    Microsoft 365のアイコンをマウスで右クリックします。
  3. 「Microsoft 365 Copilot を終了する」を選択する
    表示されたメニューの中から、一番上の「Microsoft 365 Copilot を終了する」という項目を探し、クリックします。これにより、バックグラウンドで動いているCopilotのプロセスが完全に終了します。
  4. 再度Copilotを起動する
    改めて、通常の手順でMicrosoft 365 Copilotを起動してみてください。

この手順で、前回起動時の不具合がリセットされ、正常にログイン画面が表示されて利用できるようになるケースが多いです。Microsoft 365 Copilotが白い画面になって表示されないという問題に直面した際は、まず慌てずにタスクバーからアプリケーションを完全に終了させる方法をお試しください。このシンプルで迅速な手順が、多くの場合で最も効果的な解決策となる可能性があります。

YAMAHAルーターRTXのコンソール接続で文字化け?TeraTermの設定変更で解決!

古いヤマハルーターをTeraTermで操作している際に、日本語の表示が文字化けしてしまい、困った経験はありませんか?設定情報を確認しようとしても、意味不明な文字列が表示されては作業になりません。
特にコマンドを間違えた時など・・・わけワカメ状態になってしまいます。

この文字化けは、簡単な設定変更で解決できます。YAMAHAルーターで文字化けが起こる原因と、TeraTermの文字セットを変更して正しく日本語を表示させる手順を解説します。

文字化けの原因は文字コードの違い

ヤマハルーターのコンソールにTeraTermで接続した際に文字化けが発生する主な原因は、文字コードの不一致です。

  • ヤマハルーター側: 日本語の文字コードとして「Shift-JIS (SJIS)」を使用しています。
  • TeraTerm側: デフォルトの文字コードが「UTF-8」になっています。

このように、ルーターが送信するSJISのデータを、TeraTermがUTF-8として解釈しようとするために、文字が正しく表示されず「文字化け」が発生してしまうのです。

TeraTermの文字セットを変更する簡単ステップ

この問題を解決するには、TeraTerm側の文字コード設定をヤマハルーターに合わせて「SJIS」に変更します。手順は以下の通りです。

  1. 設定画面を開く
    TeraTermのメニューバーから「設定(S)」を選択し、ドロップダウンメニューから「端末(T)…」をクリックします。
  2. 文字コードを変更する
    「端末の設定」画面が開いたら、「漢字-受信(K)」と「漢字-送信(J)」の項目を探します。デフォルトでは「UTF-8」になっていることが多いので、両方のプルダウンメニューをクリックし、「SJIS」を選択してください。
  3. 設定を保存する
    変更が完了したら、「OK」ボタンをクリックして設定画面を閉じます。これで、コンソール上の日本語が正しく表示されるようになります。次回以降もこの設定を維持したい場合は、メニューバーの「設定(S)」から「設定の保存(S)…」を選択し、設定ファイル(TERATERM.INI)に上書き保存しておきましょう。

ヤマハルーター使用時の文字化けは、TeraTermの文字コード設定を「SJIS」に変更するだけで簡単に解消できます。急なトラブルにも慌てず対応できるよう、この手順を覚えておくと便利ですよ。

Windows 11で「アプリケーションがプロビジョ-ニングされていません」とエラーが表示された時の解決方法

Windowsを使っていると、突如として「アプリケーションがプロビジョニングされていません。」という見慣れないエラーメッセージに遭遇することがあります。これは、Microsoft Store アプリが現在のユーザー向けに正しく設定されていない状態を示すものです。

エラーの原因:「プロビジョニング」とは?

このエラーメッセージを理解するために、まずは「プロビジョニング」という言葉の意味を簡単に説明します。

Windowsにおけるプロビジョニングとは、OSにプレインストールされているアプリケーションを、各ユーザーが利用できるように準備・設定するプロセスを指します。通常、Windowsに新しいユーザーアカウントでサインインすると、このプロセスが自動的に実行されます。

しかし、何らかの理由でこの自動設定に失敗したり、アプリケーションのファイルが破損したりすると、「アプリケーションがプロビジョニングされていません」というエラーが発生します。

PowerShellを使った解決手順

この問題を解決するには、管理者権限でPowerShellを起動し、特定のコマンドを実行して、Windowsストアアプリを再登録する必要があります。どこまで壊れているかわからないため、すべてのパッケージを対象に再導入するのがおすすめです。

手順1:PowerShellを管理者として実行する

  1. 画面左下の「スタートボタン」を右クリックします。
  2. 表示されたメニューから「Windows PowerShell (管理者)」または「ターミナル (管理者)」を選択します。
  3. 「ユーザーアカウント制御」の画面が表示されたら、「はい」をクリックします。

手順2:コマンドをコピーして実行する

開いた青い(または黒い)PowerShellの画面に、以下のコマンドをコピー&ペーストし、Enterキーを押して実行します。

Get-AppxPackage -AllUsers | ForEach-Object {
    Add-AppxPackage -DisableDevelopmentMode -Register "$($_.InstallLocation)\AppXManifest.xml"
}

このコマンドは、コンピュータにインストールされている全てのWindowsストアアプリを取得し、一つずつ再登録を試みるものです。処理には数分かかる場合があります。

手順3:処理の完了を待つ

コマンドを実行すると、画面に多くのテキストが流れます。処理が完了すると、入力待ちの状態に戻ります。これで、アプリの再登録は完了です。念のためPC再起動して、エラーが表示されなくなったか確認してください。

補足と注意点

  • 管理者権限が必須: この操作には必ず管理者権限のPowerShellが必要です。
  • エラーが表示される場合がある: コマンドの実行中に、赤い文字でエラーメッセージが表示されることがあります。これは、一部のシステムアプリケーションなど、再登録できないアプリがあるために表示されるものです。多くの場合、これらのエラーは無視しても問題ありません。
  • コマンドの意味: Get-AppxPackageでアプリの情報を取得し、Add-AppxPackageでその情報をもとにアプリを再登録(再インストールに近い処理)しています。

「アプリケーションがプロビジョニングされていません」というエラーは、主にWindowsストアアプリのユーザーごとの設定に問題がある場合に発生します。今回のPowerShellのコマンドを実行することで、これらのアプリを一括で再登録し、問題を解決できる可能性が高いです。同様のエラーでお困りの際は、ぜひこの手順をお試しください。

リモートデスクトップ接続でコピー&ペーストやファイル転送ができなくなった時の簡単解決策

リモートワークやサーバー管理で欠かせないリモートデスクトップ接続。しかし、これまで問題なく使えていたのに、突然ローカルPCと接続先サーバーとの間でコピー&ペーストができなくなったり、ファイルのドラッグ&ドロップ(ファイル転送)が機能しなくなったりしてお困りではありませんか?
仕方なしにテキストファイルにコピペする内容を保存して、ファイ共有で開いたCドライブに保存して、そこからさらにコピペするなど回避策はあるかと思いますがメンドイですよね。

  • ローカルPCでコピーしたテキストが、リモートデスクトップ先のサーバーに貼り付けられない。
  • サーバー上でコピーしたエラーメッセージなどを、ローカルPCのメモ帳に貼り付けられない。
  • ローカルPCにあるファイルを、リモートデスクトップの画面へドラッグ&ドロップしてコピーできない。
  • これまで出来ていたファイルのコピー&ペーストでの転送が、急にできなくなった。

これらの問題は、多くの場合、リモートデスクトップのクリップボード機能を担うプロセスに一時的な不具合が発生していることが原因のことが多いです。
Windows 11の場合は、違う原因のことが多いです。

トラブルの原因は「rdpclip.exe」です。たぶん。

リモートデスクトップ接続におけるクリップボードの共有(コピー&ペースト機能)は、「rdpclip.exe」というプロセスによって管理されています。このプロセスは「Remote Desktop Clipboard」の略で、ローカルPCとリモートPCとの間でクリップボードのデータを橋渡しする重要な役割を担っています。

何らかの理由でこのrdpclip.exeが応答しなくなったり、正常に動作しなくなったりすると、クリップボードの同期が停止し、結果としてコピー&ペーストやファイル転送ができなくなるのです。

【解決策】rdpclip.exeを再起動する

この問題を解決するための最も手軽で効果的な方法が、原因となっているrdpclip.exeプロセスを再起動することです。以下の手順で、接続先のリモートサーバー上で操作を行ってください。

手順1:タスクマネージャーを起動する

まず、リモートデスクトップで接続しているサーバー側でタスクマネージャーを起動します。
キーボードの Ctrl + Shift + Esc を押すか、タスクバーを右クリックして「タスクマネージャー」を選択してください。

手順2:「rdpclip.exe」のタスクを終了する

  1. タスクマネージャーが開いたら、「詳細」タブをクリックします。
  2. プロセスの一覧が表示されるので、「名前」列を基準にrdpclip.exeを探します。
  3. rdpclip.exeを見つけたら、それを選択し、右下にある「タスクの終了」ボタンをクリックします。

もしrdpclip.exeが一覧にない場合は、すでにプロセスが異常終了している可能性があります。その場合は、次の手順3に進んでください。

手順3:新しいタスクとして「rdpclip.exe」を実行する

  1. タスクマネージャーの左上にある「ファイル」メニューをクリックし、「新しいタスクの実行」を選択します。
  2. 「新しいタスクの作成」というウィンドウが表示されたら、入力欄に rdpclip.exe と入力し、「OK」ボタンをクリックします。

これで、rdpclip.exeプロセスが新しく起動され、クリップボードの機能がリフレッシュされます。

手順4:動作を確認する

操作完了後、再度リモートデスクトップ接続を行ってみてください。
ローカルPCとサーバー間でテキストのコピー&ペーストやファイルの転送が正常に行えるか確認してください。多くの場合、この手順で問題は解決するはずです。

それでも解決しない場合の確認事項

上記のrdpclip.exeの再起動を試しても問題が解決しない場合は、以下の点も確認してみてください。

  • リモートデスクトップ接続の設定: 接続元のPCでリモートデスクトップ接続アプリを起動し、「オプションの表示」から「ローカルリソース」タブを開きます。「クリップボード」にチェックが入っているか確認してください。
  • サーバーのポリシー設定: 企業のセキュリティポリシーなどにより、サーバー側でクリップボードのリダイレクト機能が意図的に無効化されている場合があります。この場合は、システム管理者に確認が必要です。

リモートデスクトップ接続中のクリップボードトラブルは、作業効率を大きく低下させる厄介な問題ですが、rdpclip.exeの再起動という簡単な手順で解決できることがほとんどです。サーバー自体の再起動は不要なため、他のユーザーへの影響も最小限に抑えられます。同様の症状でお困りの際は、ぜひこの方法をお試しください。

【解決策】BitLockerのPIN/パスワードの入力画面が真っ青なときの対応方法

Windowsのドライブ暗号化機能「BitLocker」を使用していて、PCを起動すると表示される青い画面に文字がなく、PINやパスワードの入力欄が出ない。しかし、PIN/パスワードを入力後にEnterキーを押すとWindowsが正常に起動する、という不思議な現象に遭遇することがあります。

この現象の多くは、Windowsの起動情報を管理しているブート構成データ(BCD)や、UEFIファームウェアが使用するEFIシステムパーティション(ESP)内のファイルが、何らかの理由で破損または不整合を起こしていることが原因のことが多いです。BCDの言語パックが読み込めない場合もこのような事象になります。

BitLockerを有効にすると、OSが起動する前に認証を求める「プリブート認証」画面が表示されます。しかし、ブート関連のファイルに問題があると、この認証画面を正しく描画できず、背景の青い画面だけが表示されてしまうのです。

キーボード入力で先に進めるのは、画面には表示されていないものの、バックグラウンドでは認証プロセスが待機状態になっているためです。

コマンドを使った修復手順

この問題を解決するには、ブート構成データを再構築するのが最もお手軽な方法です。Administrators権限(管理者)を持つアカウントを使って、以下の手順に従って、コマンドプロンプトで修復作業を行います。


ステップ1:管理者としてコマンドプロンプトを起動

  1. Windowsのスタートボタンを右クリックし、「Windows PowerShell (管理者)」または「コマンドプロンプト (管理者)」を選択します。
  2. 「ユーザーアカウント制御」の画面が表示されたら、「はい」をクリックします。

ステップ2:EFIシステムパーティション(ESP)をマウント

まず、ブート関連のファイルが格納されているEFIシステムパーティションに、一時的なドライブ文字(ここでは Y:)を割り当てます。

以下のコマンドを入力し、Enterキーを押してください。

mountvol y: /s
このコマンドは、システムパーティションをYドライブとしてマウント(接続)するものです。

ステップ3:ブート構成データ(BCD)を再構築

次に、Windowsのブートファイルを再構築し、システムパーティションに正しくコピーします。

以下のコマンドを入力し、Enterキーを押してください。

bcdboot C:\Windows /s y: /f uefi /l ja-jp

コマンドが成功すると、「ブート ファイルは正常に作成されました。」と表示されます。

コマンドの意味:

  • bcdboot C:\Windows: C:\Windows フォルダにある起動ファイルを使用します。
  • /s y:: ブートファイルを書き込む先のシステムパーティション(先ほどマウントしたYドライブ)を指定します。
  • /f uefi: ファームウェアのタイプがUEFIであることを指定します。
  • /l ja-jp: 言語(ロケール)を日本語に設定します。

ステップ4:PCを再起動

コマンドプロンプトを閉じて、PCを再起動します。

再起動後、BitLockerのPINやパスワードを入力する画面が正常に表示されるか確認してください。

Windows 11が勝手にスリープになる原因は?「スリープの理由: System Idle」の場合の対処法

Windows 11 ProでLM Studioを使ってAIで遊ぶときに、常時起動させるために電源プランで「高パフォーマンス」を選択して一晩放置してみました。
朝起きてみるとPCのファンが回っておらず、起動させてイベントビューアーを見たら、スリープの理由が『System Idle』になっているという事象に遭遇。この記事では、Windows 11が意図せずスリープ状態になる問題、特にその原因が「System Idle」である場合の具体的な解決策を記載します。

記録されるイベントログは下記の内容でした。

ログの名前:         System
ソース:           Microsoft-Windows-Kernel-Power
日付:            2025/10/07 22:15:16
イベント ID:       42
タスクのカテゴリ:      (64)
レベル:           情報
キーワード:         (1024),(4)
ユーザー:          N/A
コンピューター:       LAPTOP-BQB9QPJI
説明:
システムがスリープ状態になります。

スリープの理由: System Idle

なぜ「System Idle」で勝手にスリープするのか?

Windowsには、ユーザーがPCを操作していないアイドル状態が一定時間続くと、自動的にスリープに移行する「システム無人スリープタイムアウト」という機能があります。これは、Wake On LAN(WOL)や特定のイベントによってPCが自動で起動した後、何も操作がない場合に再びスリープさせるための省電力機能です。

しかし、この機能が通常のスリープからの復帰後にも意図せず作動してしまい、デフォルトで設定されている「2分」という短い時間で再びスリープに入ってしまうことがあります。

この設定は通常、電源オプションの詳細設定画面には表示されていません。そのため、ユーザーが気づかないうちにこの機能によってPCがスリープ状態になってしまうのです。
特に、メーカー製PCの場合に多く出る事象で、インストールメディアを使用して自分でOSを導入した場合はあまり出ることはないと思います。

レジストリ編集で電源プランに「システム無人スリープタイムアウト」を表示

この問題を解決するには、レジストリを編集して、電源オプションでは通常隠されている「システム無人スリープタイムアウト」の設定を表示させ、その時間を変更する必要があります。

手順1:レジストリエディタを起動する

  1. タスクバーの検索ボックスに「regedit」または「レジストリエディタ」と入力します。
  2. 表示された「レジストリエディタ」をクリックして起動します。
  3. 「ユーザーアカウント制御」のダイアログボックスが表示されたら、「はい」をクリックします。

手順2:目的のレジストリキーに移動する

  1. レジストリエディタの上部にあるアドレスバーに、以下のパスをコピーして貼り付け、Enterキーを押します。
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\PowerSettings\238C9FA8-0AAD-41ED-83F4-97BE242C8F20\7bc4a2f9-d8fc-4469-b07b-33eb785aaca0

手順3:「Attributes」の値を変更する

  1. 右側のペインに表示されている「Attributes」という名前の項目をダブルクリックします。
  2. 「値のデータ」のボックスが表示されます。現在の値が「1」になっているのを「2」に変更し、「OK」ボタンをクリックします。

この操作により、電源オプションの詳細設定に隠されていた「システム無人スリープタイムアウト」の項目が表示されるようになります。

電源オプションでスリープ時間を変更する

レジストリの編集が完了したら、最後に電源オプションでスリープまでの時間を設定します。

  1. タスクバーの検索ボックスに「電源プランの編集」と入力し、表示された項目をクリックします。
  2. 「プラン設定の編集」ウィンドウが表示されたら、「詳細な電源設定の変更」をクリックします。
  3. 「電源オプション」ダイアログが表示されます。「スリープ」の項目を展開すると、先ほど表示させた「システム無人スリープタイムアウト」が現れます。
  4. この項目を展開し、「バッテリ駆動」と「電源に接続」の両方の時間を、デフォルトの「2分」から任意の値、例えば「0」を入力すると、この機能が無効になります。
  5. 「適用」→「OK」の順にクリックして設定を保存します。

これで、意図せず短時間でスリープしてしまう問題が解決されるはずです。