junkerstock
 代読テスト03 

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>iPhone対応・音声変換</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; text-align: center; background: #f0f0f0; margin: 0; padding: 20px; }
.container { display: flex; flex-direction: column; gap: 20px; align-items: center; max-width: 600px; margin: 0 auto; }

/* ボタンのデザイン調整 */
.big-btn {
width: 100%;
padding: 30px 20px;
font-size: 22px;
font-weight: bold;
color: white;
border: none;
border-radius: 20px;
box-shadow: 0 4px 10px rgba(0,0,0,0.2);
cursor: pointer;
transition: transform 0.1s;
-webkit-tap-highlight-color: transparent; /* タップ時のハイライトを消す */
}
.big-btn:active { transform: scale(0.96); }

#recBtn { background-color: #ff4757; }
#playBtn { background-color: #2ed573; display: none; }

select { width: 100%; padding: 15px; font-size: 16px; border-radius: 10px; border: 1px solid #ccc; background: white; }
#output { width: 90%; background: white; padding: 20px; border-radius: 15px; min-height: 80px; font-size: 20px; border: 1px solid #ddd; text-align: left; }
.label { font-size: 14px; color: #666; margin-bottom: 5px; width: 100%; text-align: left; }
</style>
</head>
<body>

<div class="container">
<h2>音声変換器</h2>

<div style="width:100%">
<div class="label">声の種類を選んでください</div>
<select id="voiceSelect"><option>読み込み中...</option></select>
</div>

<button id="recBtn" class="big-btn">① 声を録音する</button>

<div style="width:100%">
<div class="label">聞き取り結果:</div>
<div id="output">(ここに文字が出ます)</div>
</div>

<button id="playBtn" class="big-btn">② 別の声で再生!</button>
</div>

<script>
const voiceSelect = document.getElementById('voiceSelect');
const recBtn = document.getElementById('recBtn');
const playBtn = document.getElementById('playBtn');
const output = document.getElementById('output');

// ★修正:絞り込んだ声を保存する配列を用意
let availableVoices = [];
let recognizedText = "";

// 音声リスト読み込み
function loadVoices() {
// 全ての声を取得
const allVoices = window.speechSynthesis.getVoices();
if (allVoices.length === 0) return;

// ★修正:日本語と英語だけに絞り込み、それを availableVoices に保存
availableVoices = allVoices.filter(v => v.lang.includes('ja') || v.lang.includes('en'));

// 利用可能な声がない場合のガード
if (availableVoices.length === 0) {
voiceSelect.innerHTML = '<option>利用可能な声がありません</option>';
return;
}

// セレクトボックスを作る
voiceSelect.innerHTML = availableVoices
.map((voice, index) => `<option value="${index}">${voice.name} (${voice.lang})</option>`)
.join('');
}

// iOSはタイミングによって読み込みが遅れるため、複数回発火を想定
window.speechSynthesis.onvoiceschanged = loadVoices;
loadVoices();

// 音声認識の設定
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;

// ★修正:非対応ブラウザ(LINE内ブラウザ等)への対策
if (!SpeechRecognition) {
output.innerText = "このブラウザは音声認識に対応していません。Safariで開いてください。";
recBtn.disabled = true;
recBtn.style.backgroundColor = "#ccc";
} else {
const recognition = new SpeechRecognition();
recognition.lang = 'ja-JP';
recognition.interimResults = false; // 確定した結果だけ取得

recBtn.onclick = () => {
// ★修正:録音前に一度音声をキャンセル(iOSのオーディオコンテキスト対策)
window.speechSynthesis.cancel();

try {
recognition.start();
recBtn.innerText = "聞いています...";
recBtn.style.opacity = "0.7";
playBtn.style.display = "none";
} catch(e) {
// 連打防止:すでに開始されている場合のエラー無視
console.log("Recognition already started");
}
};

recognition.onresult = (event) => {
recognizedText = event.results[0][0].transcript;
output.innerText = recognizedText;

recBtn.innerText = "① 声を録音する";
recBtn.style.opacity = "1";
playBtn.style.display = "block";
};

recognition.onend = () => {
// 無言で終わった場合などのリセット
if(recBtn.innerText === "聞いています...") {
recBtn.innerText = "① 声を録音する";
recBtn.style.opacity = "1";
}
};

recognition.onerror = (event) => {
console.error(event.error);
recBtn.innerText = "エラー:もう一度";
recBtn.style.opacity = "1";
if (event.error === 'not-allowed') {
alert("マイクの使用が許可されていません。設定を確認してください。");
}
};
}

playBtn.onclick = () => {
if (!recognizedText) return;

const uttr = new SpeechSynthesisUtterance(recognizedText);

// ★修正:絞り込んだリスト(availableVoices)から選択する
const selectedVoice = availableVoices[voiceSelect.value];
if (selectedVoice) {
uttr.voice = selectedVoice;
}

// ピッチと速度
uttr.pitch = 1.5; // 少し高めに
uttr.rate = 1.0;

window.speechSynthesis.cancel();
window.speechSynthesis.speak(uttr);
};
</script>

</body>
</html>


使用変数

allVoices
availableVoices
backgroundColor
charset
class
content
disabled
display
error
id
innerHTML
innerText
interimResults
lang
length
loadVoices -------( Function )
name
onclick
onend
onerror
onresult
onvoiceschanged
opacity
output
pitch
playBtn
rate
recBtn
recognition
recognizedText
scalable
scale
selectedVoice
SpeechRecognition
style
uttr
v
value
voice
voiceSelect
width