Unity+chatgpt+webgl实现声音录制+语音识别

news2024/11/27 0:42:20

一、前言 

        AI二次元女友这个项目持续更新,在window端的语音识别和语音合成的功能,在上一篇博文里已经详细说明了微软Azure语音服务的代码实现。也是为了实现一次代码,多端复用这样的诉求,所以全部的代码实现都改成了web api的方式。然而在实测发布到webgl的时候,就发现了一个问题,因为这个项目涉及到麦克风录制声音,用作语音识别,结果在发布的时候,引擎报错,提示unity发布到webgl失败了,查了一下相关资料,原来是unity内置的microphone类是不支持webgl了,所以只好另找解决方案。

        ·我在网上也查了不少资料,从unity官方也得到了一些信息,解决方案的思路,参见unity官方文档,文档里描述了unity如何调用js的方法,涉及到unity端*.jslib的拓展方法,unity文档:

https://docs.unity3d.com/cn/2020.3/Manual/webgl-interactingwithbrowserscripting.html

        因为自己技术能力也有限,所以找了好几个现成的解决方案,做了实测,最后找到一个博主提供的解决方案以及源码。把他的源码集成到我项目里,确实是解决了我的问题,是实现了发布到webgl之后,可以调用麦克风录音,并且通过Azure的语音识别,正确识别到文本了。这过程其实也遇到不少问题,花了点时间,不过最终都解决了,怕自己忘记了,所以就赶忙写个文档留存,以防未来需要的时候,可以查阅。我参考的博主的解决方案里,除了在unity端调用js代码外,还有js回传数据到unity。部分代码实现是在js里实现了,所以在发布webgl后,需要修改一点代码,并加入js库,具体配置方法,见下文。

二、发布Webgl

        unity发布webgl项目的设置,首先确保引擎安装了webgl平台的拓展,这个在buildsetting里如果切换不到webgl平台,那就上官网下在安装包,安装上就可以了。转换一下平台即可。发布之前确认一下工程的路径,保证工程的路径是全英文的,不然导出webgl会报错。

        发布设置方面:

        1、Other Settings里,Color Space修改为Gamma

        

        2、Publishing Settings里,勾选Decompression Fallback

        

         如果说在发布webgl过程发现其他问题,自行到百度或其他渠道去搜一下,这方面的资料很多,基本上都可以找到相关问题的解决方案,以上是我在发布时遇到的一些配置问题,做一下记录。

三、脚本说明

        项目涉及到unity端还有js端两部分的代码。

        1、unity端代码说明

        在unity端,参见官方解决方案,需要在unity的Plugins文件下,创建*jslib文件,在这个库里,按照unity官方提供的示例编写js代码,然后在C#脚本里按照指定的方式引用就可以在unity端调用js代码了。

        jslib官方代码示例(这个文件要放在Plugins文件夹下面)

mergeInto(LibraryManager.library, {

  Hello: function () {
    window.alert("Hello, world!");
  },
});

        在C#端,根据官方示例,编写以下代码就可以引用到js的方法了

 [DllImport("__Internal")]
    private static extern void Hello();

        以上是C#调用js代码的示例,在项目功能里已经把录音和结束录音的功能封装好了,直接调用就可以了,根据自己的需要,调用StartRecord()开始录制,调用StopRecord()结束录制

    [DllImport("__Internal")]
    private static extern void StartRecord();
    [DllImport("__Internal")]
    private static extern void StopRecord();

      2、js代码说明

        源码提供了js代码,因为项目涉及到js端完成录制后,需要把录音数据回传到unity中使用,所以还需要增加一部分的js代码,实现上述功能。源码里提供了相关的代码,我们只需要在导出的webgl包里,修改相应的代码文件,就可以了。具体的修改方法,见下一节内容。

四、脚本配置

        这节里详细说明一下在导出的webgl包中的代码配置内容。

        1、添加js脚本

        找到[recorder.wav.min.js]脚本,把脚本拷贝到输出的webgl包,index.html相同的文件夹下[根目录]即可。

        

        2、修改index.html 

        先找到[AddToIndex.js]文件,后续需要添加的代码都在这个文件里了,直接复制就可以了。

        ①在index.html中引用/recorder.wav.min.js脚本

        在[AddToIndex.js]文件,里拷贝"<script src="./recorder.wav.min.js"></script>",添加到index.html里,引用[recorder.wav.min.js]脚本。

 <script src="./recorder.wav.min.js"></script>
    <script>
      var container = document.querySelector("#unity-container");
      var canvas = document.querySelector("#unity-canvas");
      var loadingBar = document.querySelector("#unity-loading-bar");
      var progressBarFull = document.querySelector("#unity-progress-bar-full");
      var fullscreenButton = document.querySelector("#unity-fullscreen-button");
      var warningBanner = document.querySelector("#unity-warning");
    ....

    后续面代码略
