IESM项目实训五——语音合成提醒、成绩录入类型识别和前后端定时语音识别交互

IESM项目实训五

语音录入方式设计有两种

第一种:系统从后端获取未录入(初始成绩值为空或者为0)的学生列表,由系统播放学生姓名,教师只需要说明成绩即可。规定时间间隔内只需说明一次,否则获取到重复内容,录入错误数据。
第二种:学生姓名和成绩都由教师说明,系统分析姓名和成绩部分。为获取音频,每间隔六秒录入一位同学,播放下一位同学的音频提示。
在这里插入图片描述
两种方式共同步骤:播放开始音频录制成绩提示音,教师语音说明录入成绩类型。

语音提醒接口

涉及不同字符串提醒,除姓名外,设计统一接口。

//前端接口
startInputScoresVoice: "ScoresInput/scores/startInputScoresVoice",

//后端实现
//合成开始语音或者其他语音录入语音
    @getMapping(value = "/startInputScoresVoice")
    public Result<Boolean> startInputScoresVoice(@RequestParam(name = "startVoice") String startVoice) {
        VoiceCompose voiceCompose = new VoiceCompose();
        Boolean result = false;
        if (!voiceCompose.getMP3ByText(startVoice)) {
            System.out.println("转换失败");
        } else {
            result = true;
            voiceCompose.playMP3();
        }
        return Result.OK(result);
    }

完整播放提示语音超过服务器设定响应时间9秒,为避免超时,将系统的响应时间上限进行了修改。

识别教师录入成绩类型

单独识别录入成绩类型,后续按照对应类型获取下一个录入学生索引和成绩更新操作的执行。

    @ResponseBody
    @RequestMapping(value = "/voiceInputType")
    public Result<Integer> voiceInputType(@RequestParam(name = "voiceBlobFile") MultipartFile voiceBlobFile) {
        int type = 0;
        byte[] voiceData = new byte[1024];
        //将前端的参数转换为byte数组,进行识别处理
        try {
            voiceData = voiceBlobFile.getBytes();
        } catch (IOException e) {
            e.printStackTrace();
        }
        VoiceCompose voiceCompose = new VoiceCompose();
        VoiceRecognition voiceRecognition = new VoiceRecognition();
        boolean typeJudge = voiceRecognition.recognizeDataVoice(voiceData);
        if (typeJudge) {
            String scoreType = VoiceRecognition.getResultText();
            System.out.println(scoreType);
            if (scoreType.contains("平时成绩")) {
                type = 3;
                String usualScores = "现在开始语音录入平时成绩";
                if (!voiceCompose.getMP3ByText(usualScores)) {
                    System.out.println("转换失败");
                } else {
                    voiceCompose.playMP3();
                }
            } else if (scoreType.contains("考试成绩")) {
                type = 1;
                String testScores = "现在开始语音录入考试成绩";
                if (!voiceCompose.getMP3ByText(testScores)) {
                    System.out.println("转换失败");
                } else {
                    voiceCompose.playMP3();
                }
            } else if (scoreType.contains("实验成绩")) {
                type = 2;
                String examScores = "现在开始语音录入实验成绩";
                if (!voiceCompose.getMP3ByText(examScores)) {
                    System.out.println("转换失败");
                } else {
                    voiceCompose.playMP3();
                }
            } else {
                String remind = "请按要求说明";
                if (!voiceCompose.getMP3ByText(remind)) {
                    System.out.println("转换失败");
                } else {
                    voiceCompose.playMP3();
                }
            }
        } else {
            String remind = "语音识别失败,请您重说一次";
            if (!voiceCompose.getMP3ByText(remind)) {
                System.out.println("转换失败");
            } else {
                voiceCompose.playMP3();
            }
        }
        return Result.OK(type);
    }

    //

实现前端和后端音频传递和语音合成的交互

识别类型交互:在开始提示语音播放后,教师说明录入成绩类型,按照提示在五秒内说“平时成绩”“实验成绩”“考试成绩”,教师可以重复说明。识别成功后提示开始录入成绩,识别失败后台合成“语音识别失败,请您重说一次”。其中关键是,该过程不能视为简单的循环,因为音频录制需要时间,并且在录制几秒后将音频数据传给后端识别,识别很快速返回成功失败信息,如果成功录音停止,如果失败还要再次录音,而且并不确定会失败几次。为实现使用setInterval() 方法可按照指定的周期(以毫秒计)来调用识别音频等的方法。
下面是可以定期获取音频再识别的简单测试:

    voiceRecorder: function (stuNum) {
      this.timer = setInterval(() => {
      //判断是否存在未录入的学生,不存在则不需要录入成绩,n是测试的一个固定数,测试不断交互是否成功
        if (n <= stuNum) {
          Recorder.get(function (rec) {
            recorder = rec
            recorder.start()
          })
          voiceBlobFile = recorder.getBlob();  //得到需要的pcm文件
          this.voiceInputScoresType(n, voiceBlobFile);
          console.log(voiceBlobFile)
          console.log("执行" + n + "次");
        } else if (n > stuNum) {
          n = 0;
          recorder.stop();
          this.voiceController = true;
          clearInterval(this.timer);
          console.log("终止该循环");
        }
        n = n + 1;
      }, 5000);
    },
//随便写的测试接收前端数据方法
    @ResponseBody
    @RequestMapping(value = "/voiceInputScoresTwo")
    public Result<?> voiceInputScoresTwo(@RequestParam(name = "n") int n, @RequestParam(name = "voiceBlobFile") MultipartFile voiceBlobFile) {
        System.out.println(n);
        System.out.println(voiceBlobFile.getSize());
        byte[] testFile = new byte[1024];
        VoiceRecognition voiceRecognition = new VoiceRecognition();
        try {
            testFile = voiceBlobFile.getBytes();
            voiceRecognition.recognizeNum(testFile);
            System.out.println("输出结果是" + voiceRecognition.getResultText());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Result.OK(voiceRecognition.getResultText());
    }

前端生成音频数据为BLOB格式,后端需要使用 MultipartFile类型接收,且添加注解@ResponseBody @RequestMapping,不可以使用getMapping等较为常规的方式,后端也不可以使用Blob直接接收,报错会String不能与Blob匹配。
尝试将Blob转为byte[]数组,再进行语音识别,后使用MultipartFile类型,可以调用getBytes()方法转为byte[]。

//blob转为byte数组,音频文件用MultipartFile接收
    public byte[] blobToByte(Blob blob) throws Exception {
        byte[] bytes = null;
        try {
            InputStream in = blob.getBinaryStream();
            BufferedInputStream inBuffered = new BufferedInputStream(in);
            ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
            byte[] temp = new byte[1024];
            int size = 0;
            while ((size = inBuffered.read(temp)) != -1) {
                out.write(temp, 0, size);
            }
            inBuffered.close();
            in.close();
            bytes = out.toByteArray();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return bytes;
    }

如果使用该方法需要明确所传递音频数据是哪一阶段的,否则识别结果会与插入数据库内容不匹配。
测试成功后,实现了类型识别前端方法,测试发现语音输入类型用户感觉并不太好,存在两个原因。一是交互时间过长,包括音频录入和与语音合成播放,如果用户多次使用都要经过该过程有一些累赘。二是识别结果存在一定问题,用户反应时间和交互时间冲突的话,识别结果不完整意味失败,需要再次说明,开始录入过程太长,交互不合理。所以后面选择使用弹窗的形式手动选择录入成绩类型,尽量简化交互方式,可以更快速的录入成绩。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值