示例成品 看全文 ↓

做语音转文字接入

要接入语音转文字功能,方言和实时性要求让选型很纠结,怕接错服务识别效果差还返工。

免费看示例成品:文字写代码做网站 ›

有人这样说「我得做个语音输入框,要能听懂东北话,还得实时出字,急着用。」

能替你选的都选好了。不想动,直接点下面「直接生成语音转文字接入」,点一下就开始做;想改,就点一下要的那个。

已替你选好想改就点一下

只有你知道的不填就先空着,成品里留【待填】

还想怎么改可以不选

高级选项一般不用动 · 这件事通常 1 分钟左右做好

示例成品全文按上面这组示例选项真跑出来的

直接上能跑的方案:前端录音 PCM 16k/16bit → 后端 WebSocket 代理 → 火山流式 ASR → 实时回传文字。
东北话先按“普通话大模型 + 热词表”落地,最稳;如果火山控制台有“东北话”方言选项,就把语言参数改成对应值。


1. 先到火山控制台拿 4 个东西

  1. 开通:火山引擎 → 语音技术 / 豆包语音识别
  2. 创建应用,选“流式语音识别”
  3. 拿到:
  • App ID
  • Access Token
  • Cluster
  1. 热词表:创建东北话热词表,绑定这个应用

热词表至少塞这些常见东北口语:

咋地
咋整
干啥
嘎哈
唠嗑
得劲
磨叽
磕碜
老铁
稀罕
拉倒
可劲造
忽悠
波棱盖儿
埋汰
别介
嗯呐
扒拉
秃噜反仗
吭哧瘪肚

这些词能明显减少同音字识别错。


2. 代码目录

dongbei-asr/
├── package.json
├── server.js
└── public/
    ├── index.html
    └── pcm-worklet.js

3. 后端:Node.js 代理

package.json
{
  "name": "dongbei-asr",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "ws": "^8.16.0"
  }
}
server.js
const http = require('http');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const { WebSocketServer, WebSocket } = require('ws');

const PORT = process.env.PORT || 8000;

// 火山控制台配置
const ASR_APPID = process.env.ASR_APPID;
const ASR_TOKEN = process.env.ASR_TOKEN;
const ASR_CLUSTER = process.env.ASR_CLUSTER || 'volcengine_streaming_common';
const ASR_LANGUAGE = process.env.ASR_LANGUAGE || 'zh-CN';

// 老版流式 ASR 接口。如果你开的是豆包方舟新版,只改这里和认证头即可。
const ASR_WS_BASE =
  process.env.ASR_WS_BASE || 'wss://openspeech.bytedance.com/api/v2/asr';

function buildUpstreamUrl(uid) {
  const q = new URLSearchParams({
    appid: ASR_APPID,
    token: ASR_TOKEN,
    cluster: ASR_CLUSTER,
    uid,
    workflow: 'audio_in,resample,partition,vad,fe,decode,itn,nlu_punctuate',
    language: ASR_LANGUAGE,
    use_itn: 'true',
    nlu_punctuate: 'true',
    format: 'pcm',
    sample_rate: '16000',
    bits: '16',
    channel: '1'
  });

  return `${ASR_WS_BASE}?${q.toString()}`;
}

function extractText(msg) {
  const r = msg.result || msg.payload_msg || {};

  if (typeof r.text === 'string' && r.text.trim()) return r.text.trim();

  const u = (r.utterances || msg.utterances || [])[0];
  if (u && typeof u.text === 'string' && u.text.trim()) return u.text.trim();

  if (typeof msg.text === 'string' && msg.text.trim()) return msg.text.trim();

  return '';
}

function extractIsFinal(msg) {
  const r = msg.result || msg.payload_msg || {};

  for (const key of ['is_final', 'definite', 'final', 'end']) {
    if (typeof r[key] === 'boolean') return r[key];
  }

  const u = (r.utterances || msg.utterances || [])[0];
  if (u) {
    for (const key of ['is_final', 'definite', 'final', 'end']) {
      if (typeof u[key] === 'boolean') return u[key];
    }
  }

  return false;
}

const server = http.createServer((req, res) => {
  const urlPath = (req.url || '/').split('?')[0];
  const publicDir = path.join(__dirname, 'public');

  let file = null;
  if (urlPath === '/' || urlPath === '/index.html') {
    file = path.join(publicDir, 'index.html');
  } else if (urlPath === '/pcm-worklet.js') {
    file = path.join(publicDir, 'pcm-worklet.js');
  }

  if (!file) {
    res.writeHead(404);
    res.end('not found');
    return;
  }

  fs.stat(file, (err) => {
    if (err) {
      res.writeHead(404);
      res.end('file not found');
      return;
    }

    const ext = path.extname(file);
    const mime = ext === '.html'
      ? 'text/html; charset=utf-8'
      : 'application/javascript; charset=utf-8';

    res.writeHead(200, { 'Content-Type': mime });
    fs.createReadStream(file).pipe(res);
  });
});

const wss = new WebSocketServer({ server, path: '/asr' });

wss.on('connection', (browserW

同类还能办

全部 ›

同样出写代码做网站的

首页