32 KiB
Претварање текста у говор - Wio Terminal
У овом делу лекције, претворићете текст у говор како бисте обезбедили звучне повратне информације.
Претварање текста у говор
SDK за говорне услуге који сте користили у претходној лекцији за претварање говора у текст може се користити и за претварање текста у говор.
Добијање листе гласова
Када захтевате говор, потребно је да наведете глас који ће се користити, јер се говор може генерисати помоћу различитих гласова. Сваки језик подржава низ различитих гласова, а листу подржаних гласова за сваки језик можете добити из SDK-а за говорне услуге. Овде долазе до изражаја ограничења микроконтролера - позив за добијање листе гласова подржаних од стране услуга за претварање текста у говор је JSON документ величине преко 77KB, што је превелико за обраду на Wio Terminal-у. У време писања, комплетна листа садржи 215 гласова, од којих је сваки дефинисан JSON документом попут следећег:
{
"Name": "Microsoft Server Speech Text to Speech Voice (en-US, AriaNeural)",
"DisplayName": "Aria",
"LocalName": "Aria",
"ShortName": "en-US-AriaNeural",
"Gender": "Female",
"Locale": "en-US",
"StyleList": [
"chat",
"customerservice",
"narration-professional",
"newscast-casual",
"newscast-formal",
"cheerful",
"empathetic"
],
"SampleRateHertz": "24000",
"VoiceType": "Neural",
"Status": "GA"
}
Овај JSON је за глас Aria, који има више стилова гласа. Све што је потребно за претварање текста у говор је кратко име, en-US-AriaNeural
.
Уместо преузимања и декодирања целе ове листе на вашем микроконтролеру, потребно је да напишете још мало serverless кода како бисте преузели листу гласова за језик који користите и позвали ово са вашег Wio Terminal-а. Ваш код затим може изабрати одговарајући глас са листе, као што је први који пронађе.
Задатак - креирање serverless функције за добијање листе гласова
-
Отворите свој пројекат
smart-timer-trigger
у VS Code-у и отворите терминал, осигуравајући да је виртуелно окружење активирано. Ако није, угасите и поново креирајте терминал. -
Отворите датотеку
local.settings.json
и додајте подешавања за API кључ и локацију услуге за говор:"SPEECH_KEY": "<key>", "SPEECH_LOCATION": "<location>"
Замените
<key>
са API кључем за ваш ресурс услуге за говор. Замените<location>
са локацијом коју сте користили приликом креирања ресурса услуге за говор. -
Додајте нови HTTP тригер овој апликацији под називом
get-voices
користећи следећу команду из терминала у VS Code-у у коренском фолдеру пројекта функција:func new --name get-voices --template "HTTP trigger"
Ово ће креирати HTTP тригер под називом
get-voices
. -
Замените садржај датотеке
__init__.py
у фолдеруget-voices
следећим:import json import os import requests import azure.functions as func def main(req: func.HttpRequest) -> func.HttpResponse: location = os.environ['SPEECH_LOCATION'] speech_key = os.environ['SPEECH_KEY'] req_body = req.get_json() language = req_body['language'] url = f'https://{location}.tts.speech.microsoft.com/cognitiveservices/voices/list' headers = { 'Ocp-Apim-Subscription-Key': speech_key } response = requests.get(url, headers=headers) voices_json = json.loads(response.text) voices = filter(lambda x: x['Locale'].lower() == language.lower(), voices_json) voices = map(lambda x: x['ShortName'], voices) return func.HttpResponse(json.dumps(list(voices)), status_code=200)
Овај код прави HTTP захтев ка крајњој тачки за добијање гласова. Листа гласова је велики блок JSON-а са гласовима за све језике, па се гласови за језик који је прослеђен у телу захтева филтрирају, а затим се кратко име извлачи и враћа као JSON листа. Кратко име је вредност потребна за претварање текста у говор, па се враћа само ова вредност.
💁 Можете променити филтер по потреби како бисте изабрали само гласове које желите.
Ово смањује величину података са 77KB (у време писања) на много мањи JSON документ. На пример, за гласове на енглеском језику из САД-а, ово је 408 бајтова.
-
Покрените своју апликацију функција локално. Затим је можете позвати помоћу алата као што је curl на исти начин као што сте тестирали HTTP тригер
text-to-timer
. Обавезно проследите свој језик као JSON тело:{ "language":"<language>" }
Замените
<language>
са вашим језиком, као што јеen-GB
илиzh-CN
.
💁 Овај код можете пронаћи у фолдеру code-spoken-response/functions.
Задатак - преузимање гласа са вашег Wio Terminal-а
-
Отворите пројекат
smart-timer
у VS Code-у ако већ није отворен. -
Отворите хедер датотеку
config.h
и додајте URL за вашу апликацију функција:const char *GET_VOICES_FUNCTION_URL = "<URL>";
Замените
<URL>
са URL-ом за HTTP тригерget-voices
на вашој апликацији функција. Ово ће бити исто као вредност заTEXT_TO_TIMER_FUNCTION_URL
, осим што ће име функције битиget-voices
уместоtext-to-timer
. -
Креирајте нову датотеку у фолдеру
src
под називомtext_to_speech.h
. Ова датотека ће се користити за дефинисање класе за претварање текста у говор. -
Додајте следеће директиве за укључивање на врх нове датотеке
text_to_speech.h
:#pragma once #include <Arduino.h> #include <ArduinoJson.h> #include <HTTPClient.h> #include <Seeed_FS.h> #include <SD/Seeed_SD.h> #include <WiFiClient.h> #include <WiFiClientSecure.h> #include "config.h" #include "speech_to_text.h"
-
Испод овога додајте код за декларацију класе
TextToSpeech
, заједно са инстанцом која се може користити у остатку апликације:class TextToSpeech { public: private: }; TextToSpeech textToSpeech;
-
Да бисте позвали апликацију функција, потребно је да декларишете WiFi клијент. Додајте следеће у
private
секцију класе:WiFiClient _client;
-
У
private
секцији додајте поље за изабрани глас:String _voice;
-
У
public
секцији додајте функцијуinit
која ће добити први глас:void init() { }
-
Да бисте добили гласове, потребно је послати JSON документ апликацији функција са језиком. Додајте следећи код у функцију
init
за креирање овог JSON документа:DynamicJsonDocument doc(1024); doc["language"] = LANGUAGE; String body; serializeJson(doc, body);
-
Затим креирајте
HTTPClient
, а затим га користите за позивање апликације функција ради добијања гласова, шаљући JSON документ:HTTPClient httpClient; httpClient.begin(_client, GET_VOICES_FUNCTION_URL); int httpResponseCode = httpClient.POST(body);
-
Испод овога додајте код за проверу кода одговора, и ако је 200 (успех), извуците листу гласова, преузимајући први са листе:
if (httpResponseCode == 200) { String result = httpClient.getString(); Serial.println(result); DynamicJsonDocument doc(1024); deserializeJson(doc, result.c_str()); JsonArray obj = doc.as<JsonArray>(); _voice = obj[0].as<String>(); Serial.print("Using voice "); Serial.println(_voice); } else { Serial.print("Failed to get voices - error "); Serial.println(httpResponseCode); }
-
Након овога, завршите HTTP клијент конекцију:
httpClient.end();
-
Отворите датотеку
main.cpp
и додајте следећу директиву за укључивање на врх како бисте укључили ову нову хедер датотеку:#include "text_to_speech.h"
-
У функцији
setup
, испод позиваspeechToText.init();
, додајте следеће за иницијализацију класеTextToSpeech
:textToSpeech.init();
-
Компилирајте овај код, отпремите га на ваш Wio Terminal и тестирајте га преко серијског монитора. Уверите се да ваша апликација функција ради.
Видећете листу доступних гласова враћених из апликације функција, заједно са изабраним гласом.
--- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time --- More details at http://bit.ly/pio-monitor-filters --- Miniterm on /dev/cu.usbmodem1101 9600,8,N,1 --- --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- Connecting to WiFi.. Connected! Got access token. ["en-US-JennyNeural", "en-US-JennyMultilingualNeural", "en-US-GuyNeural", "en-US-AriaNeural", "en-US-AmberNeural", "en-US-AnaNeural", "en-US-AshleyNeural", "en-US-BrandonNeural", "en-US-ChristopherNeural", "en-US-CoraNeural", "en-US-ElizabethNeural", "en-US-EricNeural", "en-US-JacobNeural", "en-US-MichelleNeural", "en-US-MonicaNeural", "en-US-AriaRUS", "en-US-BenjaminRUS", "en-US-GuyRUS", "en-US-ZiraRUS"] Using voice en-US-JennyNeural Ready.
Претварање текста у говор
Када имате глас који ћете користити, он се може користити за претварање текста у говор. Иста ограничења меморије са гласовима примењују се и када се говор претвара у текст, па ћете морати да снимите говор на SD картицу како би се репродуковао преко ReSpeaker-а.
💁 У ранијим лекцијама у овом пројекту користили сте флеш меморију за чување говора снимљеног са микрофона. Ова лекција користи SD картицу јер је лакше репродуковати аудио са ње користећи Seeed аудио библиотеке.
Такође постоји још једно ограничење које треба узети у обзир, доступни аудио подаци из услуге за говор и формати које Wio Terminal подржава. За разлику од рачунара, аудио библиотеке за микроконтролере могу бити веома ограничене у форматима које подржавају. На пример, Seeed Arduino Audio библиотека која може репродуковати звук преко ReSpeaker-а подржава само аудио са фреквенцијом узорковања од 44.1KHz. Azure услуге за говор могу обезбедити аудио у више формата, али ниједан од њих не користи ову фреквенцију узорковања, већ само 8KHz, 16KHz, 24KHz и 48KHz. То значи да аудио треба поново узорковати на 44.1KHz, што би захтевало више ресурса него што Wio Terminal има, посебно меморије.
Када је потребно манипулисати подацима попут ових, често је боље користити serverless код, посебно ако се подаци добијају преко веб позива. Wio Terminal може позвати serverless функцију, прослеђујући текст за претварање, а serverless функција може и позвати услугу за говор ради претварања текста у говор, као и поново узорковати аудио на потребну фреквенцију узорковања. Затим може вратити аудио у облику који Wio Terminal треба да би га сачувао на SD картици и репродуковао преко ReSpeaker-а.
Задатак - креирање serverless функције за претварање текста у говор
-
Отворите свој пројекат
smart-timer-trigger
у VS Code-у и отворите терминал, осигуравајући да је виртуелно окружење активирано. Ако није, угасите и поново креирајте терминал. -
Додајте нови HTTP тригер овој апликацији под називом
text-to-speech
користећи следећу команду из терминала у VS Code-у у коренском фолдеру пројекта функција:func new --name text-to-speech --template "HTTP trigger"
Ово ће креирати HTTP тригер под називом
text-to-speech
. -
Pip пакет librosa има функције за поновно узорковање аудио записа, па га додајте у датотеку
requirements.txt
:librosa
Када ово додате, инсталирајте Pip пакете користећи следећу команду из терминала у VS Code-у:
pip install -r requirements.txt
⚠️ Ако користите Linux, укључујући Raspberry Pi OS, можда ћете морати да инсталирате
libsndfile
помоћу следеће команде:sudo apt update sudo apt install libsndfile1-dev
-
Да бисте претворили текст у говор, не можете директно користити API кључ за говор, већ морате затражити приступни токен, користећи API кључ за аутентификацију захтева за приступни токен. Отворите датотеку
__init__.py
из фолдераtext-to-speech
и замените сав код у њој следећим:import io import os import requests import librosa import soundfile as sf import azure.functions as func location = os.environ['SPEECH_LOCATION'] speech_key = os.environ['SPEECH_KEY'] def get_access_token(): headers = { 'Ocp-Apim-Subscription-Key': speech_key } token_endpoint = f'https://{location}.api.cognitive.microsoft.com/sts/v1.0/issuetoken' response = requests.post(token_endpoint, headers=headers) return str(response.text)
Ово дефинише константе за локацију и кључ за говор који ће се читати из подешавања. Затим дефинише функцију
get_access_token
која ће преузети приступни токен за услугу за говор. -
Испод овог кода додајте следеће:
playback_format = 'riff-48khz-16bit-mono-pcm' def main(req: func.HttpRequest) -> func.HttpResponse: req_body = req.get_json() language = req_body['language'] voice = req_body['voice'] text = req_body['text'] url = f'https://{location}.tts.speech.microsoft.com/cognitiveservices/v1' headers = { 'Authorization': 'Bearer ' + get_access_token(), 'Content-Type': 'application/ssml+xml', 'X-Microsoft-OutputFormat': playback_format } ssml = f'<speak version=\'1.0\' xml:lang=\'{language}\'>' ssml += f'<voice xml:lang=\'{language}\' name=\'{voice}\'>' ssml += text ssml += '</voice>' ssml += '</speak>' response = requests.post(url, headers=headers, data=ssml.encode('utf-8')) raw_audio, sample_rate = librosa.load(io.BytesIO(response.content), sr=48000) resampled = librosa.resample(raw_audio, sample_rate, 44100) output_buffer = io.BytesIO() sf.write(output_buffer, resampled, 44100, 'PCM_16', format='wav') output_buffer.seek(0) return func.HttpResponse(output_buffer.read(), status_code=200)
Ово дефинише HTTP тригер који претвара текст у говор. Извлачи текст за претварање, језик и глас из JSON тела послатог у захтеву, гради SSML за захтев за говор, а затим позива одговарајући REST API аутентификујући се помоћу приступног токена. Овај REST API позив враћа аудио кодирани као 16-битни, 48KHz моно WAV фајл, дефинисан вредношћу
playback_format
, која се шаље у REST API позив.Ово се затим поново узоркује помоћу
librosa
са фреквенције узорковања од 48KHz на фреквенцију узорковања од 44.1KHz, затим се овај аудио чува у бинарном баферу који се затим враћа. -
Покрените своју апликацију функција локално или је поставите у облак. Затим је можете позвати помоћу алата као што је curl на исти начин као што сте тестирали HTTP тригер
text-to-timer
. Обавезно проследите језик, глас и текст као JSON тело:{ "language": "<language>", "voice": "<voice>", "text": "<text>" }
Замените
<language>
са вашим језиком, као што јеen-GB
илиzh-CN
. Замените<voice>
са гласом који желите да користите. Замените<text>
са текстом који желите да претворите у говор. Можете сачувати излаз у фајл и репродуковати га помоћу било ког аудио плејера који може репродуковати WAV фајлове.На пример, да бисте претворили "Hello" у говор користећи енглески језик из САД-а са гласом Jenny Neural, са апликацијом функција која ради локално, можете користити следећу curl команду:
curl -X GET 'http://localhost:7071/api/text-to-speech' \ -H 'Content-Type: application/json' \ -o hello.wav \ -d '{ "language":"en-US", "voice": "en-US-JennyNeural", "text": "Hello" }'
Ово ће сачувати аудио у
hello.wav
у тренутном директоријуму.
💁 Овај код можете пронаћи у фолдеру code-spoken-response/functions.
Задатак - преузимање говора са вашег Wio Terminal-а
-
Отворите пројекат
smart-timer
у VS Code-у ако већ није отворен. -
Отворите хедер датотеку
config.h
и додајте URL за вашу апликацију функција:const char *TEXT_TO_SPEECH_FUNCTION_URL = "<URL>";
Замените
<URL>
са URL-ом за HTTP тригерtext-to-speech
на вашој апликацији функција. Ово ће бити исто као вредност заTEXT_TO_TIMER_FUNCTION_URL
, осим што ће име функције битиtext-to-speech
уместоtext-to-timer
. -
Отворите хедер датотеку
text_to_speech.h
и додајте следећи метод уpublic
секцију класеTextToSpeech
:void convertTextToSpeech(String text) { }
-
У метод
convertTextToSpeech
додајте следећи код за креирање JSON-а који ће се послати апликацији функција:DynamicJsonDocument doc(1024); doc["language"] = LANGUAGE; doc["voice"] = _voice; doc["text"] = text; String body; serializeJson(doc, body);
Ово пише језик, глас и текст у JSON документ, а затим га серијализује у стринг.
-
Испод овога додајте следећи код за позивање апликације функција:
HTTPClient httpClient; httpClient.begin(_client, TEXT_TO_SPEECH_FUNCTION_URL); int httpResponseCode = httpClient.POST(body);
Ово креира HTTPClient, а затим прави POST захтев користећи JSON документ ка HTTP тригеру за претварање текста у говор.
-
Ако позив успе, сирови бинарни подаци враћени из позива апликације функција могу се стримовати у фајл на SD картици. Додајте следећи код за то:
if (httpResponseCode == 200) { File wav_file = SD.open("SPEECH.WAV", FILE_WRITE); httpClient.writeToStream(&wav_file); wav_file.close(); } else { Serial.print("Failed to get speech - error "); Serial.println(httpResponseCode); }
Овај код проверава одговор, и ако је 200 (успех), бинарни подаци се стримују у фајл у корену SD картице под називом
SPEECH.WAV
. -
На крају овог метода, затворите HTTP конекцију:
httpClient.end();
-
Текст који треба изговорити сада може бити претворен у аудио. У датотеци
main.cpp
, додајте следећу линију на крај функцијеsay
како бисте текст за изговор претворили у аудио:
textToSpeech.convertTextToSpeech(text);
```
### Задатак - пуштање звука са вашег Wio Terminal-а
**Ускоро доступно**
## Деплојовање ваше функцијске апликације у облак
Разлог за покретање функцијске апликације локално је тај што `librosa` Pip пакет на Линуксу има зависност од библиотеке која није подразумевано инсталирана и мора бити инсталирана пре него што функцијска апликација може да ради. Функцијске апликације су серверлес - нема сервера које можете сами управљати, па самим тим нема начина да се ова библиотека унапред инсталира.
Начин да се ово реши је деплојовање ваше функцијске апликације користећи Docker контејнер. Овај контејнер се деплојује у облак кад год је потребно покренути нову инстанцу ваше функцијске апликације (на пример, када потражња премашује доступне ресурсе или ако функцијска апликација није коришћена неко време и затворена је).
Упутства за подешавање функцијске апликације и деплојовање преко Docker-а можете пронаћи у [документацији за креирање функције на Линуксу користећи прилагођени контејнер на Microsoft Docs](https://docs.microsoft.com/azure/azure-functions/functions-create-function-linux-custom-image?WT.mc_id=academic-17441-jabenn&tabs=bash%2Cazurecli&pivots=programming-language-python).
Када ово буде деплојовано, можете пренети ваш Wio Terminal код како бисте приступили овој функцији:
1. Додајте Azure Functions сертификат у `config.h`:
```cpp
const char *FUNCTIONS_CERTIFICATE =
"-----BEGIN CERTIFICATE-----\r\n"
"MIIFWjCCBEKgAwIBAgIQDxSWXyAgaZlP1ceseIlB4jANBgkqhkiG9w0BAQsFADBa\r\n"
"MQswCQYDVQQGEwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJl\r\n"
"clRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTIw\r\n"
"MDcyMTIzMDAwMFoXDTI0MTAwODA3MDAwMFowTzELMAkGA1UEBhMCVVMxHjAcBgNV\r\n"
"BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEgMB4GA1UEAxMXTWljcm9zb2Z0IFJT\r\n"
"QSBUTFMgQ0EgMDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqYnfP\r\n"
"mmOyBoTzkDb0mfMUUavqlQo7Rgb9EUEf/lsGWMk4bgj8T0RIzTqk970eouKVuL5R\r\n"
"IMW/snBjXXgMQ8ApzWRJCZbar879BV8rKpHoAW4uGJssnNABf2n17j9TiFy6BWy+\r\n"
"IhVnFILyLNK+W2M3zK9gheiWa2uACKhuvgCca5Vw/OQYErEdG7LBEzFnMzTmJcli\r\n"
"W1iCdXby/vI/OxbfqkKD4zJtm45DJvC9Dh+hpzqvLMiK5uo/+aXSJY+SqhoIEpz+\r\n"
"rErHw+uAlKuHFtEjSeeku8eR3+Z5ND9BSqc6JtLqb0bjOHPm5dSRrgt4nnil75bj\r\n"
"c9j3lWXpBb9PXP9Sp/nPCK+nTQmZwHGjUnqlO9ebAVQD47ZisFonnDAmjrZNVqEX\r\n"
"F3p7laEHrFMxttYuD81BdOzxAbL9Rb/8MeFGQjE2Qx65qgVfhH+RsYuuD9dUw/3w\r\n"
"ZAhq05yO6nk07AM9c+AbNtRoEcdZcLCHfMDcbkXKNs5DJncCqXAN6LhXVERCw/us\r\n"
"G2MmCMLSIx9/kwt8bwhUmitOXc6fpT7SmFvRAtvxg84wUkg4Y/Gx++0j0z6StSeN\r\n"
"0EJz150jaHG6WV4HUqaWTb98Tm90IgXAU4AW2GBOlzFPiU5IY9jt+eXC2Q6yC/Zp\r\n"
"TL1LAcnL3Qa/OgLrHN0wiw1KFGD51WRPQ0Sh7QIDAQABo4IBJTCCASEwHQYDVR0O\r\n"
"BBYEFLV2DDARzseSQk1Mx1wsyKkM6AtkMB8GA1UdIwQYMBaAFOWdWTCCR1jMrPoI\r\n"
"VDaGezq1BE3wMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYI\r\n"
"KwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADA0BggrBgEFBQcBAQQoMCYwJAYI\r\n"
"KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTA6BgNVHR8EMzAxMC+g\r\n"
"LaArhilodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vT21uaXJvb3QyMDI1LmNybDAq\r\n"
"BgNVHSAEIzAhMAgGBmeBDAECATAIBgZngQwBAgIwCwYJKwYBBAGCNyoBMA0GCSqG\r\n"
"SIb3DQEBCwUAA4IBAQCfK76SZ1vae4qt6P+dTQUO7bYNFUHR5hXcA2D59CJWnEj5\r\n"
"na7aKzyowKvQupW4yMH9fGNxtsh6iJswRqOOfZYC4/giBO/gNsBvwr8uDW7t1nYo\r\n"
"DYGHPpvnpxCM2mYfQFHq576/TmeYu1RZY29C4w8xYBlkAA8mDJfRhMCmehk7cN5F\r\n"
"JtyWRj2cZj/hOoI45TYDBChXpOlLZKIYiG1giY16vhCRi6zmPzEwv+tk156N6cGS\r\n"
"Vm44jTQ/rs1sa0JSYjzUaYngoFdZC4OfxnIkQvUIA4TOFmPzNPEFdjcZsgbeEz4T\r\n"
"cGHTBPK4R28F44qIMCtHRV55VMX53ev6P3hRddJb\r\n"
"-----END CERTIFICATE-----\r\n";
```
1. Промените све укључене фајлове из `<WiFiClient.h>` у `<WiFiClientSecure.h>`.
1. Промените сва поља типа `WiFiClient` у `WiFiClientSecure`.
1. У свакој класи која има поље типа `WiFiClientSecure`, додајте конструктор и подесите сертификат у том конструктору:
```cpp
_client.setCACert(FUNCTIONS_CERTIFICATE);
```
---
**Одрицање од одговорности**:
Овај документ је преведен коришћењем услуге за превођење помоћу вештачке интелигенције [Co-op Translator](https://github.com/Azure/co-op-translator). Иако се трудимо да обезбедимо тачност, молимо вас да имате у виду да аутоматски преводи могу садржати грешке или нетачности. Оригинални документ на његовом изворном језику треба сматрати ауторитативним извором. За критичне информације препоручује се професионални превод од стране људског преводиоца. Не преузимамо одговорност за било каква погрешна тумачења или неспоразуме који могу настати услед коришћења овог превода.