Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理

news2024/11/29 2:53:14

Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理

目录

Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理

一、简单介绍

二、实现原理

三、注意实现

四、实现步骤

 六、关键脚本


一、简单介绍

Unity 工具类,自己整理的一些游戏开发可能用到的模块,单独独立使用,方便游戏开发。

本节介绍,这里在使用微软的Azure 进行语音合成的两个方法的做简单整理,这里简单说明,如果你有更好的方法,欢迎留言交流。

官网注册:

面向学生的 Azure - 免费帐户额度 | Microsoft Azure

官网技术文档网址:

技术文档 | Microsoft Learn

官网的TTS:

文本转语音快速入门 - 语音服务 - Azure Cognitive Services | Microsoft Learn

Azure Unity SDK  包官网:

安装语音 SDK - Azure Cognitive Services | Microsoft Learn

SDK具体链接:

https://aka.ms/csspeech/unitypackage

 

二、实现原理

1、官网申请得到语音合成对应的 SPEECH_KEY 和 SPEECH_REGION

2、然后对应设置 语言 和需要的声音 配置

3、使用 普通方式 和 流式获取得到音频数据,在声源中播放即可

三、注意实现

1、在合成语音文本较长的情况下,流式获取的速度明显会优于普通的方式

2、目前流式获取的方式,我是暂时没有好的方式管理网络错误和音频播放结束的事件

(如果有兄弟集美们知道,还请留言赐教哈)

四、实现步骤

1、下载好SDK 导入

2、简单的搭建场景

3、写测试脚本,和普通获取和流式获取方式

4、把测试脚本添加到场景中,并赋值

 5、运行,输入文字,点击对应按钮即可

 六、关键脚本

1、Test


using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour
{
    public InputField m_InputField;
    public Button m_StreamButton;
    public Button m_NormalButton;
    public AudioSource m_AudioSource;

    // Start is called before the first frame update
    void Start()
    {

        m_StreamButton.onClick.AddListener(() => {
            AzureTTSStream.Instance.StartTTS(m_InputField.text, m_AudioSource);
        });
        m_NormalButton.onClick.AddListener(() => {
            AzureTTSNormal.Instance.StartTTS(m_InputField.text, m_AudioSource);
        });
    }

}

2、AzureTTSNormal

using Microsoft.CognitiveServices.Speech;
using System;
using System.Collections;
using UnityEngine;

public class AzureTTSNormal : MonoSingleton<AzureTTSNormal>
{
    private AudioSource m_AudioSource;

    private string m_SubscriptionKey = "Your";
    private string m_Region = "Your";
    private string m_SpeechSynthesisLanguage = "zh-CN";
    private string m_SpeechSynthesisVoiceName = "zh-CN-XiaochenNeural";
    private Coroutine m_TTSCoroutine;

    /// <summary>
    /// 你的授权
    /// </summary>
    /// <param name="subscriptionKey">子脚本的Key</param>
    /// <param name="region">地区</param>
    public void SetAzureAuthorization(string subscriptionKey, string region)
    {
        m_SubscriptionKey = subscriptionKey;
        m_Region = region;
    }

    /// <summary>
    /// 设置语音和声音
    /// </summary>
    /// <param name="language">语言</param>
    /// <param name="voiceName">声音</param>
    public void SetLanguageVoiceName(SpeechSynthesisLanguage language, SpeechSynthesisVoiceName voiceName)
    {
        m_SpeechSynthesisLanguage = language.ToString().Replace('_', '-');
        m_SpeechSynthesisVoiceName = voiceName.ToString().Replace('_', '-');
    }

    /// <summary>
    /// 设置音源
    /// </summary>
    /// <param name="audioSource"></param>
    public void SetAudioSource(AudioSource audioSource)
    {
        m_AudioSource = audioSource;
    }

    /// <summary>
    /// 开始TTS
    /// </summary>
    /// <param name="spkMsg"></param>
    /// <param name="errorAction"></param>
    public void StartTTS(string spkMsg, Action<string> errorAction = null)
    {
        StopTTS();
        m_TTSCoroutine = StartCoroutine(SynthesizeAudioCoroutine(spkMsg, errorAction));
    }

    /// <summary>
    /// 开始TTS
    /// </summary>
    /// <param name="spkMsg"></param>
    /// <param name="audioSource"></param>
    /// <param name="errorAction"></param>
    public void StartTTS(string spkMsg, AudioSource audioSource, Action<string> errorAction = null)
    {
        SetAudioSource(audioSource);
        StartTTS(spkMsg, errorAction);
    }

