Menurunkan latensi sintesis ucapan menggunakan Speech SDK

Latensi sintesis sangat penting untuk aplikasi Anda. Dalam artikel ini, kami akan memperkenalkan praktik terbaik untuk menurunkan latensi dan menghadirkan performa terbaik bagi pengguna akhir Anda.

Biasanya, kami mengukur latensi berdasarkan first byte latency dan finish latency, sebagai berikut:

Latensi Deskripsi Kunci properti SpeechSynthesisResult
latensi byte pertama Menunjukkan penundaan waktu antara permulaan tugas sintesis dan penerimaan gugus pertama data audio. SpeechServiceResponse_SynthesisFirstByteLatencyMs
latensi akhir Menunjukkan penundaan waktu antara permulaan tugas sintesis dan penerimaan seluruh data audio yang disintesiskan. SpeechServiceResponse_SynthesisFinishLatencyMs

Speech SDK menempatkan durasi latensi dalam koleksi Properti SpeechSynthesisResult. Contoh kode berikut menunjukkan nilai-nilai ini.

var result = await synthesizer.SpeakTextAsync(text);
Console.WriteLine($"first byte latency: \t{result.Properties.GetProperty(PropertyId.SpeechServiceResponse_SynthesisFirstByteLatencyMs)} ms");
Console.WriteLine($"finish latency: \t{result.Properties.GetProperty(PropertyId.SpeechServiceResponse_SynthesisFinishLatencyMs)} ms");
// you can also get the result id, and send to us when you need help for diagnosis
var resultId = result.ResultId;
Latensi Deskripsi Kunci properti SpeechSynthesisResult
first byte latency Menunjukkan penundaan waktu antara permulaan sintesis dan penerimaan gugus audio pertama. SpeechServiceResponse_SynthesisFirstByteLatencyMs
finish latency Menunjukkan penundaan waktu antara permulaan sintesis dan penerimaan seluruh audio yang disintesiskan. SpeechServiceResponse_SynthesisFinishLatencyMs

Speech SDK mengukur latensi dan memasukkannya ke dalam kantong properti SpeechSynthesisResult. Lihat kode berikut untuk mendapatkannya.

auto result = synthesizer->SpeakTextAsync(text).get();
auto firstByteLatency = std::stoi(result->Properties.GetProperty(PropertyId::SpeechServiceResponse_SynthesisFirstByteLatencyMs));
auto finishedLatency = std::stoi(result->Properties.GetProperty(PropertyId::SpeechServiceResponse_SynthesisFinishLatencyMs));
// you can also get the result id, and send to us when you need help for diagnosis
auto resultId = result->ResultId;
Latensi Deskripsi Kunci properti SpeechSynthesisResult
first byte latency Menunjukkan penundaan waktu antara permulaan sintesis dan penerimaan gugus audio pertama. SpeechServiceResponse_SynthesisFirstByteLatencyMs
finish latency Menunjukkan penundaan waktu antara permulaan sintesis dan penerimaan seluruh audio yang disintesiskan. SpeechServiceResponse_SynthesisFinishLatencyMs

Speech SDK mengukur latensi dan memasukkannya ke dalam kantong properti SpeechSynthesisResult. Lihat kode berikut untuk mendapatkannya.

SpeechSynthesisResult result = synthesizer.SpeakTextAsync(text).get();
System.out.println("first byte latency: \t" + result.getProperties().getProperty(PropertyId.SpeechServiceResponse_SynthesisFirstByteLatencyMs) + " ms.");
System.out.println("finish latency: \t" + result.getProperties().getProperty(PropertyId.SpeechServiceResponse_SynthesisFinishLatencyMs) + " ms.");
// you can also get the result id, and send to us when you need help for diagnosis
String resultId = result.getResultId();
Latensi Deskripsi Kunci properti SpeechSynthesisResult
first byte latency Menunjukkan penundaan waktu antara permulaan sintesis dan penerimaan gugus audio pertama. SpeechServiceResponse_SynthesisFirstByteLatencyMs
finish latency Menunjukkan penundaan waktu antara permulaan sintesis dan penerimaan seluruh audio yang disintesiskan. SpeechServiceResponse_SynthesisFinishLatencyMs

