当前位置:   article > 正文

unity3d下byte[]字节数组转AudioClip_unity byte转audioclip

unity byte转audioclip

最近在做语音合成,涉及到在unity3d下byte[]字节数组转AudioClip,在这里说一下大概的业务逻辑:

unity3d客户端发送文本数据到wcf服务端,wcf服务端利用微软的文字转语音技术(涉及到的dll为DotNetSpeech.dll)转换为wav格式的音频文件,再把音频文件转为byte[]字节数组返回给unity3d客户端,unity3d客户端接收到byte[]字节数组后转换成AudioClip,然后提供给AudioSource进行播放

 

在这里,我要讲的转换方法有两种,方法一效果很好(不但可以转wav格式的,还可以转mp3格式的byte),方法二效果不好,播放时有杂音,下面进行一一介绍

 

方法一:

步骤如下:

1  下载NAudio.dll动态链接库

    下载地址1:https://github.com/naudio/NAudio/releases

    下载地址2:

     链接:https://pan.baidu.com/s/1ZfzQUwgh4gZXKJhmjZA9Rg 
     提取码:ss42

2  把下载的NAudio.dll放到Assets/Plugins目录下,文件夹Plugins需要自己新建,并把设置 unity3的API Compatiblity Level 为NET 2.0 (Edit->Project Settings->Player)

3  新建一个类,名为NAudioPlayer(注意,现在是转wav格式的,要转mp3的,需要把下面注释的代码开一下并做相应的修改)并编辑代码如下:

  1. using UnityEngine;
  2. using System.IO;
  3. using System;
  4. using NAudio;
  5. using NAudio.Wave;
  6. public static class NAudioPlayer
  7. {
  8. public static AudioClip FromWavData(byte[] buffer)
  9. {
  10. // 转换mp3格式的代码
  11. //MemoryStream mp3stream = new MemoryStream(buffer);
  12. // Convert the data in the stream to WAV format
  13. //Mp3FileReader mp3audio = new Mp3FileReader(mp3stream);
  14. //转换wave格式的代码
  15. MemoryStream wavstream = new MemoryStream(buffer);
  16. WaveFileReader waveAudio = new WaveFileReader(wavstream);
  17. WaveStream waveStream = WaveFormatConversionStream.CreatePcmStream(waveAudio);
  18. // Convert to WAV data
  19. WAV wav = new WAV(AudioMemStream(waveStream).ToArray());
  20. Debug.Log(wav);
  21. AudioClip audioClip = AudioClip.Create("testSound", wav.SampleCount, 1, wav.Frequency, false);
  22. audioClip.SetData(wav.LeftChannel, 0);
  23. // Return the clip
  24. return audioClip;
  25. }
  26. private static MemoryStream AudioMemStream(WaveStream waveStream)
  27. {
  28. MemoryStream outputStream = new MemoryStream();
  29. using (WaveFileWriter waveFileWriter = new WaveFileWriter(outputStream, waveStream.WaveFormat))
  30. {
  31. byte[] bytes = new byte[waveStream.Length];
  32. waveStream.Position = 0;
  33. waveStream.Read(bytes, 0, Convert.ToInt32(waveStream.Length));
  34. waveFileWriter.Write(bytes, 0, bytes.Length);
  35. waveFileWriter.Flush();
  36. }
  37. return outputStream;
  38. }
  39. }
  40. public class WAV
  41. {
  42. // convert two bytes to one float in the range -1 to 1
  43. static float bytesToFloat(byte firstByte, byte secondByte)
  44. {
  45. // convert two bytes to one short (little endian)
  46. short s = (short)((secondByte << 8) | firstByte);
  47. // convert to range from -1 to (just below) 1
  48. return s / 32768.0F;
  49. }
  50. static int bytesToInt(byte[] bytes, int offset = 0)
  51. {
  52. int value = 0;
  53. for (int i = 0; i < 4; i++)
  54. {
  55. value |= ((int)bytes[offset + i]) << (i * 8);
  56. }
  57. return value;
  58. }
  59. // properties
  60. public float[] LeftChannel { get; internal set; }
  61. public float[] RightChannel { get; internal set; }
  62. public int ChannelCount { get; internal set; }
  63. public int SampleCount { get; internal set; }
  64. public int Frequency { get; internal set; }
  65. public WAV(byte[] wav)
  66. {
  67. // Determine if mono or stereo
  68. ChannelCount = wav[22]; // Forget byte 23 as 99.999% of WAVs are 1 or 2 channels
  69. // Get the frequency
  70. Frequency = bytesToInt(wav, 24);
  71. // Get past all the other sub chunks to get to the data subchunk:
  72. int pos = 12; // First Subchunk ID from 12 to 16
  73. // Keep iterating until we find the data chunk (i.e. 64 61 74 61 ...... (i.e. 100 97 116 97 in decimal))
  74. while (!(wav[pos] == 100 && wav[pos + 1] == 97 && wav[pos + 2] == 116 && wav[pos + 3] == 97))
  75. {
  76. pos += 4;
  77. int chunkSize = wav[pos] + wav[pos + 1] * 256 + wav[pos + 2] * 65536 + wav[pos + 3] * 16777216;
  78. pos += 4 + chunkSize;
  79. }
  80. pos += 8;
  81. // Pos is now positioned to start of actual sound data.
  82. SampleCount = (wav.Length - pos) / 2; // 2 bytes per sample (16 bit sound mono)
  83. if (ChannelCount == 2) SampleCount /= 2; // 4 bytes per sample (16 bit stereo)
  84. // Allocate memory (right will be null if only mono sound)
  85. LeftChannel = new float[SampleCount];
  86. if (ChannelCount == 2) RightChannel = new float[SampleCount];
  87. else RightChannel = null;
  88. // Write to double array/s:
  89. int i = 0;
  90. while (pos < wav.Length)
  91. {
  92. LeftChannel[i] = bytesToFloat(wav[pos], wav[pos + 1]);
  93. pos += 2;
  94. if (ChannelCount == 2)
  95. {
  96. RightChannel[i] = bytesToFloat(wav[pos], wav[pos + 1]);
  97. pos += 2;
  98. }
  99. i++;
  100. }
  101. }
  102. public override string ToString()
  103. {
  104. return string.Format("[WAV: LeftChannel={0}, RightChannel={1}, ChannelCount={2}, SampleCount={3}, Frequency={4}]", LeftChannel, RightChannel, ChannelCount, SampleCount, Frequency);
  105. }
  106. }

 

