zoukankan      html  css  js  c++  java
  • 初探机器学习之使用讯飞TTS服务实现在线语音合成

    最近在调研使用各个云平台提供的AI服务,有个语音合成的需求因此就使用了一下科大讯飞的TTS服务,也用.NET Core写了一个小示例,下面就是这个小示例及其相关背景知识的介绍。

    一、什么是语音合成(TTS)

    1.1 What is 语音合成?

      

      将文字信息转化为声音信息,给应用配上“嘴巴”,这就是语音合成。

    Note:语音合成和语音识别技术是实现人机语音通信,建立一个有听和讲能力的口语系统所必需的两项关键技术。使电脑具有类似于人一样的说话能力,是当今时代信息产业的重要竞争市场。和语音识别相比,语音合成的技术相对说来要成熟一些,并已开始向产业化方向成功迈进,大规模应用指日可待。

    1.2 语音合成的应用场景

      目前,语音合成技术在我们生活中具有广泛的应用,如机器人发声、有声读物制作、语音播报等等,这些应用场景都离不开语音合成。

      

    Note:在语音导航应用、新闻类 APP 中,语音合成可以快速生成高质量的播报音频,实现在开车、走路等不方便阅读消息的情况下,音频消息的即时传达。

    1.3 语音合成的基本原理

      这里借用网易智能的一篇文章中的介绍如下:

      

      简单来说语音合成分为文本分析、韵律分析和声学分析三个部分。通过文本分析提取出文本特征,在此基础上预测基频、时长、节奏等多种韵律特征,然后通过声学模型实现从前端参数到语音参数的映射,最后通过声码器合成语音。整个过程类似于“编码、信息匹配,解码的过程”。

      

      对语音合成有兴趣的朋友,可以阅读以下这篇文章《吴恩达盛赞的Deep Voice详解教程,教你快速理解百度的语音合成原理》。

    二、使用.NET Core调用讯飞API

    2.1 科大讯飞TTS服务

      讯飞提供了众多极具特色的发音人(音库)供您选择。其合成音在音色、自然度等方面的表现均接近甚至超过了人声。这种语音合成体验,达到了真正可商用的标准。在我对百度、阿里、腾讯及讯飞的TTS服务对比中,讯飞的TTS体验好一些,且发音人(音库)最为丰富,还有四川话(准确来说是成都话)音库,碉堡了。大家可以去讯飞TTS服务网页体验一下。

    2.2 .NET Core调用示例

      (1)首先得去讯飞开放平台注册一个账号,并申请一个勾选有“在线语音合成”的应用

      

      这里主要是拿到AppID及ApiKey,并且可以通过发音人管理增加发音库,当然,高级版的音库都是要单独收费的。免费版本的每天有500次的免费API调用机会。

      (2)参考官方API文档和C# DEMO

      PS:由于讯飞官方提供的C# DEMO是一个基于.NET Framework的比较老的DEMO,在.NET Core下无法正常使用,我将其简单地改写成了.NET Core版本,并封装了一个XunFeiCloudTtsService类如下:(每个属性都有注释,不再赘述,只是对API调用需要的一些属性的简单封装)

        public class XunFeiCloudTtsService
        {
            // Header Type : audio/mpeg
            private const string AUDIO_MPEG_TYPE = "audio/mpeg";
            // AppID, AppKey 从讯飞开放云平台获取
            public string AppID { get; set; } = "your AppID";
            public string ApiKey { get; set; } = "your ApiKey";
            // 要进行合成的文字
            public string TextToSpeech { get; set; } = "这是一段测试文字";
            // 讯飞TTS服务API地址
            public string ServiceUrl { get; set; } = "http://api.xfyun.cn/v1/service/v1/tts";
            // aue = raw, 音频文件保存类型为 wav
            // aue = lame, 音频文件保存类型为 mp3
            public string AUE { get; set; } = "raw";
            // 音频采样率,可选值:audio/L16;rate=8000,audio/L16;rate=16000
            public string AUF { get; set; } = "audio/L16;rate=16000";
            // 发音人,可选值:详见控制台-我的应用-在线语音合成服务管理-发音人授权管理
            public string VoiceName { get; set; } = "xiaoyan";
            // 引擎类型,可选值:aisound(普通效果),intp65(中文),intp65_en(英文),
            // mtts(小语种,需配合小语种发音人使用),x(优化效果),
            // 默认为intp65
            public string EngineType { get; set; } = "intp65";
            // 语速,可选值:[0-100],默认为50
            public string Speed { get; set; } = "50";
            // 音量,可选值:[0-100],默认为50
            public string Volume { get; set; } = "50";
            // 音高,可选值:[0-100],默认为50
            public string Pitch { get; set; } = "50";
            // URL加密后的TextToSpeech
            public string Bodys { get; set; }
            // 要保存的文件夹路径
            public string SavePath { get; set; } = "./Output/";
            // 要保存的文件名
            public string FileName { get; set; } = $"TTS-{ Guid.NewGuid().ToString() }";
    
            static XunFeiCloudTtsService()
            {
                Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
            }
    
            public XunFeiCloudTtsService()
            { }
    
            public XunFeiCloudTtsService(string appID, string apiKey, string serviceUrl)
            {
                AppID = appID;
                ApiKey = apiKey;
                ServiceUrl = serviceUrl;
            }
    
            public string GetTtsResult()
            {
                SetTextDataBodys();
    
                string param = "{"aue":"" + AUE + "","auf":"" + AUF + "","voice_name":"" + VoiceName + "","engine_type":""
                    + EngineType + "","speed":"" + Speed + "","volume":"" + Volume + "","pitch":"" + Pitch + ""}";
                // 获取十位的时间戳
                TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
                string curTime = Convert.ToInt64(ts.TotalSeconds).ToString();
                // 对参数先utf-8然后用base64编码
                byte[] paramData = Encoding.UTF8.GetBytes(param);
                string paraBase64 = Convert.ToBase64String(paramData);
                // 形成签名
                string checkSum = Md5(ApiKey + curTime + paraBase64);
                // 设置HttpHeaders
                var ttsRequest = (HttpWebRequest)WebRequest.Create(ServiceUrl);
                ttsRequest.Method = "POST";
                ttsRequest.ContentType = "application/x-www-form-urlencoded";
                ttsRequest.Headers.Add("X-Param", paraBase64);
                ttsRequest.Headers.Add("X-CurTime", curTime);
                ttsRequest.Headers.Add("X-Appid", AppID);
                ttsRequest.Headers.Add("X-CheckSum", checkSum);
    
                using (Stream requestStream = ttsRequest.GetRequestStream())
                {
                    using (StreamWriter streamWriter = new StreamWriter(requestStream, Encoding.GetEncoding("gb2312")))
                    {
                        streamWriter.Write(Bodys);
                    }
                }
    
                string responseText = string.Empty;
                HttpWebResponse ttsResponse = ttsRequest.GetResponse() as HttpWebResponse;
                using (Stream responseStream = ttsResponse.GetResponseStream())
                {
                    using (StreamReader streamReader = new StreamReader(responseStream, Encoding.GetEncoding("UTF-8")))
                    {
                        string headerType = ttsResponse.Headers["Content-Type"];
                        if (headerType.Equals(AUDIO_MPEG_TYPE))
                        {
                            responseText = GetSuccessResponseText(ttsResponse);
                        }
                        else
                        {
                            responseText = streamReader.ReadToEnd();
                        }
                    }
                }
    
                return responseText;
            }
    
            #region 私有辅助方法
            /// <summary>
            /// 获取请求成功的响应文本
            /// </summary>
            /// <param name="ttsResponse">HttpWebResponse</param>
            /// <returns>响应Headers文本</returns>
            private string GetSuccessResponseText(HttpWebResponse ttsResponse)
            {
                string responseText = string.Empty;
                using (Stream stream = ttsResponse.GetResponseStream())
                {
                    MemoryStream memoryStream = StreamToMemoryStream(stream);
                    if (!Directory.Exists(SavePath))
                    {
                        Directory.CreateDirectory(SavePath);
                    }
    
                    string fileType = string.Empty;
                    switch (AUE.ToLower())
                    {
                        case "raw":
                            fileType = "wav";
                            break;
                        case "lame":
                            fileType = "lame";
                            break;
                    }
    
                    File.WriteAllBytes($"{SavePath}{FileName}.{fileType}", streamTobyte(memoryStream));
                    responseText = ttsResponse.Headers.ToString();
                }
    
                return responseText;
            }
    
            /// <summary>
            ///  对要合成语音的文字先用utf-8然后进行URL加密
            /// </summary>
            private void SetTextDataBodys()
            {
                byte[] textData = Encoding.UTF8.GetBytes(TextToSpeech);
                TextToSpeech = HttpUtility.UrlEncode(textData);
                Bodys = string.Format("text={0}", TextToSpeech);
            }
    
            /// <summary>
            /// 生成令牌 :X-CheckSum
            /// 计算方法:MD5(apiKey + curTime + param),三个值拼接的字符串,进行MD5哈希计算(32位小写),其中apiKey由讯飞提供,调用方管理。
            /// </summary>
            /// <param name="token">apiKey + curTime + param</param>
            /// <returns>X-CheckSum</returns>
            private string Md5(string token)
            {
                MD5 md5 = new MD5CryptoServiceProvider();
                byte[] bytes = Encoding.UTF8.GetBytes(token);
                bytes = md5.ComputeHash(bytes);
                md5.Clear();
    
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < bytes.Length; i++)
                {
                    sb.Append(Convert.ToString(bytes[i], 16).PadLeft(2, '0'));
                }
    
                return sb.ToString().PadLeft(32, '0');
            }
    
            /// <summary>
            /// 将流转换为缓存流
            /// </summary>
            /// <param name="instream">输入流</param>
            /// <returns>输出流</returns>
            private MemoryStream StreamToMemoryStream(Stream instream)
            {
                MemoryStream outstream = new MemoryStream();
                const int bufferLen = 4096;
                byte[] buffer = new byte[bufferLen];
                int count = 0;
                while ((count = instream.Read(buffer, 0, bufferLen)) > 0)
                {
                    outstream.Write(buffer, 0, count);
                }
    
                return outstream;
            }
    
            /// <summary>
            /// 把缓存流转换成字节组
            /// </summary>
            /// <param name="memoryStream">缓存刘</param>
            /// <returns>字节数组</returns>
            private byte[] streamTobyte(MemoryStream memoryStream)
            {
                byte[] buffer = new byte[memoryStream.Length];
                memoryStream.Seek(0, SeekOrigin.Begin);
                memoryStream.Read(buffer, 0, buffer.Length);
    
                return buffer;
            }
            #endregion
        }

      *.由于.NET Core下Encoding默认不支持GB2312,因此需要事先引入一个包:System.Text.Encoding.CodePages,然后在程序启动时注册一下,这里已经封装静态构造函数里面了。

    NuGet>Install-Package System.Text.Encoding.CodePages

      客户端调用:

        public class Program
        {
            public static void Main(string[] args)
            {
                XunFeiCloudTtsService ttsService = new XunFeiCloudTtsService();
                ttsService.AppID = "5c3806f12121";
                ttsService.ApiKey = "a99fff9fc537d883a181231231a37bf56dc8f28";
                ttsService.TextToSpeech = "大家好我是第一个语音合成!";
                ttsService.SavePath = "./TTS-Result/";
                ttsService.FileName = "Hello-TTS";
                ttsService.Speed = "60";
    
                var result = ttsService.GetTtsResult();
                Console.WriteLine(result);
    
                Console.ReadKey();
            }
        }

      *.这里AppID和ApiKey请使用你自己的,我博文这里是乱写的。

      调用结果:

      (1)响应结果

      

      *.如果提示illegal client ip,请到控制台中加入白名单列表:

      

      (2)语音文件

      

      So,播放一下?

      

    三、小结

      有了语音合成,我们可以在我们的业务系统或者App中有了更多的玩法,虽然我们不了解语音合成的具体实现原理。此文只是一个简单的使用示例,无更多的内容,希望对你有帮助,本文的示例代码可以点击这里

    参考资料

    (1)科大讯飞开放云平台,《在线语音合成服务

    (2)科大讯飞TTS服务,API说明文档

    (3)网易智能,《让机器说话更自然,语音合成还能干什么

  • 相关阅读:
    Lodash JS实用类库 数组操作 延时执行 功能强大
    7.【nuxt起步】-Nuxt与后端数据交互
    vue图片懒加载
    猎鹰与龙飞船基于 Linux,采用 C++、Chromium 与 JS 开发
    | 和 ||,& 和 && 的区别
    Linux安装.net core
    Linux下程序后台运行:nohup和&
    vuejs如何调试代码
    全局安装 Vue cli3 和 继续使用 Vue-cli2.x
    导入sass文件
  • 原文地址:https://www.cnblogs.com/edisonchou/p/edc_machine_learning_xunfeicloud_online_tts_introduction.html
Copyright © 2011-2022 走看看