Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理
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 updatevoid 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 monovar 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;}}
}
附加:
声音设置相关
语言支持 - 语音服务 - Azure Cognitive Services | Azure Docs
中国部分声音选择设置:
wuu-CN | 中文(吴语,简体) | wuu-CN-XiaotongNeural 1,2(女)wuu-CN-YunzheNeural 1,2(男) |
yue-CN | 中文(粤语,简体) | yue-CN-XiaoMinNeural 1,2(女)yue-CN-YunSongNeural 1,2(男) |
zh-cn | 中文(普通话,简体) | zh-cn-XiaochenNeural (女)zh-cn-XiaohanNeural (女)zh-cn-XiaomengNeural (女)zh-cn-XiaomoNeural (女)zh-cn-XiaoqiuNeural (女)zh-cn-XiaoruiNeural (女)zh-cn-XiaoshuangNeural (女性、儿童)zh-cn-XiaoxiaoNeural (女)zh-cn-XiaoxuanNeural (女)zh-cn-XiaoyanNeural (女)zh-cn-XiaoyiNeural (女)zh-cn-XiaoyouNeural (女性、儿童)zh-cn-XiaozhenNeural (女)zh-cn-YunfengNeural (男)zh-cn-YunhaoNeural (男)zh-cn-YunjianNeural (男)zh-cn-YunxiaNeural (男)zh-cn-YunxiNeural (男)zh-cn-YunyangNeural (男)zh-cn-YunyeNeural (男)zh-cn-YunzeNeural (男) |
zh-cn-henan | 中文(中原官话河南,简体) | zh-cn-henan-YundengNeural 2(男) |
zh-cn-liaoning | 中文(东北官话,简体) | zh-cn-liaoning-XiaobeiNeural 1,2(女) |
zh-cn-shaanxi | 中文(中原官话陕西,简体) | zh-cn-shaanxi-XiaoniNeural 1,2(女) |
zh-cn-shandong | 中文(冀鲁官话,简体) | zh-cn-shandong-YunxiangNeural 2(男) |
zh-cn-sichuan | 中文(西南普通话,简体) | zh-cn-sichuan-YunxiNeural 1,2(男) |
zh-HK | 中文(粤语,繁体) | zh-HK-HiuGaaiNeural (女)zh-HK-HiuMaanNeural (女)zh-HK-WanLungNeural 1(男) |
zh-TW | 中文(台湾普通话,繁体) | zh-TW-HsiaoChenNeural (女)zh-TW-HsiaoYuNeural (女)zh-TW-YunJheNeural (男) |
zu-ZA | 祖鲁语(南非) | zu-ZA-ThandoNeural 2(女)zu-ZA-ThembaNeural 2(男) |
相关文章:

Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理
Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理 目录 Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理 一、简单介绍 二、实现原理 三、注意实现 四、实现步骤 六、关键脚本 附加: 声音设置相关 一、简单介绍…...

【A卡,Windows】stable diffusion webui下载安装避坑指南
观前提醒 本文内容都是本人亲身经历的,一个一个安装下载测试所感,当然如果你更想用傻瓜式集成包的,那还是跳过这篇文章吧。 当然我不推荐这篇文章的操作,因为我用了差不多1h才有一副图,有N卡,就用N卡&…...

并发编程-系统学习篇
并发编程的掌握过程并不容易。 我相信为了解决这个问题,你也听别人总结过:并发编程的第 一原则, 那就是不要写并发程序 这个原则在我刚毕业的那几年曾经是行得通的,那个时候多核服务器还是一种奢侈品,系统的并发量也很…...
在浏览器网页上使用JavaScript如何将mp4视频转换成gif动态图片
前言 要将mp4视频转换为gif动态图像,可以使用JavaScript库中的FFmpeg.js。这个库可以使用JavaScript读取和写入文件,也可以使用canvas和WebGL在浏览器中进行视频处理。 步骤如下: 1.在网站中引入FFmpeg.js库 <script src"https:/…...

Nginx网络服务——主配置文件-nginx.conf
Nginx网络服务——主配置文件-nginx.conf 一、全局配置的六个模块简介二、nginx配置文件的详解1.全局配置模块2.I/O 事件配置3.HTTP 配置4.Web 服务的监听配置5.其他设置 三、访问状态统计与控制1.访问状态统计2.基于授权的访问控制3.基于客户端的访问控制 一、全局配置的六个模…...

Java Map集合
8 Map集合 HashMap: 元素按照键是无序,不重复,无索引,值不做要求LinkedHashMap: 元素按照键是有序,不重复,无索引,值不做要求8.1 Map集合概述和特点 Map集合是一种双列集合,每个元素包含两个值Interface Map<K,V>; K:键的类型,V:值的类型Map集合的每个元素的格…...
数据库中的中英文术语大全
一、基础理论 基础理论英文术语中文释义data数据database(DB)数据库database system(dbs)数据库系统database management system数据库管理系统database administrator数据库管理员relational model关系模型relational database关…...

