You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

基于Java逻辑开发TinyMCE语音听写插件遇阻求助

解决TinyMCE语音转文字插件的跨环境兼容问题

嘿,作为刚接触插件开发的新手,你碰到了一个典型的跨运行环境问题——TinyMCE插件是运行在浏览器的JavaScript环境里,而CMU Sphinx-4是Java库,这俩根本没法直接对接!我来帮你拆解问题,然后给你可行的解决方案:

先说说你代码里的核心问题

  1. Java代码无法在浏览器JS中运行:你在plugin.js里写的Packages.javapackage.Dictate.dictate是Java专属语法,浏览器的JavaScript引擎完全不认识这个东西——浏览器没有Java虚拟机,没法执行你的Dictate.java代码。
  2. 插件JS的语法错误body配置里的name: {result}写法不对,TinyMCE的textbox组件中name应该是字符串类型,value才是用来显示内容的字段。

解决方案一:用浏览器原生Web Speech API(最快实现,适合学校项目)

不用折腾Java,直接用浏览器自带的Web Speech API就能实现前端语音转文字,大部分现代浏览器都支持。修改你的plugin.js如下:

const plugin = (editor) => {
  editor.addButton('voiceDictation', {
    text: '语音输入',
    icon: false,
    onclick: () => {
      // 检查浏览器是否支持Web Speech API
      if (!('webkitSpeechRecognition' in window) && !('SpeechRecognition' in window)) {
        editor.windowManager.alert('你的浏览器不支持语音识别功能,请使用Chrome/Edge等现代浏览器');
        return;
      }

      const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
      const recognition = new SpeechRecognition();
      recognition.continuous = true; // 开启持续识别
      recognition.interimResults = false; // 只返回最终识别结果
      recognition.lang = 'en-US'; // 设置识别语言为英文

      recognition.onresult = (event) => {
        const transcript = event.results[0][0].transcript;
        // 将识别结果插入到编辑器当前光标位置
        editor.insertContent(transcript);
        recognition.stop();
      };

      recognition.onerror = (event) => {
        editor.windowManager.alert(`识别出错:${event.error}`);
        recognition.stop();
      };

      recognition.start();
      editor.windowManager.alert('开始语音输入,请说话...');
    }
  });
};

export default plugin;

对应的index.htmlindex.js不需要大改,只要确保插件正确加载即可。

解决方案二:如果必须用CMU Sphinx-4(比如学校要求)

那你需要做前后端分离,把Java代码放到后端服务中:

  1. 后端部分:把你的Dictate.java改造成一个HTTP接口服务(比如用Spring Boot实现),接收前端上传的语音文件/流,用CMU Sphinx-4处理后返回识别结果。
  2. 前端部分:在plugin.js里用MediaRecorder API录制用户语音,通过AJAX把语音数据发送到后端接口,拿到结果后插入到TinyMCE编辑器中。

简单后端接口示例(Spring Boot)

@RestController
@RequestMapping("/api")
public class SpeechRecognitionController {
    @PostMapping("/recognize")
    public ResponseEntity<String> recognizeSpeech(@RequestBody MultipartFile audioFile) throws IOException {
        Configuration configuration = new Configuration();
        configuration.setAcousticModelPath("resource:/edu/cmu/sphinx/models/en-us/en-us");
        configuration.setDictionaryPath("resource:/edu/cmu/sphinx/models/en-us/cmudict-en-us.dict");
        configuration.setLanguageModelPath("resource:/edu/cmu/sphinx/models/en-us/en-us.lm.bin");
        
        // 注意:处理文件输入需要用StreamSpeechRecognizer,而非LiveSpeechRecognizer
        StreamSpeechRecognizer recognizer = new StreamSpeechRecognizer(configuration);
        recognizer.startRecognition(audioFile.getInputStream());
        String result = recognizer.getResult().getHypothesis();
        recognizer.stopRecognition();
        return ResponseEntity.ok(result);
    }
}

前端修改后的点击事件逻辑

onclick: () => {
  navigator.mediaDevices.getUserMedia({ audio: true })
    .then(stream => {
      const mediaRecorder = new MediaRecorder(stream);
      const audioChunks = [];

      mediaRecorder.addEventListener('dataavailable', event => {
        audioChunks.push(event.data);
      });

      mediaRecorder.addEventListener('stop', () => {
        const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
        const formData = new FormData();
        formData.append('audioFile', audioBlob);

        // 发送到后端接口
        fetch('/api/recognize', {
          method: 'POST',
          body: formData
        })
        .then(response => response.text())
        .then(transcript => {
          editor.insertContent(transcript);
        })
        .catch(error => {
          editor.windowManager.alert(`请求出错:${error}`);
        });
      });

      mediaRecorder.start();
      editor.windowManager.alert('开始录音,5秒后自动停止...');
      setTimeout(() => {
        mediaRecorder.stop();
        stream.getTracks().forEach(track => track.stop());
      }, 5000); // 5秒后自动停止录音
    })
    .catch(error => {
      editor.windowManager.alert(`无法获取麦克风权限:${error}`);
    });
}

最后给你个小提示

如果是学校项目,优先用Web Speech API方案——不用搭后端,代码量少,容易实现,而且用户体验更好(实时识别)。如果必须用CMU Sphinx,记得处理好前后端的音频格式兼容问题,Sphinx4对音频格式有严格要求(比如16kHz采样率、单声道等)。

内容的提问来源于stack exchange,提问作者tenas ryan

火山引擎 最新活动