Speech SDK mengukur latensi dan memasukkannya ke dalam kantong properti SpeechSynthesisResult. Lihat kode berikut untuk mendapatkannya.

result = synthesizer.speak_text_async(text).get()
first_byte_latency = int(result.properties.get_property(speechsdk.PropertyId.SpeechServiceResponse_SynthesisFirstByteLatencyMs))
finished_latency = int(result.properties.get_property(speechsdk.PropertyId.SpeechServiceResponse_SynthesisFinishLatencyMs))
# you can also get the result id, and send to us when you need help for diagnosis
result_id = result.result_id
Latensi Deskripsi Kunci properti SPXSpeechSynthesisResult
first byte latency Menunjukkan penundaan waktu antara permulaan sintesis dan penerimaan gugus audio pertama. SPXSpeechServiceResponseSynthesisFirstByteLatencyMs
finish latency Menunjukkan penundaan waktu antara permulaan sintesis dan penerimaan seluruh audio yang disintesiskan. SPXSpeechServiceResponseSynthesisFinishLatencyMs

Speech SDK mengukur latensi dan memasukkannya ke dalam kantong properti SPXSpeechSynthesisResult. Lihat kode berikut untuk mendapatkannya.

SPXSpeechSynthesisResult *speechResult = [speechSynthesizer speakText:text];
int firstByteLatency = [intString [speechResult.properties getPropertyById:SPXSpeechServiceResponseSynthesisFirstByteLatencyMs]];
int finishedLatency = [intString [speechResult.properties getPropertyById:SPXSpeechServiceResponseSynthesisFinishLatencyMs]];
// you can also get the result id, and send to us when you need help for diagnosis
NSString *resultId = result.resultId;

Latensi byte pertama lebih rendah dari latensi akhir dalam banyak kasus. Latensi byte pertama tidak bergantung pada panjang teks, sedangkan latensi akhir bertambah seiring panjang teks.

Idealnya, kami ingin meminimalkan latensi yang dialami pengguna (latensi sebelum pengguna mendengar suara) ke satu waktu perjalanan rute jaringan plus latensi gugus audio pertama layanan sintesis ucapan.

Streaming

Streaming sangat penting untuk menurunkan latensi. Kode klien dapat memulai pemutaran saat gugus audio pertama diterima. Dalam skenario layanan, Anda dapat meneruskan gugus audio langsung ke klien, tanpa menunggu seluruh audio.

Anda dapat menggunakan PullAudioOutputStream, PushAudioOutputStream, Synthesizing peristiwa, dan AudioDataStream Speech SDK untuk mengaktifkan streaming.

Ambil contoh AudioDataStream:

using (var synthesizer = new SpeechSynthesizer(config, null as AudioConfig))
{
    using (var result = await synthesizer.StartSpeakingTextAsync(text))
    {
        using (var audioDataStream = AudioDataStream.FromResult(result))
        {
            byte[] buffer = new byte[16000];
            uint filledSize = 0;
            while ((filledSize = audioDataStream.ReadData(buffer)) > 0)
            {
                Console.WriteLine($"{filledSize} bytes received.");
            }
        }
    }
}

Anda dapat menggunakan PullAudioOutputStream, PushAudioOutputStream, Synthesizing peristiwa, dan AudioDataStream Speech SDK untuk mengaktifkan streaming.

Ambil contoh AudioDataStream:

auto synthesizer = SpeechSynthesizer::FromConfig(config, nullptr);
auto result = synthesizer->SpeakTextAsync(text).get();
auto audioDataStream = AudioDataStream::FromResult(result);
uint8_t buffer[16000];
uint32_t filledSize = 0;
while ((filledSize = audioDataStream->ReadData(buffer, sizeof(buffer))) > 0)
{
    cout << filledSize << " bytes received." << endl;
}

