Azure Speech Services

A Flutter package for real-time Speech-to-Text (STT), Text-to-Speech (TTS), Realtime-Translation using Microsoft Azure Cognitive Services. No Azure SDK binary dependency: it communicates directly with the Azure WebSocket/REST APIs.

  • STT: Stream-based real-time transcription with intermediate (hypothesis) and finalized results, multi-language identification (LID), and auto-silence timeout.
  • TTS: Synthesize text to audio bytes using any Azure Neural voice, with configurable output format. *. Translation: Stream-based real-time translation in many different languages
  • Cross-platform: Mobile (iOS, Android), Desktop (macOS, Windows, Linux), and Web.

Example app


Getting Started

Installation

import 'package:azure_speech_services/azure_speech_services.dart';

Authentication

Both STT and TTS use the same AzureSpeechConfig. You must provide exactly one credential:

// Option A: Subscription Key
final config = AzureSpeechConfig.subscriptionKey(
  region: 'westeurope', // or other supported region
  key: 'YOUR_AZURE_KEY',
);

// Option B: Short-lived Authorization Token (recommended for Web)
final config = AzureSpeechConfig.authorizationToken(
  region: 'westeurope', // or other supported region
  token: 'YOUR_BACKEND_GENERATED_TOKEN',
);

Web security note: Browser WebSocket APIs cannot set custom HTTP headers. On Web, auth is passed as a URL query parameter. Always use authorizationToken (generated server-side) on Web: never expose subscriptionKey in the browser URL.

To generate a short-lived token:

curl -X POST \
  "https://westeurope.api.cognitive.microsoft.com/sts/v1.0/issueToken" \
  -H "Content-Length: 0" \
  -H "Ocp-Apim-Subscription-Key: YOUR_KEY"

Speech-to-Text (STT)

Permissions

STT requires microphone access. TTS does not.

Androidandroid/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />

iOSios/Runner/Info.plist:

<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access for speech recognition.</string>

macOSmacos/Runner/DebugProfile.entitlements and Release.entitlements:

<key>com.apple.security.device.audio-input</key>
<true/>

Basic usage

final azureStt = AzureSpeechToText(
  config: AzureSpeechConfig.subscriptionKey(
    region: 'westeurope', // or other supported region
    key: 'YOUR_AZURE_KEY',
  ),
  sttConfig: SpeechToTextConfig(
    languages: ['en-US'],
    textClearTimeout: const Duration(seconds: 2),
  ),
);

// Start / stop
await azureStt.start();
await azureStt.stop();

// Check state
azureStt.isListening;

// Dispose when done
await azureStt.dispose();

Listening to transcription updates

azureStt.stream emits TranscriptionState on every change:

StreamBuilder<TranscriptionState>(
  stream: azureStt.stream,
  builder: (context, snapshot) {
    final state = snapshot.data ?? const TranscriptionState();

    if (state.error != null) return Text('Error: ${state.error}');

    return Column(
      children: [
        if (state.detectedLanguage != null)
          Text('Detected: ${state.detectedLanguage}'),
        // Combined text (finalized + intermediate)
        Text(state.text),
        // state.intermediateText // live hypothesis (no punctuation)
        // state.finalizedText   // list of confirmed sentences
        // state.isListening     // mic active
      ],
    );
  },
)

Using the BLoC cubit

AzureSpeechToText owns a TranscriptionCubit. Provide it higher in the widget tree to avoid rebuilding the whole subtree:

BlocProvider.value(
  value: azureStt.cubit,
  child: MyWidget(),
)

// Then in a descendant:
BlocBuilder<TranscriptionCubit, TranscriptionState>(
  builder: (context, state) => Text(state.text),
)

Language Identification (LID)

The number of languages you provide determines the mode automatically:

Mode Languages count
Single language (fastest) 1
At-Start LID 1 - 4
Continuous LID 1 - 10

Bare language codes are expanded automatically ("it""it-IT").

// Single language: fastest, no identification overhead
SpeechToTextConfig(languages: ['en-US'])

// At-Start: identifies once at the beginning of the session
SpeechToTextConfig(
  languages: ['en-US', 'it', 'es-ES', 'fr-FR'],
  languageIdMode: .atStart, // default
)

// Continuous: re-identifies throughout the session, up to 10 languages
SpeechToTextConfig(
  languages: ['en-US', 'it-IT', 'es-ES', 'de-DE', 'pt-PT', 'nb-NO', 'sv-SE', 'uk-UA'],
  languageIdMode: .continuous,
)

External audio stream

To bypass the microphone (e.g. VoIP or file audio), pass your own Stream<Uint8List> to start(). It must emit raw PCM: 16 kHz, mono, 16-bit little-endian — the same format the microphone produces.

await azureStt.start(); // uses mic by default

// or supply your own audio source instead
await azureStt.start(externalAudioStream: myPcmStream);

Text-to-Speech (TTS)

No microphone permissions required.

Basic usage

final azureTts = AzureTextToSpeech(
  config: AzureSpeechConfig.subscriptionKey(
    region: 'westeurope', // or other supported region
    key: 'YOUR_AZURE_KEY',
  ),
);

// 1. Fetch available voices
final voices = await azureTts.listVoices();

// 2. Pick a voice
final voice = voices.firstWhere(
  (v) => v.locale == 'en-US' && v.gender == 'Female',
  orElse: () => voices.first,
);

// 3. Synthesize: returns raw audio bytes
final audioBytes = await azureTts.synthesize(
  TtsRequest(
    text: 'Hello from Azure Speech Flutter!',
    voice: voice,
    outputFormat: .audio16khz128kbitrateMonoMp3, // default
  ),
);

Output formats

AudioOutputFormat covers MP3, PCM, mulaw, Opus, and WebM at various sample rates and bitrates. The default is audio16khz128kbitrateMonoMp3.

Playing the audio

The package returns raw bytes. Plug them into any audio player. Example with just_audio:

class _BytesAudioSource extends StreamAudioSource {
  final List<int> bytes;
  _BytesAudioSource(this.bytes) : super(tag: 'tts');

  @override
  Future<StreamAudioResponse> request([int? start, int? end]) async {
    start ??= 0;
    end ??= bytes.length;
    return StreamAudioResponse(
      sourceLength: bytes.length,
      contentLength: end - start,
      offset: start,
      contentType: 'audio/mpeg',
      stream: Stream.value(Uint8List.fromList(bytes.sublist(start, end))),
    );
  }
}

final player = AudioPlayer();
await player.setAudioSource(_BytesAudioSource(audioBytes));
await player.play();

Voice metadata

Each TtsVoice exposes:

Field Description
name Azure short name (e.g. en-US-JennyNeural)
locale BCP-47 locale (e.g. en-US)
localeName Locale display name (e.g. English (United States))
localName Voice display name in its own language
gender "Male" or "Female"

License

MIT. See LICENSE.

Libraries

azure_speech_services
Flutter package for real-time Speech-to-Text and Text-to-Speech using Microsoft Azure Cognitive Services.