Volume Normalization
Relevant source files
The following files were used as context for generating this wiki page:
Volume Normalization is a core feature of the Speech SDK that ensures generated audio output matches a specific loudness target. By using Root-Mean-Square (RMS) amplitude calculations, the SDK normalizes audio segments to an absolute target, typically following the broadcast and podcast convention of -20 dBFS. This process ensures that audio from different TTS providers or different turns in a conversation play back at a consistent and comfortable volume level.
Sources: src/conversation/pcm-concat.ts:167-175, README.md:162-167
Architecture and Data Flow
The normalization system operates by decoding provider-specific audio (such as MP3 or raw PCM) into a standardized 16-bit mono PCM format. Once in this format, the system calculates the RMS amplitude and applies a linear gain to reach the desired target. The final output is then re-encoded into a WAV container to ensure compatibility across client environments.
Volume Adjustment Logic Flow
The following diagram illustrates how a request for volume normalization is processed within the SDK:
Core Components
RMS Normalization
The primary mathematical operation is handled by the normalizeRms function. It processes segments independently, ensuring that the output level is deterministic and regardless of the input mix.
- RMS Calculation: The system calculates the square root of the mean of the squares of all samples in a PCM segment.
- Gain Application: A gain factor is derived by dividing the target amplitude by the measured RMS.
- Clamping: To prevent digital clipping, boosted samples are clamped to the 16-bit signed integer range (-32,768 to 32,767).
Sources: src/conversation/pcm-concat.ts:145-155, src/conversation/pcm-concat.ts:182-192
Decoders and Encoders
To perform normalization, the SDK must first transition audio into a raw format.
| Component | Responsibility | Relevant File |
|---|---|---|
decodeToPcm16 | Converts audio/pcm or audio/wav into a Pcm16Segment, handling downmixing to mono and bit-depth conversion. | src/conversation/pcm-concat.ts:80 |
float32ToInt16 | Converts 32-bit float PCM (common in high-quality providers) to 16-bit integers with clamping. | src/conversation/pcm-concat.ts:54 |
concatPcmToWav | Re-encodes the normalized PCM samples back into a standard WAV file. | src/conversation/pcm-concat.ts:200 |
Sources: src/conversation/pcm-concat.ts:54-135, src/conversation/pcm-concat.ts:200-233
Configuration and Defaults
The SDK uses specific constants to maintain industry-standard loudness.
| Option | Type | Default | Description |
|---|---|---|---|
volumeDbfs | number | -20 | Target loudness in decibels relative to full scale. Must be ≤ 0. |
DEFAULT_TARGET_RMS | number | 3277 | The integer representation of -20 dBFS in 16-bit PCM. |
normalizeVolume | boolean | true | (Conversations only) Whether to automatically level all turns. |
Sources: src/conversation/pcm-concat.ts:173-180, src/tests/conversation-pcm-concat.test.ts:13, README.md:92
Implementation Detail: dBFS to Int16 Conversion
The conversion from decibels to linear amplitude follows the standard formula:
targetRms = Math.round(32767 * 10 ** (dbfs / 20))
Sources: src/conversation/pcm-concat.ts:178-180, src/tests/conversation-pcm-concat.test.ts:121-128
Integration with generateSpeech
When the volumeDbfs parameter is provided to generateSpeech, the SDK modifies its internal request flow:
- Stitch Options: It calls the provider's
getStitchOptionshook to determine which provider-specific parameters (likeresponse_format) are needed to get decodable PCM. - Lazy Loading: The
volume-adjust.tsmodule is imported dynamically only when normalization is required, reducing the initial bundle size for users who do not use this feature. - Override Protection: Stitch-mode options are applied after user
providerOptionsto prevent callers from accidentally requesting a compressed format (like MP3) that would break the normalization decoder.
Sources: src/generate-speech.ts:74-88, src/generate-speech.ts:133-140
Multi-Speaker Conversations
In generateConversation, volume normalization is particularly critical. Each turn is processed independently to the same target, ensuring that a "quiet" voice and a "loud" voice from different providers are balanced in the final stitched audio file.
Sources: src/conversation/pcm-concat.ts:182-185, README.md:92
Summary
Volume Normalization in the Speech SDK provides a robust mechanism for ensuring audio consistency. By leveraging RMS-based scaling and industry-standard targets (-20 dBFS), it eliminates loudness discrepancies between different TTS providers and models. The system is designed with safety in mind, employing clamping to prevent clipping and lazy-loading to optimize performance.
Sources: src/conversation/pcm-concat.ts:173-192, src/generate-speech.ts:133-140