Anda dapat menggunakan PullAudioOutputStream, PushAudioOutputStream, Synthesizing peristiwa, dan AudioDataStream Speech SDK untuk mengaktifkan streaming.

Ambil contoh AudioDataStream:

SpeechSynthesizer synthesizer = new SpeechSynthesizer(config, null);
SpeechSynthesisResult result = synthesizer.StartSpeakingTextAsync(text).get();
AudioDataStream audioDataStream = AudioDataStream.fromResult(result);
byte[] buffer = new byte[16000];
long filledSize = audioDataStream.readData(buffer);
while (filledSize > 0) {
    System.out.println(filledSize + " bytes received.");
    filledSize = audioDataStream.readData(buffer);
}

Anda dapat menggunakan PullAudioOutputStream, PushAudioOutputStream, Synthesizing peristiwa, dan AudioDataStream Speech SDK untuk mengaktifkan streaming.

Ambil contoh AudioDataStream:

speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config, audio_config=None)
result = speech_synthesizer.start_speaking_text_async(text).get()
audio_data_stream = speechsdk.AudioDataStream(result)
audio_buffer = bytes(16000)
filled_size = audio_data_stream.read_data(audio_buffer)
while filled_size > 0:
    print("{} bytes received.".format(filled_size))
    filled_size = audio_data_stream.read_data(audio_buffer)

Anda dapat menggunakan SPXPullAudioOutputStream, SPXPushAudioOutputStream, Synthesizing peristiwa, dan SPXAudioDataStream Speech SDK untuk mengaktifkan streaming.

Ambil contoh AudioDataStream:

SPXSpeechSynthesizer *synthesizer = [[SPXSpeechSynthesizer alloc] initWithSpeechConfiguration:speechConfig audioConfiguration:nil];
SPXSpeechSynthesisResult *speechResult = [synthesizer startSpeakingText:inputText];
SPXAudioDataStream *stream = [[SPXAudioDataStream alloc] initFromSynthesisResult:speechResult];
NSMutableData* data = [[NSMutableData alloc]initWithCapacity:16000];
while ([stream readData:data length:16000] > 0) {
    // Read data here
}

Pra-penyambungan dan penggunaan kembali SpeechSynthesizer

Speech SDK menggunakan websocket untuk berkomunikasi dengan layanan. Idealnya, latensi jaringan harus berupa satu rute waktu perjalanan (RTT). Jika koneksi baru dibuat, latensi jaringan mencakup waktu tambahan untuk membuat koneksi. Pembentukan koneksi websocket membutuhkan jabat tangan TCP, jabat tangan SSL, koneksi HTTP, dan peningkatan protokol, yang menimbulkan penundaan waktu. Untuk menghindari latensi koneksi, sebaiknya pra-hubungkan dan gunakan kembali SpeechSynthesizer.

Pra-sambung

Untuk melakukan pra-koneksi, buat koneksi ke layanan Ucapan saat Anda tahu koneksi diperlukan segera. Misalnya, jika Anda membangun bot ucapan di klien, Anda dapat melakukan pra-koneksi ke layanan sintesis ucapan saat pengguna mulai berbicara, dan memanggil SpeakTextAsync saat teks balasan bot siap.

using (var synthesizer = new SpeechSynthesizer(uspConfig, null as AudioConfig))
{
    using (var connection = Connection.FromSpeechSynthesizer(synthesizer))
    {
        connection.Open(true);
    }
    await synthesizer.SpeakTextAsync(text);
}
auto synthesizer = SpeechSynthesizer::FromConfig(config, nullptr);
auto connection = Connection::FromSpeechSynthesizer(synthesizer);
connection->Open(true);
SpeechSynthesizer synthesizer = new SpeechSynthesizer(speechConfig, (AudioConfig) null);
Connection connection = Connection.fromSpeechSynthesizer(synthesizer);
connection.openConnection(true);
synthesizer = speechsdk.SpeechSynthesizer(config, None)
connection = speechsdk.Connection.from_speech_synthesizer(synthesizer)
connection.open(True)
SPXSpeechSynthesizer* synthesizer = [[SPXSpeechSynthesizer alloc]initWithSpeechConfiguration:self.speechConfig audioConfiguration:nil];
SPXConnection* connection = [[SPXConnection alloc]initFromSpeechSynthesizer:synthesizer];
[connection open:true];

