Как воспроизвести Neural Voice в Audio Source в Unity – Microsoft Cognitive Services

Вопрос или проблема

Я пытаюсь получить сгенерированную речь от SpeechSynthesizer и воспроизвести аудио через Audio Source в Unity. Мне нужно воспроизвести аудио через Audio Source в Unity, потому что я использую аудиодвижок для пакета липсинха.

Я могу воспроизводить нейронный голос и даже создавать аудиоклипы локально. Но мне не удалось извлечь сгенерированный аудиоклип из локального хранилища во время работы программы и воспроизвести его в AudioSource.

Это возможно как для настольных ПК, так и для Oculus? Я использую Cognitive Services Speech SDK для Unity.

//
// Copyright (c) Microsoft. Все права защищены.
// Лицензировано по лицензии MIT. См. файл LICENSE.md в корне проекта для получения полной информации о лицензии.
//
// <code>
using System;
using System.Threading;
using UnityEngine;
using UnityEngine.UI;
using Microsoft.CognitiveServices.Speech;

public class HelloWorld : MonoBehaviour
{
    // Привяжите три свойства ниже к элементам Text, InputField и Button в вашем интерфейсе.
    public Text outputText;
    public InputField inputField;
    public Button speakButton;
    public AudioSource audioSource;

    // Замените на свой собственный ключ подписки и регион службы (например, "westus").
    private const string SubscriptionKey = "YourSubscriptionKey";
    private const string Region = "YourServiceRegion";

    private const int SampleRate = 24000;

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

    private SpeechConfig speechConfig;
    private SpeechSynthesizer synthesizer;

    public void ButtonClick()
    {
        lock (threadLocker)
        {
            waitingForSpeak = true;
        }

        string newMessage = null;
        var startTime = DateTime.Now;

        // Начинает синтез речи и возвращает результат, когда синтез начат.
        using (var result = synthesizer.StartSpeakingTextAsync(inputField.text).Result)
        {
            // Натуральное воспроизведение пока еще не поддерживается в Unity (в данный момент поддерживается только на настольных Windows/Linux).
            // Используйте API Unity для воспроизведения аудио здесь как краткосрочное решение.
            // Поддержка натурального воспроизведения будет добавлена в будущем релизе.
            var audioDataStream = AudioDataStream.FromResult(result);
            var isFirstAudioChunk = true;
            var audioClip = AudioClip.Create(
                "Speech",
                SampleRate * 600, // Максимальная громкость звука на 10 минут
                1,
                SampleRate,
                true,
                (float[] audioChunk) =>
                {
                    var chunkSize = audioChunk.Length;
                    var audioChunkBytes = new byte[chunkSize * 2];
                    var readBytes = audioDataStream.ReadData(audioChunkBytes);
                    if (isFirstAudioChunk && readBytes > 0)
                    {
                        var endTime = DateTime.Now;
                        var latency = endTime.Subtract(startTime).TotalMilliseconds;
                        newMessage = $"Синтез речи выполнен успешно!\nЗадержка: {latency} мс.";
                        isFirstAudioChunk = false;
                    }

                    for (int i = 0; i < chunkSize; ++i)
                    {
                        if (i < readBytes / 2)
                        {
                            audioChunk[i] = (short)(audioChunkBytes[i * 2 + 1] << 8 | audioChunkBytes[i * 2]) / 32768.0F;
                        }
                        else
                        {
                            audioChunk[i] = 0.0f;
                        }
                    }

                    if (readBytes == 0)
                    {
                        Thread.Sleep(200); // Оставьте немного времени для завершения воспроизведения audioSource
                        audioSourceNeedStop = true;
                    }
                });

            audioSource.clip = audioClip;
            audioSource.Play();
        }

        lock (threadLocker)
        {
            if (newMessage != null)
            {
                message = newMessage;
            }

            waitingForSpeak = false;
        }
    }

    void Start()
    {
        if (outputText == null)
        {
            UnityEngine.Debug.LogError("Свойство outputText null! Назначьте элемент UI Text для него.");
        }
        else if (inputField == null)
        {
            message = "Свойство inputField null! Назначьте элемент UI InputField для него.";
            UnityEngine.Debug.LogError(message);
        }
        else if (speakButton == null)
        {
            message = "Свойство speakButton null! Назначьте элемент UI Button для него.";
            UnityEngine.Debug.LogError(message);
        }
        else
        {
            // Продолжите инициализацию, элементы Text, InputField и Button присутствуют.
            inputField.text = "Введите текст, который вы хотите произнести здесь.";
            message = "Нажмите кнопку для синтеза речи";
            speakButton.onClick.AddListener(ButtonClick);

            // Создает экземпляр конфигурации речи с указанным ключом подписки и регионом службы.
            speechConfig = SpeechConfig.FromSubscription(SubscriptionKey, Region);

            // Формат по умолчанию - RIFF, который имеет заголовок riff.
            // Мы воспроизводим звук в памяти как аудиоклип, который не требует заголовка riff.
            // Поэтому нам нужно установить формат в raw (24KHz для лучшего качества).
            speechConfig.SetSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat.Raw24Khz16BitMonoPcm);

            // Создает синтезатор речи.
            // Не забудьте освободить ресурс синтезатора после его использования!
            synthesizer = new SpeechSynthesizer(speechConfig, null);

            synthesizer.SynthesisCanceled += (s, e) =>
            {
                var cancellation = SpeechSynthesisCancellationDetails.FromResult(e.Result);
                message = $"ОТМЕНЕНО:\nПричина=[{cancellation.Reason}]\nПодробности об ошибке=[{cancellation.ErrorDetails}]\nВы обновили информацию о подписке?";
            };
        }
    }

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

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

            if (audioSourceNeedStop)
            {
                audioSource.Stop();
                audioSourceNeedStop = false;
            }
        }
    }

    void OnDestroy()
    {
        if (synthesizer != null)
        {
            synthesizer.Dispose();
        }
    }
}
// </code>

