unity接入阿里云语音转文字,文字转语音功能

1、通过openAPI去获取Token

首先按照阿里云的步骤获取AccessKey ID,和AccessKey Secret。

因为没有C#的请求流程,将java的流程修改成Unity的C#的

public class AliyunVoiceSDK:MonoBehaviour

{

public static AliyunVoiceSDK Instance;

private string Token { get; set; }

private long ExpireTime { get; set; }

private string Appkey { get; set; } //语音SDK的 appkey

private void Start()

{

Init();

}

public void Init()

{

SendGetVoiceSdkToken();

}

public void SendGetVoiceSdkToken()

{

Dictionary<string, string> queryParamsMap = new Dictionary<string, string>();

queryParamsMap.Add("AccessKeyId", "你申请的AccessKey ID");

queryParamsMap.Add("Action", "CreateToken");

queryParamsMap.Add("Version", "2019-02-28");

queryParamsMap.Add("Timestamp", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"));

queryParamsMap.Add("Format", "JSON");

queryParamsMap.Add("RegionId", "cn-shanghai");

queryParamsMap.Add("SignatureMethod", "HMAC-SHA1");

queryParamsMap.Add("SignatureVersion", "1.0");

queryParamsMap.Add("SignatureNonce", Guid.NewGuid().ToString());

/**

* 1.构造规范化的请求字符串

*/

string queryString = CanonicalizedQuery(queryParamsMap);

if (null == queryString)

{

Debug.LogError("构造规范化的请求字符串失败!");

return;

}

/**

* 2.构造签名字符串

*/

String method = "GET"; // 发送请求的 HTTP 方法,GET

String urlPath = "/"; // 请求路径

String stringToSign = CreateStringToSign(method, urlPath, queryString);

if (null == stringToSign)

{

Debug.LogError("构造签名字符串失败");

return;

}

/**

* 3.计算签名

*/

String signature = Sign(stringToSign, "你申请的AccessKey Secret");

if (null == signature)

{

Debug.LogError("计算签名失败!");

return;

}

/**

* 4.将签名加入到第1步获取的请求字符串

*/

String queryStringWithSign = "Signature=" + signature + "&" + queryString;

Debug.Log("带有签名的请求字符串:" + queryStringWithSign);

/**

* 5.发送HTTP GET请求,获取token。

*/

StartCoroutine(ProcessGETRequest(queryStringWithSign));

}

private string ComputeSignature(string stringToSign, string accessKeySecret)

{

using (HMACSHA1 hmac = new HMACSHA1(Encoding.UTF8.GetBytes(accessKeySecret + "&")))

{

byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));

return Convert.ToBase64String(hashBytes);

}

}

public string CanonicalizedQuery(Dictionary<string,string> queryParamsMap)

{

string queryString = null;

string[] sortKeys = queryParamsMap.Keys.ToArray();

Array.Sort(sortKeys);

try

{

StringBuilder canonicalizedQueryString = new StringBuilder();

foreach (string key in sortKeys)

{

canonicalizedQueryString.Append("&")

.Append(Uri.EscapeDataString(key)).Append("=")

.Append(Uri.EscapeDataString(queryParamsMap[key]));

}

queryString = canonicalizedQueryString.ToString().Substring(1);

Debug.Log("规范化后的请求参数串:" + queryString);

}

catch (Exception e)

{

Debug.LogError("规范化后的请求参数串:" + e.ToString());

}

return queryString;

}

public string CreateStringToSign(string method,string urlPath,string queryString)

{

string stringToSign = null;

try

{

StringBuilder strBuilderSign = new StringBuilder();

strBuilderSign.Append(method);

strBuilderSign.Append("&");

strBuilderSign.Append(Uri.EscapeDataString(urlPath));

strBuilderSign.Append("&");

strBuilderSign.Append(Uri.EscapeDataString(queryString));

stringToSign = strBuilderSign.ToString();

Debug.Log("构造的签名字符串:" + stringToSign);

}

catch (Exception e)

{

Debug.LogError("规范化后的请求参数串:" + e.ToString());

}

return stringToSign;

}

