初探机器学习之使用讯飞TTS服务实现在线语音合成

摘要:
最近在调研使用各个云平台提供的AI服务,有个语音合成的需求因此就使用了一下科大讯飞的TTS服务,也用.NETCore写了一个小示例,下面就是这个小示例及其相关背景知识的介绍。这种语音合成体验,达到了真正可商用的标准。

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

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

1.1 What is 语音合成?

初探机器学习之使用讯飞TTS服务实现在线语音合成第1张

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

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

1.2 语音合成的应用场景

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

初探机器学习之使用讯飞TTS服务实现在线语音合成第2张

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

1.3 语音合成的基本原理

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

初探机器学习之使用讯飞TTS服务实现在线语音合成第3张

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

初探机器学习之使用讯飞TTS服务实现在线语音合成第4张

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

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

2.1 科大讯飞TTS服务

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

2.2 .NET Core调用示例

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

初探机器学习之使用讯飞TTS服务实现在线语音合成第5张

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

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

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

    public classXunFeiCloudTtsService
    {//Header Type : audio/mpegprivate 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, 音频文件保存类型为 mp3public string AUE { get; set; } = "raw";//音频采样率,可选值:audio/L16;rate=8000,audio/L16;rate=16000public string AUF { get; set; } = "audio/L16;rate=16000";//发音人,可选值:详见控制台-我的应用-在线语音合成服务管理-发音人授权管理public string VoiceName { get; set; } = "xiaoyan";//引擎类型,可选值:aisound(普通效果),intp65(中文),intp65_en(英文),//mtts(小语种,需配合小语种发音人使用),x(优化效果),//默认为intp65public string EngineType { get; set; } = "intp65";//语速,可选值:[0-100],默认为50public string Speed { get; set; } = "50";//音量,可选值:[0-100],默认为50public string Volume { get; set; } = "50";//音高,可选值:[0-100],默认为50public string Pitch { get; set; } = "50";//URL加密后的TextToSpeechpublic string Bodys { get; set; }//要保存的文件夹路径public string SavePath { get; set; } = "./Output/";//要保存的文件名public string FileName { get; set; } = $"TTS-{ Guid.NewGuid().ToString() }";staticXunFeiCloudTtsService()
        {
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
        }publicXunFeiCloudTtsService()
        { }public XunFeiCloudTtsService(string appID, string apiKey, stringserviceUrl)
        {
            AppID =appID;
            ApiKey =apiKey;
            ServiceUrl =serviceUrl;
        }public stringGetTtsResult()
        {
            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);//设置HttpHeadersvar 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() asHttpWebResponse;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();
                    }
                }
            }returnresponseText;
        }#region 私有辅助方法/// <summary>///获取请求成功的响应文本/// </summary>/// <param name="ttsResponse">HttpWebResponse</param>/// <returns>响应Headers文本</returns>private stringGetSuccessResponseText(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();
            }returnresponseText;
        }/// <summary>///对要合成语音的文字先用utf-8然后进行URL加密/// </summary>private voidSetTextDataBodys()
        {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(stringtoken)
        {
            MD5 md5 = newMD5CryptoServiceProvider();byte[] bytes =Encoding.UTF8.GetBytes(token);
            bytes =md5.ComputeHash(bytes);
            md5.Clear();
            StringBuilder sb = newStringBuilder();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>privateMemoryStream StreamToMemoryStream(Stream instream)
        {
            MemoryStream outstream = newMemoryStream();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);
            }returnoutstream;
        }/// <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);returnbuffer;
        }#endregion}

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

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

客户端调用:

    public classProgram
    {public static void Main(string[] args)
        {
            XunFeiCloudTtsService ttsService = newXunFeiCloudTtsService();
            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)响应结果

初探机器学习之使用讯飞TTS服务实现在线语音合成第6张

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

初探机器学习之使用讯飞TTS服务实现在线语音合成第7张

(2)语音文件

初探机器学习之使用讯飞TTS服务实现在线语音合成第8张

So,播放一下?

初探机器学习之使用讯飞TTS服务实现在线语音合成第9张

三、小结

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

参考资料

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

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

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

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

免责声明:文章转载自《初探机器学习之使用讯飞TTS服务实现在线语音合成》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇HTML邮件模板Mock下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

[Android]Android四大组件之ContentProvider

URI简介 URI(Universal Resource Identifier),又被称为"通用资源标志符"。 URI由许多部分所组成,示例及解说如下: Content URIs介绍 Android遵循URI的标准,定义了一套专用的Uri(即,Content URIs)。并且,Android提供了ContentUris、UriMatcher等类用于操作...

vxworks固件分析

前言 vxworks 的固件分析流程 1.用binwalk查看固件基本信息并解压固件 2.获取固件相关信息, cpu架构,大小端 3.确定固件的加载地址 4.用IDA加载固件,并修复符号表 5. 分析固件 实战分析 一道CTF题 分析固件 用到的例子 http://www.icsmaster.org/wp-content/uploads/2018/01/...

对json进行封装

项目中经常用到对于JSON的各种转换处理,保存一份,免得以后忘了。 using System; using System.Collections.Generic; using System.Text; using System.Reflection; using System.Data; using System.Collections; using CR...

函数对象与仿函数(function object and functor)

part 1. 仿函数在STL组件中的关系   如下图:   # 仿函数配合算法完成不同的策略变化。   # 适配器套接仿函数。 part 2. 仿函数介绍   传递给算法的“函数型实参”不一定得是函数,可以是行为类似函数的对象。这种对象称为函数对象(function object),或称为仿函数(functor)。——《STL标准库(第2版)》 P23...

Preference 使用详解

极力推荐文章:欢迎收藏Android 干货分享 阅读五分钟,每日十点,和您一起终身学习,这里是程序员Android 本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容: Perference 简介 Perference 使用方法 使用XML定义Preference 使用Fragment 定义Preference...

十九、多文件上传(ajaxFileupload实现多文件上传功能)

来源于https://www.jb51.net/article/128647.htm 打开google 搜索"ajaxFileupload' ‘多文件上传"可以搜到许许多多类似的,那我为什么还要写一下呢?一个是对之前大神的贡献表示感谢;二个是自己知识的总结;三个是自己在原有的基础上改动了下,在此记录,可能帮助其他朋友。 用过这个插件的都知道这个插件的基本用...