Ответ или решение

Чтобы интегрировать синтезированный голос из Microsoft Cognitive Services в Unity и воспроизвести его с использованием Audio Source, следуйте детальной инструкции ниже. Это решение будет работать как для настольных ПК, так и для Oculus, используя предоставленный вами код.

Общая структура решения

  1. Настройки проекта: Убедитесь, что ваш проект настроен для использования Cognitive Services Speech SDK.
  2. Изменения в скрипте: Реализуйте корректное воспроизведение аудиоданных через аудиоклип Unity.
  3. Обработка ошибок: Обрабатывайте возможные ошибки, возникающие во время синтеза речи.

Шаг 1: Настройка проекта

  1. Убедитесь, что вы установили Cognitive Services Speech SDK для Unity. Подключите необходимые библиотеки в вашем проекте.
  2. Получите ключ подписки и регион, чтобы подключаться к API. Замените YourSubscriptionKey и YourServiceRegion в коде на свои данные.

Шаг 2: Изменения в коде

Ниже приведен адаптированный код, чтобы правильно воспроизводить аудио с использованием AudioSource в Unity:

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

public class TextToSpeech : MonoBehaviour
{
    public Text outputText;
    public InputField inputField;
    public Button speakButton;
    public AudioSource audioSource;

    private const string SubscriptionKey = "ВашКлючПодписки";
    private const string Region = "ВашРегионСервиса";

    private SpeechConfig speechConfig;
    private SpeechSynthesizer synthesizer;

    void Start()
    {
        if (outputText == null || inputField == null || speakButton == null || audioSource == null)
        {
            Debug.LogError("Пожалуйста, проверьте все поля на наличие ошибок.");
            return;
        }

        speakButton.onClick.AddListener(ButtonClick);
        InitializeSpeechSynthesizer();
    }

    private void InitializeSpeechSynthesizer()
    {
        speechConfig = SpeechConfig.FromSubscription(SubscriptionKey, Region);
        speechConfig.SetSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat.Raw24Khz16BitMonoPcm);
        synthesizer = new SpeechSynthesizer(speechConfig, null);
    }

    public async void ButtonClick()
    {
        outputText.text = "Синтез речи...";
        var result = await synthesizer.SpeakTextAsync(inputField.text);

        if (result.Reason == ResultReason.SynthesizingAudioCompleted)
        {
            Debug.Log("Синтез завершен.");
            var audioDataStream = AudioDataStream.FromResult(result);
            PlayAudio(audioDataStream);
        }
        else
        {
            Debug.LogError($"Ошибка синтеза: {result.Reason}");
        }
    }

    private void PlayAudio(AudioDataStream audioDataStream)
    {
        int audioSampleRate = 24000; // 24kHz
        var audioClip = AudioClip.Create("SynthesizedSpeech", audioSampleRate * 60, 1, audioSampleRate, true, false);

        audioClip.SetData(ReadAudioData(audioDataStream, audioClip.samples), 0);
        audioSource.clip = audioClip;
        audioSource.Play();
    }

    private float[] ReadAudioData(AudioDataStream audioDataStream, int samples)
    {
        var audioChunkBytes = new byte[samples * 2]; // 16 бит на сэмпл
        var audioChunk = new float[samples];
        int readBytes = audioDataStream.ReadData(audioChunkBytes);

        for (int i = 0; i < samples && i * 2 < readBytes; ++i)
        {
            audioChunk[i] = (short)(audioChunkBytes[i * 2 + 1] << 8 | audioChunkBytes[i * 2]) / 32768.0f;
        }

        return audioChunk;
    }

    void OnDestroy()
    {
        synthesizer?.Dispose();
    }
}

Шаг 3: Обработка ошибок и оптимизация

  1. Проверка конфигурации: Убедитесь, что все компоненты Unity, такие как Text, InputField, Button, и AudioSource, правильно настроены и прикреплены к скрипту.
  2. Проверка доступности аудио: Перед воспроизведением AudioSource проверьте, что аудиоданные успешно считаны из потока.

Итог

Следуя этому руководству, вы сможете интегрировать Neural Voice от Microsoft Cognitive Services в Unity и воспроизводить результат через AudioSource. Эта реализация подходит как для настольных ПК, так и для VR-устройств, таких как Oculus. Надеюсь, что эта инструкция будет вам полезна!

Оцените материал
Добавить комментарий

Капча загружается...