public string Sign(string stringToSign ,string accessKeySecret)

{

using (HMACSHA1 hmac = new HMACSHA1(Encoding.UTF8.GetBytes(accessKeySecret + "&")))

{

byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));

return Convert.ToBase64String(hashBytes);

}

}

public IEnumerator ProcessGETRequest(string queryString)

{

yield return null;

string url = "http://nls-meta.cn-shanghai.aliyuncs.com";

url = url + "/";

url = url + "?" + queryString;

Debug.Log("HTTP请求:" + url);

UnityWebRequest request = UnityWebRequest.Get(url);

request.SetRequestHeader("Content-Type", "application/json");

yield return request.SendWebRequest();

if (request.result == UnityWebRequest.Result.ProtocolError)

{

Debug.LogError(request.error);

}

else

{

Debug.Log(request.downloadHandler.text);

JObject obj = JObject.Parse(request.downloadHandler.text);

JObject tokenObj = JObject.Parse(obj["Token"].ToString());

if (tokenObj != null)

{

Token = tokenObj["Id"].ToString();

ExpireTime = long.Parse(tokenObj["ExpireTime"].ToString());

if (!string.IsNullOrEmpty(Token))

{

Debug.Log("获取的Token:" + Token + ", 有效期时间戳(秒):" + ExpireTime);

// 将10位数的时间戳转换为北京时间

Debug.Log("Token有效期的北京时间:" + TimeUtil.FormatTime(ExpireTime * 1000));

}

}

else

{

Debug.LogError("obj[\"Token\"] == NULL!! = " + request.downloadHandler.text);

}

}

yield break;

}

// 发送mirphone 录音数据

public IEnumerator ProcessVoiceData(byte[] audioData, string format = "pcm", int sampleRate = 16000,

bool enablePunctuationPrediction = true,

bool enableInverseTextNormalization = true,

bool enableVoiceDetection = false)

{

yield return null;

/**

* 设置HTTPS REST POST请求

* 1.使用HTTPS协议

* 2.语音识别服务域名:nls-gateway-cn-shanghai.aliyuncs.com

* 3.语音识别接口请求路径:/stream/v1/asr

* 4.设置必选请求参数:appkey、format、sample_rate,

* 5.设置可选请求参数:enable_punctuation_prediction、enable_inverse_text_normalization、enable_voice_detection

*/

string url = "https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/asr";

url = url + "?appkey=" + Appkey;

url = url + "&format=" + format;

url = url + "&sample_rate=" + sampleRate;

if (enablePunctuationPrediction)

{

url = url + "&enable_punctuation_prediction=" + true;

}

if (enableInverseTextNormalization)

{

url = url + "&enable_inverse_text_normalization=" + true;

}

if (enableVoiceDetection)

{

url = url + "&enable_voice_detection=" + true;

}

Debug.Log("URL: " + url);

UnityWebRequest request = new UnityWebRequest(url, "POST");

request.SetRequestHeader("X-NLS-Token", Token);

request.SetRequestHeader("Content-Type", "application/octet-stream");

request.uploadHandler = new UploadHandlerRaw(audioData);

request.downloadHandler = new DownloadHandlerBuffer();

yield return request.SendWebRequest();

if (request.result == UnityWebRequest.Result.Success)

{

Debug.Log("Response: " + request.downloadHandler.text);

JObject obj = JObject.Parse(request.downloadHandler.text);

string result = obj["result"].ToString();

Debug.Log("识别结果: " + result);

}

yield break;

}

public IEnumerator ProcessPostTextRequest( string text,AudioSource speaker, string format,int sampleRate)