</script>

        ②拷贝js处理代码到index.html中

        [AddToIndex.js]里拷贝第7行到110行的代码,到<script>脚本里(可以直接添加到" document.body.appendChild(script);"这行代码后面)


document.body.appendChild(script);这行不要拷贝,就是告诉你从哪行开始粘贴

// 全局录音实例
let RecorderIns = null;
      //全局Unity实例   (全局找 unityInstance , 然后等于它就行)
      let UnityIns = null;

      // 初始化 ,   记得调用
      function initRecord(opt = {}) {
        let defaultOpt = {
          serviceCode: "asr_aword",
          audioFormat: "wav",
          sampleRate: 44100,
          sampleBit: 16,
          audioChannels: 1,
          bitRate: 96000,
          audioData: null,
          punctuation: "true",
          model: null,
          intermediateResult: null,
          maxStartSilence: null,
          maxEndSilence: null,
        };

        let options = Object.assign({}, defaultOpt, opt);
        let sampleRate = options.sampleRate;
        let bitRate = options.sampleBit;
        if (RecorderIns) {
          RecorderIns.close();
        }

        RecorderIns = Recorder({
          type: "wav",
          sampleRate: sampleRate,
          bitRate: bitRate,
          onProcess(buffers, powerLevel, bufferDuration, bufferSampleRate) {
            // 60秒时长限制 
            const LEN = 59 * 1000;
            if (bufferDuration > LEN) {
              RecorderIns.recStop();
            }
          },
        });
        RecorderIns.open(
          () => {
            // 打开麦克风授权获得相关资源
            console.log("打开麦克风成功");
          },
          (msg, isUserNotAllow) => {
            // 用户拒绝未授权或不支持
            console.log((isUserNotAllow ? "UserNotAllow," : "") + "无法录音:" + msg);
          }
        );
      }

      // 开始
      function StartRecord() {
        RecorderIns.start();
      }
      // 结束
      function StopRecord() {
        RecorderIns.stop(
          (blob, duration) => {
            console.log(
              blob,
              window.URL.createObjectURL(blob),
              "时长:" + duration + "ms"
            );
            sendWavData(blob)
          },
          (msg) => {
            console.log("录音失败:" + msg);
          }
        );
      }
      
      // 切片像unity发送音频数据
      function sendWavData(blob) {
        var reader = new FileReader();
        reader.onload = function (e) {
          var _value = reader.result;
          var _partLength = 8192;
          var _length = parseInt(_value.length / _partLength);
          if (_length * _partLength < _value.length) _length += 1;
          var _head = "Head|" + _length.toString() + "|" + _value.length.toString() + "|end" ;
          // 发送数据头
          UnityIns.SendMessage("SignalManager", "GetAudioData", _head);
          for (var i = 0; i < _length; i++) {
            var _sendValue = "";
            if (i < _length - 1) {
              _sendValue = _value.substr(i * _partLength, _partLength);
            } else {
              _sendValue = _value.substr(
                i * _partLength,
                _value.length - i * _partLength
              );
            }
            _sendValue = "Part|" + i.toString() + "|" + _sendValue;
            // 发送分片数据
            UnityIns.SendMessage("SignalManager", "GetAudioData", _sendValue);
          }
          _value = null;
        };
        reader.readAsDataURL(blob);
      }

        ③初始化代码

        这里需要注意添加一下实例化代码,需要在index.html里找到unityInstance的实例化代码块里,添加"UnityIns = unityInstance;  initRecord();" 这两行代码,(可以添加到"then((unityInstance) => {" 这段代码之后)

   script.onload = () => {
        createUnityInstance(canvas, config, (progress) => {
          progressBarFull.style.width = 100 * progress + "%";
        }).then((unityInstance) => {

          UnityIns = unityInstance;//拷贝代码
          initRecord();//拷贝代码

          loadingBar.style.display = "none";
          fullscreenButton.onclick = () => {
            unityInstance.SetFullscreen(1);
          };
        }).catch((message) => {
          alert(message);
        });
      };

        修改完上述代码,就配置完成了,可以部署webgl项目实测一下效果了。这里我实测原作者项目的时候,遇到过一个问题。因为我的项目是需要拿到录音数据,发送到语音识别api的,刚开始发现识别的结果有问题,一直不准确。后来在录制声音的配置上,把采样率调整未44100之后,就识别正常,这里做一下记录,我自己的源码就已经做过这个修改了。

        五、结束语

        这次这个方案,解决了unity语音识别功能发布到webgl失效的问题,现在这个项目可以顺利部署到webgl使用了。目前这个解决方案,在发布之后还需要做一些js的代码处理,相对麻烦一些,目前我还没有找到更简单的解决方案,未来如果找到更便捷的方案再做分享。

        项目的源码,目前还没整理,待我整理好源码之后,再补充到这里,先留个空地,待后续补充上。

