Как использовать плагин
Плагин Runtime Text To Speech синтезирует текст в речь с использованием загружаемых голосовых моделей. Эти модели управляются в настройках плагина внутри редактора, загружаются и упаковываются для использования во время выполнения. Следуйте инструкциям ниже, чтобы начать работу.
В редакторе
Загрузите подходящие голосовые модели для вашего проекта, как описано здесь. Вы можете загрузить несколько голосовых моделей одновременно.
Во время выполнения
Создайте синтезатор с помощью функции CreateRuntimeTextToSpeech
. Убедитесь, что вы сохраняете ссылку на него (например, как отдельную переменную в Blueprints или UPROPERTY в C++), чтобы предотвратить его удаление сборщиком мусора.
- Blueprint
- C++
// Create the Runtime Text To Speech synthesizer in C++
URuntimeTextToSpeech* Synthesizer = URuntimeTextToSpeech::CreateRuntimeTextToSpeech();
// Ensure the synthesizer is referenced correctly to prevent garbage collection (e.g. as a UPROPERTY)
Синтез речи
Плагин предлагает два режима синтеза речи из текста:
- Обычный Text-to-Speech: Синтезирует весь текст и возвращает готовый аудиофайл по завершении
- Потоковый Text-to-Speech: Предоставляет аудиофрагменты по мере их генерации, позволяя обрабатывать их в реальном времени
Каждый режим поддерживает два метода выбора голосовых моделей:
- По имени: Выбор голосовой модели по её названию (рекомендуется для UE 5.4+)
- По объекту: Выбор голосовой модели через прямую ссылку (рекомендуется для UE 5.3 и более ранних версий)
Обычный Text-to-Speech
По имени
- Blueprint
- C++
Функция Text To Speech (By Name)
более удобна в Blueprints начиная с UE 5.4. Она позволяет выбирать голосовые модели из выпадающего списка загруженных моделей. В UE версиях ниже 5.3 этот выпадающий список не отображается, поэтому при использовании более старых версий вам потребуется вручную перебирать массив голосовых моделей, возвращаемый функцией GetDownloadedVoiceModels
, чтобы выбрать нужную.
В C++ выбор голосовых моделей может быть немного сло жнее из-за отсутствия выпадающего списка. Вы можете использовать функцию GetDownloadedVoiceModelNames
для получения названий загруженных голосовых моделей и выбрать нужную. После этого можно вызвать функцию TextToSpeechByName
для синтеза текста с использованием выбранного имени голосовой модели.
// Assuming "Synthesizer" is a valid and referenced URuntimeTextToSpeech object (ensure it is not eligible for garbage collection during the callback)
TArray<FName> DownloadedVoiceNames = URuntimeTTSLibrary::GetDownloadedVoiceModelNames();
// If there are downloaded voice models, use the first one to synthesize text, just as an example
if (DownloadedVoiceNames.Num() > 0)
{
const FName& VoiceName = DownloadedVoiceNames[0]; // Select the first available voice model
Synthesizer->TextToSpeechByName(VoiceName, 0, TEXT("Text example 123"), FOnTTSResultDelegateFast::CreateLambda([](URuntimeTextToSpeech* TextToSpeechInstance, bool bSuccess, const TArray<uint8>& AudioData, int32 SampleRate, int32 NumChannels)
{
UE_LOG(LogTemp, Log, TEXT("TextToSpeech result: %s, AudioData size: %d, SampleRate: %d, NumChannels: %d"), bSuccess ? TEXT("Success") : TEXT("Failed"), AudioData.Num(), SampleRate, NumChannels);
}));
return;
}
По объекту
- Blueprint
- C++
Функция Text To Speech (By Object)
работает во всех версиях Unreal Engine, но отображает голосовые модели в виде выпадающего списка ссылок на ассеты, что менее интуитивно. Этот метод подходит для UE 5.3 и более ранних версий, или если ваш проект требует прямого указания на ассет голосовой модели по какой-либо причине.
Если вы загрузили модели, но не видите их, откройте выпадающий список Voice Model, нажмите на настройки (иконка шестерёнки) и активируйте Show Plugin Content и Show Engine Content, чтобы модели стали видны.
В C++ выбор голосовых моделей может быть немного сложнее из-за отсутствия выпадающего списка. Вы можете использовать функцию GetDownloadedVoiceModelNames
для получения названий загруженных голосовых моделей и выбрать нужную. Затем вызовите функцию GetVoiceModelFromName
, чтобы получить объект голосовой модели, и передайте его в функцию TextToSpeechByObject
для синтеза текста.
// Assuming "Synthesizer" is a valid and referenced URuntimeTextToSpeech object (ensure it is not eligible for garbage collection during the callback)
TArray<FName> DownloadedVoiceNames = URuntimeTTSLibrary::GetDownloadedVoiceModelNames();
// If there are downloaded voice models, use the first one to synthesize text, for example
if (DownloadedVoiceNames.Num() > 0)
{
const FName& VoiceName = DownloadedVoiceNames[0]; // Select the first available voice model
TSoftObjectPtr<URuntimeTTSModel> VoiceModel;
if (!URuntimeTTSLibrary::GetVoiceModelFromName(VoiceName, VoiceModel))
{
UE_LOG(LogTemp, Error, TEXT("Failed to get voice model from name: %s"), *VoiceName.ToString());
return;
}
Synthesizer->TextToSpeechByObject(VoiceModel, 0, TEXT("Text example 123"), FOnTTSResultDelegateFast::CreateLambda([](URuntimeTextToSpeech* TextToSpeechInstance, bool bSuccess, const TArray<uint8>& AudioData, int32 SampleRate, int32 NumChannels)
{
UE_LOG(LogTemp, Log, TEXT("TextToSpeech result: %s, AudioData size: %d, SampleRate: %d, NumChannels: %d"), bSuccess ? TEXT("Success") : TEXT("Failed"), AudioData.Num(), SampleRate, NumChannels);
}));
return;
}
Потоковый синтез речи (Streaming Text-to-Speech)
Для длинных текстов или когда необходимо обрабатывать аудиоданные в реальном времени по мере их генерации, можно использовать потоковые версии функций синтеза речи:
Streaming Text To Speech (By Name)
(StreamingTextToSpeechByName
в C++)Streaming Text To Speech (By Object)
(StreamingTextToSpeechByObject
в C++)
Эти функции предоставляют аудиоданные частями по мере их генерации, позволяя обрабатывать их немедленно без ожидания завершения полного синтеза. Это полезно для различных сценариев, таких как воспроизведение аудио в реальном времени, визуализация или любые ситуации, где требуется инкрементальная обработка речевых данных.
Потоковый синтез по имени (Streaming By Name)
- Blueprint
- C++
Функция Streaming Text To Speech (By Name)
работает аналогично обычной версии, но предоставляет аудио частями через делегат On Speech Chunk
.
// Assuming "Synthesizer" is a valid and referenced URuntimeTextToSpeech object
TArray<FName> DownloadedVoiceNames = URuntimeTTSLibrary::GetDownloadedVoiceModelNames();
if (DownloadedVoiceNames.Num() > 0)
{
const FName& VoiceName = DownloadedVoiceNames[0]; // Select the first available voice model
Synthesizer->StreamingTextToSpeechByName(
VoiceName,
0,
TEXT("This is a long text that will be synthesized in chunks."),
FOnTTSStreamingChunkDelegateFast::CreateLambda([](URuntimeTextToSpeech* TextToSpeechInstance, const TArray<uint8>& ChunkAudioData, int32 SampleRate, int32 NumOfChannels, bool bIsFinalChunk)
{
// Process each chunk of audio data as it becomes available
UE_LOG(LogTemp, Log, TEXT("Received chunk %d with %d bytes of audio data. Sample rate: %d, Channels: %d, Is Final: %s"),
ChunkIndex, ChunkAudioData.Num(), SampleRate, NumOfChannels, bIsFinalChunk ? TEXT("Yes") : TEXT("No"));
// You can start processing/playing this chunk immediately
}),
FOnTTSStreamingCompleteDelegateFast::CreateLambda([](URuntimeTextToSpeech* TextToSpeechInstance, bool bSuccess, const FString& ErrorMessage)
{
// Called when the entire synthesis is complete or if it fails
if (bSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Streaming synthesis completed successfully"));
}
else
{
UE_LOG(LogTemp, Error, TEXT("Streaming synthesis failed: %s"), *ErrorMessage);
}
})
);
}
Потоковая передача по объекту
- Blueprint
- C++
Функция Streaming Text To Speech (By Object)
предоставляет те же возможности потоковой передачи, но принимает ссылку на объект голосовой модели.
// Assuming "Synthesizer" is a valid and referenced URuntimeTextToSpeech object
TArray<FName> DownloadedVoiceNames = URuntimeTTSLibrary::GetDownloadedVoiceModelNames();
if (DownloadedVoiceNames.Num() > 0)
{
const FName& VoiceName = DownloadedVoiceNames[0]; // Select the first available voice model
TSoftObjectPtr<URuntimeTTSModel> VoiceModel;
if (!URuntimeTTSLibrary::GetVoiceModelFromName(VoiceName, VoiceModel))
{
UE_LOG(LogTemp, Error, TEXT("Failed to get voice model from name: %s"), *VoiceName.ToString());
return;
}
Synthesizer->StreamingTextToSpeechByObject(
VoiceModel,
0,
TEXT("This is a long text that will be synthesized in chunks."),
FOnTTSStreamingChunkDelegateFast::CreateLambda([](URuntimeTextToSpeech* TextToSpeechInstance, const TArray<uint8>& ChunkAudioData, int32 SampleRate, int32 NumOfChannels, bool bIsFinalChunk)
{
// Process each chunk of audio data as it becomes available
UE_LOG(LogTemp, Log, TEXT("Received chunk %d with %d bytes of audio data. Sample rate: %d, Channels: %d, Is Final: %s"),
ChunkIndex, ChunkAudioData.Num(), SampleRate, NumOfChannels, bIsFinalChunk ? TEXT("Yes") : TEXT("No"));
// You can start processing/playing this chunk immediately
}),
FOnTTSStreamingCompleteDelegateFast::CreateLambda([](URuntimeTextToSpeech* TextToSpeechInstance, bool bSuccess, const FString& ErrorMessage)
{
// Called when the entire synthesis is complete or if it fails
if (bSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Streaming synthesis completed successfully"));
}
else
{
UE_LOG(LogTemp, Error, TEXT("Streaming synthesis failed: %s"), *ErrorMessage);
}
})
);
}
Воспроизведение аудио
- Обычное воспроизведение
- Streaming Playback
Для обычного (не потокового) синтеза речи, делегат On Speech Result
предоставляет синтезированное аудио в формате PCM с данными в float (как массив байтов в Blueprints или TArray<uint8>
в C++), вместе с параметрами Sample Rate
и Num Of Channels
.
Для воспроизведения рекомендуется использовать плагин Runtime Audio Importer для преобразования сырых аудио данных в воспроизводимую звуковую волну.
- Blueprint
- C++
Вот пример того, как могут выглядеть ноды Blueprint для синтеза текста и воспроизведения аудио (Копируемые ноды):
Вот пример того, как синтезировать текст и воспроизводить аудио в C++:
// Assuming "Synthesizer" is a valid and referenced URuntimeTextToSpeech object (ensure it is not eligible for garbage collection during the callback)
// Ensure "this" is a valid and referenced UObject (must not be eligible for garbage collection during the callback)
TArray<FName> DownloadedVoiceNames = URuntimeTTSLibrary::GetDownloadedVoiceModelNames();
// If there are downloaded voice models, use the first one to synthesize text, for example
if (DownloadedVoiceNames.Num() > 0)
{
const FName& VoiceName = DownloadedVoiceNames[0]; // Select the first available voice model
Synthesizer->TextToSpeechByName(VoiceName, 0, TEXT("Text example 123"), FOnTTSResultDelegateFast::CreateLambda([this](URuntimeTextToSpeech* TextToSpeechInstance, bool bSuccess, const TArray<uint8>& AudioData, int32 SampleRate, int32 NumOfChannels)
{
if (!bSuccess)
{
UE_LOG(LogTemp, Error, TEXT("TextToSpeech failed"));
return;
}
// Create the Runtime Audio Importer to process the audio data
URuntimeAudioImporterLibrary* RuntimeAudioImporter = URuntimeAudioImporterLibrary::CreateRuntimeAudioImporter();
// Prevent the RuntimeAudioImporter from being garbage collected by adding it to the root (you can also use a UPROPERTY, TStrongObjectPtr, etc.)
RuntimeAudioImporter->AddToRoot();
RuntimeAudioImporter->OnResultNative.AddWeakLambda(RuntimeAudioImporter, [this](URuntimeAudioImporterLibrary* Importer, UImportedSoundWave* ImportedSoundWave, ERuntimeImportStatus Status)
{
// Once done, remove it from the root to allow garbage collection
Importer->RemoveFromRoot();
if (Status != ERuntimeImportStatus::SuccessfulImport)
{
UE_LOG(LogTemp, Error, TEXT("Failed to import audio, status: %s"), *UEnum::GetValueAsString(Status));
return;
}
// Play the imported sound wave (ensure a reference is kept to prevent garbage collection)
UGameplayStatics::PlaySound2D(GetWorld(), ImportedSoundWave);
});
RuntimeAudioImporter->ImportAudioFromRAWBuffer(AudioData, ERuntimeRAWAudioFormat::Float32, SampleRate, NumOfChannels);
}));
return;
}
Для потокового преобразования текста в речь вы будете получать аудиоданные частями в формате PCM с числами с плавающей запятой (как массив байтов в Blueprints или TArray<uint8>
в C++), вместе с параметрами Sample Rate
и Num Of Channels
. Каждый фрагмент можно обрабатывать немедленно по мере его поступления.
Для воспроизведения в реальном времени рекомендуется использовать Runtime Audio Importer плагин, а именно его Streaming Sound Wave, который специально разработан для потокового воспроизведения аудио или обработки в реальном времени.
- Blueprint
- C++
Вот пример того, как могут выглядеть ноды Blueprint для потокового преобразования текста в речь и воспроизведения аудио (Копируемые ноды):
Вот пример реализации потокового преобразования текста в речь с воспроизведением в реальном времени на C++:
UPROPERTY()
URuntimeTextToSpeech* Synthesizer;
UPROPERTY()
UStreamingSoundWave* StreamingSoundWave;
UPROPERTY()
bool bIsPlaying = false;
void StartStreamingTTS()
{
// Create synthesizer if not already created
if (!Synthesizer)
{
Synthesizer = URuntimeTextToSpeech::CreateRuntimeTextToSpeech();
}
// Create a sound wave for streaming if not already created
if (!StreamingSoundWave)
{
StreamingSoundWave = UStreamingSoundWave::CreateStreamingSoundWave();
StreamingSoundWave->OnPopulateAudioStateNative.AddWeakLambda(this, [this]()
{
if (!bIsPlaying)
{
bIsPlaying = true;
UGameplayStatics::PlaySound2D(GetWorld(), StreamingSoundWave);
}
});
}
TArray<FName> DownloadedVoiceNames = URuntimeTTSLibrary::GetDownloadedVoiceModelNames();
// If there are downloaded voice models, use the first one to synthesize text, for example
if (DownloadedVoiceNames.Num() > 0)
{
const FName& VoiceName = DownloadedVoiceNames[0]; // Select the first available voice model
Synthesizer->StreamingTextToSpeechByName(
VoiceName,
0,
TEXT("Streaming synthesis output begins with a steady flow of data. This data is processed in real-time to ensure consistency. As the process continues, information is streamed without interruption. The output adapts seamlessly to changing inputs. Each piece of data is instantly integrated into the stream. Real-time processing allows for immediate adjustments. This constant flow ensures that the synthesis output is dynamic. As new data comes in, the output evolves accordingly. The system is designed to maintain a continuous output stream. This uninterrupted flow is what drives the efficiency of streaming synthesis."),
FOnTTSStreamingChunkDelegateFast::CreateWeakLambda(this, [this](URuntimeTextToSpeech* TextToSpeechInstance, const TArray<uint8>& ChunkAudioData, int32 SampleRate, int32 NumOfChannels, bool bIsFinalChunk)
{
StreamingSoundWave->AppendAudioDataFromRAW(ChunkAudioData, ERuntimeRAWAudioFormat::Float32, SampleRate, NumOfChannels);
}),
FOnTTSStreamingCompleteDelegateFast::CreateWeakLambda(this, [this](URuntimeTextToSpeech* TextToSpeechInstance, bool bSuccess, const FString& ErrorMessage)
{
if (bSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Streaming text-to-speech synthesis is complete"));
}
else
{
UE_LOG(LogTemp, Error, TEXT("Streaming synthesis failed: %s"), *ErrorMessage);
}
})
);
}
}