{

yield return null;

/**

* 设置HTTPS POST请求:

* 1.使用HTTPS协议

* 2.语音合成服务域名:nls-gateway-cn-shanghai.aliyuncs.com

* 3.语音合成接口请求路径:/stream/v1/tts

* 4.设置必须请求参数:appkey、token、text、format、sample_rate

* 5.设置可选请求参数:voice、volume、speech_rate、pitch_rate

*/

string url = "https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/tts";

JObject obj = new JObject();

obj["appkey"] = Appkey;

obj["token"] = Token;

obj["text"] = text;

obj["format"] = format;

obj["sample_rate"] = sampleRate;

// voice 发音人,可选,默认是xiaoyun。

// obj["voice"] = "xiaoyun";

// volume 音量,范围是0~100,可选,默认50。

// obj["volume"] = 50;

// speech_rate 语速,范围是-500~500,可选,默认是0。

// obj["speech_rate"] = 0;

// pitch_rate 语调,范围是-500~500,可选,默认是0。

// obj["pitch_rate"] = 0;

string bodyContent = obj.ToString();

byte[] rawData = Encoding.UTF8.GetBytes(bodyContent);

//StringContent content = new StringContent(bodyContent, Encoding.UTF8, "application/json");

UnityWebRequest request = new UnityWebRequest(url,"POST");

request.SetRequestHeader("Content-Type", "application/json");

request.uploadHandler = new UploadHandlerRaw(rawData);

request.downloadHandler = new DownloadHandlerBuffer();

yield return request.SendWebRequest();

if (request.result == UnityWebRequest.Result.Success)

{

Debug.Log("Response: " + request.downloadHandler.data);

StartCoroutine(WaitForPlayAudioResult(request.downloadHandler.data, speaker));

}

else

{

Debug.LogError("Error: " + request.error);

}

yield break;

}

IEnumerator WaitForPlayAudioResult(byte[] datas, AudioSource speaker)

{

yield return new WaitForEndOfFrame();

int SampleRate = 16000;

AudioClip _audioClip = AudioClip.Create("audioClip", SampleRate * 600, 1, SampleRate, false);

byte[] originalData = datas;

float[] _clipData = new float[originalData.Length / 2];

for (int i = 0; i < originalData.Length; i += 2)

{

_clipData[i / 2] = (short)((originalData[i + 1] << 8) | originalData[i]) / 32768.0f;

}

_audioClip.SetData(_clipData, 0);

speaker.clip = _audioClip;

speaker.Play();

yield break;

}

}

2、外部调用两个接口

语音转文字

1、麦克风获取转audioclip

IEnumerator StartMicroPhoneRecord()

{

Microphone.GetDeviceCaps(CurrentDeviceName, out int minFreq, out int maxFreq);

MicrophoneClip = Microphone.Start(CurrentDeviceName, true, 60, 16000);

// set the latency to "0" samples before the audio starts to play.

while (!(Microphone.GetPosition(CurrentDeviceName) > 0))

{

yield return null;

}

isRecordSuccess = true;

yield return new WaitForSeconds(60);

Microphone.End(CurrentDeviceName);

yield return WaitEndMicroPhoneRecord();

yield break;

}

2、audioclip转wav格式的byte[]格式

IEnumerator WaitEndMicroPhoneRecord()

{

yield return new WaitForEndOfFrame();

byte[] bytes = WavUtility.FromAudioClip(MicrophoneClip, false);

yield return AliyunVoiceSDK.Instance.ProcessVoiceData(bytes);

}

3、WavUtility 将麦克风录音转换成wav的代码

using UnityEngine;

using System.Text;

using System.IO;

using System;

/// <summary>

/// WAV utility for recording and audio playback functions in Unity.

/// Version: 1.0 alpha 1

///

/// - Use "ToAudioClip" method for loading wav file / bytes.

/// Loads .wav (PCM uncompressed) files at 8,16,24 and 32 bits and converts data to Unity's AudioClip.

///

/// - Use "FromAudioClip" method for saving wav file / bytes.

/// Converts an AudioClip's float data into wav byte array at 16 bit.

/// </summary>

/// <remarks>

/// For documentation and usage examples: https://github.com/deadlyfingers/UnityWav

/// </remarks>

public class WavUtility