4   调用如下:

  1. public AudioClip GetAudioClipByByte(byte[] buffer)
  2. {
  3. AudioClip audioClip = NAudioPlayer.FromWavData(buffer);
  4. return audioClip;
  5. }

 

 

方式二:

步骤如下:

1    设置unity3d的 API Compatiblity Level 为NET 2.0 (Edit->Project Settings->Player)

2   新建一个名为WavUtility的类,并编辑如下:

  1. using UnityEngine;
  2. using System.Text;
  3. using System.IO;
  4. using System;
  5. /// <summary>
  6. /// WAV utility for recording and audio playback functions in Unity.
  7. /// Version: 1.0 alpha 1
  8. ///
  9. /// - Use "ToAudioClip" method for loading wav file / bytes.
  10. /// Loads .wav (PCM uncompressed) files at 8,16,24 and 32 bits and converts data to Unity's AudioClip.
  11. ///
  12. /// - Use "FromAudioClip" method for saving wav file / bytes.
  13. /// Converts an AudioClip's float data into wav byte array at 16 bit.
  14. /// </summary>
  15. /// <remarks>
  16. /// For documentation and usage examples: https://github.com/deadlyfingers/UnityWav
  17. /// </remarks>
  18. public class WavUtility
  19. {
  20. // Force save as 16-bit .wav
  21. const int BlockSize_16Bit = 2;
  22. /// <summary>
  23. /// Load PCM format *.wav audio file (using Unity's Application data path) and convert to AudioClip.
  24. /// </summary>
  25. /// <returns>The AudioClip.</returns>
  26. /// <param name="filePath">Local file path to .wav file</param>
  27. public static AudioClip ToAudioClip (string filePath)
  28. {
  29. if (!filePath.StartsWith (Application.persistentDataPath) && !filePath.StartsWith (Application.dataPath)) {
  30. 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");
  31. return null;
  32. }
  33. byte[] fileBytes = File.ReadAllBytes (filePath);
  34. return ToAudioClip (fileBytes, 0);
  35. }
  36. public static AudioClip ToAudioClip (byte[] fileBytes, int offsetSamples = 0, string name = "wav")
  37. {
  38. //string riff = Encoding.ASCII.GetString (fileBytes, 0, 4);
  39. //string wave = Encoding.ASCII.GetString (fileBytes, 8, 4);
  40. int subchunk1 = BitConverter.ToInt32 (fileBytes, 16);
  41. UInt16 audioFormat = BitConverter.ToUInt16 (fileBytes, 20);
  42. // NB: Only uncompressed PCM wav files are supported.
  43. string formatCode = FormatCode (audioFormat);
  44. Debug.AssertFormat (audioFormat == 1 || audioFormat == 65534, "Detected format code '{0}' {1}, but only PCM and WaveFormatExtensable uncompressed formats are currently supported.", audioFormat, formatCode);
  45. UInt16 channels = BitConverter.ToUInt16 (fileBytes, 22);
  46. int sampleRate = BitConverter.ToInt32 (fileBytes, 24);
  47. //int byteRate = BitConverter.ToInt32 (fileBytes, 28);
  48. //UInt16 blockAlign = BitConverter.ToUInt16 (fileBytes, 32);
  49. UInt16 bitDepth = BitConverter.ToUInt16 (fileBytes, 34);
  50. int headerOffset = 16 + 4 + subchunk1 + 4;
  51. int subchunk2 = BitConverter.ToInt32 (fileBytes, headerOffset);
  52. //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);
  53. float[] data;
  54. Debug.Log("bitDepth:"+bitDepth);
  55. switch (bitDepth) {
  56. case 8:
  57. data = Convert8BitByteArrayToAudioClipData (fileBytes, headerOffset, subchunk2);
  58. break;
  59. case 16:
  60. data = Convert16BitByteArrayToAudioClipData (fileBytes, headerOffset, subchunk2);
  61. break;
  62. case 24:
  63. data = Convert24BitByteArrayToAudioClipData (fileBytes, headerOffset, subchunk2);
  64. break;
  65. case 32:
  66. data = Convert32BitByteArrayToAudioClipData (fileBytes, headerOffset, subchunk2);
  67. break;
  68. default:
  69. throw new Exception (bitDepth + " bit depth is not supported.");
  70. }
  71. AudioClip audioClip = AudioClip.Create (name, data.Length, (int)channels, sampleRate, false);
  72. Debug.Log("name:"+name);
  73. Debug.Log("lengthSample:"+data.Length);
  74. Debug.Log("Chanel"+channels);
  75. Debug.Log("frequency:"+sampleRate);
  76. audioClip.SetData (data, 0);
  77. return audioClip;
  78. }
  79. #region wav file bytes to Unity AudioClip conversion methods
  80. private static float[] Convert8BitByteArrayToAudioClipData (byte[] source, int headerOffset, int dataSize)
  81. {
  82. int wavSize = BitConverter.ToInt32 (source, headerOffset);
  83. headerOffset += sizeof(int);
  84. 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);
  85. float[] data = new float[wavSize];
  86. sbyte maxValue = sbyte.MaxValue;
  87. int i = 0;
  88. while (i < wavSize) {
  89. data [i] = (float)source [i] / maxValue;
  90. ++i;
  91. }
  92. return data;
  93. }
  94. private static float[] Convert16BitByteArrayToAudioClipData (byte[] source, int headerOffset, int dataSize)
  95. {
  96. int wavSize = BitConverter.ToInt32 (source, headerOffset);
  97. headerOffset += sizeof(int);
  98. 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);
  99. int x = sizeof(Int16); // block size = 2
  100. int convertedSize = wavSize / x;
  101. float[] data = new float[convertedSize];
  102. Int16 maxValue = Int16.MaxValue;
  103. int offset = 0;
  104. int i = 0;
  105. while (i < convertedSize) {
  106. offset = i * x + headerOffset;
  107. data [i] = (float)BitConverter.ToInt16 (source, offset) / maxValue;
  108. ++i;
  109. }
  110. Debug.AssertFormat (data.Length == convertedSize, "AudioClip .wav data is wrong size: {0} == {1}", data.Length, convertedSize);
  111. return data;
  112. }
  113. private static float[] Convert24BitByteArrayToAudioClipData (byte[] source, int headerOffset, int dataSize)
  114. {
  115. int wavSize = BitConverter.ToInt32 (source, headerOffset);
  116. headerOffset += sizeof(int);
  117. 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);
  118. int x = 3; // block size = 3
  119. int convertedSize = wavSize / x;
  120. int maxValue = Int32.MaxValue;
  121. float[] data = new float[convertedSize];
  122. byte[] block = new byte[sizeof(int)]; // using a 4 byte block for copying 3 bytes, then copy bytes with 1 offset
  123. int offset = 0;
  124. int i = 0;
  125. while (i < convertedSize) {
  126. offset = i * x + headerOffset;
  127. Buffer.BlockCopy (source, offset, block, 1, x);
  128. data [i] = (float)BitConverter.ToInt32 (block, 0) / maxValue;
  129. ++i;
  130. }
  131. Debug.AssertFormat (data.Length == convertedSize, "AudioClip .wav data is wrong size: {0} == {1}", data.Length, convertedSize);
  132. return data;
  133. }
  134. private static float[] Convert32BitByteArrayToAudioClipData (byte[] source, int headerOffset, int dataSize)
  135. {
  136. int wavSize = BitConverter.ToInt32 (source, headerOffset);
  137. headerOffset += sizeof(int);
  138. 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);
  139. int x = sizeof(float); // block size = 4
  140. int convertedSize = wavSize / x;
  141. Int32 maxValue = Int32.MaxValue;
  142. float[] data = new float[convertedSize];
  143. int offset = 0;
  144. int i = 0;
  145. while (i < convertedSize) {
  146. offset = i * x + headerOffset;
  147. data [i] = (float)BitConverter.ToInt32 (source, offset) / maxValue;
  148. ++i;
  149. }
  150. Debug.AssertFormat (data.Length == convertedSize, "AudioClip .wav data is wrong size: {0} == {1}", data.Length, convertedSize);
  151. return data;
  152. }
  153. #endregion
  154. public static byte[] FromAudioClip (AudioClip audioClip)
  155. {
  156. string file;
  157. return FromAudioClip (audioClip, out file, false);
  158. }
  159. public static byte[] FromAudioClip (AudioClip audioClip, out string filepath, bool saveAsFile = true, string dirname = "recordings")
  160. {
  161. MemoryStream stream = new MemoryStream ();
  162. const int headerSize = 44;
  163. // get bit depth
  164. UInt16 bitDepth = 16; //BitDepth (audioClip);
  165. // NB: Only supports 16 bit
  166. //Debug.AssertFormat (bitDepth == 16, "Only converting 16 bit is currently supported. The audio clip data is {0} bit.", bitDepth);
  167. // total file size = 44 bytes for header format and audioClip.samples * factor due to float to Int16 / sbyte conversion
  168. int fileSize = audioClip.samples * BlockSize_16Bit + headerSize; // BlockSize (bitDepth)
  169. // chunk descriptor (riff)
  170. WriteFileHeader (ref stream, fileSize);
  171. // file header (fmt)
  172. WriteFileFormat (ref stream, audioClip.channels, audioClip.frequency, bitDepth);
  173. // data chunks (data)
  174. WriteFileData (ref stream, audioClip, bitDepth);
  175. byte[] bytes = stream.ToArray ();
  176. // Validate total bytes
  177. Debug.AssertFormat (bytes.Length == fileSize, "Unexpected AudioClip to wav format byte count: {0} == {1}", bytes.Length, fileSize);
  178. // Save file to persistant storage location
  179. if (saveAsFile) {
  180. filepath = string.Format ("{0}/{1}/{2}.{3}", Application.persistentDataPath, dirname, DateTime.UtcNow.ToString ("yyMMdd-HHmmss-fff"), "wav");
  181. Directory.CreateDirectory (Path.GetDirectoryName (filepath));
  182. File.WriteAllBytes (filepath, bytes);
  183. //Debug.Log ("Auto-saved .wav file: " + filepath);
  184. } else {
  185. filepath = null;
  186. }
  187. stream.Dispose ();
  188. return bytes;
  189. }
  190. #region write .wav file functions
  191. private static int WriteFileHeader (ref MemoryStream stream, int fileSize)
  192. {
  193. int count = 0;
  194. int total = 12;
  195. // riff chunk id
  196. byte[] riff = Encoding.ASCII.GetBytes ("RIFF");
  197. count += WriteBytesToMemoryStream (ref stream, riff, "ID");
  198. // riff chunk size
  199. int chunkSize = fileSize - 8; // total size - 8 for the other two fields in the header
  200. count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (chunkSize), "CHUNK_SIZE");
  201. byte[] wave = Encoding.ASCII.GetBytes ("WAVE");
  202. count += WriteBytesToMemoryStream (ref stream, wave, "FORMAT");
  203. // Validate header
  204. Debug.AssertFormat (count == total, "Unexpected wav descriptor byte count: {0} == {1}", count, total);
  205. return count;
  206. }
  207. private static int WriteFileFormat (ref MemoryStream stream, int channels, int sampleRate, UInt16 bitDepth)
  208. {
  209. int count = 0;
  210. int total = 24;
  211. byte[] id = Encoding.ASCII.GetBytes ("fmt ");
  212. count += WriteBytesToMemoryStream (ref stream, id, "FMT_ID");
  213. int subchunk1Size = 16; // 24 - 8
  214. count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (subchunk1Size), "SUBCHUNK_SIZE");
  215. UInt16 audioFormat = 1;
  216. count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (audioFormat), "AUDIO_FORMAT");
  217. UInt16 numChannels = Convert.ToUInt16 (channels);
  218. count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (numChannels), "CHANNELS");
  219. count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (sampleRate), "SAMPLE_RATE");
  220. int byteRate = sampleRate * channels * BytesPerSample (bitDepth);
  221. count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (byteRate), "BYTE_RATE");
  222. UInt16 blockAlign = Convert.ToUInt16 (channels * BytesPerSample (bitDepth));
  223. count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (blockAlign), "BLOCK_ALIGN");
  224. count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (bitDepth), "BITS_PER_SAMPLE");
  225. // Validate format
  226. Debug.AssertFormat (count == total, "Unexpected wav fmt byte count: {0} == {1}", count, total);
  227. return count;
  228. }
  229. private static int WriteFileData (ref MemoryStream stream, AudioClip audioClip, UInt16 bitDepth)
  230. {
  231. int count = 0;
  232. int total = 8;
  233. // Copy float[] data from AudioClip
  234. float[] data = new float[audioClip.samples * audioClip.channels];
  235. audioClip.GetData (data, 0);
  236. byte[] bytes = ConvertAudioClipDataToInt16ByteArray (data);
  237. byte[] id = Encoding.ASCII.GetBytes ("data");
  238. count += WriteBytesToMemoryStream (ref stream, id, "DATA_ID");
  239. int subchunk2Size = Convert.ToInt32 (audioClip.samples * BlockSize_16Bit); // BlockSize (bitDepth)
  240. count += WriteBytesToMemoryStream (ref stream, BitConverter.GetBytes (subchunk2Size), "SAMPLES");
  241. // Validate header
  242. Debug.AssertFormat (count == total, "Unexpected wav data id byte count: {0} == {1}", count, total);
  243. // Write bytes to stream
  244. count += WriteBytesToMemoryStream (ref stream, bytes, "DATA");
  245. // Validate audio data
  246. Debug.AssertFormat (bytes.Length == subchunk2Size, "Unexpected AudioClip to wav subchunk2 size: {0} == {1}", bytes.Length, subchunk2Size);
  247. return count;
  248. }
  249. private static byte[] ConvertAudioClipDataToInt16ByteArray (float[] data)
  250. {
  251. MemoryStream dataStream = new MemoryStream ();
  252. int x = sizeof(Int16);
  253. Int16 maxValue = Int16.MaxValue;
  254. int i = 0;
  255. while (i < data.Length) {
  256. dataStream.Write (BitConverter.GetBytes (Convert.ToInt16 (data [i] * maxValue)), 0, x);
  257. ++i;
  258. }
  259. byte[] bytes = dataStream.ToArray ();
  260. // Validate converted bytes
  261. Debug.AssertFormat (data.Length * x == bytes.Length, "Unexpected float[] to Int16 to byte[] size: {0} == {1}", data.Length * x, bytes.Length);
  262. dataStream.Dispose ();
  263. return bytes;
  264. }
  265. private static int WriteBytesToMemoryStream (ref MemoryStream stream, byte[] bytes, string tag = "")
  266. {
  267. int count = bytes.Length;
  268. stream.Write (bytes, 0, count);
  269. //Debug.LogFormat ("WAV:{0} wrote {1} bytes.", tag, count);
  270. return count;
  271. }
  272. #endregion
  273. /// <summary>
  274. /// Calculates the bit depth of an AudioClip
  275. /// </summary>
  276. /// <returns>The bit depth. Should be 8 or 16 or 32 bit.</returns>
  277. /// <param name="audioClip">Audio clip.</param>
  278. public static UInt16 BitDepth (AudioClip audioClip)
  279. {
  280. UInt16 bitDepth = Convert.ToUInt16 (audioClip.samples * audioClip.channels * audioClip.length / audioClip.frequency);
  281. Debug.AssertFormat (bitDepth == 8 || bitDepth == 16 || bitDepth == 32, "Unexpected AudioClip bit depth: {0}. Expected 8 or 16 or 32 bit.", bitDepth);
  282. return bitDepth;
  283. }
  284. private static int BytesPerSample (UInt16 bitDepth)
  285. {
  286. return bitDepth / 8;
  287. }
  288. private static int BlockSize (UInt16 bitDepth)
  289. {
  290. switch (bitDepth) {
  291. case 32:
  292. return sizeof(Int32); // 32-bit -> 4 bytes (Int32)
  293. case 16:
  294. return sizeof(Int16); // 16-bit -> 2 bytes (Int16)
  295. case 8:
  296. return sizeof(sbyte); // 8-bit -> 1 byte (sbyte)
  297. default:
  298. throw new Exception (bitDepth + " bit depth is not supported.");
  299. }
  300. }
  301. private static string FormatCode (UInt16 code)
  302. {
  303. switch (code) {
  304. case 1:
  305. return "PCM";
  306. case 2:
  307. return "ADPCM";
  308. case 3:
  309. return "IEEE";
  310. case 7:
  311. return "μ-law";
  312. case 65534:
  313. return "WaveFormatExtensable";
  314. default:
  315. Debug.LogWarning ("Unknown wav code format:" + code);
  316. return "";
  317. }
  318. }
  319. }

3  调用如下:

  1. public AudioClip GetAudioClipByByte(byte[] buffer)
  2. {
  3. AudioClip audioClip = WavUtility.ToAudioClip(buffer);
  4. return audioClip;
  5. }

 

 

 

 

 

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/108343?site
推荐阅读
相关标签
  

闽ICP备14008679号