AI二次元女友项目源码:

Github地址:https://github.com/zhangliwei7758/unity-AI-Chat-Toolkit

Gitee地址:https://gitee.com/DammonSpace/unity-ai-chat-toolkit

项目视频可参见我的B站主页:

【重大更新->简单部署+跨平台】重构AI二次元小姐姐项目,发布了新的unity工具包,更易用+实用的AI套件,开源代码等你来拿!!

        六、参考资料

        这个解决方案也查阅了很多大佬的资料,以下是查阅和参考的相关资料传送门:      

CSDN博客:https://blog.csdn.net/Wenhao_China/article/details/126779212?spm=1001.2014.3001.5502t

CSDN博文:https://blog.csdn.net/a987654sd/article/details/105551560

解决方案作者的源码地址:

Github:https://github.com/HiWenHao/UnityWebGLMicrophone

附加材料:

以下两个webgl使用microphone方案我尚未验证,也放在这里供参考

解决方案1:https://gitcode.net/mirrors/xiangyuecn/recorder?utm_source=csdn_github_accelerator

解决方案2:https://github.com/tgraupmann/UnityWebGLMicrophone/tree/maste

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/778286.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

openSUSE leap 15.3安装mysql-community-server

openSUSE Software 下载"https://software.opensuse.org/ymp/home:bjoernv/15.3/mysql-community-server.ymp" wget "https://software.opensuse.org/ymp/home:bjoernv/15.3/mysql-community-server.ymp" 双击"mysql-community-server.ymp" 添…

Flutter Windows通过嵌入Native窗口实现渲染视频

Flutter视频渲染系列 第一章 Android使用Texture渲染视频 第二章 Windows使用Texture渲染视频 第三章 Linux使用Texture渲染视频 第四章 全平台FFICustomPainter渲染视频 第五章 Windows使用Native窗口渲染视频&#xff08;本章&#xff09; 文章目录 Flutter视频渲染系列前言…

FPGA XDMA 中断模式实现 PCIE3.0 QT上位机图片传输 提供工程源码和QT上位机源码

目录 1、前言2、我已有的PCIE方案3、PCIE理论4、总体设计思路和方案图像产生、发送、缓存XDMA简介XDMA中断模式图像读取、输出、显示QT上位机及其源码 5、vivado工程详解6、上板调试验证7、福利&#xff1a;工程代码的获取 1、前言 PCIE&#xff08;PCI Express&#xff09;采…

【深度学习之YOLO8】环境部署