    /// <summary>
    /// 暂停TTS
    /// </summary>
    public void StopTTS()
    {
        if (m_TTSCoroutine != null)
        {
            StopCoroutine(m_TTSCoroutine);
            m_TTSCoroutine = null;
        }
        if (m_AudioSource != null)
        {
            m_AudioSource.Stop();
            m_AudioSource.clip = null;
        }
    }

    public IEnumerator SynthesizeAudioCoroutine(string spkMsg, Action<string> errorAction)
    {
        yield return null;
        var config = SpeechConfig.FromSubscription(m_SubscriptionKey, m_Region);
        config.SpeechSynthesisLanguage = m_SpeechSynthesisLanguage;
        config.SpeechSynthesisVoiceName = m_SpeechSynthesisVoiceName;
        // Creates a speech synthesizer.
        // Make sure to dispose the synthesizer after use!
        using (var synthsizer = new SpeechSynthesizer(config, null))
        {

            // Starts speech synthesis, and returns after a single utterance is synthesized.
            var result = synthsizer.SpeakTextAsync(spkMsg).Result;
            //print("after   " + DateTime.Now);

            // Checks result.
            string newMessage = string.Empty;
            if (result.Reason == ResultReason.SynthesizingAudioCompleted)
            {
                // Since native playback is not yet supported on Unity yet (currently only supported on Windows/Linux Desktop),
                // use the Unity API to play audio here as a short term solution.
                // Native playback support will be added in the future release.
                var sampleCount = result.AudioData.Length / 2;
                var audioData = new float[sampleCount];
                for (var i = 0; i < sampleCount; ++i)
                {
                    audioData[i] = (short)(result.AudioData[i * 2 + 1] << 8 | result.AudioData[i * 2]) / 32768.0F;

                }
                // The default output audio format is 16K 16bit mono
                var audioClip = AudioClip.Create("SynthesizedAudio", sampleCount, 1, 16000, false);
                audioClip.SetData(audioData, 0);
                m_AudioSource.clip = audioClip;
                Debug.Log(" audioClip.length " + audioClip.length);
                m_AudioSource.Play();


            }

            else if (result.Reason == ResultReason.Canceled)
            {
                var cancellation = SpeechSynthesisCancellationDetails.FromResult(result);
                newMessage = $"CANCELED:\nReason=[{cancellation.Reason}]\nErrorDetails=[{cancellation.ErrorDetails}]\nDid you update the subscription info?";
                Debug.Log(" newMessage "+ newMessage);
                if (errorAction!=null) { errorAction.Invoke(newMessage); }

            }


        }
    }
}

3、AzureTTSStream

using UnityEngine;
using Microsoft.CognitiveServices.Speech;
using System.IO;
using System;
using System.Collections;

public class AzureTTSStream : MonoSingleton<AzureTTSStream>
{
    private AudioSource m_AudioSource;

    private string m_SubscriptionKey = "Your";

    private string m_Region = "Your";

    private string m_SpeechSynthesisLanguage = "zh-CN";

    private string m_SpeechSynthesisVoiceName = "zh-CN-XiaochenNeural";

    public const int m_SampleRate = 16000;

    public const int m_BufferSize = m_SampleRate * 60; //最大支持60s音频,但是也可以调大,流式的无所谓

    public const int m_UpdateSize = m_SampleRate / 10; //采样容量,越大越卡

    private Coroutine m_TTSCoroutine;
    private int m_DataIndex = 0;

    private AudioDataStream m_AudioDataStream;

    private void OnEnable()
    {
        StopTTS();
    }

    private void OnDisable()
    {
        StopTTS();
    }

    /// <summary>
    /// 你的授权
    /// </summary>
    /// <param name="subscriptionKey">子脚本的Key</param>
    /// <param name="region">地区</param>
    public void SetAzureAuthorization(string subscriptionKey, string region)
    {
        m_SubscriptionKey = subscriptionKey;
        m_Region = region;
    }

    /// <summary>
    /// 设置语音和声音
    /// </summary>
    /// <param name="language">语言</param>
    /// <param name="voiceName">声音</param>
    public void SetLanguageVoiceName(SpeechSynthesisLanguage language, SpeechSynthesisVoiceName voiceName)
    {
        m_SpeechSynthesisLanguage = language.ToString().Replace('_', '-');
        m_SpeechSynthesisVoiceName = voiceName.ToString().Replace('_', '-');
    }

