SpeechToText Recognize from file - #3304
Conversation
3a7cdc2 to
58060e9
Compare
There was a problem hiding this comment.
🟡 Changes recommended
It introduces a build-breaking global.json change (invalid JSON) and has correctness/API issues in the new recognition implementations (e.g., cancellation not wired; Tizen returns null for “unsupported”).
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds a new RecognizeAsync(Stream, SpeechToTextOptions, CancellationToken) API to the SpeechToText essentials so callers can transcribe speech from an audio file/stream (in addition to the existing live “listen” flow), and wires it into the sample app UI.
Changes:
- Introduces
ISpeechToText.RecognizeAsync(...)plus corresponding wrappers inSpeechToTextandSpeechToTextImplementation. - Implements stream-based recognition for Windows (
System.Speech) and Android (pipe +RecognizerIntent.ExtraAudioSource), and adds a macOS/iOS stream-feed implementation usingSFSpeechAudioBufferRecognitionRequest. - Updates the sample page/view-model to pick a media file and display the recognized text.
File summaries
| File | Description |
|---|---|
| src/CommunityToolkit.Maui.Core/Essentials/SpeechToText/SpeechToTextImplementation.windows.cs | Adds stream-to-text recognition via SpeechRecognitionEngine. |
| src/CommunityToolkit.Maui.Core/Essentials/SpeechToText/SpeechToTextImplementation.tizen.cs | Adds a placeholder InternalRecognizeAsync stub for Tizen. |
| src/CommunityToolkit.Maui.Core/Essentials/SpeechToText/SpeechToTextImplementation.shared.cs | Exposes RecognizeAsync(...) on the main implementation type. |
| src/CommunityToolkit.Maui.Core/Essentials/SpeechToText/SpeechToTextImplementation.net.cs | Adds RecognizeAsync fallback throwing NotSupportedException. |
| src/CommunityToolkit.Maui.Core/Essentials/SpeechToText/SpeechToTextImplementation.android.cs | Adds Android stream transcription helper and InternalRecognizeAsync. |
| src/CommunityToolkit.Maui.Core/Essentials/SpeechToText/SpeechToText.shared.cs | Adds static SpeechToText.RecognizeAsync(...) convenience method. |
| src/CommunityToolkit.Maui.Core/Essentials/SpeechToText/SharedSpeechToTextImplementation.macios.cs | Implements stream recognition by feeding PCM buffers into Apple speech APIs. |
| src/CommunityToolkit.Maui.Core/Essentials/SpeechToText/OfflineSpeechToTextImplementation.shared.cs | Adds RecognizeAsync forwarding to the platform implementation. |
| src/CommunityToolkit.Maui.Core/Essentials/SpeechToText/ISpeechToText.shared.cs | Extends the interface with RecognizeAsync(...) and XML docs. |
| samples/CommunityToolkit.Maui.Sample/ViewModels/Essentials/SpeechToTextViewModel.cs | Adds a command to pick an audio file and call RecognizeAsync. |
| samples/CommunityToolkit.Maui.Sample/Pages/Essentials/SpeechToTextPage.xaml | Adds a button to trigger file recognition in the sample UI. |
| global.json | Comments out the SDK selection block. |
Review details
Suppressed comments (1)
src/CommunityToolkit.Maui.Core/Essentials/SpeechToText/SpeechToTextImplementation.android.cs:257
- Use
Trace.WriteLine()instead ofDebug.WriteLine()so the logging remains available in Release builds per toolkit guidelines.
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Pipe streaming error: {ex}");
}
- Files reviewed: 11/11 changed files
- Comments generated: 12
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| var text = await speechToText.RecognizeAsync(audioStream, new SpeechToTextOptions(){Culture = CultureInfo.GetCultureInfo(CurrentLocale?.Language ?? defaultLanguage), | ||
| }, cancellationToken: token); | ||
| await Shell.Current.DisplayAlertAsync("Recognition Result", text, "OK"); | ||
| } |
| /// <inheritdoc /> | ||
| public Task<string?> RecognizeAsync(Stream stream, SpeechToTextOptions options, CancellationToken cancellationToken = default) | ||
| { | ||
| return new SpeechToTextImplementation().RecognizeAsync(stream, options, cancellationToken); | ||
| } |
| int bytesRead; | ||
| while ((bytesRead = await stream.ReadAsync(byteBuffer, 0, byteBuffer.Length).ConfigureAwait(false)) > 0) | ||
| { |
| async Task<string?> InternalRecognizeAsync(System.IO.Stream stream, SpeechToTextOptions options, CancellationToken cancellationToken) | ||
| { | ||
| using var transcriber = new AudioStreamTranscriber(Application.Context); | ||
| return await transcriber.TranscribePcmStreamAsync(stream, language: Java.Util.Locale.ForLanguageTag(options.Culture.Name).ToLanguageTag()); | ||
| } |
|
|
||
| tcs = new TaskCompletionSource<string>(); | ||
|
|
| var sb = new StringBuilder(); | ||
| var tcs = new TaskCompletionSource<string>(); | ||
|
|
| /// <remarks> | ||
| /// Speech recognition results will be surfaced via <see cref="RecognitionResultCompleted"/> | ||
| /// </remarks> |
| /// <inheritdoc cref="ISpeechToText.StopListenAsync"/> | ||
| public static Task<string?> RecognizeAsync(Stream stream, SpeechToTextOptions options, CancellationToken cancellationToken = default) => | ||
| Default.RecognizeAsync(stream, options, cancellationToken); |
| using System.Diagnostics.CodeAnalysis; | ||
| using System.Net.Mime; | ||
| using Android.Content; | ||
| using Android.Media; | ||
| using Android.OS; |
| public class AudioStreamTranscriber : Java.Lang.Object, IRecognitionListener | ||
| { |
Description of Change
Linked Issues
PR Checklist
approved(bug) orChampioned(feature/proposal)mainat time of PRAdditional information