c# - 如何使用 Azure 的文本转语音创建音频文件而不是实时文本转语音? (C# Unity SDK)

标签 c# azure unity-game-engine audio azure-cognitive-services

我目前在我的小应用程序中使用 Microsoft Azure 的 Text to Speech Unity SDK。使用我当前使用的 C# Unity 脚本,它会在按下按钮后立即播放音频。但是,我不想播放音频,而是希望将其保存为稍后可以播放的音频文件。我知道您可以使用speechSynthesizer.SetOutputToWaveFile()将TTS音频保存到.wav文件,但我似乎在Azure的Unity SDK中找不到该方法。以下是生成实时 TTS 的示例代码。

using UnityEngine;
using UnityEngine.UI;
using Microsoft.CognitiveServices.Speech;

public class TextToSpeech : MonoBehaviour
{
    // Hook up the three properties below with a Text, InputField and Button object in your UI.
    public Text outputText;
    public InputField inputField;
    public Button speakButton;
    public AudioSource audioSource;

    private object threadLocker = new object();
    private bool waitingForSpeak;
    private string message;

    public void ButtonClick()
    {
        // Creates an instance of a speech config with specified subscription key and service region.
        // Replace with your own subscription key and service region (e.g., "westus").
        var config = SpeechConfig.FromSubscription("YourSubscriptionKey", "YourServiceRegion");

        // Creates a speech synthesizer.
        // Make sure to dispose the synthesizer after use!
        using (var synthsizer = new SpeechSynthesizer(config, null))
        {
            lock (threadLocker)
            {
                waitingForSpeak = true;
            }

            // Starts speech synthesis, and returns after a single utterance is synthesized.
            var result = synthsizer.SpeakTextAsync(inputField.text).Result;

            // Checks result.
            string newMessage = string.Empty;
            if (result.Reason == ResultReason.SynthesizingAudioCompleted)
            {
                // Since native playback is not yet supported on Unity yet (currently only supported on Windows/Linux Desktop),
                // use the Unity API to play audio here as a short term solution.
                // Native playback support will be added in the future release.
                var sampleCount = result.AudioData.Length / 2;
                var audioData = new float[sampleCount];
                for (var i = 0; i < sampleCount; ++i)
                {
                    audioData[i] = (short)(result.AudioData[i * 2 + 1] << 8 | result.AudioData[i * 2]) / 32768.0F;
                }

                // The default output audio format is 16K 16bit mono
                var audioClip = AudioClip.Create("SynthesizedAudio", sampleCount, 1, 16000, false);
                audioClip.SetData(audioData, 0);
                audioSource.clip = audioClip;
                audioSource.Play();

                newMessage = "Speech synthesis succeeded!";
            }
            else if (result.Reason == ResultReason.Canceled)
            {
                var cancellation = SpeechSynthesisCancellationDetails.FromResult(result);
                newMessage = $"CANCELED:\nReason=[{cancellation.Reason}]\nErrorDetails=[{cancellation.ErrorDetails}]\nDid you update the subscription info?";
            }

            lock (threadLocker)
            {
                message = newMessage;
                waitingForSpeak = false;
            }
        }
    }

    void Start()
    {
        if (outputText == null)
        {
            UnityEngine.Debug.LogError("outputText property is null! Assign a UI Text element to it.");
        }
        else if (inputField == null)
        {
            message = "inputField property is null! Assign a UI InputField element to it.";
            UnityEngine.Debug.LogError(message);
        }
        else if (speakButton == null)
        {
            message = "speakButton property is null! Assign a UI Button to it.";
            UnityEngine.Debug.LogError(message);
        }
        else
        {
            // Continue with normal initialization, Text, InputField and Button objects are present.
            inputField.text = "Enter text you wish spoken here.";
            message = "Click button to synthesize speech";
            speakButton.onClick.AddListener(ButtonClick);
        }
    }

    void Update()
    {
        lock (threadLocker)
        {
            if (speakButton != null)
            {
                speakButton.interactable = !waitingForSpeak;
            }

            if (outputText != null)
            {
                outputText.text = message;
            }
        }
    }
}

正如您所看到的,一旦按下按钮,此代码就会将文本输入作为声音播放,但理想情况下,我想预先将音频输出保存到文件中,这样会更高效。任何帮助将不胜感激。

最佳答案

您可以使用rest API来实现此需求。这是一个调用rest API并将音频文件保存到本地的c#代码演示:

using System;
using System.Net.Http;
using System.Text;
using System.IO;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace TTSSample
{
    public class Authentication
    {
      private string subscriptionKey;
      private string tokenFetchUri;

      public Authentication(string tokenFetchUri, string subscriptionKey)
      {
          if (string.IsNullOrWhiteSpace(tokenFetchUri))
          {
              throw new ArgumentNullException(nameof(tokenFetchUri));
          }
          if (string.IsNullOrWhiteSpace(subscriptionKey))
          {
              throw new ArgumentNullException(nameof(subscriptionKey));
          }
          this.tokenFetchUri = tokenFetchUri;
          this.subscriptionKey = subscriptionKey;
      }

      public async Task<string> FetchTokenAsync()
      {
          using (HttpClient client = new HttpClient())
          {
              client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", this.subscriptionKey);
              UriBuilder uriBuilder = new UriBuilder(this.tokenFetchUri);

              HttpResponseMessage result = await client.PostAsync(uriBuilder.Uri.AbsoluteUri, null).ConfigureAwait(false);
              return await result.Content.ReadAsStringAsync().ConfigureAwait(false);
          }
      }
    }
    class Program
    {
        static async Task Main(string[] args)
        {
            // Prompts the user to input text for TTS conversion
            Console.Write("What would you like to convert to speech? ");
            string text = Console.ReadLine();

            // Gets an access token
            string accessToken;
            Console.WriteLine("Attempting token exchange. Please wait...\n");

            // Add your subscription key here
            // If your resource isn't in WEST US, change the endpoint
            Authentication auth = new Authentication("https://westus.api.cognitive.microsoft.com/sts/v1.0/issueToken", "REPLACE_WITH_YOUR_KEY");
            try
            {
                accessToken = await auth.FetchTokenAsync().ConfigureAwait(false);
                Console.WriteLine("Successfully obtained an access token. \n");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed to obtain an access token.");
                Console.WriteLine(ex.ToString());
                Console.WriteLine(ex.Message);
                return;
            }

            string host = "https://westus.tts.speech.microsoft.com/cognitiveservices/v1";

            // Create SSML document.
            XDocument body = new XDocument(
                    new XElement("speak",
                        new XAttribute("version", "1.0"),
                        new XAttribute(XNamespace.Xml + "lang", "en-US"),
                        new XElement("voice",
                            new XAttribute(XNamespace.Xml + "lang", "en-US"),
                            new XAttribute(XNamespace.Xml + "gender", "Female"),
                            new XAttribute("name", "en-US-Jessa24kRUS"), // Short name for "Microsoft Server Speech Text to Speech Voice (en-US, Jessa24KRUS)"
                            text)));

            using (HttpClient client = new HttpClient())
            {
                using (HttpRequestMessage request = new HttpRequestMessage())
                {
                    // Set the HTTP method
                    request.Method = HttpMethod.Post;
                    // Construct the URI
                    request.RequestUri = new Uri(host);
                    // Set the content type header
                    request.Content = new StringContent(body.ToString(), Encoding.UTF8, "application/ssml+xml");
                    // Set additional header, such as Authorization and User-Agent
                    request.Headers.Add("Authorization", "Bearer " + accessToken);
                    request.Headers.Add("Connection", "Keep-Alive");
                    // Update your resource name
                    request.Headers.Add("User-Agent", "YOUR_RESOURCE_NAME");
                    // Audio output format. See API reference for full list.
                    request.Headers.Add("X-Microsoft-OutputFormat", "riff-24khz-16bit-mono-pcm");
                    // Create a request
                    Console.WriteLine("Calling the TTS service. Please wait... \n");
                    using (HttpResponseMessage response = await client.SendAsync(request).ConfigureAwait(false))
                    {
                        response.EnsureSuccessStatusCode();
                        // Asynchronously read the response
                        using (Stream dataStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
                        {
                            Console.WriteLine("Your speech file is being written to file...");
                            using (FileStream fileStream = new FileStream(@"sample.wav", FileMode.Create, FileAccess.Write, FileShare.Write))
                            {
                                await dataStream.CopyToAsync(fileStream).ConfigureAwait(false);
                                fileStream.Close();
                            }
                            Console.WriteLine("\nYour file is ready. Press any key to exit.");
                            Console.ReadLine();
                        }
                    }
                }
            }
        }
    }
}

关于c# - 如何使用 Azure 的文本转语音创建音频文件而不是实时文本转语音? (C# Unity SDK),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57915170/

相关文章:

c# - 开源 .NET 嵌入式 web/http 服务器

c# - 如何重新解释将 float 转换为 int?是否有用于 'this' 转换的非静态转换运算符或用户定义的赋值运算符?

Azure 数据工厂 - 捕获数据流事件的错误详细信息

firebase - Unity Firebase 加载失败 : "Failed to read Firebase options from the app' s resources. “

c# - 如何在Unity中从Uri.fromfile转换为FileProvider.getUriForFile?

c# - 通过 https 调用 W2008R2 上托管的 wcf rest api 可以从 W7 但不能从 W10

python - PyPyodbc 在之前使用 SQL 查询后返回游标对象

asp.net - 是否可以从 ASP MVC 项目执行 Azure PowerShell 脚本?

colors - 在运行时更改文本颜色(Unity)?

c# - 在字符串 : "Input string was not in a correct format" 中使用括号时