{

// Force save as 16-bit .wav

const int BlockSize_16Bit = 2;

/// <summary>

/// Load PCM format *.wav audio file (using Unity's Application data path) and convert to AudioClip.

/// </summary>

/// <returns>The AudioClip.</returns>

/// <param name="filePath">Local file path to .wav file</param>

public static AudioClip ToAudioClip (string filePath)

{

if (!filePath.StartsWith (Application.persistentDataPath) && !filePath.StartsWith (Application.dataPath)) {

Debug.LogWarning ("This only supports files that are stored using Unity's Application data path. \nTo load bundled resources use 'Resources.Load(\"filename\") typeof(AudioClip)' method. \nhttps://docs.unity3d.com/ScriptReference/Resources.Load.html");

return null;

}

byte[] fileBytes = File.ReadAllBytes (filePath);

return ToAudioClip (fileBytes, 0);

}

public static AudioClip ToAudioClip (byte[] fileBytes, int offsetSamples = 0, string name = "wav")

{

//string riff = Encoding.ASCII.GetString (fileBytes, 0, 4);

//string wave = Encoding.ASCII.GetString (fileBytes, 8, 4);

int subchunk1 = BitConverter.ToInt32 (fileBytes, 16);

UInt16 audioFormat = BitConverter.ToUInt16 (fileBytes, 20);

// NB: Only uncompressed PCM wav files are supported.

string formatCode = FormatCode (audioFormat);

Debug.AssertFormat (audioFormat == 1 || audioFormat == 65534, "Detected format code '{0}' {1}, but only PCM and WaveFormatExtensable uncompressed formats are currently supported.", audioFormat, formatCode);

UInt16 channels = BitConverter.ToUInt16 (fileBytes, 22);

int sampleRate = BitConverter.ToInt32 (fileBytes, 24);

//int byteRate = BitConverter.ToInt32 (fileBytes, 28);

//UInt16 blockAlign = BitConverter.ToUInt16 (fileBytes, 32);

UInt16 bitDepth = BitConverter.ToUInt16 (fileBytes, 34);

int headerOffset = 16 + 4 + subchunk1 + 4;

int subchunk2 = BitConverter.ToInt32 (fileBytes, headerOffset);

//Debug.LogFormat ("riff={0} wave={1} subchunk1={2} format={3} channels={4} sampleRate={5} byteRate={6} blockAlign={7} bitDepth={8} headerOffset={9} subchunk2={10} filesize={11}", riff, wave, subchunk1, formatCode, channels, sampleRate, byteRate, blockAlign, bitDepth, headerOffset, subchunk2, fileBytes.Length);

float[] data;

switch (bitDepth) {

case 8:

data = Convert8BitByteArrayToAudioClipData (fileBytes, headerOffset, subchunk2);

break;

case 16:

data = Convert16BitByteArrayToAudioClipData (fileBytes, headerOffset, subchunk2);

break;

case 24:

data = Convert24BitByteArrayToAudioClipData (fileBytes, headerOffset, subchunk2);

break;

case 32:

data = Convert32BitByteArrayToAudioClipData (fileBytes, headerOffset, subchunk2);

break;

default:

throw new Exception (bitDepth + " bit depth is not supported.");

}

AudioClip audioClip = AudioClip.Create (name, data.Length, (int)channels, sampleRate, false);

audioClip.SetData (data, 0);

return audioClip;

}

#region wav file bytes to Unity AudioClip conversion methods

private static float[] Convert8BitByteArrayToAudioClipData (byte[] source, int headerOffset, int dataSize)

{

int wavSize = BitConverter.ToInt32 (source, headerOffset);

headerOffset += sizeof(int);

Debug.AssertFormat (wavSize > 0 && wavSize == dataSize, "Failed to get valid 8-bit wav size: {0} from data bytes: {1} at offset: {2}", wavSize, dataSize, headerOffset);

float[] data = new float[wavSize];

sbyte maxValue = sbyte.MaxValue;

int i = 0;

while (i < wavSize) {

data [i] = (float)source [i] / maxValue;

++i;

}

return data;

}

private static float[] Convert16BitByteArrayToAudioClipData (byte[] source, int headerOffset, int dataSize)

{

int wavSize = BitConverter.ToInt32 (source, headerOffset);

headerOffset += sizeof(int);

Debug.AssertFormat (wavSize > 0 && wavSize == dataSize, "Failed to get valid 16-bit wav size: {0} from data bytes: {1} at offset: {2}", wavSize, dataSize, headerOffset);

int x = sizeof(Int16); // block size = 2

int convertedSize = wavSize / x;

float[] data = new float[convertedSize];

Int16 maxValue = Int16.MaxValue;

int offset = 0;

int i = 0;

while (i < convertedSize) {

offset = i * x + headerOffset;

data [i] = (float)BitConverter.ToInt16 (source, offset) / maxValue;

++i;

}

Debug.AssertFormat (data.Length == convertedSize, "AudioClip .wav data is wrong size: {0} == {1}", data.Length, convertedSize);

return data;

}

private static float[] Convert24BitByteArrayToAudioClipData (byte[] source, int headerOffset, int dataSize)

{

int wavSize = BitConverter.ToInt32 (source, headerOffset);

headerOffset += sizeof(int);

Debug.AssertFormat (wavSize > 0 && wavSize == dataSize, "Failed to get valid 24-bit wav size: {0} from data bytes: {1} at offset: {2}", wavSize, dataSize, headerOffset);

int x = 3; // block size = 3

int convertedSize = wavSize / x;

int maxValue = Int32.MaxValue;

float[] data = new float[convertedSize];

byte[] block = new byte[sizeof(int)]; // using a 4 byte block for copying 3 bytes, then copy bytes with 1 offset

int offset = 0;

int i = 0;

while (i < convertedSize) {

offset = i * x + headerOffset;

Buffer.BlockCopy (source, offset, block, 1, x);

data [i] = (float)BitConverter.ToInt32 (block, 0) / maxValue;

++i;

}

Debug.AssertFormat (data.Length == convertedSize, "AudioClip .wav data is wrong size: {0} == {1}", data.Length, convertedSize);

return data;

}

private static float[] Convert32BitByteArrayToAudioClipData (byte[] source, int headerOffset, int dataSize)

{

int wavSize = BitConverter.ToInt32 (source, headerOffset);

headerOffset += sizeof(int);

Debug.AssertFormat (wavSize > 0 && wavSize == dataSize, "Failed to get valid 32-bit wav size: {0} from data bytes: {1} at offset: {2}", wavSize, dataSize, headerOffset);

int x = sizeof(float); // block size = 4

int convertedSize = wavSize / x;

Int32 maxValue = Int32.MaxValue;

float[] data = new float[convertedSize];

int offset = 0;

int i = 0;

while (i < convertedSize) {

offset = i * x + headerOffset;

data [i] = (float)BitConverter.ToInt32 (source, offset) / maxValue;

++i;

}

Debug.AssertFormat (data.Length == convertedSize, "AudioClip .wav data is wrong size: {0} == {1}", data.Length, convertedSize);

return data;

}

#endregion

public static byte[] FromAudioClip (AudioClip audioClip,bool saveFile = true)

{

string file;

return FromAudioClip (audioClip, out file, saveFile);

}

public static byte[] FromAudioClip (AudioClip audioClip, out string filepath, bool saveAsFile = true, string dirname = "recordings")

{

MemoryStream stream = new MemoryStream ();

const int headerSize = 44;

// get bit depth

UInt16 bitDepth = 16; //BitDepth (audioClip);

// NB: Only supports 16 bit

//Debug.AssertFormat (bitDepth == 16, "Only converting 16 bit is currently supported. The audio clip data is {0} bit.", bitDepth);

// total file size = 44 bytes for header format and audioClip.samples * factor due to float to Int16 / sbyte conversion

int fileSize = audioClip.samples * BlockSize_16Bit + headerSize; // BlockSize (bitDepth)

// chunk descriptor (riff)

WriteFileHeader (ref stream, fileSize);

// file header (fmt)

WriteFileFormat (ref stream, audioClip.channels, audioClip.frequency, bitDepth);

// data chunks (data)

WriteFileData (ref stream, audioClip, bitDepth);

byte[] bytes = stream.ToArray ();

// Validate total bytes

//Debug.AssertFormat (bytes.Length == fileSize, "Unexpected AudioClip to wav format byte count: {0} == {1}", bytes.Length, fileSize);

// Save file to persistant storage location

if (saveAsFile) {

filepath = string.Format ("{0}/{1}/{2}.{3}", Application.persistentDataPath, dirname, "MicrphoneRecoiding", "wav");

Debug.Log("filepath = " + filepath);

Directory.CreateDirectory (Path.GetDirectoryName (filepath));

File.WriteAllBytes (filepath, bytes);

//Debug.Log ("Auto-saved .wav file: " + filepath);

} else {

filepath = null;

}

stream.Dispose ();

return bytes;

}

#region write .wav file functions

private static int WriteFileHeader (ref MemoryStream stream, int fileSize)

{

int count = 0;

int total = 12;

// riff chunk id

byte[] riff = Encoding.ASCII.GetBytes ("RIFF");

count += WriteBytesToMemoryStream (ref stream, riff, "ID");

// riff chunk size

int chunkSize = fileSize - 8; // total size - 8 for the other two fields in the header

count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (chunkSize), "CHUNK_SIZE");

byte[] wave = Encoding.ASCII.GetBytes ("WAVE");

count += WriteBytesToMemoryStream (ref stream, wave, "FORMAT");

// Validate header

Debug.AssertFormat (count == total, "Unexpected wav descriptor byte count: {0} == {1}", count, total);

return count;

}