调用华为API实现身份证识别
调用华为API实现身份证识别 1、作者介绍2、调用华为API实现身份证识别2.1 算法介绍2.1.1OCR简介2.1.2身份证识别原理2.1.3身份证识别应用场景 2.2 调用华为API流程 3、代码实现3.1安装相关的包3.2代码复现3.3实验结果 1、作者介绍 雷千龙,男,西安工程大…...

一个简单的基于C/S模型的TCP通信实例
1 TCP协议 1.1 概念 TCP是一种面向连接的、可靠的协议,有点像打电话,双方拿起电话互通身份之后就建立了连接,然后说话就行了,这边说的话那边保证听得到,并且是按说话的顺序听到的,说完话挂机断开连接。也…...

VMware ESXi 8.0b Unlocker OEM BIOS 集成 REALTEK 网卡驱动和 NVMe 驱动 (集成驱动版)
VMware ESXi 8.0b Unlocker & OEM BIOS 集成 REALTEK 网卡驱动和 NVMe 驱动 (集成驱动版) 发布 ESXi 8.0 集成驱动版,在个人电脑上运行企业级工作负载 请访问原文链接:https://sysin.org/blog/vmware-esxi-8-sysin/,查看最新版。原创作…...

ShardingSphere笔记(三):自定义分片算法 — 按月分表·真·自动建表
ShardingSphere笔记(二):自定义分片算法 — 按月分表真自动建表 文章目录 ShardingSphere笔记(二):自定义分片算法 — 按月分表真自动建表一、 前言二、 Springboot 的动态数据库三、 实现我们自己的动态数…...

SpringBoot 如何实现文件上传和下载
当今Web应用程序通常需要支持文件上传和下载功能,Spring Boot提供了简单且易于使用的方式来实现这些功能。在本篇文章中,我们将介绍Spring Boot如何实现文件上传和下载,同时提供相应的代码示例。 文件上传 Spring Boot提供了Multipart文件上…...

Linux系统下imx6ull QT编程—— Ubuntu 下编写程序(一)
Linux QT编程 文章目录 Linux QT编程前言一、C简介二、C环境设置1.安装编译 C 语言和 C的环境。2.创建文件编写代码3.编译运行代码 总结 前言 绍在 Ubuntu 在终端窗口下使用 vi/vim 编辑一个 C源文件。通过编写最简单的示例“Hello,World QCX”。 一、C简介 C (c…...

网络编程--多线程服务器客户端
写在前面 此前的回声服务器/客户端都是在主线程中阻塞交互,本文将使用多线程方式实现服务器/客户端。 互斥量相关接口 使用多线程,自然避免不了线程同步问题。 因本文使用互斥量实现线程同步,因此仅介绍互斥量相关接口,其他实…...
如何使用vue的计算属性来处理数据计算?
计算属性是Vue.js中非常强大的功能,它可以帮助我们轻松地处理数据计算和管理数据。 先说个段子:有一天,一个新手问一个Vue大师,“大师,我的数据计算和管理怎么那么麻烦?”,大师回答:…...

游戏研发项目管理
基于阶段模式进行游戏新产品研发过程,以及基于这种研发过程使用Leangoo 领歌敏捷工具管理 二、游戏产品开发流程 通常开发一款新游戏大体上会按照如下流程来进行: 1) 概念阶段 – Concept 主策根据产品创意,确定游戏策划草案&a…...
P1249 乘积最大
最大乘积 题目描述 一个正整数一般可以分为几个互不相同的自然数的和,如 3 1 2 312 312, 4 1 3 413 413, 5 = 1 4 2 3 5=1423 5=1423, 6 1 5 = 2 4 615=24 …...

【7 Vue3 – Composition API】
1 认识Composition API Options API的弊端 setup函数 2 setup函数的参数 3 setup简单使用 1 注意不再有响应式数据 要做到响应式数据需要在数据定义时使用ref包装数据,并且在使用时,使用value解包 2 注意template要使用的数据或者函数,必须要return 返回才能被使用 <templa…...

设计模式-模板方法模式
模板方法模式 问题背景解决方案:模板方法模式基本介绍解决问题代码示例运行结果 钩子方法注意事项和细节 问题背景 豆浆的制作: 1)制作豆浆的流程:选材—>添加配料—>浸泡—>放到豆浆机打碎 2)通过添加不同…...

9. python的if语句
文章目录 一、if结构1.1 比较符号1.1.1 使用比较两个数据是否相等:1.1.2 使用!号比较数据是否不相等1.1.3 使用<号比较数字大小关系1.1.4 使用<号比较数字大小关系1.1.5 使用>号比较数字大小关系1.1.6 使用>号比较数字大小关系 1.2 关键字1.2.1 and关键…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...

短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...