Azure Speech
Neural voices from Azure AI Speech with AzureTTSService (streaming, with word timestamps) and AzureHttpTTSService (REST).
Overview
pipedog ports both of Pipecat's Azure text-to-speech services:
AzureTTSService: streams each sentence's audio over the Speech service's WebSocket synthesis protocol, with word boundaries turned into word-timedTTSTextFrames.AzureHttpTTSService: onePOSTto the REST endpoint per sentence, returning the whole sentence's audio.
Both build the same SSML from their settings (voice, style, prosody, emphasis, HD voice parameters, locale pinning). Upstream drives Azure through the native Speech SDK; pipedog speaks the service's WebSocket and REST protocols directly, so no SDK is needed.
Upstream page
Pipecat's documentation for the same services.
Source
packages/azure/src/tts.ts and speech_protocol.ts.
Azure docs
Voices, SSML styles and prosody.
Installation
bun add @pipedog/azureimport { AzureHttpTTSService, AzureTTSService } from "@pipedog/azure/tts";Prerequisites
- An Azure AI Speech (or Azure AI Foundry) resource, with its key and region from the portal's Keys and Endpoint page.
- Pass the key as
apiKeyand the region (e.g."eastus") asregion, or a Private Link or custom-domain URL asprivateEndpoint. The examples read them fromAZURE_SPEECH_API_KEYandAZURE_SPEECH_REGION.
AzureTTSService
Configuration
Prop
Type
Settings
Runtime-updatable settings, passed as settings: new AzureTTSService.Settings({...}). Any
setting you leave out keeps the default below. AzureHttpTTSService uses the same settings class.
| Setting | Type | Default | Description |
|---|---|---|---|
voice | string | "en-US-SaraNeural" | Azure voice name. |
language | Language | string | "en-US" | xml:lang of the SSML. |
forceLocale | boolean | false | Wrap the text in <lang xml:lang> so a multilingual voice speaks in language instead of auto-detecting. |
style | string | null | null | Speaking style ("cheerful", "sad", ...), as <mstts:express-as>. |
styleDegree | string | null | null | Style intensity, 0.01 to 2.0. |
role | string | null | null | Role-play voice ("YoungAdultFemale", ...). |
rate | string | null | null | Prosody rate ("1.25", "slow"). |
pitch | string | null | null | Prosody pitch ("+10%", "-5Hz"). |
volume | string | null | null | Prosody volume ("+20%", "loud"). |
emphasis | string | null | null | "strong", "moderate" or "reduced". |
effect | string | null | null | Device effect: "eq_car", or "eq_telecomhp8k" with an 8 kHz pipeline. |
voiceParameters | string | null | null | HD voice parameters ("temperature=0.7;top_p=0.8"). |
Usage
import { AzureHttpTTSService, AzureTTSService } from "@pipedog/azure/tts";
import { TTSUpdateSettingsFrame } from "@pipedog/core/frames/frames";
const tts = new AzureTTSService({
apiKey: process.env.AZURE_SPEECH_API_KEY ?? "",
region: process.env.AZURE_SPEECH_REGION ?? "eastus",
settings: new AzureTTSService.Settings({
voice: "en-US-AvaMultilingualNeural",
language: "en-US",
style: "cheerful",
rate: "1.1",
}),
});Change settings mid-call with a TTSUpdateSettingsFrame. The SSML is built per sentence, so the
change applies from the next one:
// Queue it on the worker, or push it from a processor upstream of the service.
// SSML settings apply from the next sentence.
const update = new TTSUpdateSettingsFrame({
delta: new AzureTTSService.Settings({ language: "en-GB", forceLocale: true }),
});Notes
- The synthesis connection opens on the first sentence and is reused. Audio is raw 16-bit mono PCM at the pipeline's output rate (8, 16, 22.05, 24, 44.1 or 48 kHz; other rates fall back to 24 kHz).
- Word boundaries become
TTSTextFrames timed against the audio. Trailing punctuation is merged into the word before it, and Chinese, Japanese and Korean characters are merged up to the next punctuation or space. The last word always arrives before theTTSStoppedFrame. - On an interruption the connection is closed, which stops Azure from sending more audio for the
interrupted sentence; the next sentence opens a new one. Upstream calls the SDK's
stop_speaking_async()instead.
AzureHttpTTSService
Configuration
Prop
Type
Usage
const httpTts = new AzureHttpTTSService({
apiKey: process.env.AZURE_SPEECH_API_KEY ?? "",
region: process.env.AZURE_SPEECH_REGION ?? "eastus",
sampleRate: 24000,
});Notes
- Posts the SSML to
https://<region>.tts.speech.microsoft.com/cognitiveservices/v1(or<privateEndpoint>/tts/cognitiveservices/v1) and yields the sentence as one audio frame. - It requests a
raw-*-16bit-mono-pcmformat, which has no WAV header. Upstream strips the 44 header bytes of the SDK's RIFF result instead. - Pass
fetchto send the request through your own client.
Runtime notes
| Runtime | Works | What it needs |
|---|---|---|
| Bun | ✅ yes | Nothing. |
| Node.js | ✅ yes | Nothing. |
| Deno | ✅ yes | Nothing. |
| Cloudflare Workers | ✅ yes | Nothing. |
Generated from pipecat.runtimes and pipecat.compat in packages/azure/package.json: ✅ means the compat smoke test passes on that runtime.
Both services work on Bun, Node, Deno and Cloudflare Workers: AzureTTSService uses core's
websocketConnect, AzureHttpTTSService uses fetch. The key is sent as the
Ocp-Apim-Subscription-Key header, and AzureTTSService also sends it as a query parameter, as
the Speech SDK does, for runtimes whose WebSocket can't send headers. With a custom-domain
privateEndpoint, AzureTTSService first asks the endpoint for its regional redirect, as the SDK
does.
The deprecated voice, params (InputParams) and aggregateSentences options aren't ported;
use settings and textAggregationMode.
pipedog is an unofficial community TypeScript port of Pipecat. It is not affiliated with or endorsed by Daily or the Pipecat project.
Portions adapted from the Pipecat documentation, Copyright (c) 2024–2025 Daily, BSD-2-Clause (see apps/docs/LICENSE-pipecat-docs).