private static int WriteFileFormat (ref MemoryStream stream, int channels, int sampleRate, UInt16 bitDepth)

{

int count = 0;

int total = 24;

byte[] id = Encoding.ASCII.GetBytes ("fmt ");

count += WriteBytesToMemoryStream (ref stream, id, "FMT_ID");

int subchunk1Size = 16; // 24 - 8

count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (subchunk1Size), "SUBCHUNK_SIZE");

UInt16 audioFormat = 1;

count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (audioFormat), "AUDIO_FORMAT");

UInt16 numChannels = Convert.ToUInt16 (channels);

count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (numChannels), "CHANNELS");

count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (sampleRate), "SAMPLE_RATE");

int byteRate = sampleRate * channels * BytesPerSample (bitDepth);

count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (byteRate), "BYTE_RATE");

UInt16 blockAlign = Convert.ToUInt16 (channels * BytesPerSample (bitDepth));

count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (blockAlign), "BLOCK_ALIGN");

count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (bitDepth), "BITS_PER_SAMPLE");

// Validate format

Debug.AssertFormat (count == total, "Unexpected wav fmt byte count: {0} == {1}", count, total);

return count;

}

private static int WriteFileData (ref MemoryStream stream, AudioClip audioClip, UInt16 bitDepth)

{

int count = 0;

int total = 8;

// Copy float[] data from AudioClip

float[] data = new float[audioClip.samples * audioClip.channels];

audioClip.GetData (data, 0);

byte[] bytes = ConvertAudioClipDataToInt16ByteArray (data);

byte[] id = Encoding.ASCII.GetBytes ("data");

count += WriteBytesToMemoryStream (ref stream, id, "DATA_ID");

int subchunk2Size = Convert.ToInt32 (audioClip.samples * BlockSize_16Bit); // BlockSize (bitDepth)

count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (subchunk2Size), "SAMPLES");