目录 一、确定版本CUDA toolkit、cuDNN版本Python、PyTorch版本 二、安装Python下载环境变量验证安装 三、安装Anaconda安装环境变量验证安装创建conda虚拟环境常用命令 四、安装CUDA toolkit下载环境变量验证安装 五、配置cuDNN下载 六、安装PyTorch(torchtorchversiontorchau…

JavaWeb课程设计项目实战(05)——项目编码实践2

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 在本节教程中&#xff0c;我们完成项目的登录功能。 User 请在bean包中创建User类&#xff0c;代码如下&#xff1a; package com.cn.bean; /*** 本文作者&#xff1a;谷…

离线安装mysql8

一、先前往官网下载mysql8 下载地址&#xff1a; https://dev.mysql.com/downloads/ 选择Linux 二、删除Linux中的mysql&#xff08;如果有的话&#xff09;&#xff0c;上传安装包 1、先查看mysql是否存在&#xff0c;命令如下&#xff1a; rpm -qa|grep -i mysql 如果使用…

集成了Eureka的应用启动失败,端口号变为8080

问题 报错&#xff1a;集成了Eureka的应用启动失败&#xff0c;端口号变为8080。 原来运行的项目&#xff0c;突然报错&#xff0c;端口号变为8080&#xff1a; Tomcat initialized with port(s): 8080 (http)并且&#xff0c;还有如下的错误提示&#xff1a; RedirectingE…

keil5编辑器主题配色美化使用(附六款暗黑主题)

一、通过配置文件修改主题 1、在软件安装目下备份以下三个文件&#xff0c;更换主题只需要替换global.prop arm.propglobal.propglobal.prop.def 2、替换配置文件 将已经准备好的配色文件复制到\UV4下替换 https://download.csdn.net/download/qq_43445867/88064961 Theme1…

通讯录--动态版

简易的通讯录往往需要朴实的“烹饪”就能完成一道“美味的佳肴”。 我们需要一个通讯录&#xff0c;能够存储联系人的信息&#xff0c;能够对联系人的信息进行增删查改&#xff0c;查询&#xff0c;按姓名排序。相比对之前的三子棋、扫雷&#xff0c;有了一定的了解&#xff0c…

git commit -m时候没有保存package.json等文件

项目场景&#xff1a; 提示&#xff1a;git add . 和 git commit -m "保存" 操作&#xff0c;没有保存package.json等文件。 解决方案&#xff1a; 1.确保 package.json 文件没有被列在 .gitignore 文件中。打开 .gitignore 文件&#xff0c;检查是否有类似于 packa…

数据库运维——备份恢复

数据库备份&#xff0c;数据库为school&#xff0c;素材如下 1.创建student和score表 CREATE TABLE student ( id INT(10) NOT NULL UNIQUE PRIMARY KEY , name VARCHAR(20) NOT NULL , sex VARCHAR(4) , birth YEAR, department VARCHAR(20) , address VARCHAR(…

交换一个整数二进制位的奇数位和偶数位

目录 一、方案一 1.求待操作数的二进制序列 2.创建一个数组存放待操作数的二进制序列 3.交换二进制序列奇偶位 4.输出奇偶位交换之后的二进制序列 5.代码 二、方案二&#xff08;宏的实现&#xff09; 1.求待操作数二进制序列偶数位 2.求待操作数二进制序列奇数位 3.求待…

目标检测——yoloV3案例

目录 数据获取TFrecord文件什么是TFrecord文件将数据转换成TFrecord文件读取TFrecord文件数据处理 模型构建模型训练损失函数的计算正负样本的设定模型训练获取数据集加载模型模型训练 模型预测 数据获取 labellmage使用方法 TFrecord文件 什么是TFrecord文件 将数据转换成T…

python暴力破解wifi密码

python破解WiFi密码 无图形化界面版图形化界面版 刚开始怀着是以为可以自动生成并匹配密码进行破解&#xff0c;然后才知道都需要使用密码本&#xff0c;破解都不知道要猴年马月去了。。。。。但可以研究理解一下代码过程 使用pycharm需要调试一下&#xff0c;比较麻烦&#xf…

【Docker】Docker网络与存储(三)

前言&#xff1a; Docker网络与存储的作用是实现容器之间的通信和数据持久化&#xff0c;以便有效地部署、扩展和管理容器化应用程序。 文章目录 Docker网络桥接网络容器之间的通信 覆盖网络创建一个覆盖网络 Docker存储卷 总结 Docker网络 Docker网络是在容器之间提供通信的机…

速成版-带您一天学完python自动化测试(selenium)

Selenium是一套web网站的程序自动化操作解决方案。我们通过编写自动化程序&#xff0c;使得自动完成浏览器界面的相关操作&#xff0c;除了能够自动化的完成相关操作&#xff0c;还能从web页面获取相关信息&#xff0c;然后通过程序进行分析处理&#xff0c;本质上就是提升从网…

微信小程序的微信一键登录与验证码登录

验证码登录 <template><view class"wx-login"><view class"login-Box"><text class"title">欢迎登录</text><text class"subTitle">再就业男团系统</text><view class"login-Form…

pytorch安装GPU版本 (Cuda12.1)教程: Windows、Mac和Linux系统快速安装指南

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

java List的stream().collect()方法实战

文章目录 订单类测试类测试结果 这几天在做银联的银行卡对账时&#xff0c;一开始只使用参考号来确定一笔交易&#xff0c;但是这样是不严谨的&#xff0c;项目经理要求使用商户号、终端号、流水号、批次号、参考号五个数据项来确定唯一性&#xff0c;所以本次list转map是在此背…

分布式调用与高并发处理 Nginx

一、初识Nginx 1.1 Nginx概述 Nginx是一款轻量级的Web服务器、反向代理服务器&#xff0c;由于它的内存占用少&#xff0c;启动极快&#xff0c;高并发能力强&#xff0c;在互联网项目中广泛应用。Nginx 专为性能优化而开发&#xff0c;使用异步非阻塞事件驱动模型。 常见服务…