    /// <summary>
    /// 设置音源
    /// </summary>
    /// <param name="audioSource"></param>
    public void SetAudioSource(AudioSource audioSource)
    {
        m_AudioSource = audioSource;
    }

    /// <summary>
    /// 开始TTS
    /// </summary>
    /// <param name="spkMsg"></param>
    /// <param name="errorAction"></param>
    public void StartTTS(string spkMsg, Action<string> errorAction = null)
    {
        StopTTS();
        m_TTSCoroutine = StartCoroutine(SynthesizeAudioCoroutine(spkMsg, errorAction));
    }

    /// <summary>
    /// 开始TTS
    /// </summary>
    /// <param name="spkMsg"></param>
    /// <param name="audioSource"></param>
    /// <param name="errorAction"></param>
    public void StartTTS(string spkMsg, AudioSource audioSource, Action<string> errorAction = null)
    {
        SetAudioSource(audioSource);
        StartTTS(spkMsg, errorAction);
    }

    /// <summary>
    /// 暂停TTS
    /// </summary>
    public void StopTTS()
    {
        // 释放流
        if (m_AudioDataStream != null)
        {
            m_AudioDataStream.Dispose();
            m_AudioDataStream = null;
        }

        if (m_TTSCoroutine != null)
        {
            StopCoroutine(m_TTSCoroutine);
            m_TTSCoroutine = null;

        }
        if (m_AudioSource != null)
        {
            m_AudioSource.Stop();
            m_AudioSource.clip = null;
            m_DataIndex = 0;
        }
    }

    /// <summary>
    /// 发起TTS
    /// </summary>
    /// <param name="speakMsg">TTS的文本</param>
    /// <param name="errorAction">错误事件(目前没有好的判断方法)</param>
    /// <returns></returns>
    private IEnumerator SynthesizeAudioCoroutine(string speakMsg, Action<string> errorAction)
    {
        var config = SpeechConfig.FromSubscription(m_SubscriptionKey, m_Region);
        config.SpeechSynthesisLanguage = m_SpeechSynthesisLanguage;
        config.SpeechSynthesisVoiceName = m_SpeechSynthesisVoiceName;
        var audioClip = AudioClip.Create("SynthesizedAudio", m_BufferSize, 1, m_SampleRate, false);
        m_AudioSource.clip = audioClip;
        using (var synthesizer = new SpeechSynthesizer(config, null))
        {

            var result = synthesizer.StartSpeakingTextAsync(speakMsg);
            yield return new WaitUntil(() => result.IsCompleted);
            m_AudioSource.Play();
            using (m_AudioDataStream = AudioDataStream.FromResult(result.Result))
            {
                MemoryStream memStream = new MemoryStream();
                byte[] buffer = new byte[m_UpdateSize * 2];
                uint bytesRead;
                do
                {
                    bytesRead = m_AudioDataStream.ReadData(buffer);
                    memStream.Write(buffer, 0, (int)bytesRead);
                    if (memStream.Length >= m_UpdateSize * 2)
                    {
                        var tempData = memStream.ToArray();
                        var audioData = new float[m_UpdateSize];
                        for (int i = 0; i < m_UpdateSize; ++i)
                        {
                            audioData[i] = (short)(tempData[i * 2 + 1] << 8 | tempData[i * 2]) / 32768.0F;
                        }

                        audioClip.SetData(audioData, m_DataIndex);
                        m_DataIndex = (m_DataIndex + m_UpdateSize) % m_BufferSize;

                        memStream = new MemoryStream();
                        yield return null;
                    }
                } while (bytesRead > 0);

            }
        }

        if (m_DataIndex == 0)
        {
            if (errorAction != null)
            {
                errorAction.Invoke(" AudioData error");
            }
        }
    }
}

/// <summary>
/// 添加更多的其他语言
/// 形式类似为 Zh_CN 对应 "zh-CN";
/// </summary>
public enum SpeechSynthesisLanguage
{
    Zh_CN,
}

/// <summary>
/// 添加更多的其他声音
/// 形式类似为 Zh_CN_XiaochenNeural 对应 "zh-CN-XiaochenNeural";
/// </summary>
public enum SpeechSynthesisVoiceName
{
    Zh_CN_XiaochenNeural,
}

4、MonoSingleton