// Validate header

Debug.AssertFormat (count == total, "Unexpected wav data id byte count: {0} == {1}", count, total);

// Write bytes to stream

count += WriteBytesToMemoryStream (ref stream, bytes, "DATA");

// Validate audio data

Debug.AssertFormat (bytes.Length == subchunk2Size, "Unexpected AudioClip to wav subchunk2 size: {0} == {1}", bytes.Length, subchunk2Size);

return count;

}

private static byte[] ConvertAudioClipDataToInt16ByteArray (float[] data)

{

MemoryStream dataStream = new MemoryStream ();

int x = sizeof(Int16);

Int16 maxValue = Int16.MaxValue;

int i = 0;

while (i < data.Length) {

dataStream.Write (BitConverter.GetBytes (Convert.ToInt16 (data [i] * maxValue)), 0, x);

++i;

}

byte[] bytes = dataStream.ToArray ();

// Validate converted bytes

Debug.AssertFormat (data.Length * x == bytes.Length, "Unexpected float[] to Int16 to byte[] size: {0} == {1}", data.Length * x, bytes.Length);

dataStream.Dispose ();

return bytes;

}

private static int WriteBytesToMemoryStream (ref MemoryStream stream, byte[] bytes, string tag = "")

{

int count = bytes.Length;

stream.Write (bytes, 0, count);

//Debug.LogFormat ("WAV:{0} wrote {1} bytes.", tag, count);

return count;

}

