Skip to content

Add Flutter/Dart Web support via WebAssembly - #3842

Merged
csukuangfj merged 18 commits into
k2-fsa:masterfrom
csukuangfj:dart-wasm
Aug 6, 2026
Merged

csukuangfj merged 18 commits into
k2-fsa:masterfrom
csukuangfj:dart-wasm

Conversation

@csukuangfj

@csukuangfj csukuangfj commented Aug 6, 2026 •

Copy link
Copy Markdown
Collaborator

Only TTS has been tested.

Will test other functions in separate PRs.

Fixes #1080

Summary by CodeRabbit

  • New Features
    • Added Flutter Web and WebAssembly support for Sherpa-ONNX.
    • Added browser-based text-to-speech with streaming playback, model selection, voice cloning, audio downloads, and WAV handling.
    • Added cross-platform asynchronous initialization and web-compatible APIs.
    • Added WebAssembly builds and downloadable release artifacts for supported platforms.
  • Documentation
    • Added setup, build, deployment, and browser usage guidance for Flutter Web and TTS examples.
  • Bug Fixes
    • Improved asset discovery and packaging, including generated audio resources.

@dosubot dosubot Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Aug 6, 2026
@coderabbitai

coderabbitai Bot commented Aug 6, 2026 •

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c16c9b3-7787-42a3-9710-6756dbcb5559

📥 Commits

Reviewing files that changed from the base of the PR and between 840bc82 and f89f865.

📒 Files selected for processing (11)
  • .github/workflows/test-flutter-package.yaml
  • .github/workflows/test-flutter.yaml
  • flutter-examples/hello_world/README.md
  • flutter-examples/hello_world/lib/main.dart
  • flutter-examples/tts/lib/audio_list.dart
  • flutter-examples/tts/lib/model_web.dart
  • flutter-examples/tts/lib/tts_manager.dart
  • flutter-examples/tts/lib/worker_web.dart
  • flutter/sherpa_onnx/lib/src/offline_recognizer_config.dart
  • flutter/sherpa_onnx/lib/src/web/tts.dart
  • flutter/sherpa_onnx_web/lib/sherpa_onnx_web.dart

📝 Walkthrough

Walkthrough

The change adds Flutter Web and WebAssembly support, introduces cross-platform TTS execution and browser audio handling, reorganizes Dart configuration APIs, updates native integrations, and adds web CI and release packaging.

Changes

Flutter Web and WASM pipeline

