一、介绍

1.产品说明

依托新一代大模型能力,火山语音模型能够根据上下文,智能预测文本的情绪、语调等信息,并生成超自然、高保真、个性化的语音,以满足不同用户的个性化需求。相较于传统语音合成技术,大语音模型在口语自然度、连贯性、拟人度、音质、韵律、气口、情感、语气词表达等各方面为客户带来更生动、更具情感表现力的听觉体验。

2.产品优势

  • **自动情感理解和演绎:**依托新一代语音大模型能力,语音模型可以根据上下文,智能预测文本情绪、语调等信息,并进行自然演绎;
  • **高自然度:**在口语自然度、连贯性、拟人度、音质、韵律、气口、情感、语气词表达等各方面,可以带来更生动、更具情感表现力的听觉体验;
  • 个性化:可提供多种风格的超自然音色,包括趣味口音、角色扮演等类型,并且支持超强混音能力,用户可以将不同声音自由组合,生成新的声音,以满足不同用户的个性化需求,适配趣味聊天、视频剪辑、有声阅读等多个场景。

二、代码

API文档地址:

大模型语音合成API--语音技术-火山引擎https://www.volcengine.com/docs/6561/1257584

 appid和token可以在下面的语音合成控制台中创建:语音技术https://console.volcengine.com/speech/app

/// <summary>
/// 语音合成类-火山引擎语音合成
/// </summary>
public class VolcengineTTSManager : TTSManager
{
	#region 火山引擎TTS参数
	string _url = "https://openspeech.bytedance.com/api/v1/tts";
	string appid = "******";//填写你的appid
	string _token = "******";//填写你的token
	string speaker_id = "zh_female_qingxinnvsheng_mars_bigtts";//清新女声
	#endregion
	Task _task;
	UnityWebRequest _request;
	UploadHandlerRaw _uploadHandler;
	DownloadHandlerBuffer _downHandler;
	public override void RequestTTS(string text, Action<string> callback = null)
	{
		_task = new Task(TTS(text));
	}

	private void Start()
	{
		//RequestTTS("好的!左侧墙面将为您关联详细的图文讲解,请关注放大且高亮部分");
	}

	IEnumerator TTS(string content)
	{
		var data = new
		{
			app = new
			{
				appid = appid,
				token = _token,
				cluster = "volcano_tts",
			},
			user = new
			{
				uid = "uid123"
			},
			audio = new
			{
				voice_type = speaker_id,
				encoding = "wav",
				speed_ratio = 1,
			},
			request = new
			{
				//每次合成时 reqid 这个参数需要重新设置,且要保证唯一性(建议使用 UUID/GUID 等生成)														  //
				reqid = Guid.NewGuid().ToString(),
				text = content,
				operation = "query",
			}
		};
		using ( _request = new UnityWebRequest(_url, "Post"))
		{
			byte[] jsonBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data));
			_request.uploadHandler = new UploadHandlerRaw(jsonBytes);
			_request.downloadHandler = new DownloadHandlerBuffer();
			// 设置请求头
			_request.SetRequestHeader("Content-Type", "application/json");
			_request.SetRequestHeader("Authorization", $"Bearer; {_token}");
			using (_uploadHandler = new UploadHandlerRaw(jsonBytes))
			{
				_request.uploadHandler.Dispose();
				_request.uploadHandler = _uploadHandler;
				using (_downHandler = new DownloadHandlerBuffer())
				{
					_request.downloadHandler.Dispose();
					_request.downloadHandler = _downHandler;
					// 发送请求
					yield return _request.SendWebRequest();					
					if (_request.isDone && _request.error == null)
					{
						Debug.Log(_request.downloadHandler.text);
						//使用 HTTP Post 方式进行请求,返回的结果为 JSON 格式,需要进行解析
						//因json格式无法直接携带二进制音频,音频经base64编码。使用base64解码后,即为二进制音频
						// 反序列化
						AudioResponse response = JsonUtility.FromJson<AudioResponse>(_request.downloadHandler.text);

						// Base64解码
						byte[] audioBytes = Convert.FromBase64String(response.data);

						AudioClip audioClip = ToAudioClip(audioBytes);

						//AudioSource.PlayClipAtPoint(audioClip, Vector3.zero);
						//储存本地
						//System.IO.File.WriteAllBytes(Application.dataPath + "/Resources/Assets/Audio/LeftHighlightTip.wav", audioBytes);
						if (audioClip != null)
						{
							
						}
						else
						{
							
						}
					}
					else
					{
						Debug.LogError("语音合成失败: " + _request.error);
						
					}
					_uploadHandler.Dispose();
					_downHandler.Dispose();
					_request.disposeDownloadHandlerOnDispose = true;
					_request.disposeUploadHandlerOnDispose = true;
					_request.Dispose();
				}
			}
		}
	}
	public static AudioClip ToAudioClip(byte[] wavBytes, string clipName = "audio")
	{
		// WAV头解析(44字节头结构)
		int channels = BitConverter.ToInt16(wavBytes, 22);   // 声道数
		int frequency = BitConverter.ToInt32(wavBytes, 24);  // 采样率
		int bitDepth = BitConverter.ToInt16(wavBytes, 34);   // 位深

		// 音频数据起始位置(头长度44字节)
		float[] audioData = new float[(wavBytes.Length - 44) / 2];
		for (int i = 44, j = 0; i < wavBytes.Length; i += 2, j++)
		{
			short sample = BitConverter.ToInt16(wavBytes, i);
			audioData[j] = sample / 32768f; // 16位PCM转float
		}

		// 创建AudioClip
		AudioClip clip = AudioClip.Create(clipName,
			audioData.Length,
			channels,
			frequency,
			false);
		clip.SetData(audioData, 0);

		return clip;
	}

	public override void Reset()
	{
		if (_task != null)
		{
			_task.Stop();
		}
		if (_uploadHandler != null)
		{
			_uploadHandler.Dispose();
		}
		if (_downHandler != null)
		{
			_downHandler.Dispose();
		}
		if (_request != null)
		{
			_request.disposeDownloadHandlerOnDispose = true;
			_request.disposeUploadHandlerOnDispose = true;
			_request.Dispose();
		}
	}

}
[System.Serializable]
public class AudioResponse
{
	public string reqid;
	public int code;
	public string operation;
	public string message;
	public int sequence;
	public string data; // Base64编码的音频数据
}

更多推荐