0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ブラウザだけで動くローカル音声認識+ChatGPTプロンプト統合ツールの実装

Posted at

1. 概要

本記事では、Web Speech API を利用して ブラウザのみで日本語・英語の音声認識を実行できる Web アプリの実装構成を解説する。Google アカウントや API キーは不要で、Chrome ブラウザ上で完結する。
また、取得した音声文字起こしをそのまま ChatGPT に投げるためのプロンプトテンプレートを自動挿入する領域も備えており、実務での議事録作成・インタビュー書き起こし・研修記録など幅広い用途に適用できる。


2. システム構成

2.1 使用技術

  • Web Speech API(SpeechRecognition)
     音声認識。Chromeでのみ安定動作。
  • JavaScript(ES6)
     認識制御、UI、テキスト処理。
  • HTML/CSS
     UIレイアウト。
  • ローカル保存(Blob + a.download)
     文字起こしテキストを、PCへ .txt として保存。

2.2 特徴

  • APIキー不要・完全ローカル処理
  • 日本語 / 英語を即時切替可能
  • 認識結果の「二重書き込み」を防止
  • テキスト領域へ ChatGPT 用プロンプトを自動挿入
  • ボタン操作のみで
     - 録音開始 / 停止
     - 保存
     - コピー
     が可能

3. 実装のポイント

3.1 音声認識の初期化

const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
recognition = new SR();
recognition.lang = lang;
recognition.interimResults = false;
recognition.continuous = true;

interimResults を false にする理由は、
「途中経過が何回も返ってくる → 同じ文章が重複して並ぶ」
という現象を防ぐためである。


3.2 二重書き込み防止ロジック

let lastFinalText = "";

if (finalText === lastFinalText) {
    return;
}
lastFinalText = finalText;

Chrome の Web Speech API は同一文を繰り返し返す挙動があるため、
「最後に確定した文と同じなら無視」
というロジックで対処している。


3.3 言語切り替え

langSelect.onchange = () => {
    const lang = langSelect.value;
    const wasRunning = isRecognizing;

    recognition.stop();
    initRecognition(lang);
    if (wasRunning) recognition.start();

    output.value += `\n--- 言語を変更しました (${lang}) ---\n`;
};

録音中でも即時切替できるよう、
停止 → 再初期化 → 再開
のフローで処理している。


3.4 文字起こし結果の保存

const blob = new Blob([output.value], { type: "text/plain" });
a.download = "transcript_" + new Date().toISOString().replace(/[:.]/g, "-") + ".txt";

ローカルファイルとしてダウンロードできるため、
議事録・授業記録・研究インタビューなどのログ管理に適している。


4. ChatGPT 向けプロンプトテンプレート

初期状態で textarea に埋め込まれているテンプレート:

【ChatGPTへの依頼】
以下の「文字起こしテキスト」を自然な日本語に整形し、
意味が通るように段落化・句読点挿入・誤認識修正をしてください。

必要なら:
・要約
・話者ごとの整理
・読みやすい文章化
なども行ってください。

------------------------------
【文字起こしテキストここから】

これにより、録音停止後すぐ ChatGPT に貼り付けて処理できる。

実務では以下の作業が効率化される:

  • 会議議事録の整形
  • 研修/授業の要約
  • インタビュー原稿化
  • 研究の聞き取りデータの整理

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Local Speech Recognition + ChatGPT Prompt</title>
<style>
body {
    font-family: sans-serif;
    margin: 20px;
    background: #f5f5f5;
}
button, select {
    font-size: 18px;
    padding: 10px 20px;
    margin-right: 10px;
}
textarea {
    width: 100%;
    height: 450px;
    font-size: 16px;
    padding: 10px;
}
</style>
</head>
<body>

<h2>ローカル音声認識(ChatGPTプロンプト付き / 日本語・英語切替)</h2>

<label>言語:</label>
<select id="langSelect">
    <option value="ja-JP">日本語</option>
    <option value="en-US">English</option>
</select>

<button id="startBtn">開始</button>
<button id="stopBtn">停止</button>
<button id="saveBtn">保存</button>
<button id="copyBtn">コピー</button>

<p>
※Google アカウント不要・APIキー不要<br>
※Chrome推奨(Safari/Firefox不可)
</p>

<textarea id="output">
【ChatGPTへの依頼】
以下の「文字起こしテキスト」を自然な日本語に整形し、
意味が通るように段落化・句読点挿入・誤認識修正をしてください。

必要なら:
・要約
・話者ごとの整理
・読みやすい文章化
なども行ってください。

------------------------------
【文字起こしテキストここから】
</textarea>

<script>
let recognition;
let isRecognizing = false;

// ★ 重複防止用:最後に確定した文
let lastFinalText = "";

// 初期化
function initRecognition(lang) {
    try {
        const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
        recognition = new SR();
        recognition.lang = lang;
        recognition.interimResults = false;   // ★ 二重起きやすい interim は切る
        recognition.continuous = true;
    } catch (e) {
        alert("SpeechRecognition API がサポートされていません。Chrome を使用してください。");
    }

    recognition.onresult = (event) => {
        let finalText = event.results[event.results.length - 1][0].transcript;

        // ★ 二重防止:前回と同じなら無視
        if (finalText === lastFinalText) {
            return;
        }

        lastFinalText = finalText;

        output.value += finalText + "\n";
        output.scrollTop = output.scrollHeight;
    };

    recognition.onend = () => {
        if (isRecognizing) recognition.start();
    };
}

// 初期言語
initRecognition("ja-JP");

const output = document.getElementById("output");
const startBtn = document.getElementById("startBtn");
const stopBtn = document.getElementById("stopBtn");
const saveBtn = document.getElementById("saveBtn");
const copyBtn = document.getElementById("copyBtn");
const langSelect = document.getElementById("langSelect");

// 言語変更
langSelect.onchange = () => {
    const lang = langSelect.value;
    const wasRunning = isRecognizing;

    if (isRecognizing) {
        recognition.stop();
        isRecognizing = false;
    }

    initRecognition(lang);

    if (wasRunning) {
        recognition.start();
        isRecognizing = true;
    }

    output.value += `\n--- 言語を変更しました (${lang}) ---\n`;
};

startBtn.onclick = () => {
    if (!isRecognizing) {
        lastFinalText = "";  // ★ 重複チェックリセット
        recognition.start();
        isRecognizing = true;
        output.value += "\n--- 録音開始 ---\n";
    }
};

stopBtn.onclick = () => {
    if (isRecognizing) {
        recognition.stop();
        isRecognizing = false;
        output.value += "\n--- 録音停止 ---\n";
    }
};

// 保存
saveBtn.onclick = () => {
    const blob = new Blob([output.value], { type: "text/plain" });
    const url = URL.createObjectURL(blob);

    const a = document.createElement("a");
    a.href = url;
    a.download = "transcript_" + new Date().toISOString().replace(/[:.]/g, "-") + ".txt";
    a.click();

    URL.revokeObjectURL(url);
};

// コピー
copyBtn.onclick = () => {
    output.select();
    document.execCommand("copy");
    alert("コピーしました");
};
</script>

</body>
</html>

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?