#endregion

/// <summary>

/// Calculates the bit depth of an AudioClip

/// </summary>

/// <returns>The bit depth. Should be 8 or 16 or 32 bit.</returns>

/// <param name="audioClip">Audio clip.</param>

public static UInt16 BitDepth (AudioClip audioClip)

{

UInt16 bitDepth = Convert.ToUInt16 (audioClip.samples * audioClip.channels * audioClip.length / audioClip.frequency);

Debug.AssertFormat (bitDepth == 8 || bitDepth == 16 || bitDepth == 32, "Unexpected AudioClip bit depth: {0}. Expected 8 or 16 or 32 bit.", bitDepth);

return bitDepth;

}

private static int BytesPerSample (UInt16 bitDepth)

{

return bitDepth / 8;

}

private static int BlockSize (UInt16 bitDepth)

{

switch (bitDepth) {

case 32:

return sizeof(Int32); // 32-bit -> 4 bytes (Int32)

case 16:

return sizeof(Int16); // 16-bit -> 2 bytes (Int16)

case 8:

return sizeof(sbyte); // 8-bit -> 1 byte (sbyte)

default:

throw new Exception (bitDepth + " bit depth is not supported.");

}

}

private static string FormatCode (UInt16 code)

{

switch (code) {

case 1:

return "PCM";

case 2:

return "ADPCM";

case 3:

return "IEEE";

case 7:

return "μ-law";

case 65534:

return "WaveFormatExtensable";

default:

Debug.LogWarning ("Unknown wav code format:" + code);

return "";

}

}

}

GitHub - deadlyfingers/UnityWav: WAV utility for saving and loading wav files in Unity

二、 /文本转语音 调用

//AISpeakSource为播放用的AduioSource

IEnumerator WaitForTextToVoice(string content)

{

yield return new WaitForEndOfFrame();

yield return AliyunVoiceSDK.Instance.ProcessPostTextRequest(content, AISpeakSource, "wav", 16000);

yield break;

}

相关推荐
₯㎕星空&繁华8 小时前
阿里云服务器安装MySQL服务器
服务器·ubuntu·阿里云·云计算
云雾J视界21 小时前
Flink Checkpoint与反压问题排查手册:从日志分析到根因定位
大数据·阿里云·flink·linq·checkpoint·反压
你的大佬99921 小时前
阿里云百炼ai模型
人工智能·阿里云·云计算
一只栖枝1 天前
备考华为HCIA - 云计算,培训与自学到底该怎么选?
云计算·华为认证·hcia·考证·职业规划
数据与人工智能律师1 天前
AI的法治迷宫:技术层、模型层、应用层的法律痛点
大数据·网络·人工智能·云计算·区块链
荣光波比2 天前
Docker(三)—— Docker Compose 编排与 Harbor 私有仓库实战指南
运维·docker·容器·云计算
企鹅侠客2 天前
mysqldump导入备份数据到阿里云RDS会报错吗
阿里云·adb·云计算
iHero2 天前
【Jitsi Meet】阿里云Docker安装Jitsi Meet后的调整
阿里云·docker·云计算
荣光波比2 天前
Ansible(三)—— 使用Ansible自动化部署LNMP环境实战指南
运维·自动化·云计算·ansible
荣光波比2 天前
Docker(五)—— Docker Compose 一键搭建 LNMP 架构并部署 WordPress
运维·docker·容器·云计算