Layer / File(s) Summary
WASM build and release pipeline
.github/workflows/*, CMakeLists.txt, wasm/*, build-*-web.sh
Adds browser WASM targets, shared Emscripten settings, web build scripts, release inspection archives, and web CI jobs.
Flutter web plugin and initialization
flutter/sherpa_onnx/*, flutter/sherpa_onnx_web/*
Adds conditional web imports, asynchronous binding initialization, JavaScript/WASM loading, web API implementations, and plugin registration.

Cross-platform TTS example

Layer / File(s) Summary
TTS configuration and runtime
flutter-examples/tts/lib/model*.dart, tts_manager.dart, worker_*.dart, web/tts-worker.js
Adds selectable model configurations, native isolate execution, Web Worker execution, streaming generation, cancellation, and completion handling.
TTS interface and audio handling
flutter-examples/tts/lib/tts_screen.dart, tts_controls.dart, audio_list.dart, wav_encoder.dart, web_audio.dart
Adds the TTS screen, controls, generated-audio list, WAV conversion, browser playback, downloads, and native save support.

Dart API and native support

Layer / File(s) Summary
Shared configuration contracts
flutter/sherpa_onnx/lib/src/*_config.dart, flutter/sherpa_onnx/lib/src/*.dart
Moves feature configuration and result classes into platform-independent modules with JSON serialization and public re-exports.
Native integration and example projects
flutter-examples/*/macos/*, flutter-examples/*/ios/*, sherpa-onnx/c-api/c-api.cc
Adds CocoaPods and Swift package wiring, file-access entitlements, native library loading, and WASM thread-count normalization.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the pull request's primary change: adding Flutter and Dart WebAssembly support.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (11)
notes-flutter-web.md-91-112 (1)

91-112: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the web initializer references to src/web/init.dart.

notes-flutter-web.md still calls the web initializer flutter/sherpa_onnx/lib/src/init_web.dart in the stack and the working flow. The actual Dart initializer is flutter/sherpa_onnx/lib/src/web/init.dart, and the package imports that path from flutter/sherpa_onnx/lib/sherpa_onnx.dart.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@notes-flutter-web.md` around lines 91 - 112, Update the web initializer
references in the documentation’s stack and “How it works” flow to use the
actual src/web/init.dart location, including the import relationship from
sherpa_onnx.dart. Replace references to src/init_web.dart without changing the
native initializer references.
notes-flutter-web.md-70-80 (1)

70-80: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a language tag to the architecture code block.

The fenced block at Line 70 has no language identifier. Use text to satisfy markdownlint and preserve the ASCII diagram formatting.

Proposed fix
-```
+```text
 sherpa-onnx C API
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@notes-flutter-web.md` around lines 70 - 80, Add the text language tag to the
fenced architecture diagram block surrounding the sherpa-onnx C API flow,
preserving the existing ASCII diagram content and formatting.

Source: Linters/SAST tools

flutter/sherpa_onnx/lib/src/web/wave_reader.dart-11-15 (1)

11-15: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Correct the web readWave contract.

The documentation states that this function returns an empty WaveData when decoding fails. The implementation always throws UnsupportedError. State that WAV reading is unsupported on web, or implement the documented fallback.

Proposed fix
-/// Read a WAV file from disk.
-///
-/// Returns an empty [WaveData] object if the file cannot be read or decoded.
+/// WAV reading is not supported on web.
 WaveData readWave(String filename) {
   throw UnsupportedError('readWave is not yet supported on web');
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter/sherpa_onnx/lib/src/web/wave_reader.dart` around lines 11 - 15,
Update the web readWave function’s documentation and behavior to match: either
explicitly document that WAV reading is unsupported on web while retaining
UnsupportedError, or implement the documented empty WaveData fallback when
reading or decoding fails.
flutter-examples/tts/web/tts-worker.js-158-163 (1)

158-163: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Forward numSteps without requiring reference audio.

The message contract accepts numSteps for every generate request. This code forwards it only when referenceAudio exists. A request without reference audio therefore uses the WASM default of 5 instead of its supplied value.

Proposed fix
       const genCfg = {
         silenceScale: 0.2,
         speed: msg.speed || 1.0,
         sid: msg.sid || 0,
+        numSteps: msg.numSteps || 5,
       };
 
       // Reference audio for voice cloning (e.g. Pocket TTS).
       if (msg.referenceAudio) {
         genCfg.referenceAudio = new Float32Array(msg.referenceAudio);
         genCfg.referenceSampleRate = msg.referenceSampleRate || 0;
-        genCfg.numSteps = msg.numSteps || 5;
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/web/tts-worker.js` around lines 158 - 163, Update the
generate-request configuration in the message handler so msg.numSteps is
forwarded to genCfg independently of the referenceAudio condition. Keep
referenceAudio and referenceSampleRate assignment conditional on
msg.referenceAudio, while preserving the default of 5 when numSteps is absent.
flutter/sherpa_onnx/lib/src/web/offline_recognizer.dart-17-21 (1)

17-21: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the unreachable private constructor.

OfflineRecognizer._ is never called, and the factory always throws UnsupportedError. Keeping it can emit an analyzer warning for an unreferenced private declaration.

♻️ Proposed cleanup
   OfflineRecognizer.fromPtr({required this.ptr, required this.config});
-  OfflineRecognizer._({required this.ptr, required this.config});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter/sherpa_onnx/lib/src/web/offline_recognizer.dart` around lines 17 -
21, Remove the unused private constructor OfflineRecognizer._ from the
OfflineRecognizer class, leaving the factory constructor and its
UnsupportedError behavior unchanged.
flutter/sherpa_onnx/lib/src/online_punctuation_config.dart-15-22 (1)

15-22: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Restore defaults when optional JSON fields are absent.

fromJson passes null for omitted fields, so JSON with only cnnBiLstm and bpeVocab throws instead of using the constructor defaults numThreads = 1, provider = 'cpu', and debug = true. Apply ? ?? ... only before constructing the model config.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter/sherpa_onnx/lib/src/online_punctuation_config.dart` around lines 15 -
22, Update OnlinePunctuationModelConfig.fromJson to apply fallback defaults only
when reading optional numThreads, provider, and debug fields: use 1, 'cpu', and
true respectively when those JSON keys are absent, while preserving explicit
provided values and the existing cnnBiLstm and bpeVocab mappings.
flutter-examples/tts/README.md-47-49 (1)

47-49: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

The example model name no longer matches the download commands.

Line 39 states that the reader selects vits-piper-en_US-libritts_r-medium. Lines 47-49 now download vits-piper-en_US-amy-low, and line 64 selects index 0, which is vits-piper-en_US-amy-low in flutter-examples/tts/lib/model_config.dart. Update line 39 to name vits-piper-en_US-amy-low so the walkthrough is consistent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/README.md` around lines 47 - 49, Update the model name
referenced in the README walkthrough around the model-selection instructions to
vits-piper-en_US-amy-low, matching the archive downloaded by the wget/tar
commands and the index-0 configuration in model_config.dart.
flutter-examples/tts/lib/model_web.dart-86-86 (1)

86-86: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

replaceFirst('assets/', '') is not anchored to the start of the path.

replaceFirst removes the first occurrence anywhere in the string. AssetManifest.listAssets() also returns plugin assets in the form packages/<name>/assets/<file>. For such a path this produces packages/<name>/<file> instead of stripping a leading directory, and the key written into the WASM filesystem does not match the path in the config.

Strip the prefix only when the path starts with it.

🐛 Proposed fix
-    final relativePath = asset.replaceFirst('assets/', '');
+    final relativePath =
+        asset.startsWith('assets/') ? asset.substring('assets/'.length) : asset;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/lib/model_web.dart` at line 86, Update the asset path
normalization around relativePath so the "assets/" prefix is removed only when
it appears at the beginning of asset. Preserve embedded "assets/" segments in
plugin paths such as packages/<name>/assets/<file>, ensuring the resulting
filesystem key matches the configured path.
flutter/sherpa_onnx/lib/src/tts_config.dart-478-488 (1)

478-488: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

OfflineTtsConfig.fromJson throws when model is absent.

Line 481 casts json['model'] to Map<String, dynamic> with no fallback. Every other nested read in this file uses ?? const {} (see lines 412-431). A JSON payload without a model key throws a TypeError instead of producing a default configuration.

Apply the same fallback for consistency.

🛡️ Proposed fix
       model: OfflineTtsModelConfig.fromJson(
-        json['model'] as Map<String, dynamic>,
+        json['model'] as Map<String, dynamic>? ?? const {},
       ),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter/sherpa_onnx/lib/src/tts_config.dart` around lines 478 - 488, Update
OfflineTtsConfig.fromJson to provide an empty map fallback when reading
json['model'] before passing it to OfflineTtsModelConfig.fromJson, matching the
existing nested JSON parsing pattern and allowing payloads without model to
produce a default configuration.
flutter-examples/tts/lib/tts_manager.dart-468-483 (1)

468-483: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Report the failure when writeWave returns false.

If writeWave fails, the method emits nothing on _audioController and logs nothing. The generation appears to hang from the user's perspective. Add a log entry on the failure branch.

🛠️ Proposed fix
     if (ok) {
       _audioController.add(GeneratedAudioItem(
         label: label,
         filePath: filename,
         duration: msg.duration,
         elapsed: msg.elapsed,
         sampleRate: msg.sampleRate,
         generationId: msg.generationId,
       ));
+    } else {
+      _logController.add('Error: failed to write wave file $filename');
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/lib/tts_manager.dart` around lines 468 - 483, Update the
writeWave handling in the audio generation method to add an explicit failure
branch when ok is false, logging that writing the generated audio file failed.
Preserve the existing _audioController.add behavior for successful writes.
flutter-examples/tts/lib/worker_web.dart-189-192 (1)

189-192: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Guard the worker log print with kDebugMode.

Line 43 in this file guards its debug output with kDebugMode, but line 192 does not. Every worker log message reaches the browser console in a release build.

🛠️ Proposed fix
     } else if (type == 'log') {
       final msg =
           (data.getProperty('message'.toJS)! as JSString).toDart;
-      print('[tts-worker] $msg');
+      if (kDebugMode) {
+        print('[tts-worker] $msg');
+      }
     } else if (type == 'error') {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/lib/worker_web.dart` around lines 189 - 192, Update the
`type == 'log'` branch in the worker message handler so the `print('[tts-worker]
$msg')` call executes only when `kDebugMode` is true, matching the existing
debug-output guard in this file.
🧹 Nitpick comments (15)
wasm/wasm-common.cmake (1)

15-15: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the duplicate SherpaOnnxWriteWave entry.

SherpaOnnxWriteWave is listed at Line 15 and again at Line 95. The generated -sEXPORTED_FUNCTIONS list then contains _SherpaOnnxWriteWave twice. Emscripten accepts this, but the duplicate is redundant.

♻️ Proposed cleanup
   #
   SherpaOnnxFileExists
   SherpaOnnxReadWave
   SherpaOnnxReadWaveFromBinaryData
   SherpaOnnxFreeWave
-  SherpaOnnxWriteWave
   # speech enhancement

Also applies to: 95-95

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wasm/wasm-common.cmake` at line 15, Remove the duplicate SherpaOnnxWriteWave
entry from the export list, retaining only one occurrence so the generated
-sEXPORTED_FUNCTIONS list exports _SherpaOnnxWriteWave once.
flutter/sherpa_onnx/lib/src/offline_recognizer_config.dart (1)

729-729: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

tokens uses an unguarded non-null cast while every sibling field is defaulted.

tokens is a required constructor parameter, so failing on a missing key is defensible. The cast message is not, however. A missing tokens key produces a generic TypeError rather than a message that names the field. Consider an explicit check.

♻️ Proposed change
-      tokens: json['tokens'] as String,
+      tokens: json['tokens'] as String? ??
+          (throw ArgumentError.notNull('tokens')),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter/sherpa_onnx/lib/src/offline_recognizer_config.dart` at line 729,
Update the tokens parsing in the OfflineRecognizerConfig construction to
explicitly validate that json['tokens'] is present and non-null before assigning
it. If missing, throw an error that clearly names the tokens field; preserve the
existing String type requirement for provided values.
flutter/sherpa_onnx/lib/src/web/wave_writer.dart (1)

7-15: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Correct the doc comment for the web stub.

The doc comment states that the function returns true or false. The body always throws UnsupportedError. Align the comment with the stub behavior.

♻️ Proposed doc fix
 /// Write normalized mono PCM samples to a WAV file.
 ///
-/// Returns `true` on success and `false` otherwise.
+/// Not supported on web. Always throws [UnsupportedError].
 bool writeWave(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter/sherpa_onnx/lib/src/web/wave_writer.dart` around lines 7 - 15, Update
the doc comment for writeWave to state that the web implementation is
unsupported and throws UnsupportedError, removing the inaccurate true/false
return description.
flutter/sherpa_onnx_web/lib/sherpa_onnx_web.dart (1)

56-60: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

Consider streaming compilation instead of buffering the full WASM binary.

rootBundle.load reads the entire .wasm file into a Dart ByteData, and wasmBytes.toJS copies it into a JS typed array. Passing wasmBinary also disables Emscripten streaming compilation. For a sherpa-onnx build this binary is large, so the page holds two full copies during startup. If the assets are served over HTTP, prefer letting Emscripten fetch and stream-compile the .wasm through locateFile.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter/sherpa_onnx_web/lib/sherpa_onnx_web.dart` around lines 56 - 60,
Update the WASM initialization flow in the surrounding startup method to avoid
loading the full binary with rootBundle.load and passing wasmBinary; configure
Emscripten’s locateFile to resolve sherpa-onnx-wasm-web.wasm from the served
asset URL so it can fetch and stream-compile it. Preserve the existing prefix
and asset resolution behavior for other files.
flutter-examples/tts/lib/model.dart (1)

97-100: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

silenceScale is not forwarded from selectedTtsConfig.

The returned OfflineTtsConfig copies ruleFsts, ruleFars, and maxNumSenetences, but not silenceScale. No entry in flutter-examples/tts/lib/model_config.dart sets silenceScale today, so both sides use the default 0.2. A future config value would be dropped silently.

Forward the field to keep the mapping complete.

♻️ Proposed change
     ruleFsts: _absMulti(d, cfg.ruleFsts),
     ruleFars: _absMulti(d, cfg.ruleFars),
     maxNumSenetences: cfg.maxNumSenetences,
+    silenceScale: cfg.silenceScale,
   );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/lib/model.dart` around lines 97 - 100, Update the
OfflineTtsConfig construction in the selectedTtsConfig mapping to forward
cfg.silenceScale, preserving the complete field mapping alongside ruleFsts,
ruleFars, and maxNumSenetences.
flutter-examples/tts/generate-asset-list.py (2)

29-29: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

lstrip('./') removes a character set, not the ./ prefix.

str.lstrip treats its argument as a set of characters. For a directory name that begins with ., this strips more than intended. Current inputs always produce ./assets/<name>, so the result is correct today, but the call is fragile.

Use an explicit prefix removal.

♻️ Proposed change
-                path = path.lstrip('./')
+                if path.startswith('./'):
+                    path = path[2:]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/generate-asset-list.py` at line 29, Replace the
lstrip('./') call in the path-processing logic with explicit removal of only a
leading "./" prefix, preserving directory names that begin with dots or other
characters and leaving paths without that exact prefix unchanged.

88-96: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Simplify the write conditions.

Line 88 tests index + 1 < loc_of_end_flutter or index + 1 > loc_of_end_flutter, which is index + 1 != loc_of_end_flutter. The enumerate/break at lines 92-96 also only adds a trailing newline after the last entry; the loop already ends there.

♻️ Proposed change
-                if index + 1 < loc_of_end_flutter or index + 1 > loc_of_end_flutter:
+                if index + 1 != loc_of_end_flutter:
                     f.write(line)
                 if index + 1 == loc_of_end_flutter:
                     f.write("  assets:\n")
-                    for indexOfEntry, entry in enumerate(entries):
+                    for entry in entries:
                         f.write("{entry}\n".format(entry=entry))
-                        if indexOfEntry == len(entries) - 1:
-                            f.write("\n")
-                            break
+                    if entries:
+                        f.write("\n")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/generate-asset-list.py` around lines 88 - 96, In the
asset-list generation loop, simplify the first condition around
loc_of_end_flutter to write lines only when index + 1 differs from the boundary,
and remove the redundant last-entry check and break inside the entries enumerate
loop while preserving the trailing newline after all entries.
flutter-examples/tts/README.md (1)

115-125: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a language to the new fenced code blocks.

markdownlint reports MD040 for the fences at lines 63, 115, and 121. The blocks at 115 and 121 contain shell commands.

📝 Proposed fix
-```
+```bash
 flutter run -d chrome

Also, you can use

- +bash
flutter build web
cd build/web
python3 -m http.server 6006

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/README.md` around lines 115 - 125, Update the fenced
shell-command blocks in the README, including the blocks near the Flutter web
run and build instructions, to declare the bash language; also add an
appropriate language identifier to the fence at line 63 so all newly added
fences satisfy markdownlint MD040.

Source: Linters/SAST tools

flutter-examples/tts/lib/model_web.dart (1)

62-81: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The model directory list is hand-maintained and matches by substring.

Lines 62-70 enumerate a subset of the config path fields. The omitted fields (for example matcha.dataDir, kokoro.lexicon, the remaining pocket and supertonic files, ruleFsts, and ruleFars) are covered today only because they sit in a directory that one listed field already names. A new model family, or a config whose listed fields are empty, silently loads no assets, and the worker then fails with a missing-file error.

Derive the directory set from all string values in cfg.toJson() instead of a fixed list. Also match on a path-segment boundary rather than contains, so one asset name cannot match an unrelated asset.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/lib/model_web.dart` around lines 62 - 81, Replace the
hand-maintained path list in the model asset filtering flow with directory
extraction from every string value in cfg.toJson(), including nested
configuration fields. Update the allAssets predicate to match each directory
only at a path-segment boundary rather than using substring contains, while
preserving loading of assets for all configured model paths.
flutter-examples/tts/lib/audio_list.dart (1)

61-65: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Guard the RTF division against a zero duration.

If item.duration is 0.0, the expression item.elapsed / item.duration evaluates to double.infinity. The label then renders as RTF Infinity. This is display-only, but the guard is one line.

♻️ Proposed guard
                       Text(
                         '${item.duration.toStringAsPrecision(3)}s | '
-                        'RTF ${(item.elapsed / item.duration).toStringAsPrecision(3)}',
+                        'RTF ${item.duration > 0 ? (item.elapsed / item.duration).toStringAsPrecision(3) : "n/a"}',
                         style: Theme.of(context).textTheme.bodySmall,
                       ),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/lib/audio_list.dart` around lines 61 - 65, Update the
RTF calculation in the Text widget to guard against zero item.duration before
dividing item.elapsed by it. Preserve the existing formatted RTF output for
nonzero durations and provide a finite fallback display for zero duration.
flutter-examples/tts/lib/worker_web.dart (2)

178-188: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Key the label on generationId instead of a single _pendingLabel field.

generate() overwrites _pendingLabel on every call, and the done branch consumes it and then clears it at line 188. The done message already carries generationId, which line 176 reads but does not use for labeling.

If a second generate() runs before the first done arrives, the first item receives the second request's label. The following item then receives an empty label, because line 188 already cleared the field. Store labels in a Map<int, String> keyed by generationId and remove the entry on completion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/lib/worker_web.dart` around lines 178 - 188, Replace the
single _pendingLabel state with a Map<int, String> keyed by generationId,
storing each label when generate() starts. In the done branch near
GeneratedAudioItem, retrieve and remove the label using the received genId, pass
it to the item, and eliminate the global-label reset so overlapping generations
retain their correct labels.

63-80: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid structured cloning for the init buffer payloads.

_worker!.postMessage(initMsg) structured-clones the nested buffers in wasmBinary and modelFiles each time init() runs. Since these buffers are not read after the worker startup message is sent, pass the Uint8Array.buffer values through a transfer list instead; web.Worker in package:web accepts the transfer list as the second argument.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/lib/worker_web.dart` around lines 63 - 80, Update the
worker initialization flow around initMsg and _worker!.postMessage to pass the
wasmData and modelFiles ArrayBuffer values in the postMessage transfer list.
Reuse each Uint8Array.buffer, including all model file buffers, so they are
transferred rather than structured-cloned, while preserving the existing init
message payload.
flutter-examples/tts/lib/tts_screen.dart (1)

50-50: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace the hardcoded model index with a named flag.

Line 50 identifies Pocket TTS by the literal index 9. If the model list in model_config.dart is reordered or a model is inserted before index 9, this check binds the reference-audio UI to the wrong model. The failure is silent.

Export a boolean such as selectedModelIsPocketTts from model_config.dart and use it here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/lib/tts_screen.dart` at line 50, Replace the hardcoded
index comparison in _isPocketTts with the exported selectedModelIsPocketTts flag
from model_config.dart, ensuring the reference-audio UI remains tied to the
Pocket TTS model regardless of model ordering.
flutter-examples/tts/lib/save_file.dart (1)

9-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use the async read and a logger instead of print.

Line 10 calls readAsBytesSync inside an async function. The synchronous read blocks the UI isolate. Use await File(sourcePath).readAsBytes() instead.

Lines 19, 26, and 27 use print for diagnostics. print output stays in release builds. Use debugPrint or a logger.

♻️ Proposed refactor
 Future<String?> saveFileAs(String sourcePath, String suggestedName) async {
   try {
-    final bytes = File(sourcePath).readAsBytesSync();
+    final bytes = await File(sourcePath).readAsBytes();
 
     final result = await FilePicker.platform.saveFile(
       dialogTitle: 'Save audio file',
       fileName: suggestedName,
       type: FileType.custom,
       allowedExtensions: ['wav'],
     );
 
-    print('FilePicker result: $result');
+    debugPrint('FilePicker result: $result');
 
     if (result == null) return null;
 
     await File(result).writeAsBytes(bytes);
     return result;
   } catch (e, st) {
-    print('Error in saveFileAs: $e');
-    print(st);
+    debugPrint('Error in saveFileAs: $e');
+    debugPrint('$st');
     rethrow;
   }
 }

debugPrint requires import 'package:flutter/foundation.dart';.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/lib/save_file.dart` around lines 9 - 29, Update
saveFileAs to asynchronously load the source bytes with await
File(sourcePath).readAsBytes() instead of readAsBytesSync(), and replace all
diagnostic print calls in this method, including the picker result and catch
block, with debugPrint or the project’s logger; add the required foundation
import if using debugPrint.
flutter-examples/tts/lib/web_audio.dart (1)

15-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace eval with typed web interop.

The five functions in flutter-examples/tts/lib/web_audio.dart run browser logic through globalContext.getProperty('eval'.toJS).callAsFunction(...). Move this to package:web bindings: AudioContext, AudioBufferSourceNode, Blob, URL, HTMLAnchorElement, and HTMLAudioElement. Keep the current AudioContext and HTMLAudioElement in Dart fields instead of window properties to make the code tree-shakeable and compatible with Content-Security-Policies that omit unsafe-eval.

Also applies to: 43, 76, 93, 114.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/lib/web_audio.dart` around lines 15 - 16, Replace the
eval-based browser logic in all five functions with typed package:web bindings
for AudioContext, AudioBufferSourceNode, Blob, URL, HTMLAnchorElement, and
HTMLAudioElement. Store the active AudioContext and HTMLAudioElement in Dart
fields rather than window properties, while preserving the existing behavior and
making all browser interactions CSP-compatible without unsafe-eval.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/flutter-macos.yaml:
- Line 19: Restore the macOS and Windows build/release jobs by removing the
unconditional if: false guards from the ASR and TTS jobs in
.github/workflows/flutter-macos.yaml at lines 19-19 and 133-133, and
.github/workflows/flutter-windows-x64.yaml at lines 19-19 and 125-125.

In @.github/workflows/test-flutter-package.yaml:
- Around line 394-408: Update the “Build Flutter web app” setup around the
existing pubspec patch so the local flutter/sherpa_onnx package also resolves
sherpa_onnx_web from its local flutter/sherpa_onnx_web directory before flutter
pub get. Keep the existing hello_world path override and cleanup behavior, and
ensure dependency resolution does not use the published ^1.13.4 web package.

In @.github/workflows/test-flutter.yaml:
- Around line 659-671: Update the path in the “Upload artifact” step to
reference the archive’s new location under build/hello-world-web.zip, matching
the destination created by the preceding “Zip web app” step.

In `@flutter-examples/tts/lib/model_web.dart`:
- Around line 101-104: Update _loadAsset to create the Uint8List using the
loaded ByteData’s offsetInBytes and lengthInBytes, ensuring only the asset’s
bytes—not the entire backing buffer—are returned.

In `@flutter-examples/tts/lib/model.dart`:
- Around line 124-135: Update _copyAssetFile to determine whether the
destination exists and matches the expected asset size before calling
rootBundle.load(src); only load and write the asset when the copy is required.
Obtain the expected size from an existing manifest or model_config.dart rather
than loading the asset solely to measure it.

In `@flutter-examples/tts/lib/tts_manager.dart`:
- Around line 458-462: The completion handlers must correlate results by
generationId rather than assuming serialized requests. In
flutter-examples/tts/lib/tts_manager.dart lines 458-462, align the ID sent by
generate() with the worker’s echoed ID, key _pending by that ID, and look up
msg.generationId directly instead of removing the first entry. In
flutter-examples/tts/lib/worker_web.dart lines 178-188, replace _pendingLabel
with a Map<int, String>, store labels by generationId in generate(), and remove
the matching entry in the done branch.
- Around line 341-363: In the _WorkerError branch of the isolate message
handler, reset _state from TtsState.initializing to the uninitialized state
before completing readyCompleter with the error, so subsequent init() and
generate() calls can retry after worker initialization failures.
- Around line 257-272: Update TtsManager.dispose() to mark the manager disposed,
reset _state from initialized, cancel the receivePort subscription, and then
release the transport and close controllers. Ensure generate() rejects or
returns before dereferencing _sendPort after disposal, and guard
_handleGenerateDone’s post-await controller add so an in-flight generation
cannot write to closed controllers.
- Around line 400-438: Update _handleGenerate so cancellation is visible to the
synchronous onProgress callback during generateWithConfig, replacing the
isolate-event-loop-dependent cancelled state with a thread-visible/shared cancel
flag that the callback reads. Keep the existing cancelPort signaling and
stop-generation return behavior, ensuring cancellation requests received from
the native callback path can take effect before generation completes.

In `@flutter-examples/tts/lib/tts_screen.dart`:
- Around line 285-297: Clamp the parsed sid in the generation flow before
passing it to _manager.generate, constraining it to the supported 0 through
_maxSpeakerID range. Also clamp numSteps after parsing so it cannot be below the
valid minimum (at least 1) or outside the supported step range, while preserving
the existing default of 5 for invalid input and applying the step value to the
Pocket TTS path.

In `@flutter-examples/tts/lib/wav_encoder.dart`:
- Around line 35-95: Update decodeWav to validate WAV metadata before reading or
using it: ensure each fmt chunk contains all fields accessed before parsing,
ensure the declared data chunk size fits within the remaining bytes before
creating the view, and reject numChannels or sampleRate values of zero by
returning null. Preserve normal decoding for valid PCM and float formats, while
returning null instead of allowing malformed or truncated headers to throw.

In `@flutter-examples/tts/lib/worker_web.dart`:
- Around line 101-106: Update the request construction around the message posted
by _worker?.postMessage so numSteps is assigned unconditionally, while keeping
referenceAudio and referenceSampleRate conditional on non-empty reference audio.
Serialize only the caller’s referenceAudio view, respecting its byteOffset and
byteLength, rather than posting the entire backing buffer.
- Around line 47-53: Update the worker initialization in init to register an
error handler for startup and message failures, including both onerror and
onmessageerror as supported by the Worker API, and route those events to onError
so TtsManager._initWeb can complete with visible failure feedback.

In `@flutter-examples/tts/pubspec.yaml`:
- Around line 11-12: Update the environment.flutter constraint in pubspec.yaml
from >=2.8.1 to >=3.13.0, keeping the existing environment.sdk constraint
unchanged.

In `@flutter-examples/tts/web/tts-worker.js`:
- Around line 188-229: Update the generation flow in the worker’s message
handler so cancellation can be observed while
SherpaOnnxOfflineTtsGenerateWithConfig is running; the current synchronous call
blocks processing of the cancel branch and prevents the callback from seeing
_cancelled. Replace it with an asynchronous native-generation approach, separate
cancellation channel, or worker termination/recreation strategy, while
preserving the existing result and error messaging behavior.

In `@flutter/sherpa_onnx_web/lib/sherpa_onnx_web.dart`:
- Around line 38-45: Replace the boolean-only guard in loadWasm with an
in-flight Future<void> cache: return the existing Future when initialization is
already running or complete, and assign the Future before the first await so
concurrent callers share the same initialization. Move the current
asset/WASM/module setup into that cached operation and remove the later
_initialized = true assignment, preserving existing completion and error
behavior.

In `@flutter/sherpa_onnx/lib/src/offline_recognizer_config.dart`:
- Around line 213-229: Update OfflineFunAsrNanoModelConfig.fromJson so the
systemPrompt and userPrompt fallbacks match the constructor defaults: use the
established assistant prompt and Chinese transcription prompt when those JSON
keys are absent, while preserving explicitly provided values.
- Around line 823-841: Update OfflineRecognizerConfig.fromJson so the optional
hr field is null-checked like feat and lm, falling back to the existing const
HomophoneReplacerConfig() when json['hr'] is absent; only call
HomophoneReplacerConfig.fromJson when a map is present.

In `@flutter/sherpa_onnx/lib/src/web/tts.dart`:
- Around line 74-88: Update generateWithConfig’s genConfig construction to
preserve OfflineTtsGenerationConfig.referenceAudio, referenceSampleRate,
referenceText, and extra when invoking the web JS path, using the expected
JS-compatible representations. If any of these fields cannot be supported by the
web API, explicitly reject configured values instead of silently dropping them.
- Around line 38-44: Update OfflineTts.free() to release the JS wrapper stored
by factory OfflineTts(), using ptr.free() or the raw destroy export with
ptr.handle rather than passing the wrapper itself. Preserve the _freed guard and
mark the wrapper freed after successful cleanup.

---

Minor comments:
In `@flutter-examples/tts/lib/model_web.dart`:
- Line 86: Update the asset path normalization around relativePath so the
"assets/" prefix is removed only when it appears at the beginning of asset.
Preserve embedded "assets/" segments in plugin paths such as
packages/<name>/assets/<file>, ensuring the resulting filesystem key matches the
configured path.

In `@flutter-examples/tts/lib/tts_manager.dart`:
- Around line 468-483: Update the writeWave handling in the audio generation
method to add an explicit failure branch when ok is false, logging that writing
the generated audio file failed. Preserve the existing _audioController.add
behavior for successful writes.

In `@flutter-examples/tts/lib/worker_web.dart`:
- Around line 189-192: Update the `type == 'log'` branch in the worker message
handler so the `print('[tts-worker] $msg')` call executes only when `kDebugMode`
is true, matching the existing debug-output guard in this file.

In `@flutter-examples/tts/README.md`:
- Around line 47-49: Update the model name referenced in the README walkthrough
around the model-selection instructions to vits-piper-en_US-amy-low, matching
the archive downloaded by the wget/tar commands and the index-0 configuration in
model_config.dart.

In `@flutter-examples/tts/web/tts-worker.js`:
- Around line 158-163: Update the generate-request configuration in the message
handler so msg.numSteps is forwarded to genCfg independently of the
referenceAudio condition. Keep referenceAudio and referenceSampleRate assignment
conditional on msg.referenceAudio, while preserving the default of 5 when
numSteps is absent.

In `@flutter/sherpa_onnx/lib/src/online_punctuation_config.dart`:
- Around line 15-22: Update OnlinePunctuationModelConfig.fromJson to apply
fallback defaults only when reading optional numThreads, provider, and debug
fields: use 1, 'cpu', and true respectively when those JSON keys are absent,
while preserving explicit provided values and the existing cnnBiLstm and
bpeVocab mappings.

In `@flutter/sherpa_onnx/lib/src/tts_config.dart`:
- Around line 478-488: Update OfflineTtsConfig.fromJson to provide an empty map
fallback when reading json['model'] before passing it to
OfflineTtsModelConfig.fromJson, matching the existing nested JSON parsing
pattern and allowing payloads without model to produce a default configuration.

In `@flutter/sherpa_onnx/lib/src/web/offline_recognizer.dart`:
- Around line 17-21: Remove the unused private constructor OfflineRecognizer._
from the OfflineRecognizer class, leaving the factory constructor and its
UnsupportedError behavior unchanged.

In `@flutter/sherpa_onnx/lib/src/web/wave_reader.dart`:
- Around line 11-15: Update the web readWave function’s documentation and
behavior to match: either explicitly document that WAV reading is unsupported on
web while retaining UnsupportedError, or implement the documented empty WaveData
fallback when reading or decoding fails.

In `@notes-flutter-web.md`:
- Around line 91-112: Update the web initializer references in the
documentation’s stack and “How it works” flow to use the actual
src/web/init.dart location, including the import relationship from
sherpa_onnx.dart. Replace references to src/init_web.dart without changing the
native initializer references.
- Around line 70-80: Add the text language tag to the fenced architecture
diagram block surrounding the sherpa-onnx C API flow, preserving the existing
ASCII diagram content and formatting.

---

Nitpick comments:
In `@flutter-examples/tts/generate-asset-list.py`:
- Line 29: Replace the lstrip('./') call in the path-processing logic with
explicit removal of only a leading "./" prefix, preserving directory names that
begin with dots or other characters and leaving paths without that exact prefix
unchanged.
- Around line 88-96: In the asset-list generation loop, simplify the first
condition around loc_of_end_flutter to write lines only when index + 1 differs
from the boundary, and remove the redundant last-entry check and break inside
the entries enumerate loop while preserving the trailing newline after all
entries.

In `@flutter-examples/tts/lib/audio_list.dart`:
- Around line 61-65: Update the RTF calculation in the Text widget to guard
against zero item.duration before dividing item.elapsed by it. Preserve the
existing formatted RTF output for nonzero durations and provide a finite
fallback display for zero duration.

In `@flutter-examples/tts/lib/model_web.dart`:
- Around line 62-81: Replace the hand-maintained path list in the model asset
filtering flow with directory extraction from every string value in
cfg.toJson(), including nested configuration fields. Update the allAssets
predicate to match each directory only at a path-segment boundary rather than
using substring contains, while preserving loading of assets for all configured
model paths.

In `@flutter-examples/tts/lib/model.dart`:
- Around line 97-100: Update the OfflineTtsConfig construction in the
selectedTtsConfig mapping to forward cfg.silenceScale, preserving the complete
field mapping alongside ruleFsts, ruleFars, and maxNumSenetences.

In `@flutter-examples/tts/lib/save_file.dart`:
- Around line 9-29: Update saveFileAs to asynchronously load the source bytes
with await File(sourcePath).readAsBytes() instead of readAsBytesSync(), and
replace all diagnostic print calls in this method, including the picker result
and catch block, with debugPrint or the project’s logger; add the required
foundation import if using debugPrint.

In `@flutter-examples/tts/lib/tts_screen.dart`:
- Line 50: Replace the hardcoded index comparison in _isPocketTts with the
exported selectedModelIsPocketTts flag from model_config.dart, ensuring the
reference-audio UI remains tied to the Pocket TTS model regardless of model
ordering.

In `@flutter-examples/tts/lib/web_audio.dart`:
- Around line 15-16: Replace the eval-based browser logic in all five functions
with typed package:web bindings for AudioContext, AudioBufferSourceNode, Blob,
URL, HTMLAnchorElement, and HTMLAudioElement. Store the active AudioContext and
HTMLAudioElement in Dart fields rather than window properties, while preserving
the existing behavior and making all browser interactions CSP-compatible without
unsafe-eval.

In `@flutter-examples/tts/lib/worker_web.dart`:
- Around line 178-188: Replace the single _pendingLabel state with a Map<int,
String> keyed by generationId, storing each label when generate() starts. In the
done branch near GeneratedAudioItem, retrieve and remove the label using the
received genId, pass it to the item, and eliminate the global-label reset so
overlapping generations retain their correct labels.
- Around line 63-80: Update the worker initialization flow around initMsg and
_worker!.postMessage to pass the wasmData and modelFiles ArrayBuffer values in
the postMessage transfer list. Reuse each Uint8Array.buffer, including all model
file buffers, so they are transferred rather than structured-cloned, while
preserving the existing init message payload.

In `@flutter-examples/tts/README.md`:
- Around line 115-125: Update the fenced shell-command blocks in the README,
including the blocks near the Flutter web run and build instructions, to declare
the bash language; also add an appropriate language identifier to the fence at
line 63 so all newly added fences satisfy markdownlint MD040.

In `@flutter/sherpa_onnx_web/lib/sherpa_onnx_web.dart`:
- Around line 56-60: Update the WASM initialization flow in the surrounding
startup method to avoid loading the full binary with rootBundle.load and passing
wasmBinary; configure Emscripten’s locateFile to resolve
sherpa-onnx-wasm-web.wasm from the served asset URL so it can fetch and
stream-compile it. Preserve the existing prefix and asset resolution behavior
for other files.

In `@flutter/sherpa_onnx/lib/src/offline_recognizer_config.dart`:
- Line 729: Update the tokens parsing in the OfflineRecognizerConfig
construction to explicitly validate that json['tokens'] is present and non-null
before assigning it. If missing, throw an error that clearly names the tokens
field; preserve the existing String type requirement for provided values.

In `@flutter/sherpa_onnx/lib/src/web/wave_writer.dart`:
- Around line 7-15: Update the doc comment for writeWave to state that the web
implementation is unsupported and throws UnsupportedError, removing the
inaccurate true/false return description.

In `@wasm/wasm-common.cmake`:
- Line 15: Remove the duplicate SherpaOnnxWriteWave entry from the export list,
retaining only one occurrence so the generated -sEXPORTED_FUNCTIONS list exports
_SherpaOnnxWriteWave once.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c00ea01e-dc0d-4449-85e9-7729903eb33f

📥 Commits

Reviewing files that changed from the base of the PR and between 2459194 and 840bc82.

⛔ Files ignored due to path filters (2)
  • flutter-examples/hello_world/macos/Runner.xcworkspace/contents.xcworkspacedata is excluded by !**/*.xcworkspace/contents.xcworkspacedata
  • flutter-examples/tts/macos/Runner.xcworkspace/contents.xcworkspacedata is excluded by !**/*.xcworkspace/contents.xcworkspacedata
📒 Files selected for processing (124)
  • .github/workflows/flutter-android.yaml
  • .github/workflows/flutter-linux.yaml
  • .github/workflows/flutter-macos.yaml
  • .github/workflows/flutter-windows-x64.yaml
  • .github/workflows/release-dart-package.yaml
  • .github/workflows/test-flutter-package.yaml
  • .github/workflows/test-flutter.yaml
  • .gitignore
  • CMakeLists.txt
  • build-flutter-web-wasm.sh
  • build-wasm-simd-web.sh
  • flutter-examples/hello_world/README.md
  • flutter-examples/hello_world/ios/Flutter/Debug.xcconfig
  • flutter-examples/hello_world/ios/Flutter/Release.xcconfig
  • flutter-examples/hello_world/lib/main.dart
  • flutter-examples/hello_world/macos/Flutter/Flutter-Debug.xcconfig
  • flutter-examples/hello_world/macos/Flutter/Flutter-Release.xcconfig
  • flutter-examples/hello_world/macos/Runner.xcodeproj/project.pbxproj
  • flutter-examples/tts/.gitignore
  • flutter-examples/tts/README.md
  • flutter-examples/tts/generate-asset-list.py
  • flutter-examples/tts/ios/Flutter/Debug.xcconfig
  • flutter-examples/tts/ios/Flutter/Release.xcconfig
  • flutter-examples/tts/lib/audio_list.dart
  • flutter-examples/tts/lib/generated_audio.dart
  • flutter-examples/tts/lib/info.dart
  • flutter-examples/tts/lib/isolate_tts.dart
  • flutter-examples/tts/lib/main.dart
  • flutter-examples/tts/lib/model.dart
  • flutter-examples/tts/lib/model_config.dart
  • flutter-examples/tts/lib/model_web.dart
  • flutter-examples/tts/lib/play_bytes.dart
  • flutter-examples/tts/lib/play_bytes_stub.dart
  • flutter-examples/tts/lib/play_ref.dart
  • flutter-examples/tts/lib/play_ref_stub.dart
  • flutter-examples/tts/lib/save_file.dart
  • flutter-examples/tts/lib/save_file_stub.dart
  • flutter-examples/tts/lib/tts.dart
  • flutter-examples/tts/lib/tts_controls.dart
  • flutter-examples/tts/lib/tts_manager.dart
  • flutter-examples/tts/lib/tts_screen.dart
  • flutter-examples/tts/lib/utils_web.dart
  • flutter-examples/tts/lib/wav_encoder.dart
  • flutter-examples/tts/lib/web_audio.dart
  • flutter-examples/tts/lib/web_audio_stub.dart
  • flutter-examples/tts/lib/worker_stub.dart
  • flutter-examples/tts/lib/worker_web.dart
  • flutter-examples/tts/macos/Flutter/Flutter-Debug.xcconfig
  • flutter-examples/tts/macos/Flutter/Flutter-Release.xcconfig
  • flutter-examples/tts/macos/Runner.xcodeproj/project.pbxproj
  • flutter-examples/tts/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
  • flutter-examples/tts/macos/Runner/AppDelegate.swift
  • flutter-examples/tts/macos/Runner/DebugProfile.entitlements
  • flutter-examples/tts/macos/Runner/MainFlutterWindow.swift
  • flutter-examples/tts/macos/Runner/Release.entitlements
  • flutter-examples/tts/pubspec.yaml
  • flutter-examples/tts/web/index.html
  • flutter-examples/tts/web/tts-worker.js
  • flutter/sherpa_onnx/lib/sherpa_onnx.dart
  • flutter/sherpa_onnx/lib/src/audio_tagging.dart
  • flutter/sherpa_onnx/lib/src/audio_tagging_config.dart
  • flutter/sherpa_onnx/lib/src/init_native.dart
  • flutter/sherpa_onnx/lib/src/init_stub.dart
  • flutter/sherpa_onnx/lib/src/keyword_spotter.dart
  • flutter/sherpa_onnx/lib/src/keyword_spotter_config.dart
  • flutter/sherpa_onnx/lib/src/offline_punctuation.dart
  • flutter/sherpa_onnx/lib/src/offline_punctuation_config.dart
  • flutter/sherpa_onnx/lib/src/offline_recognizer.dart
  • flutter/sherpa_onnx/lib/src/offline_recognizer_config.dart
  • flutter/sherpa_onnx/lib/src/offline_speaker_diarization.dart
  • flutter/sherpa_onnx/lib/src/offline_speaker_diarization_config.dart
  • flutter/sherpa_onnx/lib/src/offline_speech_denoiser.dart
  • flutter/sherpa_onnx/lib/src/offline_speech_denoiser_config.dart
  • flutter/sherpa_onnx/lib/src/online_punctuation.dart
  • flutter/sherpa_onnx/lib/src/online_punctuation_config.dart
  • flutter/sherpa_onnx/lib/src/online_recognizer.dart
  • flutter/sherpa_onnx/lib/src/online_recognizer_config.dart
  • flutter/sherpa_onnx/lib/src/online_speech_denoiser.dart
  • flutter/sherpa_onnx/lib/src/online_speech_denoiser_config.dart
  • flutter/sherpa_onnx/lib/src/speaker_identification.dart
  • flutter/sherpa_onnx/lib/src/speaker_identification_config.dart
  • flutter/sherpa_onnx/lib/src/spoken_language_identification.dart
  • flutter/sherpa_onnx/lib/src/spoken_language_identification_config.dart
  • flutter/sherpa_onnx/lib/src/tts.dart
  • flutter/sherpa_onnx/lib/src/tts_config.dart
  • flutter/sherpa_onnx/lib/src/vad.dart
  • flutter/sherpa_onnx/lib/src/vad_config.dart
  • flutter/sherpa_onnx/lib/src/wave_reader.dart
  • flutter/sherpa_onnx/lib/src/wave_reader_config.dart
  • flutter/sherpa_onnx/lib/src/web/audio_tagging.dart
  • flutter/sherpa_onnx/lib/src/web/init.dart
  • flutter/sherpa_onnx/lib/src/web/keyword_spotter.dart
  • flutter/sherpa_onnx/lib/src/web/offline_punctuation.dart
  • flutter/sherpa_onnx/lib/src/web/offline_recognizer.dart
  • flutter/sherpa_onnx/lib/src/web/offline_speaker_diarization.dart
  • flutter/sherpa_onnx/lib/src/web/offline_speech_denoiser.dart
  • flutter/sherpa_onnx/lib/src/web/offline_stream.dart
  • flutter/sherpa_onnx/lib/src/web/online_punctuation.dart
  • flutter/sherpa_onnx/lib/src/web/online_recognizer.dart
  • flutter/sherpa_onnx/lib/src/web/online_speech_denoiser.dart
  • flutter/sherpa_onnx/lib/src/web/online_stream.dart
  • flutter/sherpa_onnx/lib/src/web/speaker_identification.dart
  • flutter/sherpa_onnx/lib/src/web/spoken_language_identification.dart
  • flutter/sherpa_onnx/lib/src/web/tts.dart
  • flutter/sherpa_onnx/lib/src/web/vad.dart
  • flutter/sherpa_onnx/lib/src/web/version.dart
  • flutter/sherpa_onnx/lib/src/web/wave_reader.dart
  • flutter/sherpa_onnx/lib/src/web/wave_writer.dart
  • flutter/sherpa_onnx/pubspec.yaml
  • flutter/sherpa_onnx_ios/ios/.gitignore
  • flutter/sherpa_onnx_macos/macos/.gitignore
  • flutter/sherpa_onnx_web/.gitignore
  • flutter/sherpa_onnx_web/README.md
  • flutter/sherpa_onnx_web/assets/.gitkeep
  • flutter/sherpa_onnx_web/lib/sherpa_onnx_web.dart
  • flutter/sherpa_onnx_web/pubspec.yaml
  • notes-flutter-web.md
  • sherpa-onnx/c-api/c-api.cc
  • sherpa-onnx/csrc/offline-tts-matcha-impl.h
  • wasm/CMakeLists.txt
  • wasm/nodejs/CMakeLists.txt
  • wasm/tts/sherpa-onnx-tts.js
  • wasm/wasm-common.cmake
  • wasm/web/CMakeLists.txt
💤 Files with no reviewable changes (3)
  • flutter-examples/tts/lib/info.dart
  • flutter-examples/tts/lib/tts.dart
  • flutter-examples/tts/lib/isolate_tts.dart


jobs:
asr:
if: false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Restore the disabled build and release jobs.

if: false skips every listed matrix. This prevents these jobs from building and publishing macOS and Windows artifacts.

  • .github/workflows/flutter-macos.yaml#L19-L19: Remove the unconditional guard from the ASR job.
  • .github/workflows/flutter-macos.yaml#L133-L133: Remove the unconditional guard from the TTS job.
  • .github/workflows/flutter-windows-x64.yaml#L19-L19: Remove the unconditional guard from the ASR job.
  • .github/workflows/flutter-windows-x64.yaml#L125-L125: Remove the unconditional guard from the TTS job.
🧰 Tools
🪛 actionlint (1.7.12)

[error] 19-19: constant expression "false" in condition. remove the if: section

(if-cond)

📍 Affects 2 files
  • .github/workflows/flutter-macos.yaml#L19-L19 (this comment)
  • .github/workflows/flutter-macos.yaml#L133-L133
  • .github/workflows/flutter-windows-x64.yaml#L19-L19
  • .github/workflows/flutter-windows-x64.yaml#L125-L125
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/flutter-macos.yaml at line 19, Restore the macOS and
Windows build/release jobs by removing the unconditional if: false guards from
the ASR and TTS jobs in .github/workflows/flutter-macos.yaml at lines 19-19 and
133-133, and .github/workflows/flutter-windows-x64.yaml at lines 19-19 and
125-125.

Source: Linters/SAST tools

Comment on lines +394 to +408
- name: Copy WASM assets to web plugin
shell: bash
run: |
./build-flutter-web-wasm.sh

- name: Build Flutter web app
shell: bash
run: |
cd flutter-examples/hello_world

sed -i.bak 's|sherpa_onnx: ^1.13.4|sherpa_onnx:\n path: ../../flutter/sherpa_onnx|' pubspec.yaml
rm -f pubspec.yaml.bak

flutter pub get
flutter build web

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 5 'sherpa_onnx_web|dependency_overrides|path:' \
  flutter/sherpa_onnx/pubspec.yaml \
  flutter-examples/hello_world/pubspec.yaml \
  build-flutter-web-wasm.sh \
  .github/workflows/test-flutter-package.yaml

# Expect this workflow or build-flutter-web-wasm.sh to replace
# sherpa_onnx_web with ../sherpa_onnx_web before `flutter pub get`.

Repository: k2-fsa/sherpa-onnx

Length of output: 12381


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow file size =="
wc -l .github/workflows/test-flutter-package.yaml

echo
echo "== web job section =="
sed -n '354,420p' .github/workflows/test-flutter-package.yaml

echo
echo "== test-flutter.yaml web/build section if present =="
if [ -f .github/workflows/test-flutter.yaml ]; then
  wc -l .github/workflows/test-flutter.yaml
  sed -n '620,660p' .github/workflows/test-flutter.yaml
else
  echo ".github/workflows/test-flutter.yaml not found"
fi

echo
echo "== relevant pubspecs =="
printf '\n--- flutter/sherpa_onnx/pubspec.yaml ---\n'
sed -n '1,95p' flutter/sherpa_onnx/pubspec.yaml
printf '\n--- flutter/sherpa_onnx_web/pubspec.yaml ---\n'
sed -n '1,120p' flutter/sherpa_onnx_web/pubspec.yaml
printf '\n--- flutter-examples/hello_world/pubspec.yaml ---\n'
sed -n '1,25p' flutter-examples/hello_world/pubspec.yaml

echo
echo "== lockfile web plugin entries if present =="
fd -a 'pubspec.lock$|pubspec.yaml$' . | sed 's#^\./##' | sort | head -80
for f in $(fd 'pubspec.lock$|pubspec.yaml$' . | sort); do
  echo "--- $(printf '%s' "$f" | sed 's#^\./##') ---"
  rg -n -C 3 'sherpa_onnx_web|ssh://git@github\.com/k2-fsa/sherpa-onnx|path:' "$f" || true
done

echo
echo "== deterministic substitution behavior for original command =="
python3 - <<'PY'
example = """name: hello_world
description: A simple Flutter app.
publish_to: 'none'
version: 1.0.0+1

environment:
  sdk: '>=3.0.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
  sherpa_onnx: ^1.13.4
"""
import subprocess, sys, tempfile, os
orig = example
# Use bash as in workflow, but no git/sed available in /bin sometimes
p = subprocess.run(['bash','-c', r'''
  cat pubspec.yaml.input | sed -i.bak 's|sherpa_onnx: ^1.13.4|sherpa_onnx:\n    path: ../../flutter/sherpa_onnx|'
  sed -n '14,22p' pubspec.yaml.input.bak
  sed -n '14,22p' pubspec.yaml.input
  sed -n '14,22p' pubspec.yaml.input
'''], input=orig.encode(), text=True, capture_output=True)
print(p.stdout)
print(p.stderr)
print("returncode", p.returncode)
PY

Repository: k2-fsa/sherpa-onnx

Length of output: 20602


Use the local sherpa_onnx_web package in the web job.

This job points flutter-examples/hello_world at the local flutter/sherpa_onnx, but flutter/sherpa_onnx/pubspec.yaml still depends on sherpa_onnx_web: ^1.13.4. build-flutter-web-wasm.sh only copies assets and does not patch that constraint. Add the same local package override before flutter pub get, instead of relying on a later published web plugin.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test-flutter-package.yaml around lines 394 - 408, Update
the “Build Flutter web app” setup around the existing pubspec patch so the local
flutter/sherpa_onnx package also resolves sherpa_onnx_web from its local
flutter/sherpa_onnx_web directory before flutter pub get. Keep the existing
hello_world path override and cleanup behavior, and ensure dependency resolution
does not use the published ^1.13.4 web package.

Comment thread .github/workflows/test-flutter.yaml Outdated
Comment thread flutter-examples/tts/lib/model_web.dart
Comment on lines +124 to +135
Future<String> _copyAssetFile(String src, [String? dst]) async {
final Directory directory = await getApplicationSupportDirectory();
if (dst == null) dst = p.basename(src);
final target = p.join(directory.path, dst);
bool exists = await File(target).exists();
final data = await rootBundle.load(src);
if (!exists || File(target).lengthSync() != data.lengthInBytes) {
final List<int> bytes =
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await (await File(target).create(recursive: true)).writeAsBytes(bytes);
}

final modelConfig = sherpa_onnx.OfflineTtsModelConfig(
vits: vits,
kokoro: kokoro,
kitten: kitten,
numThreads: 2,
debug: true,
provider: 'cpu',
);

final config = sherpa_onnx.OfflineTtsConfig(
model: modelConfig,
ruleFsts: ruleFsts,
ruleFars: ruleFars,
maxNumSenetences: 1,
);
// print(config);

final tts = sherpa_onnx.OfflineTts(config);
print('tts created successfully');

return tts;
return target;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Every asset is fully loaded into memory on each launch.

_copyAssetFile calls rootBundle.load(src) at line 129 before it decides whether the copy is needed. The size check at line 130 then uses the already-loaded data. For TTS models this reads hundreds of megabytes into memory on every start, even when the files are already on disk.

Compare the existing file size first, and load the asset only when the copy is required.

⚡ Proposed change
 Future<String> _copyAssetFile(String src, [String? dst]) async {
   final Directory directory = await getApplicationSupportDirectory();
   if (dst == null) dst = p.basename(src);
   final target = p.join(directory.path, dst);
-  bool exists = await File(target).exists();
-  final data = await rootBundle.load(src);
-  if (!exists || File(target).lengthSync() != data.lengthInBytes) {
+  final file = File(target);
+  final bool exists = await file.exists();
+  final int existingLength = exists ? await file.length() : -1;
+  final data = await rootBundle.load(src);
+  if (existingLength != data.lengthInBytes) {
     final List<int> bytes =
         data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
-    await (await File(target).create(recursive: true)).writeAsBytes(bytes);
+    await (await file.create(recursive: true)).writeAsBytes(bytes);
   }
   return target;
 }

Note: this still loads the asset to learn its length. To avoid the load entirely, record the expected sizes in model_config.dart or compare against the manifest.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter-examples/tts/lib/model.dart` around lines 124 - 135, Update
_copyAssetFile to determine whether the destination exists and matches the
expected asset size before calling rootBundle.load(src); only load and write the
asset when the copy is required. Obtain the expected size from an existing
manifest or model_config.dart rather than loading the asset solely to measure
it.

Comment thread flutter/sherpa_onnx_web/lib/sherpa_onnx_web.dart
Comment thread flutter/sherpa_onnx/lib/src/offline_recognizer_config.dart
Comment thread flutter/sherpa_onnx/lib/src/offline_recognizer_config.dart
Comment on lines +38 to +44
void free() {
if (_freed) return;
final m = getModule();
final destroyFn = m.getProperty('_SherpaOnnxDestroyOfflineTts'.toJS) as JSFunction?;
destroyFn?.callAsFunction(null, ptr);
_freed = true;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the JS wrapper API returned by createOfflineTts.
fd -t f 'sherpa-onnx-tts.js' | while IFS= read -r f; do
  rg -n -C 5 'createOfflineTts|class OfflineTts|free\s*\(|this\.handle|_SherpaOnnxDestroyOfflineTts' "$f"
done

Repository: k2-fsa/sherpa-onnx

Length of output: 4651


🏁 Script executed:

#!/bin/bash
set -e
# Locate and inspect the Dart wrapper around the JS OfflineTts factory.
fd -t f 'tts.dart' .
for f in $(fd -t f 'tts.dart' .); do
  echo "===== $f ====="
  wc -l "$f"
  rg -n -C 8 'createOfflineTts|_SherpaOnnxDestroyOfflineTts|free\(|ptr|fromPtr|OfflineTts' "$f"
done

Repository: k2-fsa/sherpa-onnx

Length of output: 15566


Release the JS OfflineTts wrapper, not the raw WASM export.

free() calls _SherpaOnnxDestroyOfflineTts(ptr), but factory OfflineTts() stores the object returned by the JS createOfflineTts wrapper. That wrapper exposes free(), handle, sampleRate, and generateWithConfig; use ptr.free() or _SherpaOnnxDestroyOfflineTts(ptr.handle) so the native TTS instance is actually released.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flutter/sherpa_onnx/lib/src/web/tts.dart` around lines 38 - 44, Update
OfflineTts.free() to release the JS wrapper stored by factory OfflineTts(),
using ptr.free() or the raw destroy export with ptr.handle rather than passing
the wrapper itself. Preserve the _freed guard and mark the wrapper freed after
successful cleanup.

Comment thread flutter/sherpa_onnx/lib/src/web/tts.dart

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds Flutter/Dart Web support by introducing a browser-targeted WASM build (Emscripten) plus a new sherpa_onnx_web Flutter web plugin, and wires it into the main sherpa_onnx package + CI.

Changes:

  • Add a dedicated WASM “web browser” build target and shared WASM CMake flags.
  • Introduce flutter/sherpa_onnx_web plugin that loads the Emscripten JS glue + .wasm and exposes the Module to Dart via dart:js_interop.
  • Update Flutter packages/examples and GitHub Actions to build and validate the web target.

Reviewed changes

Copilot reviewed 122 out of 126 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
wasm/web/CMakeLists.txt Adds browser WASM build target + install rules
wasm/wasm-common.cmake Centralizes exported C symbols + common Emscripten flags
wasm/tts/sherpa-onnx-tts.js Adds Dart-web config aliasing/typo compatibility
wasm/nodejs/CMakeLists.txt Refactors NodeJS WASM build to use shared CMake
wasm/CMakeLists.txt Enables wasm/web subdir behind a CMake option
sherpa-onnx/csrc/offline-tts-matcha-impl.h Improves vocoder-missing log message
sherpa-onnx/c-api/c-api.cc Clamps num_threads to 1 on WASM builds
notes-flutter-web.md Documents how to build/test Flutter web + WASM
flutter/sherpa_onnx_web/pubspec.yaml New Flutter web plugin package metadata/assets
flutter/sherpa_onnx_web/lib/sherpa_onnx_web.dart WASM+JS asset loader/evaluator for web
flutter/sherpa_onnx_web/README.md Minimal README for internal sub-package
flutter/sherpa_onnx_web/.gitignore Ignores generated WASM outputs + symlinked wrappers
flutter/sherpa_onnx_macos/macos/.gitignore Ignores .xcframework artifacts
flutter/sherpa_onnx_ios/ios/.gitignore Ignores .xcframework artifacts
flutter/sherpa_onnx/pubspec.yaml Adds sherpa_onnx_web and web default package
flutter/sherpa_onnx/lib/src/web/wave_writer.dart Web stub for wave writer
flutter/sherpa_onnx/lib/src/web/wave_reader.dart Web stub for wave reader
flutter/sherpa_onnx/lib/src/web/version.dart Web implementation of version getters via JS interop
flutter/sherpa_onnx/lib/src/web/vad.dart Web (currently stubbed) VAD API surface
flutter/sherpa_onnx/lib/src/web/tts.dart Web OfflineTts implementation via JS interop
flutter/sherpa_onnx/lib/src/web/spoken_language_identification.dart Web stub for SLI
flutter/sherpa_onnx/lib/src/web/speaker_identification.dart Web stub for speaker ID
flutter/sherpa_onnx/lib/src/web/online_stream.dart Web stub for online stream
flutter/sherpa_onnx/lib/src/web/online_speech_denoiser.dart Web stub for online denoiser
flutter/sherpa_onnx/lib/src/web/online_recognizer.dart Web stub for online ASR
flutter/sherpa_onnx/lib/src/web/online_punctuation.dart Web stub for online punctuation
flutter/sherpa_onnx/lib/src/web/offline_stream.dart Web stub for offline stream
flutter/sherpa_onnx/lib/src/web/offline_speech_denoiser.dart Web stub for offline denoiser
flutter/sherpa_onnx/lib/src/web/offline_speaker_diarization.dart Web stub for diarization
flutter/sherpa_onnx/lib/src/web/offline_recognizer.dart Web stub for offline ASR
flutter/sherpa_onnx/lib/src/web/offline_punctuation.dart Web stub for offline punctuation
flutter/sherpa_onnx/lib/src/web/keyword_spotter.dart Web stub for KWS
flutter/sherpa_onnx/lib/src/web/init.dart Web Module getter + init stub
flutter/sherpa_onnx/lib/src/web/audio_tagging.dart Web stub for audio tagging
flutter/sherpa_onnx/lib/src/wave_reader_config.dart Splits WaveData into platform-agnostic config file
flutter/sherpa_onnx/lib/src/wave_reader.dart Uses shared WaveData config/export
flutter/sherpa_onnx/lib/src/vad_config.dart Splits VAD config/data into platform-agnostic file
flutter/sherpa_onnx/lib/src/vad.dart Uses shared VAD config/export; trims doc noise
flutter/sherpa_onnx/lib/src/spoken_language_identification_config.dart Splits SLI config/data into platform-agnostic file
flutter/sherpa_onnx/lib/src/spoken_language_identification.dart Uses shared SLI config/export
flutter/sherpa_onnx/lib/src/speaker_identification_config.dart Splits speaker ID config into platform-agnostic file
flutter/sherpa_onnx/lib/src/speaker_identification.dart Uses shared speaker ID config/export
flutter/sherpa_onnx/lib/src/online_speech_denoiser_config.dart Splits online denoiser config file
flutter/sherpa_onnx/lib/src/online_speech_denoiser.dart Uses shared online denoiser config/export
flutter/sherpa_onnx/lib/src/online_punctuation_config.dart Splits online punctuation config file
flutter/sherpa_onnx/lib/src/online_punctuation.dart Uses shared online punctuation config/export
flutter/sherpa_onnx/lib/src/offline_speech_denoiser_config.dart Splits offline denoiser config/data file
flutter/sherpa_onnx/lib/src/offline_speech_denoiser.dart Uses shared offline denoiser config/export
flutter/sherpa_onnx/lib/src/offline_speaker_diarization_config.dart Splits diarization config/data file
flutter/sherpa_onnx/lib/src/offline_speaker_diarization.dart Uses shared diarization config/export
flutter/sherpa_onnx/lib/src/offline_punctuation_config.dart Splits offline punctuation config file
flutter/sherpa_onnx/lib/src/offline_punctuation.dart Uses shared offline punctuation config/export
flutter/sherpa_onnx/lib/src/keyword_spotter_config.dart Splits KWS config/data file
flutter/sherpa_onnx/lib/src/keyword_spotter.dart Uses shared KWS config/export
flutter/sherpa_onnx/lib/src/init_stub.dart Native stub for web loader symbol
flutter/sherpa_onnx/lib/src/init_native.dart Extracts native dylib loading into dedicated file
flutter/sherpa_onnx/lib/src/audio_tagging_config.dart Splits audio tagging config/data file
flutter/sherpa_onnx/lib/src/audio_tagging.dart Uses shared audio tagging config/export
flutter/sherpa_onnx/lib/sherpa_onnx.dart Adds web conditional exports + async init for web
flutter-examples/tts/web/tts-worker.js Adds Worker-based web TTS synthesis
flutter-examples/tts/web/index.html Web entrypoint for TTS example
flutter-examples/tts/pubspec.yaml Updates SDK constraints/deps for TTS demo
flutter-examples/tts/macos/Runner/Release.entitlements Adds user-selected file RW entitlement
flutter-examples/tts/macos/Runner/MainFlutterWindow.swift Sets fixed macOS window size
flutter-examples/tts/macos/Runner/DebugProfile.entitlements Adds user-selected file RW entitlement
flutter-examples/tts/macos/Runner/AppDelegate.swift Modernizes app entry + secure state
flutter-examples/tts/macos/Runner.xcodeproj/.../Runner.xcscheme Adds pre-build action + GPU validation
flutter-examples/tts/macos/Flutter/Flutter-Release.xcconfig Includes CocoaPods xcconfig (release)
flutter-examples/tts/macos/Flutter/Flutter-Debug.xcconfig Includes CocoaPods xcconfig (debug)
flutter-examples/tts/lib/worker_web.dart Dart-side wrapper around the web Worker
flutter-examples/tts/lib/worker_stub.dart Native stub for Worker controller
flutter-examples/tts/lib/web_audio_stub.dart Native stub for browser audio helpers
flutter-examples/tts/lib/web_audio.dart Browser audio + download helpers via JS eval
flutter-examples/tts/lib/wav_encoder.dart Cross-platform WAV encode/decode utility
flutter-examples/tts/lib/utils_web.dart Web-specific filename helper
flutter-examples/tts/lib/tts_controls.dart Extracted TTS UI controls widget
flutter-examples/tts/lib/tts.dart Removes old single-screen implementation
flutter-examples/tts/lib/save_file_stub.dart Web stub for native save dialog helper
flutter-examples/tts/lib/save_file.dart Native “Save As” helper via file_picker
flutter-examples/tts/lib/play_ref_stub.dart Web stub for reference audio playback helper
flutter-examples/tts/lib/play_ref.dart Native ref-audio playback helper
flutter-examples/tts/lib/play_bytes_stub.dart Web stub for WAV-byte playback helper
flutter-examples/tts/lib/play_bytes.dart Native WAV-byte playback helper
flutter-examples/tts/lib/model_web.dart Web asset-to-WASM-FS model loading helpers
flutter-examples/tts/lib/model_config.dart Centralized model selection/config
flutter-examples/tts/lib/main.dart Reworks app nav + adds Info screen
flutter-examples/tts/lib/isolate_tts.dart Removes isolate-based demo
flutter-examples/tts/lib/info.dart Removes old Info screen
flutter-examples/tts/lib/generated_audio.dart Introduces generated audio item + chunk types
flutter-examples/tts/lib/audio_list.dart Adds list UI for generated audio results
flutter-examples/tts/ios/Flutter/Release.xcconfig Includes CocoaPods xcconfig (release)
flutter-examples/tts/ios/Flutter/Debug.xcconfig Includes CocoaPods xcconfig (debug)
flutter-examples/tts/generate-asset-list.py Improves asset list generation robustness
flutter-examples/tts/README.md Adds web instructions + updates model selection docs
flutter-examples/tts/.gitignore Ignores additional build artifacts
flutter-examples/hello_world/macos/Flutter/Flutter-Release.xcconfig Includes CocoaPods xcconfig (release)
flutter-examples/hello_world/macos/Flutter/Flutter-Debug.xcconfig Includes CocoaPods xcconfig (debug)
flutter-examples/hello_world/lib/main.dart Uses initBindingsAsync() for web support
flutter-examples/hello_world/ios/Flutter/Release.xcconfig Includes CocoaPods xcconfig (release)
flutter-examples/hello_world/ios/Flutter/Debug.xcconfig Includes CocoaPods xcconfig (debug)
flutter-examples/hello_world/README.md Adds web run/build notes
build-wasm-simd-web.sh Adds Emscripten build script for browser WASM
build-flutter-web-wasm.sh Builds WASM and syncs assets into web plugin
CMakeLists.txt Adds SHERPA_ONNX_ENABLE_WASM_WEB option + checks
.gitignore Ignores additional model/asset directories
.github/workflows/test-flutter.yaml Adds web CI job (build WASM + Flutter web app)
.github/workflows/test-flutter-package.yaml Adds web CI job for package validation
.github/workflows/release-dart-package.yaml Publishes new sherpa_onnx_web package + artifacts
.github/workflows/flutter-windows-x64.yaml Disables some jobs via if: false
.github/workflows/flutter-macos.yaml Disables some jobs via if: false
.github/workflows/flutter-linux.yaml Disables some jobs via if: false
.github/workflows/flutter-android.yaml Disables some jobs via if: false
Files not reviewed (2)
  • flutter-examples/hello_world/macos/Runner.xcworkspace/contents.xcworkspacedata: Generated file
  • flutter-examples/tts/macos/Runner.xcworkspace/contents.xcworkspacedata: Generated file
Suppressed comments (9)

wasm/web/CMakeLists.txt:1

  • The -s flag value is wrapped in single quotes, which will be passed through to emcc literally and can break setting EXPORT_NAME. Use Emscripten’s expected form without embedded shell quotes (e.g., -sEXPORT_NAME=... or -s EXPORT_NAME=...) so the export name is actually applied.
    wasm/wasm-common.cmake:1
  • SherpaOnnxWriteWave is listed twice in exported_functions, which is redundant and can cause noisy linker/emscripten warnings or confusion when maintaining the export list. Remove the duplicate entry so each exported symbol appears only once.
    wasm/wasm-common.cmake:1
  • SherpaOnnxWriteWave is listed twice in exported_functions, which is redundant and can cause noisy linker/emscripten warnings or confusion when maintaining the export list. Remove the duplicate entry so each exported symbol appears only once.
    flutter/sherpa_onnx_web/pubspec.yaml:1
  • The Flutter SDK constraint (>=2.8.1) is far below what can actually work with Dart SDK >=3.4.0. To avoid misleading users, update the Flutter constraint to a version that ships with (or is compatible with) Dart 3.4 (e.g., align it with the repo’s documented minimum such as Flutter >= 3.24.0).
    flutter/sherpa_onnx_web/lib/sherpa_onnx_web.dart:1
  • _initialized only guards completed initialization. If loadWasm() is called concurrently (e.g., two callers await it at startup), both calls can still proceed and double-evaluate JS / double-instantiate the module. Consider caching an in-flight Future<void> (e.g., _initFuture ??= ...) so concurrent calls share the same initialization work.
    flutter/sherpa_onnx_web/lib/sherpa_onnx_web.dart:1
  • _initialized only guards completed initialization. If loadWasm() is called concurrently (e.g., two callers await it at startup), both calls can still proceed and double-evaluate JS / double-instantiate the module. Consider caching an in-flight Future<void> (e.g., _initFuture ??= ...) so concurrent calls share the same initialization work.
    .github/workflows/test-flutter.yaml:1
  • The upload path is incorrect after the mv web hello-world-web step. The zip is created at flutter-examples/hello_world/build/hello-world-web.zip, but the workflow tries to upload build/web/hello-world-web.zip (a directory that no longer exists). Update the path: to the actual zip location.
name: test-flutter

flutter-examples/tts/lib/save_file_stub.dart:1

  • This stub’s function signature does not match the native implementation (Future<String?> saveFileAs(String sourcePath, String suggestedName)). If used via conditional import, mismatched signatures will cause compile-time errors. Align the stub signature and nullability with the real function (and ideally throw UnsupportedError or implement a web download flow).
    flutter/sherpa_onnx/lib/src/web/vad.dart:1
  • These web stubs silently return default values / no-op (e.g., size => 0, get() => Float32List(0)), which can make failures hard to detect and lead to incorrect app behavior. For unsupported web functionality (per PR description), it’s safer to throw UnsupportedError in all methods/getters to fail fast and clearly.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +62 to +63
'${item.duration.toStringAsPrecision(3)}s | '
'RTF ${(item.elapsed / item.duration).toStringAsPrecision(3)}',
@csukuangfj
csukuangfj merged commit f737aeb into k2-fsa:master Aug 6, 2026
44 of 50 checks passed
@csukuangfj
csukuangfj deleted the dart-wasm branch August 6, 2026 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Help wanted] Support the Web platform for Flutter

2 participants