using UnityEngine;
public class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T instance;
    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                // 查找存在的实例
                instance = (T)FindObjectOfType(typeof(T));

                // 如果不存在实例,则创建
                if (instance == null)
                {
                    // 需要创建一个游戏对象,再把这个单例组件挂载到游戏对象上
                    var singletonObject = new GameObject();
                    instance = singletonObject.AddComponent<T>();
                    singletonObject.name = typeof(T).ToString() + " (Singleton)";

                    // 让实例不在切换场景时销毁
                    DontDestroyOnLoad(singletonObject);
                }
            }

            return instance;
        }
    }
}

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

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

相关文章

从事黑客工作十余年,究竟如何成为一名高级的安全工程师?

目录 1. 前言 2. 经验 3. 要考虑的问题 4. 学习路线详解 第一步&#xff1a;计算机基础 第二步&#xff1a;编程能力 第三步&#xff1a;安全初体验 第四步&#xff1a;分方向 尾言 参考书籍列表 1. 前言 说实话&#xff0c;一直到现在&#xff0c;我都认为绝大多数…

STP协议

目录 STP的基本概念&#xff1a; 桥ID&#xff08;Bridge ID&#xff09;&#xff1a; 根桥&#xff1a; 开销&#xff08;Cost&#xff09;&#xff1a; RPC&#xff08;根路径开销&#xff09;&#xff1a; Port ID&#xff1a; BPDU&#xff1a;&#xff08;网桥协议…

ROS:ROS是什么

目录 一、ROS简介二、ROS可以做些什么三、ROS特征四、ROS特点4.1点对点设计4.2不依赖编程语言4.3精简与集成4.4便于测试4.5开源4.6强大的库与社区 五、ROS的发展六、ROS架构6.1OS层6.2中间层6.3应用层 七、通信机制八、计算图8.1节点&#xff08;Node&#xff09;8.2节点管理器…

当ChatGPT参加中国高考,把全国A卷B卷喂给它后,竟严重偏科

作者 |Python ChatGPT作为一个智能人机对话应用&#xff0c;在推出后迅速风靡全球。仅仅一个月的时间&#xff0c;其用户数量已经突破了一亿大关。人们也用ChatGPT测试了很多考试项目&#xff0c;例如SAT、AP、GRE等。然而&#xff0c;如果让ChatGPT来参加我们中国的高考&…

Jetson Orin Nano 快速安装 ROS2 Foxy详解

大家好&#xff0c;我是虎哥&#xff0c;入手一块Jeston Orin nano 8G模块&#xff0c;这个模块因为是英伟达未来5年左右主推的模块&#xff0c;所以我逐步会将之前所有的应用都在这个模块环境上做适配&#xff0c;本章内容&#xff0c;我将主要围绕安装ROS2 Foxy版本为主展开。…

探索Java面向对象编程的奇妙世界(四)

⭐ 变量的分类和作用域⭐ 包机制(package、import)⭐ 面向对象三大特征——继承⭐ 继承的作用⭐ 继承的实现⭐ instanceof 运算符⭐ 继承使用要点⭐ 方法重写 override⭐ final 关键字⭐ 继承和组合 ⭐ 变量的分类和作用域 变量有三种类型&#xff1a;局部变量、成员变量(也称为…

Diffusion Model 深入剖析

Diffusion Model 深入剖析 最近AI生成艺术领域非常火热&#xff0c;从 Midjourney 到 Stable Diffusion&#xff0c;不管你是绘画高手还是艺术小白&#xff0c;只要输入想要绘制内容的描述或者基础图像&#xff0c;就可以生成富有艺术感的画作&#xff01; 这些风格各异、以假…

CodeForces..构建美丽数组.[简单].[情况判断].[特殊条件下的最小值奇偶问题]

题目描述&#xff1a; 题目解读&#xff1a; 给定数组a[n]&#xff0c;ai>0&#xff0c;问能否得到一个数组b[n]&#xff0c;数组b中的元素都大于0且全奇or全偶。 数组b中的元素biai or ai-aj&#xff08;1<j<n&#xff09;。 解题思路&#xff1a; 数组b中的元素都…

IDEA代码替换

IDEA代码替换 快捷键 当前文件内容 C t r l R CtrlR CtrlR 全局替换 C t r l S h i f t R CtrlShiftR CtrlShiftR 使用 第一行输入栏&#xff1a;输入被替换内容 第二行输入栏&#xff1a;输入替换内容 详细使用 第一行输入栏后第一个图标&#xff1a;换行 第一行输…