Catatan

Jika teks sintesis tersedia, cukup panggil SpeakTextAsync untuk mensintesiskan audio. SDK akan menangani koneksi.

Menggunakan Kembali SpeechSynthesizer

Cara lain untuk mengurangi latensi koneksi adalah dengan menggunakan kembali SpeechSynthesizer sehingga Anda tidak perlu membuat SpeechSynthesizer baru untuk setiap sintesis. Sebaiknya gunakan kumpulan objek dalam skenario layanan, lihat contoh kode kami untuk C# dan Java.

Mengirimkan audio terkompresi melalui jaringan

Ketika jaringan tidak stabil atau dengan bandwidth terbatas, ukuran payload juga memengaruhi latensi. Sementara itu, format audio terkompresi membantu menghemat bandwidth jaringan pengguna, yang sangat berharga bagi pengguna seluler.

Kami mendukung kebanyakan format terkompresi termasuk opus, webm, mp3, silk, dan sebagainya, lihat daftar lengkap SpeechSynthesisOutputFormat. Misalnya, laju bit format Riff24Khz16BitMonoPcm adalah 384 kbps, sedangkan Audio24Khz48KBitRateMonoMp3 hanya memerlukan 48 kbps. SDK Azure Cognitive Service untuk Ucapan akan otomatis menggunakan format terkompresi untuk transmisi jika format output pcm diatur. Untuk Linux dan Windows, GStreamer diperlukan untuk mengaktifkan fitur ini. Lihat petunjuk ini untuk memasang dan mengonfigurasi GStreamer untuk Speech SDK. Untuk Android, iOS, dan macOS, tidak ada konfigurasi tambahan yang diperlukan mulai versi 1.20.

Tips lainnya

Menyimpan cache file CRL

Speech SDK menggunakan file CRL untuk memeriksa sertifikasi. Penembolokan file CRL sampai kedaluwarsa membantu Anda menghindari pengunduhan file CRL setiap saat. Lihat Cara mengonfigurasi OpenSSL untuk Linux untuk mengetahui detailnya.

Menggunakan Speech SDK terbaru

Kami terus menyempurnakan performa Speech SDK, jadi coba gunakan Speech SDK terbaru di aplikasi Anda.

Panduan pengujian pemuatan

Anda dapat menggunakan uji beban untuk menguji kapasitas dan latensi layanan sintesis ucapan. Berikut adalah beberapa panduan:

  • Layanan sintesis ucapan memiliki kemampuan untuk menskalakan otomatis, tetapi membutuhkan waktu untuk meluaskan skala. Jika konkurensi ditingkatkan dalam waktu singkat, klien mungkin mendapatkan latensi panjang atau 429 kode kesalahan (terlalu banyak permintaan). Jadi, sebaiknya Anda meningkatkan konkurensi Anda langkah demi langkah dalam pengujian pemuatan. Lihat artikel ini untuk detail selengkapnya, terutama contoh pola beban kerja ini.
  • Anda dapat menggunakan sampel kami menggunakan kumpulan objek (C# dan Java) untuk pengujian beban dan mendapatkan angka latensi. Anda dapat memodifikasi giliran tes dan konkurensi dalam sampel untuk memenuhi persetujuan target Anda.
  • Layanan ini memiliki batasan kuota berdasarkan lalu lintas nyata, oleh karena itu, jika Anda ingin melakukan pengujian beban dengan konkurensi yang lebih tinggi dari lalu lintas nyata Anda, sambungkan sebelum pengujian Anda.

Langkah berikutnya