文字转语音

  • UNITY_WSA平台
  • 处理过程和实现原理
  • 中文需要设置声音
  • 上代码
  • 使用方法
    • 一、部署好Unity3d HoloLens项目工程
    • 二、新建节点
    • 三、添加TTS组件并配置
    • 四、调用TTS功能
    • 五、测试、打包和运行
  • 参考地址
  • 源码地址:

UNITY_WSA平台

HoloLens2 是微软通用平台,使用的类主要是Windows.Media.SpeechSynthesis;

处理过程和实现原理

是把string内容转换成语音文件,并转换为audio clip 通过audio source组件进行播放。具体请看 代码,注释比较详细,请自行研究。

中文需要设置声音

声音如下TextToSpeechVoice枚举:

 public enum TextToSpeechVoice{/// <summary>///系统默认声音./// </summary>Default,/// <summary>/// 大卫 移动/// </summary>David,/// <summary>///马克 移动/// </summary>Mark,/// <summary>/// 兹拉 移动/// </summary>Zira,/// <summary>/// 瑶瑶(谐音) 中文/// </summary>Yaoyao,/// <summary>/// 灰灰(谐音) 中文/// </summary>Huihui,/// <summary>/// 康康(谐音) 中文/// </summary>Kangkang,}

中文选用Yaoyao,HuiHui或者KangKang。

上代码

using System;
using UnityEngine;#if !UNITY_EDITOR && UNITY_WSA
using Windows.Foundation;
using Windows.Media.SpeechSynthesis;
using Windows.Storage.Streams;
using System.Linq;
using System.Threading.Tasks;
#endifnamespace HoloToolkit.Unity
{/// <summary>/// 知名的可用声音./// </summary>public enum TextToSpeechVoice{/// <summary>///系统默认声音./// </summary>Default,/// <summary>/// 大卫 移动/// </summary>David,/// <summary>///马克 移动/// </summary>Mark,/// <summary>/// 兹拉 移动/// </summary>Zira,/// <summary>/// 瑶瑶(谐音) 中文/// </summary>Yaoyao,/// <summary>/// 灰灰(谐音) 中文/// </summary>Huihui,/// <summary>/// 康康(谐音) 中文/// </summary>Kangkang,}/// <remarks>/// <see cref="SpeechSynthesizer"/>生成语音<see cref="SpeechSynthesisStream"/>. /// 这个类将流转换为UnityAudioClip 并使用你在inspector中提供的AudioSource播放/// 这可以让你在3D空间中定位声音。推荐的方法是将AudioSource放置在空对象上,/// 并将它设为主摄像头的子对象,将其放置在相机上方的大约0.6个单位。/// 这个听起来类似于Cortana在操作系统中的讲话。/// </remarks>[RequireComponent(typeof(AudioSource))]public class TextToSpeech : MonoBehaviour{[Tooltip("播放语音的AudioSource")][SerializeField]private AudioSource audioSource;public static TextToSpeech Instance = null;/// <summary>/// 获取或设置播放语音的AudioSource./// </summary>public AudioSource AudioSource { get { return audioSource; } set { audioSource = value; } }/// <summary>///获取或设置用于生成语音的声音./// </summary>public TextToSpeechVoice Voice { get { return voice; } set { voice = value; } }[Tooltip("生成语音的声音")][SerializeField]private TextToSpeechVoice voice;#if !UNITY_EDITOR && UNITY_WSAprivate SpeechSynthesizer synthesizer;private VoiceInformation voiceInfo;private bool speechTextInQueue = false;
#endif/// <summary>/// 转换2个字节为-1至1的浮点数/// </summary>/// <param name="firstByte">第一个字节</param>/// <param name="secondByte">第二个字节</param>/// <returns>转换的值</returns>private static float BytesToFloat(byte firstByte, byte secondByte){// 转换两个字节为short(从小到大)short s = (short)((secondByte << 8) | firstByte);// 转换为 -1 至 (略低于) 1return s / 32768.0F;}/// <summary>/// 转换字节数组为int./// </summary>/// <param name="bytes"> 字节数组</param>/// <param name="offset"> 读取偏移.</param>/// <returns>转换后的int.</returns>private static int BytesToInt(byte[] bytes, int offset = 0){int value = 0;for (int i = 0; i < 4; i++){value |= ((int)bytes[offset + i]) << (i * 8);}return value;}/// <summary>/// 动态创建一个AudioClip音频数据。/// </summary>/// <param name="name"> 动态生成的AudioClip的名称。</param>/// <param name="audioData">音频数据.</param>/// <param name="sampleCount">音频数据中的样本数。</param>/// <param name="frequency">音频数据的频率。</param>/// <returns>AudioClip</returns>private static AudioClip ToClip(string name, float[] audioData, int sampleCount, int frequency){var clip = AudioClip.Create(name, sampleCount, 1, frequency, false);clip.SetData(audioData, 0);return clip;}/// <summary>/// 转换原始WAV数据为统一格式的音频数据。/// </summary>/// <param name="wavAudio">WAV数据.</param>/// <param name="sampleCount">音频数据中的样本数.</param>/// <param name="frequency">音频数据的频率.</param>/// <returns>统一格式的音频数据. </returns>private static float[] ToUnityAudio(byte[] wavAudio, out int sampleCount, out int frequency){// 确定是单声道还是立体声int channelCount = wavAudio[22];// 获取频率frequency = BytesToInt(wavAudio, 24);// 通过所有其他子块,以获得数据子块:int pos = 12; // 第一个子块ID从12到16// 不断迭代,直到找到数据块 (即 64 61 74 61 ...... (即 100 97 116 97 十进制))while (!(wavAudio[pos] == 100 && wavAudio[pos + 1] == 97 && wavAudio[pos + 2] == 116 && wavAudio[pos + 3] == 97)){pos += 4;int chunkSize = wavAudio[pos] + wavAudio[pos + 1] * 256 + wavAudio[pos + 2] * 65536 + wavAudio[pos + 3] * 16777216;pos += 4 + chunkSize;}pos += 8;// Pos现在被定位为开始实际声音数据。sampleCount = (wavAudio.Length - pos) / 2;    // 每个样本2字节(16位单声道)if (channelCount == 2) { sampleCount /= 2; }  // 每个样本4字节(16位立体声)// 分配内存(仅支持左通道)var unityData = new float[sampleCount];//写入数组:int i = 0;while (pos < wavAudio.Length){unityData[i] = BytesToFloat(wavAudio[pos], wavAudio[pos + 1]);pos += 2;if (channelCount == 2){pos += 2;}i++;}return unityData;}#if !UNITY_EDITOR && UNITY_WSA/// <summary>/// 执行一个生成语音流的函数,然后在Unity中转换并播放它。/// </summary>/// <param name="text">/// 内容./// </param>/// <param name="speakFunc">/// 执行以生成语音的实际函数/// </param>private void PlaySpeech(string text, Func<IAsyncOperation<SpeechSynthesisStream>> speakFunc){//确保有内容if (speakFunc == null) throw new ArgumentNullException(nameof(speakFunc));if (synthesizer != null){try{speechTextInQueue = true;// 需要await,因此大部分将作为一个新任务在自己的线程中运行。// 这是件好事,因为它解放了Unity,让它可以继续运行。Task.Run(async () =>{// 换声?if (voice != TextToSpeechVoice.Default){// 获得名称var voiceName = Enum.GetName(typeof(TextToSpeechVoice), voice);// 查看它是一直没被找到还是有改变if ((voiceInfo == null) || (!voiceInfo.DisplayName.Contains(voiceName))){// 搜索声音信息voiceInfo = SpeechSynthesizer.AllVoices.Where(v => v.DisplayName.Contains(voiceName)).FirstOrDefault();// 如果找到则选中if (voiceInfo != null){synthesizer.Voice = voiceInfo;}else{Debug.LogErrorFormat("TTS 无法找到声音 {0}。", voiceName);}}}// 播放语音并获得流var speechStream = await speakFunc();// 获取原始流的大小var size = speechStream.Size;// 创建 bufferbyte[] buffer = new byte[(int)size];// 获取输入流和原始流的大小using (var inputStream = speechStream.GetInputStreamAt(0)){// 关闭原始的语音流,释放内存speechStream.Dispose();// 从输入流创建一个新的DataReaderusing (var dataReader = new DataReader(inputStream)){//将所有字节加载到readerawait dataReader.LoadAsync((uint)size);// 复制reader到bufferdataReader.ReadBytes(buffer);}}// 转换原始WAV数据为统一格式的音频数据int sampleCount = 0;int frequency = 0;var unityData = ToUnityAudio(buffer, out sampleCount, out frequency);// 剩下的工作须在Unity的主线程中完成UnityEngine.WSA.Application.InvokeOnAppThread(() =>{// 转换为audio clipvar clip = ToClip("Speech", unityData, sampleCount, frequency);// 设置audio clip的语音audioSource.clip = clip;// 播放声音audioSource.Play();speechTextInQueue = false;}, false);});}catch (Exception ex){speechTextInQueue = false;Debug.LogErrorFormat("语音生成错误: \"{0}\"", ex.Message);}}else{Debug.LogErrorFormat("语音合成器未初始化. \"{0}\"", text);}}
#endifprivate void Awake(){try{if (audioSource == null){audioSource = GetComponent<AudioSource>();}
#if !UNITY_EDITOR && UNITY_WSAsynthesizer = new SpeechSynthesizer();
#endifInstance = this;}catch (Exception ex){Debug.LogError("不能开始语音合成: " + ex.Message);}}// 公共方法/// <summary>/// 播放指定SSML标记语音./// </summary>/// <param name="ssml">SSML标记</param>public void SpeakSsml(string ssml){// 确保内容不为空if (string.IsNullOrEmpty(ssml)) { return; }// 传递给辅助方法
#if !UNITY_EDITOR && UNITY_WSAPlaySpeech(ssml, () => synthesizer.SynthesizeSsmlToStreamAsync(ssml));
#elseDebug.LogWarningFormat("文字转语音在编辑器下不支持.\n\"{0}\"", ssml);
#endif}/// <summary>/// 播放指定文本语音./// </summary>/// <param name="text">文本内容</param>public void StartSpeaking(string text){// 确保内容不为空if (string.IsNullOrEmpty(text)) { return; }// 传递给辅助方法
#if !UNITY_EDITOR && UNITY_WSAPlaySpeech(text, ()=> synthesizer.SynthesizeTextToStreamAsync(text));
#elseDebug.LogWarningFormat("文字转语音在编辑器下不支持.\n\"{0}\"", text);
#endif}/// <summary>/// 返回一个文本是否被提交并被PlaySpeech方法处理/// 方便避免当文本提交,但音频剪辑还没有准备好,因为音频源还没有播放的情况。/// </summary>/// <returns></returns>public bool SpeechTextInQueue(){#if !UNITY_EDITOR && UNITY_WSAreturn speechTextInQueue;
#elsereturn false;
#endif}/// <summary>/// 是否在播放语音./// </summary>/// <returns>/// True, 在播放. False,未播放./// </returns>public bool IsSpeaking(){if (audioSource != null){return audioSource.isPlaying;}return false;}/// <summary>/// 停止播放语音./// </summary>public void StopSpeaking(){if (IsSpeaking()){audioSource.Stop();}}}
}

使用方法

一、部署好Unity3d HoloLens项目工程

这一步请自行完成

二、新建节点


如上图 在camera节点下新建节点,并添加audio source组件,将position.y设置为 0.6,这样听起来类似于Cortana在操作系统中的讲话。

三、添加TTS组件并配置


选择voice 为YaoYao

四、调用TTS功能

if (TextToSpeech.Instance)TextToSpeech.Instance.StartSpeaking("这是一条测试语音");

五、测试、打包和运行


上图是编辑器下运行的效果图

参考地址

https://www.roadtomr.com/2016/05/04/1601/text-to-speech-for-hololens/

源码地址:

https://github.com/microsoft/MixedRealityToolkit-Unity/blob/htk_release/Assets/HoloToolkit/Utilities/Scripts/TextToSpeech.cs

Unity3D HoloLens2 中文文字转语音即语音合成(语音提示)功能相关推荐

  1. 关闭ipad2的语音(VoiceOver)提示功能

    无意中,将ipad2打开语音提示的功能. 打开语音提示后, 整个操作都发生了变化, 触摸一下,是选中, 连按两下,是打开. 滑动则编程了三个手指快速滑动. 整体上感觉不好用, 切换回去. " ...

  2. 开源(离线)中文文本转语音TTS(语音合成)工具整理

    开源(离线)中文文本转语音TTS(语音合成)工具整理 目录 文章目录 目录 PaddleSpeech VoiceVox TensorFlowTTS ttskit OpenTTS eSpeak 微软 T ...

  3. iOS语音合成,语音阅读《AVFoundation》-AVSpeechSynthesizer使用方法介绍

    iOS语音合成,语音阅读<AVFoundation>->AVSpeechSynthesizer使用方法介绍 一:写在前面 相关源代码已经上传到网上,里面该有的注释也都有了,感兴趣的同 ...

  4. 谷歌语音合成_如何修改Google语音合成语音

    谷歌语音合成 Ben Stockton 本·斯托克顿 While Google focuses on the Assistant, Android owners shouldn't forget ab ...

  5. 语音翻译中文并不难,我来推荐语音翻译软件哪个好用

    快到学期末了,同学们都忙着复习准备迎战期末考试,英语不好的我一直把英语作为重点复习科目,在课堂上不仅把老师做的笔记记录下来,还用手机录下了老师讲解的重点.不过,有时候我在课后复习时,听着录音里老师讲的 ...

  6. 如何一键文字转声音制作广告语音

    如何一键文字转声音制作广告语音,手机上安装应用程序"王者剪辑app",启动工具并进入智能创作中的"语音合成"功能, 输入文字内容,选择配音类型,然后点击界面右上 ...

  7. 涨知识!智能手机还能一键提取音频文件内的文字,成功地把语音变成文字

    随着科学的发展,我们所使用的手机就拥有了很多之前难以想象的黑科技,就像小酱所使用的OPPO R9手机,虽然是很早之间的机型款式了,但是作为一款智能手机,小酱依旧能够体验到许多黑科技功能.如一键提取音频 ...

  8. 助力中文文字识别突破,美团公开首个真实场景招牌图像数据集

    美团作为全球最大的本地生活服务平台,拥有由遍布全国的市场人员所拍摄的众多门脸招牌图片数据.每张图片都是由全国的不同个人,采用不同设备,在不同地点,不同时间和不同环境下所拍摄的不同目标,是难得的可以公正 ...

  9. Unity3D脚本中文系列教程(七)

    http://dong2008hong.blog.163.com/blog/static/4696882720140311445677/?suggestedreading&wumii Unit ...

最新文章

  1. window 配置wnmp(转下整理 ,全)
  2. how is opportunity detail page display first item by default
  3. 数据库系统实训——实验五——存储过程
  4. 《高性能路由器 设计与实现》高性能路由器新型体系结构 小记
  5. (26)IMPCAT软件bit文件下载流程(FPGA不积跬步101)
  6. Mysql基础知识--视图
  7. 4. tensorflow2实现抽样分布—卡方分布、F分布、t分布、Beta分布、Gamma分布——python实战
  8. JVM学习(1)——通过实例总结Java虚拟机的运行机制
  9. g4600黑苹果efi_Hackintosh黑苹果长期维护机型EFI列表及安装教程整理
  10. Java获取List长度
  11. 点云学习笔记1——激光雷达的原理
  12. YoutuBe 是如何利用深度学习解决搜索推荐问题的? (一) - 论文翻译
  13. 浪潮服务器开启虚拟化功能,浪潮服务器-虚拟化解决方案.ppt
  14. 2021年氧化工艺报名考试及氧化工艺模拟试题
  15. css将两张图片叠加(简易方法)
  16. petalinux(3)——创建APP
  17. java joda datetime_关于java:使用Joda将日期转换为DateTime
  18. 生产者消费者问题:管程法
  19. 小京鱼京东智能服务平台
  20. E18-D80NK红外避障传感器使用(附32单片机源码)

热门文章

  1. 计算机2010版本怎么样的,cad2010版本使用起来怎么样
  2. JavaWeb开启GZIP压缩
  3. 令人耳目一新的20款英文手写字体
  4. Object Detection in 20 Years: A Survey
  5. 微信支付开发,基于SpringBoot+Vue架构的Java在线支付项目
  6. 1-out-2 OT
  7. iOS调用各大地图APP导航,进行路线规划
  8. 在线视频流播放方法利弊;ffmpeg mp4 faststart;mp4 moov作用
  9. 机器人辅助的符文天赋_LOLS7辅助机器人 机器人辅助天赋加点攻略
  10. Win10重新安装Windows应用商店(Microsoft Store)