数论专题(3)逆元

目录 初步认识 逆元 定义 应用 费马小定理 好久没有更新我们的数论专题板块了&#xff0c;今天&#xff0c;我们就来探究一下新知——逆元。 初步认识 在数据非常大的情景下&#xff0c;我们通常会对数据先进行取模运算&#xff0c;来计算在一定的范围内进行处理。而运算…

SpringBoot启动扩展应用:干预优化+加快启动时间

目录 一、SpringBoot启动配置原理简述 二、SpringBoot启动过程干预 &#xff08;一&#xff09;ApplicationContextInitializer扩展 修改Spring Boot默认的environment属性 添加自定义的PropertySource 注册自定义bean &#xff08;二&#xff09;SpringApplicationRunL…

【计算思维题】少儿编程 蓝桥杯青少组计算思维真题及详细解析第6套

少儿编程 蓝桥杯青少组计算思维真题及详细解析第6套 1、兰兰有一些数字卡片,从 1 到 100 的数字都有,她拿出几张数字卡片按照一定顺序摆放。想一想,第 5 张卡片应该是 A、11 B、12 C、13 D、14 答案:C 考点分析:主要考查小朋友们的观察能力和数学推理能力,从给定的图…

[Nacos] Nacos Server处理心跳请求 (八)

文章目录 1.InstanceController#beat()1.1 serviceManager.registerInstance()1.2 serviceManager.getService()1.3 处理本次心跳 1.InstanceController#beat() CanDistroPutMapping("/beat")Secured(parser NamingResourceParser.class, action ActionTypes.WRITE…

面了个字节出来的00后,我见识到了什么叫“自动化测试+性能测试”

前两天看到字节一个老哥写的帖子&#xff0c;提到高阶测试工程师必须掌握的技能&#xff0c;其中他明确提出了“精通性能测试”。 为啥性能测试对测试工程师如此重要&#xff1f; 性能测试是指在特定的负载情况下&#xff0c;测试目标系统的响应时间、吞吐量、并发用户数、资源…

Eclipse 教程Ⅳ

Eclipse 工作空间(Workspace) eclipse 工作空间包含以下资源&#xff1a; 项目文件文件夹 项目启动时一般可以设置工作空间&#xff0c;你可以将其设置为默认工作空间&#xff0c;下次启动后无需再配置&#xff1a; 工作空间(Workspace)有明显的层次结构。 项目在最顶级&…

HTML 教程1

HTML文档的后缀名 .html.htm 以上两种后缀名没有区别&#xff0c;都可以使用。 HTML 实例 <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>菜鸟教程(runoob.com)</title> </head> <body><h1&g…

QTableWidget加载大文件数据

由于最近在项目中需要加载几GB的文件&#xff0c;并且需要在QTableWidget中进行显示&#xff1b;粗略估计可能得有几千万行&#xff0c;如果使用常规的方法&#xff0c;直接在QTableWidget中进行全部显示&#xff0c;会比较卡。所以查找相关资料&#xff0c;最终想到了一个比较…

算法基础学习笔记——⑧堆\哈希表

✨博主&#xff1a;命运之光 ✨专栏&#xff1a;算法基础学习 目录 ✨堆 &#x1f353;堆模板&#xff1a; ✨哈希表 &#x1f353;一般哈希模板&#xff1a; &#x1f353;字符串哈希模板&#xff1a; 前言&#xff1a;算法学习笔记记录日常分享&#xff0c;需要的看哈O(…

【是C++,不是C艹】 类与对象 | 认识面向对象 | 访问限定符 | 封装 | this指针

&#x1f49e;&#x1f49e;欢迎来到 Claffic 的博客&#x1f49e;&#x1f49e; &#x1f449; 专栏&#xff1a;《是C&#xff0c;不是C艹》&#x1f448; 前言&#xff1a; 在C入门之后&#xff0c;就要进入C的第一个核心&#xff1a;类与对象&#xff0c;这期带大家认识认识…

Multichain跨链无法到账,DApp真去中心化or伪去中心化?

团队出问题&#xff0c;DApp就用不了&#xff0c;multichain被不少人质疑伪去中心化&#xff0c;甚至更有人开始质疑web3&#xff0c;那么这到底是怎么回事呢&#xff1f; 跨链桥问题让DApp的去中心化引发质疑 事情是这样的&#xff0c;5月24下午0xscope发推称与multichain有关…