Fangjun Kuang
Committed by GitHub

Add Flutter GUI example for VAD with a microphone. (#905)

正在显示 44 个修改的文件 包含 2289 行增加115 行删除
  1 +name: flutter-linux
  2 +
  3 +on:
  4 + push:
  5 + branches:
  6 + - flutter
  7 + tags:
  8 + - '*'
  9 + paths:
  10 + - '.github/workflows/flutter-linux.yaml'
  11 + - 'CMakeLists.txt'
  12 + - 'cmake/**'
  13 + - 'sherpa-onnx/csrc/*'
  14 + - 'sherpa-onnx/c-api/*'
  15 + - 'sherpa-onnx/flutter/**'
  16 + pull_request:
  17 + branches:
  18 + - master
  19 + paths:
  20 + - '.github/workflows/flutter-linux.yaml'
  21 + - 'CMakeLists.txt'
  22 + - 'cmake/**'
  23 + - 'sherpa-onnx/csrc/*'
  24 + - 'sherpa-onnx/c-api/*'
  25 + - 'sherpa-onnx/flutter/**'
  26 +
  27 + workflow_dispatch:
  28 +
  29 +concurrency:
  30 + group: flutter-linux-${{ github.ref }}
  31 + cancel-in-progress: true
  32 +
  33 +jobs:
  34 + flutter_linux:
  35 + name: linux
  36 + runs-on: ${{ matrix.os }}
  37 + container: ubuntu:18.04
  38 + strategy:
  39 + fail-fast: false
  40 + matrix:
  41 + os: [ubuntu-latest]
  42 +
  43 + steps:
  44 + - uses: actions/checkout@v2
  45 + with:
  46 + fetch-depth: 0
  47 +
  48 + - name: Install deps
  49 + shell: bash
  50 + run: |
  51 + apt-get update -y
  52 + apt-get install -y build-essential jq git cmake
  53 + apt-get install -y curl
  54 +
  55 + - name: Setup Flutter SDK
  56 + uses: flutter-actions/setup-flutter@v3
  57 + with:
  58 + channel: stable
  59 + version: latest
  60 +
  61 + - name: Install ninja
  62 + shell: bash
  63 + run: |
  64 + apt-get install -y ninja-build
  65 +
  66 + - name: Display ninja version
  67 + shell: bash
  68 + run: |
  69 + ninja --version
  70 + ninja --help || true
  71 + which ninja
  72 +
  73 + - name: Display PWD
  74 + shell: bash
  75 + run: |
  76 + echo "pwd: $PWD"
  77 + ls -lh
  78 +
  79 + - name: Display machine info
  80 + shell: bash
  81 + run: |
  82 + uname -a
  83 +
  84 + - name: Display flutter info
  85 + shell: bash
  86 + run: |
  87 + which flutter
  88 + which dart
  89 +
  90 + flutter --version
  91 +
  92 + mkdir -p /__t/flutter-Linux-3.22.1-X64/flutter
  93 +
  94 + git config --global --add safe.directory /__t/flutter-Linux-3.22.1-X64/flutter
  95 +
  96 + flutter --version
  97 +
  98 + dart --version
  99 + flutter doctor
  100 +
  101 +
  102 + flutter doctor
  103 +
  104 + - name: Install libgtk-3-dev
  105 + shell: bash
  106 + run: |
  107 + apt install -y libgtk-3-dev tree clang pkg-config
  108 +
  109 + - name: Display flutter info (2)
  110 + shell: bash
  111 + run: |
  112 + which flutter
  113 + which dart
  114 +
  115 + flutter --version
  116 + dart --version
  117 + flutter doctor
  118 +
  119 + - name: Build sherpa-onnx
  120 + uses: addnab/docker-run-action@v3
  121 + with:
  122 + image: quay.io/pypa/manylinux2014_x86_64
  123 + options: |
  124 + --volume ${{ github.workspace }}/:/home/runner/work/sherpa-onnx/sherpa-onnx
  125 + shell: bash
  126 + run: |
  127 + uname -a
  128 + gcc --version
  129 + cmake --version
  130 + cat /etc/*release
  131 + id
  132 + pwd
  133 +
  134 + cd /home/runner/work/sherpa-onnx/sherpa-onnx
  135 +
  136 + mkdir build
  137 + cd build
  138 +
  139 + cmake \
  140 + -D BUILD_SHARED_LIBS=ON \
  141 + -DSHERPA_ONNX_ENABLE_PORTAUDIO=OFF \
  142 + -DSHERPA_ONNX_ENABLE_WEBSOCKET=OFF \
  143 + -DBUILD_ESPEAK_NG_EXE=OFF \
  144 + -DSHERPA_ONNX_ENABLE_BINARY=OFF \
  145 + -DCMAKE_INSTALL_PREFIX=./install \
  146 + ..
  147 +
  148 + make -j2
  149 + make install
  150 +
  151 + - name: Copy libs
  152 + shell: bash
  153 + run: |
  154 + cp -v build/install/lib/lib* ./sherpa-onnx/flutter/linux/
  155 +
  156 + echo "--------------------"
  157 +
  158 + ls -lh ./sherpa-onnx/flutter/linux/
  159 +
  160 + - name: Download model files
  161 + shell: bash
  162 + run: |
  163 + cd sherpa-onnx/flutter
  164 + cd example/assets
  165 +
  166 + curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx
  167 +
  168 + - name: Build flutter
  169 + shell: bash
  170 + run: |
  171 +
  172 + d=$PWD
  173 +
  174 + pushd sherpa-onnx/flutter
  175 + dart pub get
  176 +
  177 + cd example
  178 + flutter build linux
  179 +
  180 + ls -lh build/linux/x64/release/bundle/sherpa_onnx
  181 + file build/linux/x64/release/bundle/sherpa_onnx
  182 +
  183 + tree build/linux/x64/release/bundle/
  184 + cd build/linux/x64/release/
  185 +
  186 + dst=flutter_sherpa_onnx_linux_x64
  187 + mv bundle $dst
  188 +
  189 + pushd $dst/lib
  190 +
  191 + ls -lh
  192 + mv -v libonnxruntime.so libonnxruntime.so.1.17.1
  193 + mv -v libpiper_phonemize.so libpiper_phonemize.so.1
  194 + popd
  195 +
  196 + tar cjfv $dst.tar.bz2 $dst
  197 + ls -lh
  198 + mv $dst.tar.bz2 $d
  199 + popd
  200 +
  201 + ls -lh $dst.tar.bz2
  202 +
  203 + - uses: actions/upload-artifact@v2
  204 + with:
  205 + name: flutter-sherpa-onnx-linux-x64
  206 + path: ./*.tar.bz2
1 -name: flutter 1 +name: flutter-macos
2 2
3 on: 3 on:
4 push: 4 push:
5 branches: 5 branches:
6 - flutter 6 - flutter
7 - - flutter-sid-2  
8 tags: 7 tags:
9 - '*' 8 - '*'
10 paths: 9 paths:
11 - - '.github/workflows/flutter.yaml' 10 + - '.github/workflows/flutter-macos.yaml'
12 - 'CMakeLists.txt' 11 - 'CMakeLists.txt'
13 - 'cmake/**' 12 - 'cmake/**'
14 - 'sherpa-onnx/csrc/*' 13 - 'sherpa-onnx/csrc/*'
@@ -18,7 +17,7 @@ on: @@ -18,7 +17,7 @@ on:
18 branches: 17 branches:
19 - master 18 - master
20 paths: 19 paths:
21 - - '.github/workflows/flutter.yaml' 20 + - '.github/workflows/flutter-macos.yaml'
22 - 'CMakeLists.txt' 21 - 'CMakeLists.txt'
23 - 'cmake/**' 22 - 'cmake/**'
24 - 'sherpa-onnx/csrc/*' 23 - 'sherpa-onnx/csrc/*'
@@ -28,17 +27,18 @@ on: @@ -28,17 +27,18 @@ on:
28 workflow_dispatch: 27 workflow_dispatch:
29 28
30 concurrency: 29 concurrency:
31 - group: flutter-${{ github.ref }} 30 + group: flutter-macos-${{ github.ref }}
32 cancel-in-progress: true 31 cancel-in-progress: true
33 32
34 jobs: 33 jobs:
35 - flutter:  
36 - name: flutter 34 + flutter_macos:
  35 + name: macos ${{ matrix.arch }}
37 runs-on: ${{ matrix.os }} 36 runs-on: ${{ matrix.os }}
38 strategy: 37 strategy:
39 fail-fast: false 38 fail-fast: false
40 matrix: 39 matrix:
41 os: [macos-latest] 40 os: [macos-latest]
  41 + arch: [x86_64, arm64]
42 42
43 steps: 43 steps:
44 - uses: actions/checkout@v4 44 - uses: actions/checkout@v4
@@ -54,7 +54,7 @@ jobs: @@ -54,7 +54,7 @@ jobs:
54 - name: ccache 54 - name: ccache
55 uses: hendrikmuhs/ccache-action@v1.2 55 uses: hendrikmuhs/ccache-action@v1.2
56 with: 56 with:
57 - key: ${{ matrix.os }}-flutter 57 + key: ${{ matrix.os }}-flutter-${{ matrix.arch }}
58 58
59 - name: Display PWD 59 - name: Display PWD
60 shell: bash 60 shell: bash
@@ -67,6 +67,11 @@ jobs: @@ -67,6 +67,11 @@ jobs:
67 run: | 67 run: |
68 uname -a 68 uname -a
69 69
  70 + - name: Install tree
  71 + shell: bash
  72 + run: |
  73 + brew install tree
  74 +
70 - name: Display flutter info 75 - name: Display flutter info
71 shell: bash 76 shell: bash
72 run: | 77 run: |
@@ -83,20 +88,22 @@ jobs: @@ -83,20 +88,22 @@ jobs:
83 export CMAKE_CXX_COMPILER_LAUNCHER=ccache 88 export CMAKE_CXX_COMPILER_LAUNCHER=ccache
84 export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" 89 export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
85 cmake --version 90 cmake --version
  91 + arch=${{ matrix.arch }}
86 92
87 mkdir build 93 mkdir build
88 cd build 94 cd build
89 95
90 cmake \ 96 cmake \
91 -D BUILD_SHARED_LIBS=ON \ 97 -D BUILD_SHARED_LIBS=ON \
92 - -DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \ 98 + -DCMAKE_OSX_ARCHITECTURES=$arch \
93 -DSHERPA_ONNX_ENABLE_PORTAUDIO=OFF \ 99 -DSHERPA_ONNX_ENABLE_PORTAUDIO=OFF \
94 -DSHERPA_ONNX_ENABLE_WEBSOCKET=OFF \ 100 -DSHERPA_ONNX_ENABLE_WEBSOCKET=OFF \
95 - -DSHERPA_ONNX_ENABLE_BINARY=ON \ 101 + -DBUILD_ESPEAK_NG_EXE=OFF \
  102 + -DSHERPA_ONNX_ENABLE_BINARY=OFF \
96 -DCMAKE_INSTALL_PREFIX=./install \ 103 -DCMAKE_INSTALL_PREFIX=./install \
97 .. 104 ..
98 105
99 - - name: Build sherpa-onnx for macos 106 + - name: Build sherpa-onnx for macos ${{ matrix.arch }}
100 shell: bash 107 shell: bash
101 run: | 108 run: |
102 export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" 109 export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
@@ -105,7 +112,7 @@ jobs: @@ -105,7 +112,7 @@ jobs:
105 make -j2 112 make -j2
106 make install 113 make install
107 114
108 - rm -v ./install/lib/libcargs* 115 + rm -v ./install/lib/libonnxruntime.dylib
109 116
110 - name: Copy libs 117 - name: Copy libs
111 shell: bash 118 shell: bash
@@ -122,14 +129,13 @@ jobs: @@ -122,14 +129,13 @@ jobs:
122 cd sherpa-onnx/flutter 129 cd sherpa-onnx/flutter
123 pushd example/assets 130 pushd example/assets
124 131
125 - curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-recongition-models/3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx 132 + # curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-recongition-models/3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx
126 curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx 133 curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx
127 - git clone https://github.com/csukuangfj/sr-data 134 + # git clone https://github.com/csukuangfj/sr-data
128 135
129 rm -rf sr-data/.git 136 rm -rf sr-data/.git
130 popd 137 popd
131 138
132 -  
133 - name: Build flutter 139 - name: Build flutter
134 shell: bash 140 shell: bash
135 run: | 141 run: |
@@ -139,20 +145,25 @@ jobs: @@ -139,20 +145,25 @@ jobs:
139 dart pub get 145 dart pub get
140 146
141 cd example 147 cd example
  148 + export FLUTTER_XCODE_ARCHS=${{ matrix.arch }}
  149 + echo "FLUTTER_XCODE_ARCHS: $FLUTTER_XCODE_ARCHS"
142 flutter build macos 150 flutter build macos
143 151
144 - pushd build/macos/Build/Products/Release/  
145 - tar cjfv sherpa_onnx.app.tar.bz2 ./sherpa_onnx.app 152 + cd build/macos/Build/Products/Release/
  153 +
  154 + tree ./sherpa_onnx.app
  155 +
  156 + app=flutter_sherpa_onnx_macos_${{ matrix.arch }}.app
  157 + mv sherpa_onnx.app $app
  158 + tar cjfv $app.tar.bz2 $app
146 ls -lh 159 ls -lh
147 - mv sherpa_onnx.app.tar.bz2 $d  
148 - popd 160 + mv $app.tar.bz2 $d
149 161
150 popd 162 popd
151 163
152 - ls -lh sherpa_onnx.app.tar.bz2 164 + ls -lh $app.tar.bz2
153 165
154 - uses: actions/upload-artifact@v4 166 - uses: actions/upload-artifact@v4
155 - if: matrix.os == 'macos-latest'  
156 with: 167 with:
157 - name: flutter-sherpa-onnx-app-macos  
158 - path: ./sherpa_onnx.app.tar.bz2 168 + name: flutter-sherpa-onnx-app-macos-${{ matrix.arch }}
  169 + path: ./*.tar.bz2
  1 +name: flutter-windows-x64
  2 +
  3 +on:
  4 + push:
  5 + branches:
  6 + - flutter
  7 + tags:
  8 + - '*'
  9 + paths:
  10 + - '.github/workflows/flutter-windows-x64.yaml'
  11 + - 'CMakeLists.txt'
  12 + - 'cmake/**'
  13 + - 'sherpa-onnx/csrc/*'
  14 + - 'sherpa-onnx/c-api/*'
  15 + - 'sherpa-onnx/flutter/**'
  16 + pull_request:
  17 + branches:
  18 + - master
  19 + paths:
  20 + - '.github/workflows/flutter-windows-x64.yaml'
  21 + - 'CMakeLists.txt'
  22 + - 'cmake/**'
  23 + - 'sherpa-onnx/csrc/*'
  24 + - 'sherpa-onnx/c-api/*'
  25 + - 'sherpa-onnx/flutter/**'
  26 +
  27 + workflow_dispatch:
  28 +
  29 +concurrency:
  30 + group: flutter-windows-x64${{ github.ref }}
  31 + cancel-in-progress: true
  32 +
  33 +jobs:
  34 + flutter_windows_x64:
  35 + name: flutter windows x64
  36 + runs-on: ${{ matrix.os }}
  37 + strategy:
  38 + fail-fast: false
  39 + matrix:
  40 + os: [windows-latest]
  41 +
  42 + steps:
  43 + - uses: actions/checkout@v4
  44 + with:
  45 + fetch-depth: 0
  46 +
  47 + - name: Setup Flutter SDK
  48 + uses: flutter-actions/setup-flutter@v3
  49 + with:
  50 + channel: stable
  51 + version: latest
  52 +
  53 + - name: Display PWD
  54 + shell: bash
  55 + run: |
  56 + echo "pwd: $PWD"
  57 + ls -lh
  58 +
  59 + - name: Display machine info
  60 + shell: bash
  61 + run: |
  62 + uname -a
  63 +
  64 + - name: Display flutter info
  65 + shell: bash
  66 + run: |
  67 + which flutter
  68 + which dart
  69 +
  70 + flutter --version
  71 + dart --version
  72 + flutter doctor
  73 +
  74 + - name: Configure CMake
  75 + shell: bash
  76 + run: |
  77 + cmake --version
  78 +
  79 + mkdir build
  80 + cd build
  81 +
  82 + cmake \
  83 + -A x64 \
  84 + -DBUILD_SHARED_LIBS=ON \
  85 + -DSHERPA_ONNX_ENABLE_PORTAUDIO=OFF \
  86 + -DSHERPA_ONNX_ENABLE_WEBSOCKET=OFF \
  87 + -DBUILD_ESPEAK_NG_EXE=OFF \
  88 + -DSHERPA_ONNX_ENABLE_BINARY=OFF \
  89 + -DBUILD_ESPEAK_NG_EXE=OFF \
  90 + -DCMAKE_INSTALL_PREFIX=./install \
  91 + ..
  92 +
  93 + - name: Build sherpa-onnx for windows x64
  94 + shell: bash
  95 + run: |
  96 + cd build
  97 + cmake --build . --config Release -- -m:2
  98 + cmake --build . --config Release --target install -- -m:2
  99 +
  100 + - name: Copy libs
  101 + shell: bash
  102 + run: |
  103 + cp -v build/install/lib/*dll ./sherpa-onnx/flutter/windows/
  104 + cp -v build/install/lib/*fst* ./sherpa-onnx/flutter/windows/
  105 +
  106 + echo "--------------------"
  107 +
  108 + ls -lh ./sherpa-onnx/flutter/windows/
  109 +
  110 + - name: Download model files
  111 + shell: bash
  112 + run: |
  113 + cd sherpa-onnx/flutter
  114 +
  115 + cd example/assets
  116 +
  117 + curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx
  118 +
  119 + - name: Build flutter
  120 + shell: bash
  121 + run: |
  122 + d=$PWD
  123 +
  124 + pushd sherpa-onnx/flutter
  125 + dart pub get
  126 +
  127 + cd example
  128 + flutter build windows
  129 +
  130 + cd build/windows/x64/runner/
  131 +
  132 + dst=flutter_sherpa_onnx_windows_x64
  133 + mv Release $dst
  134 +
  135 + tar cjfv $dst.tar.bz2 ./$dst
  136 + ls -lh
  137 + mv $dst.tar.bz2 $d/
  138 +
  139 + popd
  140 +
  141 + ls -lh $dst.tar.bz2
  142 +
  143 + - uses: actions/upload-artifact@v4
  144 + with:
  145 + name: flutter-sherpa-onnx-windows-x64
  146 + path: ./*.tar.bz2
@@ -53,9 +53,9 @@ static OnlineRecognizerResult Convert(const OnlineParaformerDecoderResult &src, @@ -53,9 +53,9 @@ static OnlineRecognizerResult Convert(const OnlineParaformerDecoderResult &src,
53 mergeable = false; 53 mergeable = false;
54 54
55 if (i > 0) { 55 if (i > 0) {
56 - const uint8_t *p = reinterpret_cast<const uint8_t *>(  
57 - sym_table[src.tokens[i - 1]].c_str());  
58 - if (p[0] < 0x80) { 56 + const uint8_t p = reinterpret_cast<const uint8_t *>(
  57 + sym_table[src.tokens[i - 1]].c_str())[0];
  58 + if (p < 0x80) {
59 // put a space between ascii and non-ascii 59 // put a space between ascii and non-ascii
60 text.append(" "); 60 text.append(" ");
61 } 61 }
  1 +// Copyright (c) 2024 Xiaomi Corporation
  2 +import 'dart:async';
  3 +
  4 +import 'package:flutter/foundation.dart';
  5 +import 'package:flutter/material.dart';
  6 +import 'package:path/path.dart' as p;
  7 +import 'package:path_provider/path_provider.dart';
  8 +import 'package:record/record.dart';
  9 +
  10 +import 'package:sherpa_onnx/sherpa_onnx.dart' as sherpa_onnx;
  11 +
  12 +import './utils.dart';
  13 +
  14 +class HomeScreen extends StatefulWidget {
  15 + const HomeScreen({super.key});
  16 +
  17 + @override
  18 + State<HomeScreen> createState() => _HomeScreenState();
  19 +}
  20 +
  21 +class _HomeScreenState extends State<HomeScreen> {
  22 + late final AudioRecorder _audioRecorder;
  23 +
  24 + bool _printed = false;
  25 + var _color = Colors.black;
  26 + bool _isInitialized = false;
  27 +
  28 + sherpa_onnx.VoiceActivityDetector? _vad;
  29 + sherpa_onnx.CircularBuffer? _buffer;
  30 +
  31 + StreamSubscription<RecordState>? _recordSub;
  32 + RecordState _recordState = RecordState.stop;
  33 +
  34 + @override
  35 + void initState() {
  36 + _audioRecorder = AudioRecorder();
  37 +
  38 + _recordSub = _audioRecorder.onStateChanged().listen((recordState) {
  39 + _updateRecordState(recordState);
  40 + });
  41 +
  42 + super.initState();
  43 + }
  44 +
  45 + Future<void> _start() async {
  46 + if (!_isInitialized) {
  47 + sherpa_onnx.initBindings();
  48 + final src = 'assets/silero_vad.onnx';
  49 + final modelPath = await copyAssetFile(src: src, dst: 'silero_vad.onnx');
  50 +
  51 + final sileroVadConfig = sherpa_onnx.SileroVadModelConfig(
  52 + model: modelPath,
  53 + minSpeechDuration: 0.25,
  54 + minSilenceDuration: 0.5,
  55 + );
  56 +
  57 + final config = sherpa_onnx.VadModelConfig(
  58 + sileroVad: sileroVadConfig,
  59 + numThreads: 1,
  60 + debug: true,
  61 + );
  62 +
  63 + _vad = sherpa_onnx.VoiceActivityDetector(
  64 + config: config, bufferSizeInSeconds: 30);
  65 +
  66 + _buffer = sherpa_onnx.CircularBuffer(capacity: 16000 * 30);
  67 + print(_buffer!.ptr);
  68 +
  69 + _isInitialized = true;
  70 + }
  71 +
  72 + try {
  73 + if (await _audioRecorder.hasPermission()) {
  74 + const encoder = AudioEncoder.pcm16bits;
  75 +
  76 + if (!await _isEncoderSupported(encoder)) {
  77 + return;
  78 + }
  79 +
  80 + final devs = await _audioRecorder.listInputDevices();
  81 + debugPrint(devs.toString());
  82 +
  83 + const config = RecordConfig(
  84 + encoder: encoder,
  85 + sampleRate: 16000,
  86 + numChannels: 1,
  87 + );
  88 +
  89 + final stream = await _audioRecorder.startStream(config);
  90 +
  91 + final dir = await getApplicationDocumentsDirectory();
  92 +
  93 + stream.listen(
  94 + (data) {
  95 + final samplesFloat32 =
  96 + convertBytesToFloat32(Uint8List.fromList(data));
  97 +
  98 + _buffer!.push(samplesFloat32);
  99 +
  100 + final windowSize = _vad!.config.sileroVad.windowSize;
  101 + while (_buffer!.size > windowSize) {
  102 + final samples =
  103 + _buffer!.get(startIndex: _buffer!.head, n: windowSize);
  104 + _buffer!.pop(windowSize);
  105 + _vad!.acceptWaveform(samples);
  106 + if (_vad!.isDetected() && !_printed) {
  107 + print('detected');
  108 + _printed = true;
  109 +
  110 + setState(() => _color = Colors.red);
  111 + }
  112 +
  113 + if (!_vad!.isDetected()) {
  114 + _printed = false;
  115 + setState(() => _color = Colors.black);
  116 + }
  117 +
  118 + while (!_vad!.isEmpty()) {
  119 + final segment = _vad!.front();
  120 + final duration = segment.samples.length / 16000;
  121 + final d = DateTime.now();
  122 + final filename = p.join(dir.path,
  123 + '${d.year}-${d.month}-${d.day}-${d.hour}-${d.minute}-${d.second}-duration-${duration.toStringAsPrecision(3)}s.wav');
  124 +
  125 + bool ok = sherpa_onnx.writeWave(
  126 + filename: filename,
  127 + samples: segment.samples,
  128 + sampleRate: 16000);
  129 + if (!ok) {
  130 + print('Failed to write $filename');
  131 + } else {
  132 + print('Saved to write $filename');
  133 + }
  134 +
  135 + _vad!.pop();
  136 + }
  137 + }
  138 + },
  139 + onDone: () {
  140 + print('stream stopped.');
  141 + },
  142 + );
  143 + }
  144 + } catch (e) {
  145 + print(e);
  146 + }
  147 + }
  148 +
  149 + Future<void> _stop() async {
  150 + _buffer!.reset();
  151 + _vad!.clear();
  152 +
  153 + await _audioRecorder.stop();
  154 + }
  155 +
  156 + Future<void> _pause() => _audioRecorder.pause();
  157 +
  158 + Future<void> _resume() => _audioRecorder.resume();
  159 +
  160 + void _updateRecordState(RecordState recordState) {
  161 + setState(() => _recordState = recordState);
  162 + }
  163 +
  164 + Future<bool> _isEncoderSupported(AudioEncoder encoder) async {
  165 + final isSupported = await _audioRecorder.isEncoderSupported(
  166 + encoder,
  167 + );
  168 +
  169 + if (!isSupported) {
  170 + debugPrint('${encoder.name} is not supported on this platform.');
  171 + debugPrint('Supported encoders are:');
  172 +
  173 + for (final e in AudioEncoder.values) {
  174 + if (await _audioRecorder.isEncoderSupported(e)) {
  175 + debugPrint('- ${encoder.name}');
  176 + }
  177 + }
  178 + }
  179 +
  180 + return isSupported;
  181 + }
  182 +
  183 + @override
  184 + Widget build(BuildContext context) {
  185 + return MaterialApp(
  186 + home: Scaffold(
  187 + body: Column(
  188 + mainAxisAlignment: MainAxisAlignment.center,
  189 + children: [
  190 + Container(
  191 + width: 100.0,
  192 + height: 100.0,
  193 + decoration: BoxDecoration(
  194 + shape: BoxShape.circle,
  195 + color: _color,
  196 + ),
  197 + ),
  198 + const SizedBox(height: 50),
  199 + Row(
  200 + mainAxisAlignment: MainAxisAlignment.center,
  201 + children: <Widget>[
  202 + _buildRecordStopControl(),
  203 + const SizedBox(width: 20),
  204 + _buildText(),
  205 + ],
  206 + ),
  207 + ],
  208 + ),
  209 + ),
  210 + );
  211 + }
  212 +
  213 + @override
  214 + void dispose() {
  215 + _recordSub?.cancel();
  216 + _audioRecorder.dispose();
  217 + _vad?.free();
  218 + _buffer?.free();
  219 + super.dispose();
  220 + }
  221 +
  222 + Widget _buildRecordStopControl() {
  223 + late Icon icon;
  224 + late Color color;
  225 +
  226 + if (_recordState != RecordState.stop) {
  227 + icon = const Icon(Icons.stop, color: Colors.red, size: 30);
  228 + color = Colors.red.withOpacity(0.1);
  229 + } else {
  230 + final theme = Theme.of(context);
  231 + icon = Icon(Icons.mic, color: theme.primaryColor, size: 30);
  232 + color = theme.primaryColor.withOpacity(0.1);
  233 + }
  234 +
  235 + return ClipOval(
  236 + child: Material(
  237 + color: color,
  238 + child: InkWell(
  239 + child: SizedBox(width: 56, height: 56, child: icon),
  240 + onTap: () {
  241 + (_recordState != RecordState.stop) ? _stop() : _start();
  242 + },
  243 + ),
  244 + ),
  245 + );
  246 + }
  247 +
  248 + Widget _buildText() {
  249 + if (_recordState == RecordState.stop) {
  250 + return const Text("Start");
  251 + } else {
  252 + return const Text("Stop");
  253 + }
  254 + }
  255 +}
  1 +// Copyright (c) 2024 Xiaomi Corporation
  2 +import 'package:flutter/material.dart';
  3 +import 'package:url_launcher/url_launcher.dart';
  4 +
  5 +class InfoScreen extends StatelessWidget {
  6 + @override
  7 + Widget build(BuildContext context) {
  8 + const double height = 20;
  9 + return Container(
  10 + child: Padding(
  11 + padding: const EdgeInsets.all(8.0),
  12 + child: Column(
  13 + crossAxisAlignment: CrossAxisAlignment.start,
  14 + children: <Widget>[
  15 + Text('Everything is open-sourced.'),
  16 + SizedBox(height: height),
  17 + InkWell(
  18 + child: Text('Code: https://github.com/k2-fsa/sherpa-onnx'),
  19 + onTap: () => launch('https://k2-fsa.github.io/sherpa/onnx/'),
  20 + ),
  21 + SizedBox(height: height),
  22 + InkWell(
  23 + child: Text('Doc: https://k2-fsa.github.io/sherpa/onnx/'),
  24 + onTap: () => launch('https://k2-fsa.github.io/sherpa/onnx/'),
  25 + ),
  26 + SizedBox(height: height),
  27 + Text('QQ 群: 744602236'),
  28 + SizedBox(height: height),
  29 + InkWell(
  30 + child: Text(
  31 + '微信群: https://k2-fsa.github.io/sherpa/social-groups.html'),
  32 + onTap: () =>
  33 + launch('https://k2-fsa.github.io/sherpa/social-groups.html'),
  34 + ),
  35 + ],
  36 + ),
  37 + ),
  38 + );
  39 + }
  40 +}
@@ -4,6 +4,8 @@ import 'package:flutter/material.dart'; @@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
4 4
5 import "./speaker_identification_test.dart"; 5 import "./speaker_identification_test.dart";
6 import "./vad_test.dart"; 6 import "./vad_test.dart";
  7 +import './home.dart';
  8 +import './info.dart';
7 9
8 void main() { 10 void main() {
9 runApp(const MyApp()); 11 runApp(const MyApp());
@@ -11,110 +13,56 @@ void main() { @@ -11,110 +13,56 @@ void main() {
11 13
12 class MyApp extends StatelessWidget { 14 class MyApp extends StatelessWidget {
13 const MyApp({Key? key}) : super(key: key); 15 const MyApp({Key? key}) : super(key: key);
14 - // This widget is the root of your application.  
15 @override 16 @override
16 Widget build(BuildContext context) { 17 Widget build(BuildContext context) {
17 return MaterialApp( 18 return MaterialApp(
18 - title: 'Flutter Demo', 19 + title: 'Next-gen Kaldi',
19 theme: ThemeData( 20 theme: ThemeData(
20 - // This is the theme of your application.  
21 - //  
22 - // Try running your application with "flutter run". You'll see the  
23 - // application has a blue toolbar. Then, without quitting the app, try  
24 - // changing the primarySwatch below to Colors.green and then invoke  
25 - // "hot reload" (press "r" in the console where you ran "flutter run",  
26 - // or simply save your changes to "hot reload" in a Flutter IDE).  
27 - // Notice that the counter didn't reset back to zero; the application  
28 - // is not restarted.  
29 primarySwatch: Colors.blue, 21 primarySwatch: Colors.blue,
30 ), 22 ),
31 - home: const MyHomePage(title: 'Flutter Demo Home Page'), 23 + home: const MyHomePage(title: 'Next-gen Kaldi: VAD demo'),
32 ); 24 );
33 } 25 }
34 } 26 }
35 27
36 class MyHomePage extends StatefulWidget { 28 class MyHomePage extends StatefulWidget {
37 const MyHomePage({Key? key, required this.title}) : super(key: key); 29 const MyHomePage({Key? key, required this.title}) : super(key: key);
38 - // This widget is the home page of your application. It is stateful, meaning  
39 - // that it has a State object (defined below) that contains fields that affect  
40 - // how it looks.  
41 - // This class is the configuration for the state. It holds the values (in this  
42 - // case the title) provided by the parent (in this case the App widget) and  
43 - // used by the build method of the State. Fields in a Widget subclass are  
44 - // always marked "final".  
45 final String title; 30 final String title;
46 @override 31 @override
47 State<MyHomePage> createState() => _MyHomePageState(); 32 State<MyHomePage> createState() => _MyHomePageState();
48 } 33 }
49 34
50 class _MyHomePageState extends State<MyHomePage> { 35 class _MyHomePageState extends State<MyHomePage> {
51 - int _counter = 0;  
52 - Future<void> _incrementCounter() async {  
53 - if (_counter <= 10) {  
54 - sherpa_onnx.initBindings();  
55 - await testSpeakerID();  
56 - // await testVad();  
57 - }  
58 -  
59 - setState(() {  
60 - // This call to setState tells the Flutter framework that something has  
61 - // changed in this State, which causes it to rerun the build method below  
62 - // so that the display can reflect the updated values. If we changed  
63 - // _counter without calling setState(), then the build method would not be  
64 - // called again, and so nothing would appear to happen.  
65 - _counter++;  
66 - });  
67 - }  
68 - 36 + int _currentIndex = 0;
  37 + final List<Widget> _tabs = [
  38 + HomeScreen(),
  39 + InfoScreen(),
  40 + ];
69 @override 41 @override
70 Widget build(BuildContext context) { 42 Widget build(BuildContext context) {
71 - // This method is rerun every time setState is called, for instance as done  
72 - // by the _incrementCounter method above.  
73 - //  
74 - // The Flutter framework has been optimized to make rerunning build methods  
75 - // fast, so that you can just rebuild anything that needs updating rather  
76 - // than having to individually change instances of widgets.  
77 return Scaffold( 43 return Scaffold(
78 appBar: AppBar( 44 appBar: AppBar(
79 - // Here we take the value from the MyHomePage object that was created by  
80 - // the App.build method, and use it to set our appbar title.  
81 title: Text(widget.title), 45 title: Text(widget.title),
82 ), 46 ),
83 - body: Center(  
84 - // Center is a layout widget. It takes a single child and positions it  
85 - // in the middle of the parent.  
86 - child: Column(  
87 - // Column is also a layout widget. It takes a list of children and  
88 - // arranges them vertically. By default, it sizes itself to fit its  
89 - // children horizontally, and tries to be as tall as its parent.  
90 - //  
91 - // Invoke "debug painting" (press "p" in the console, choose the  
92 - // "Toggle Debug Paint" action from the Flutter Inspector in Android  
93 - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)  
94 - // to see the wireframe for each widget.  
95 - //  
96 - // Column has various properties to control how it sizes itself and  
97 - // how it positions its children. Here we use mainAxisAlignment to  
98 - // center the children vertically; the main axis here is the vertical  
99 - // axis because Columns are vertical (the cross axis would be  
100 - // horizontal).  
101 - mainAxisAlignment: MainAxisAlignment.center,  
102 - children: <Widget>[  
103 - const Text(  
104 - 'You have pushed the button this many times:',  
105 - ),  
106 - Text(  
107 - '$_counter',  
108 - style: Theme.of(context).textTheme.headlineSmall,  
109 - ),  
110 - ],  
111 - ), 47 + body: _tabs[_currentIndex],
  48 + bottomNavigationBar: BottomNavigationBar(
  49 + currentIndex: _currentIndex,
  50 + onTap: (int index) {
  51 + setState(() {
  52 + _currentIndex = index;
  53 + });
  54 + },
  55 + items: [
  56 + BottomNavigationBarItem(
  57 + icon: Icon(Icons.home),
  58 + label: 'Home',
  59 + ),
  60 + BottomNavigationBarItem(
  61 + icon: Icon(Icons.info),
  62 + label: 'Info',
  63 + ),
  64 + ],
112 ), 65 ),
113 - floatingActionButton: FloatingActionButton(  
114 - onPressed: _incrementCounter,  
115 - tooltip: 'Increment',  
116 - child: const Icon(Icons.add),  
117 - ), // This trailing comma makes auto-formatting nicer for build methods.  
118 ); 66 );
119 } 67 }
120 } 68 }
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 import 'package:path/path.dart'; 2 import 'package:path/path.dart';
3 import 'package:path_provider/path_provider.dart'; 3 import 'package:path_provider/path_provider.dart';
4 import 'package:flutter/services.dart' show rootBundle; 4 import 'package:flutter/services.dart' show rootBundle;
  5 +import 'dart:typed_data';
5 import "dart:io"; 6 import "dart:io";
6 7
7 // Copy the asset file from src to dst 8 // Copy the asset file from src to dst
@@ -16,3 +17,16 @@ Future<String> copyAssetFile({required String src, required String dst}) async { @@ -16,3 +17,16 @@ Future<String> copyAssetFile({required String src, required String dst}) async {
16 17
17 return target; 18 return target;
18 } 19 }
  20 +
  21 +Float32List convertBytesToFloat32(Uint8List bytes, [endian = Endian.little]) {
  22 + final values = Float32List(bytes.length ~/ 2);
  23 +
  24 + final data = ByteData.view(bytes.buffer);
  25 +
  26 + for (var i = 0; i < bytes.length; i += 2) {
  27 + int short = data.getInt16(i, endian);
  28 + values[i ~/ 2] = short / 32678.0;
  29 + }
  30 +
  31 + return values;
  32 +}
  1 +# Project-level configuration.
  2 +cmake_minimum_required(VERSION 3.10)
  3 +project(runner LANGUAGES CXX)
  4 +
  5 +# The name of the executable created for the application. Change this to change
  6 +# the on-disk name of your application.
  7 +set(BINARY_NAME "sherpa_onnx")
  8 +# The unique GTK application identifier for this application. See:
  9 +# https://wiki.gnome.org/HowDoI/ChooseApplicationID
  10 +set(APPLICATION_ID "com.k2fsa.sherpa.onnx")
  11 +
  12 +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
  13 +# versions of CMake.
  14 +cmake_policy(SET CMP0063 NEW)
  15 +
  16 +# Load bundled libraries from the lib/ directory relative to the binary.
  17 +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
  18 +
  19 +# Root filesystem for cross-building.
  20 +if(FLUTTER_TARGET_PLATFORM_SYSROOT)
  21 + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
  22 + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
  23 + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
  24 + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
  25 + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
  26 + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
  27 +endif()
  28 +
  29 +# Define build configuration options.
  30 +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  31 + set(CMAKE_BUILD_TYPE "Debug" CACHE
  32 + STRING "Flutter build mode" FORCE)
  33 + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
  34 + "Debug" "Profile" "Release")
  35 +endif()
  36 +
  37 +# Compilation settings that should be applied to most targets.
  38 +#
  39 +# Be cautious about adding new options here, as plugins use this function by
  40 +# default. In most cases, you should add new options to specific targets instead
  41 +# of modifying this function.
  42 +function(APPLY_STANDARD_SETTINGS TARGET)
  43 + target_compile_features(${TARGET} PUBLIC cxx_std_14)
  44 + target_compile_options(${TARGET} PRIVATE -Wall -Werror)
  45 + target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
  46 + target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
  47 +endfunction()
  48 +
  49 +# Flutter library and tool build rules.
  50 +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
  51 +add_subdirectory(${FLUTTER_MANAGED_DIR})
  52 +
  53 +# System-level dependencies.
  54 +find_package(PkgConfig REQUIRED)
  55 +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
  56 +
  57 +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
  58 +
  59 +# Define the application target. To change its name, change BINARY_NAME above,
  60 +# not the value here, or `flutter run` will no longer work.
  61 +#
  62 +# Any new source files that you add to the application should be added here.
  63 +add_executable(${BINARY_NAME}
  64 + "main.cc"
  65 + "my_application.cc"
  66 + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
  67 +)
  68 +
  69 +# Apply the standard set of build settings. This can be removed for applications
  70 +# that need different build settings.
  71 +apply_standard_settings(${BINARY_NAME})
  72 +
  73 +# Add dependency libraries. Add any application-specific dependencies here.
  74 +target_link_libraries(${BINARY_NAME} PRIVATE flutter)
  75 +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
  76 +
  77 +# Run the Flutter tool portions of the build. This must not be removed.
  78 +add_dependencies(${BINARY_NAME} flutter_assemble)
  79 +
  80 +# Only the install-generated bundle's copy of the executable will launch
  81 +# correctly, since the resources must in the right relative locations. To avoid
  82 +# people trying to run the unbundled copy, put it in a subdirectory instead of
  83 +# the default top-level location.
  84 +set_target_properties(${BINARY_NAME}
  85 + PROPERTIES
  86 + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
  87 +)
  88 +
  89 +
  90 +# Generated plugin build rules, which manage building the plugins and adding
  91 +# them to the application.
  92 +include(flutter/generated_plugins.cmake)
  93 +
  94 +
  95 +# === Installation ===
  96 +# By default, "installing" just makes a relocatable bundle in the build
  97 +# directory.
  98 +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
  99 +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
  100 + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
  101 +endif()
  102 +
  103 +# Start with a clean build bundle directory every time.
  104 +install(CODE "
  105 + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
  106 + " COMPONENT Runtime)
  107 +
  108 +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
  109 +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
  110 +
  111 +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
  112 + COMPONENT Runtime)
  113 +
  114 +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
  115 + COMPONENT Runtime)
  116 +
  117 +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
  118 + COMPONENT Runtime)
  119 +
  120 +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
  121 + install(FILES "${bundled_library}"
  122 + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
  123 + COMPONENT Runtime)
  124 +endforeach(bundled_library)
  125 +
  126 +# Copy the native assets provided by the build.dart from all packages.
  127 +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
  128 +install(DIRECTORY "${NATIVE_ASSETS_DIR}"
  129 + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
  130 + COMPONENT Runtime)
  131 +
  132 +# Fully re-copy the assets directory on each build to avoid having stale files
  133 +# from a previous install.
  134 +set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
  135 +install(CODE "
  136 + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
  137 + " COMPONENT Runtime)
  138 +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
  139 + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
  140 +
  141 +# Install the AOT library on non-Debug builds only.
  142 +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
  143 + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
  144 + COMPONENT Runtime)
  145 +endif()
  1 +# This file controls Flutter-level build steps. It should not be edited.
  2 +cmake_minimum_required(VERSION 3.10)
  3 +
  4 +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
  5 +
  6 +# Configuration provided via flutter tool.
  7 +include(${EPHEMERAL_DIR}/generated_config.cmake)
  8 +
  9 +# TODO: Move the rest of this into files in ephemeral. See
  10 +# https://github.com/flutter/flutter/issues/57146.
  11 +
  12 +# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
  13 +# which isn't available in 3.10.
  14 +function(list_prepend LIST_NAME PREFIX)
  15 + set(NEW_LIST "")
  16 + foreach(element ${${LIST_NAME}})
  17 + list(APPEND NEW_LIST "${PREFIX}${element}")
  18 + endforeach(element)
  19 + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
  20 +endfunction()
  21 +
  22 +# === Flutter Library ===
  23 +# System-level dependencies.
  24 +find_package(PkgConfig REQUIRED)
  25 +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
  26 +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
  27 +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
  28 +
  29 +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
  30 +
  31 +# Published to parent scope for install step.
  32 +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
  33 +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
  34 +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
  35 +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
  36 +
  37 +list(APPEND FLUTTER_LIBRARY_HEADERS
  38 + "fl_basic_message_channel.h"
  39 + "fl_binary_codec.h"
  40 + "fl_binary_messenger.h"
  41 + "fl_dart_project.h"
  42 + "fl_engine.h"
  43 + "fl_json_message_codec.h"
  44 + "fl_json_method_codec.h"
  45 + "fl_message_codec.h"
  46 + "fl_method_call.h"
  47 + "fl_method_channel.h"
  48 + "fl_method_codec.h"
  49 + "fl_method_response.h"
  50 + "fl_plugin_registrar.h"
  51 + "fl_plugin_registry.h"
  52 + "fl_standard_message_codec.h"
  53 + "fl_standard_method_codec.h"
  54 + "fl_string_codec.h"
  55 + "fl_value.h"
  56 + "fl_view.h"
  57 + "flutter_linux.h"
  58 +)
  59 +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
  60 +add_library(flutter INTERFACE)
  61 +target_include_directories(flutter INTERFACE
  62 + "${EPHEMERAL_DIR}"
  63 +)
  64 +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
  65 +target_link_libraries(flutter INTERFACE
  66 + PkgConfig::GTK
  67 + PkgConfig::GLIB
  68 + PkgConfig::GIO
  69 +)
  70 +add_dependencies(flutter flutter_assemble)
  71 +
  72 +# === Flutter tool backend ===
  73 +# _phony_ is a non-existent file to force this command to run every time,
  74 +# since currently there's no way to get a full input/output list from the
  75 +# flutter tool.
  76 +add_custom_command(
  77 + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
  78 + ${CMAKE_CURRENT_BINARY_DIR}/_phony_
  79 + COMMAND ${CMAKE_COMMAND} -E env
  80 + ${FLUTTER_TOOL_ENVIRONMENT}
  81 + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
  82 + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
  83 + VERBATIM
  84 +)
  85 +add_custom_target(flutter_assemble DEPENDS
  86 + "${FLUTTER_LIBRARY}"
  87 + ${FLUTTER_LIBRARY_HEADERS}
  88 +)
  1 +#include "my_application.h"
  2 +
  3 +int main(int argc, char** argv) {
  4 + g_autoptr(MyApplication) app = my_application_new();
  5 + return g_application_run(G_APPLICATION(app), argc, argv);
  6 +}
  1 +#include "my_application.h"
  2 +
  3 +#include <flutter_linux/flutter_linux.h>
  4 +#ifdef GDK_WINDOWING_X11
  5 +#include <gdk/gdkx.h>
  6 +#endif
  7 +
  8 +#include "flutter/generated_plugin_registrant.h"
  9 +
  10 +struct _MyApplication {
  11 + GtkApplication parent_instance;
  12 + char** dart_entrypoint_arguments;
  13 +};
  14 +
  15 +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
  16 +
  17 +// Implements GApplication::activate.
  18 +static void my_application_activate(GApplication* application) {
  19 + MyApplication* self = MY_APPLICATION(application);
  20 + GtkWindow* window =
  21 + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
  22 +
  23 + // Use a header bar when running in GNOME as this is the common style used
  24 + // by applications and is the setup most users will be using (e.g. Ubuntu
  25 + // desktop).
  26 + // If running on X and not using GNOME then just use a traditional title bar
  27 + // in case the window manager does more exotic layout, e.g. tiling.
  28 + // If running on Wayland assume the header bar will work (may need changing
  29 + // if future cases occur).
  30 + gboolean use_header_bar = TRUE;
  31 +#ifdef GDK_WINDOWING_X11
  32 + GdkScreen* screen = gtk_window_get_screen(window);
  33 + if (GDK_IS_X11_SCREEN(screen)) {
  34 + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
  35 + if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
  36 + use_header_bar = FALSE;
  37 + }
  38 + }
  39 +#endif
  40 + if (use_header_bar) {
  41 + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
  42 + gtk_widget_show(GTK_WIDGET(header_bar));
  43 + gtk_header_bar_set_title(header_bar, "sherpa_onnx");
  44 + gtk_header_bar_set_show_close_button(header_bar, TRUE);
  45 + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
  46 + } else {
  47 + gtk_window_set_title(window, "sherpa_onnx");
  48 + }
  49 +
  50 + gtk_window_set_default_size(window, 1280, 720);
  51 + gtk_widget_show(GTK_WIDGET(window));
  52 +
  53 + g_autoptr(FlDartProject) project = fl_dart_project_new();
  54 + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
  55 +
  56 + FlView* view = fl_view_new(project);
  57 + gtk_widget_show(GTK_WIDGET(view));
  58 + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
  59 +
  60 + fl_register_plugins(FL_PLUGIN_REGISTRY(view));
  61 +
  62 + gtk_widget_grab_focus(GTK_WIDGET(view));
  63 +}
  64 +
  65 +// Implements GApplication::local_command_line.
  66 +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
  67 + MyApplication* self = MY_APPLICATION(application);
  68 + // Strip out the first argument as it is the binary name.
  69 + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
  70 +
  71 + g_autoptr(GError) error = nullptr;
  72 + if (!g_application_register(application, nullptr, &error)) {
  73 + g_warning("Failed to register: %s", error->message);
  74 + *exit_status = 1;
  75 + return TRUE;
  76 + }
  77 +
  78 + g_application_activate(application);
  79 + *exit_status = 0;
  80 +
  81 + return TRUE;
  82 +}
  83 +
  84 +// Implements GApplication::startup.
  85 +static void my_application_startup(GApplication* application) {
  86 + //MyApplication* self = MY_APPLICATION(object);
  87 +
  88 + // Perform any actions required at application startup.
  89 +
  90 + G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
  91 +}
  92 +
  93 +// Implements GApplication::shutdown.
  94 +static void my_application_shutdown(GApplication* application) {
  95 + //MyApplication* self = MY_APPLICATION(object);
  96 +
  97 + // Perform any actions required at application shutdown.
  98 +
  99 + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
  100 +}
  101 +
  102 +// Implements GObject::dispose.
  103 +static void my_application_dispose(GObject* object) {
  104 + MyApplication* self = MY_APPLICATION(object);
  105 + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
  106 + G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
  107 +}
  108 +
  109 +static void my_application_class_init(MyApplicationClass* klass) {
  110 + G_APPLICATION_CLASS(klass)->activate = my_application_activate;
  111 + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
  112 + G_APPLICATION_CLASS(klass)->startup = my_application_startup;
  113 + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
  114 + G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
  115 +}
  116 +
  117 +static void my_application_init(MyApplication* self) {}
  118 +
  119 +MyApplication* my_application_new() {
  120 + return MY_APPLICATION(g_object_new(my_application_get_type(),
  121 + "application-id", APPLICATION_ID,
  122 + "flags", G_APPLICATION_NON_UNIQUE,
  123 + nullptr));
  124 +}
  1 +#ifndef FLUTTER_MY_APPLICATION_H_
  2 +#define FLUTTER_MY_APPLICATION_H_
  3 +
  4 +#include <gtk/gtk.h>
  5 +
  6 +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
  7 + GtkApplication)
  8 +
  9 +/**
  10 + * my_application_new:
  11 + *
  12 + * Creates a new Flutter-based application.
  13 + *
  14 + * Returns: a new #MyApplication.
  15 + */
  16 +MyApplication* my_application_new();
  17 +
  18 +#endif // FLUTTER_MY_APPLICATION_H_
1 -platform :osx, '10.14' 1 +platform :osx, '10.15'
2 2
3 # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 3 # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
4 ENV['COCOAPODS_DISABLE_STATS'] = 'true' 4 ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@@ -557,7 +557,7 @@ @@ -557,7 +557,7 @@
557 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 557 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
558 GCC_WARN_UNUSED_FUNCTION = YES; 558 GCC_WARN_UNUSED_FUNCTION = YES;
559 GCC_WARN_UNUSED_VARIABLE = YES; 559 GCC_WARN_UNUSED_VARIABLE = YES;
560 - MACOSX_DEPLOYMENT_TARGET = 10.14; 560 + MACOSX_DEPLOYMENT_TARGET = 10.15;
561 MTL_ENABLE_DEBUG_INFO = NO; 561 MTL_ENABLE_DEBUG_INFO = NO;
562 SDKROOT = macosx; 562 SDKROOT = macosx;
563 SWIFT_COMPILATION_MODE = wholemodule; 563 SWIFT_COMPILATION_MODE = wholemodule;
@@ -639,7 +639,7 @@ @@ -639,7 +639,7 @@
639 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 639 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
640 GCC_WARN_UNUSED_FUNCTION = YES; 640 GCC_WARN_UNUSED_FUNCTION = YES;
641 GCC_WARN_UNUSED_VARIABLE = YES; 641 GCC_WARN_UNUSED_VARIABLE = YES;
642 - MACOSX_DEPLOYMENT_TARGET = 10.14; 642 + MACOSX_DEPLOYMENT_TARGET = 10.15;
643 MTL_ENABLE_DEBUG_INFO = YES; 643 MTL_ENABLE_DEBUG_INFO = YES;
644 ONLY_ACTIVE_ARCH = YES; 644 ONLY_ACTIVE_ARCH = YES;
645 SDKROOT = macosx; 645 SDKROOT = macosx;
@@ -689,7 +689,7 @@ @@ -689,7 +689,7 @@
689 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 689 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
690 GCC_WARN_UNUSED_FUNCTION = YES; 690 GCC_WARN_UNUSED_FUNCTION = YES;
691 GCC_WARN_UNUSED_VARIABLE = YES; 691 GCC_WARN_UNUSED_VARIABLE = YES;
692 - MACOSX_DEPLOYMENT_TARGET = 10.14; 692 + MACOSX_DEPLOYMENT_TARGET = 10.15;
693 MTL_ENABLE_DEBUG_INFO = NO; 693 MTL_ENABLE_DEBUG_INFO = NO;
694 SDKROOT = macosx; 694 SDKROOT = macosx;
695 SWIFT_COMPILATION_MODE = wholemodule; 695 SWIFT_COMPILATION_MODE = wholemodule;
@@ -11,4 +11,4 @@ PRODUCT_NAME = sherpa_onnx @@ -11,4 +11,4 @@ PRODUCT_NAME = sherpa_onnx
11 PRODUCT_BUNDLE_IDENTIFIER = com.k2fsa.sherpa.onnx 11 PRODUCT_BUNDLE_IDENTIFIER = com.k2fsa.sherpa.onnx
12 12
13 // The copyright displayed in application information 13 // The copyright displayed in application information
14 -PRODUCT_COPYRIGHT = Copyright © 2024 com.k2fsa.sherpa.onnx. All rights reserved. 14 +PRODUCT_COPYRIGHT = Copyright © 2024 Next-gen Kaldi. All rights reserved.
@@ -6,6 +6,8 @@ @@ -6,6 +6,8 @@
6 <true/> 6 <true/>
7 <key>com.apple.security.cs.allow-jit</key> 7 <key>com.apple.security.cs.allow-jit</key>
8 <true/> 8 <true/>
  9 + <key>com.apple.security.device.audio-input</key>
  10 + <true/>
9 <key>com.apple.security.network.server</key> 11 <key>com.apple.security.network.server</key>
10 <true/> 12 <true/>
11 </dict> 13 </dict>
@@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
2 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 2 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 <plist version="1.0"> 3 <plist version="1.0">
4 <dict> 4 <dict>
  5 + <key>NSMicrophoneUsageDescription</key>
  6 + <string>Need microphone access for Next-gen kaldi to work</string>
5 <key>CFBundleDevelopmentRegion</key> 7 <key>CFBundleDevelopmentRegion</key>
6 <string>$(DEVELOPMENT_LANGUAGE)</string> 8 <string>$(DEVELOPMENT_LANGUAGE)</string>
7 <key>CFBundleExecutable</key> 9 <key>CFBundleExecutable</key>
@@ -4,5 +4,7 @@ @@ -4,5 +4,7 @@
4 <dict> 4 <dict>
5 <key>com.apple.security.app-sandbox</key> 5 <key>com.apple.security.app-sandbox</key>
6 <true/> 6 <true/>
  7 + <key>com.apple.security.device.audio-input</key>
  8 + <true/>
7 </dict> 9 </dict>
8 </plist> 10 </plist>
@@ -32,7 +32,9 @@ dependencies: @@ -32,7 +32,9 @@ dependencies:
32 sdk: flutter 32 sdk: flutter
33 33
34 path_provider: ^2.1.3 34 path_provider: ^2.1.3
35 - path: 35 + path: ^1.9.0
  36 + record: ^5.1.0
  37 + url_launcher: ^6.2.6
36 38
37 sherpa_onnx: 39 sherpa_onnx:
38 # When depending on this package from a real application you should use: 40 # When depending on this package from a real application you should use:
@@ -71,8 +73,8 @@ flutter: @@ -71,8 +73,8 @@ flutter:
71 # To add assets to your application, add an assets section, like this: 73 # To add assets to your application, add an assets section, like this:
72 assets: 74 assets:
73 - assets/ 75 - assets/
74 - - assets/sr-data/enroll/  
75 - - assets/sr-data/test/ 76 + # - assets/sr-data/enroll/
  77 + # - assets/sr-data/test/
76 # - images/a_dot_ham.jpeg 78 # - images/a_dot_ham.jpeg
77 79
78 # An image asset can refer to one or more resolution-specific "variants", see 80 # An image asset can refer to one or more resolution-specific "variants", see
  1 +flutter/ephemeral/
  2 +
  3 +# Visual Studio user-specific files.
  4 +*.suo
  5 +*.user
  6 +*.userosscache
  7 +*.sln.docstates
  8 +
  9 +# Visual Studio build-related files.
  10 +x64/
  11 +x86/
  12 +
  13 +# Visual Studio cache files
  14 +# files ending in .cache can be ignored
  15 +*.[Cc]ache
  16 +# but keep track of directories ending in .cache
  17 +!*.[Cc]ache/
  1 +# Project-level configuration.
  2 +cmake_minimum_required(VERSION 3.14)
  3 +project(sherpa_onnx LANGUAGES CXX)
  4 +
  5 +# The name of the executable created for the application. Change this to change
  6 +# the on-disk name of your application.
  7 +set(BINARY_NAME "sherpa_onnx")
  8 +
  9 +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
  10 +# versions of CMake.
  11 +cmake_policy(VERSION 3.14...3.25)
  12 +
  13 +# Define build configuration option.
  14 +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
  15 +if(IS_MULTICONFIG)
  16 + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
  17 + CACHE STRING "" FORCE)
  18 +else()
  19 + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  20 + set(CMAKE_BUILD_TYPE "Debug" CACHE
  21 + STRING "Flutter build mode" FORCE)
  22 + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
  23 + "Debug" "Profile" "Release")
  24 + endif()
  25 +endif()
  26 +# Define settings for the Profile build mode.
  27 +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
  28 +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
  29 +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
  30 +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
  31 +
  32 +# Use Unicode for all projects.
  33 +add_definitions(-DUNICODE -D_UNICODE)
  34 +
  35 +# Compilation settings that should be applied to most targets.
  36 +#
  37 +# Be cautious about adding new options here, as plugins use this function by
  38 +# default. In most cases, you should add new options to specific targets instead
  39 +# of modifying this function.
  40 +function(APPLY_STANDARD_SETTINGS TARGET)
  41 + target_compile_features(${TARGET} PUBLIC cxx_std_17)
  42 + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
  43 + target_compile_options(${TARGET} PRIVATE /EHsc)
  44 + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
  45 + target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
  46 +endfunction()
  47 +
  48 +# Flutter library and tool build rules.
  49 +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
  50 +add_subdirectory(${FLUTTER_MANAGED_DIR})
  51 +
  52 +# Application build; see runner/CMakeLists.txt.
  53 +add_subdirectory("runner")
  54 +
  55 +
  56 +# Generated plugin build rules, which manage building the plugins and adding
  57 +# them to the application.
  58 +include(flutter/generated_plugins.cmake)
  59 +
  60 +
  61 +# === Installation ===
  62 +# Support files are copied into place next to the executable, so that it can
  63 +# run in place. This is done instead of making a separate bundle (as on Linux)
  64 +# so that building and running from within Visual Studio will work.
  65 +set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
  66 +# Make the "install" step default, as it's required to run.
  67 +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
  68 +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
  69 + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
  70 +endif()
  71 +
  72 +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
  73 +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
  74 +
  75 +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
  76 + COMPONENT Runtime)
  77 +
  78 +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
  79 + COMPONENT Runtime)
  80 +
  81 +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
  82 + COMPONENT Runtime)
  83 +
  84 +if(PLUGIN_BUNDLED_LIBRARIES)
  85 + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
  86 + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
  87 + COMPONENT Runtime)
  88 +endif()
  89 +
  90 +# Copy the native assets provided by the build.dart from all packages.
  91 +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/")
  92 +install(DIRECTORY "${NATIVE_ASSETS_DIR}"
  93 + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
  94 + COMPONENT Runtime)
  95 +
  96 +# Fully re-copy the assets directory on each build to avoid having stale files
  97 +# from a previous install.
  98 +set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
  99 +install(CODE "
  100 + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
  101 + " COMPONENT Runtime)
  102 +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
  103 + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
  104 +
  105 +# Install the AOT library on non-Debug builds only.
  106 +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
  107 + CONFIGURATIONS Profile;Release
  108 + COMPONENT Runtime)
  1 +# This file controls Flutter-level build steps. It should not be edited.
  2 +cmake_minimum_required(VERSION 3.14)
  3 +
  4 +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
  5 +
  6 +# Configuration provided via flutter tool.
  7 +include(${EPHEMERAL_DIR}/generated_config.cmake)
  8 +
  9 +# TODO: Move the rest of this into files in ephemeral. See
  10 +# https://github.com/flutter/flutter/issues/57146.
  11 +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
  12 +
  13 +# Set fallback configurations for older versions of the flutter tool.
  14 +if (NOT DEFINED FLUTTER_TARGET_PLATFORM)
  15 + set(FLUTTER_TARGET_PLATFORM "windows-x64")
  16 +endif()
  17 +
  18 +# === Flutter Library ===
  19 +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
  20 +
  21 +# Published to parent scope for install step.
  22 +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
  23 +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
  24 +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
  25 +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
  26 +
  27 +list(APPEND FLUTTER_LIBRARY_HEADERS
  28 + "flutter_export.h"
  29 + "flutter_windows.h"
  30 + "flutter_messenger.h"
  31 + "flutter_plugin_registrar.h"
  32 + "flutter_texture_registrar.h"
  33 +)
  34 +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
  35 +add_library(flutter INTERFACE)
  36 +target_include_directories(flutter INTERFACE
  37 + "${EPHEMERAL_DIR}"
  38 +)
  39 +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
  40 +add_dependencies(flutter flutter_assemble)
  41 +
  42 +# === Wrapper ===
  43 +list(APPEND CPP_WRAPPER_SOURCES_CORE
  44 + "core_implementations.cc"
  45 + "standard_codec.cc"
  46 +)
  47 +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
  48 +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
  49 + "plugin_registrar.cc"
  50 +)
  51 +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
  52 +list(APPEND CPP_WRAPPER_SOURCES_APP
  53 + "flutter_engine.cc"
  54 + "flutter_view_controller.cc"
  55 +)
  56 +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
  57 +
  58 +# Wrapper sources needed for a plugin.
  59 +add_library(flutter_wrapper_plugin STATIC
  60 + ${CPP_WRAPPER_SOURCES_CORE}
  61 + ${CPP_WRAPPER_SOURCES_PLUGIN}
  62 +)
  63 +apply_standard_settings(flutter_wrapper_plugin)
  64 +set_target_properties(flutter_wrapper_plugin PROPERTIES
  65 + POSITION_INDEPENDENT_CODE ON)
  66 +set_target_properties(flutter_wrapper_plugin PROPERTIES
  67 + CXX_VISIBILITY_PRESET hidden)
  68 +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
  69 +target_include_directories(flutter_wrapper_plugin PUBLIC
  70 + "${WRAPPER_ROOT}/include"
  71 +)
  72 +add_dependencies(flutter_wrapper_plugin flutter_assemble)
  73 +
  74 +# Wrapper sources needed for the runner.
  75 +add_library(flutter_wrapper_app STATIC
  76 + ${CPP_WRAPPER_SOURCES_CORE}
  77 + ${CPP_WRAPPER_SOURCES_APP}
  78 +)
  79 +apply_standard_settings(flutter_wrapper_app)
  80 +target_link_libraries(flutter_wrapper_app PUBLIC flutter)
  81 +target_include_directories(flutter_wrapper_app PUBLIC
  82 + "${WRAPPER_ROOT}/include"
  83 +)
  84 +add_dependencies(flutter_wrapper_app flutter_assemble)
  85 +
  86 +# === Flutter tool backend ===
  87 +# _phony_ is a non-existent file to force this command to run every time,
  88 +# since currently there's no way to get a full input/output list from the
  89 +# flutter tool.
  90 +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
  91 +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
  92 +add_custom_command(
  93 + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
  94 + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
  95 + ${CPP_WRAPPER_SOURCES_APP}
  96 + ${PHONY_OUTPUT}
  97 + COMMAND ${CMAKE_COMMAND} -E env
  98 + ${FLUTTER_TOOL_ENVIRONMENT}
  99 + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
  100 + ${FLUTTER_TARGET_PLATFORM} $<CONFIG>
  101 + VERBATIM
  102 +)
  103 +add_custom_target(flutter_assemble DEPENDS
  104 + "${FLUTTER_LIBRARY}"
  105 + ${FLUTTER_LIBRARY_HEADERS}
  106 + ${CPP_WRAPPER_SOURCES_CORE}
  107 + ${CPP_WRAPPER_SOURCES_PLUGIN}
  108 + ${CPP_WRAPPER_SOURCES_APP}
  109 +)
  1 +cmake_minimum_required(VERSION 3.14)
  2 +project(runner LANGUAGES CXX)
  3 +
  4 +# Define the application target. To change its name, change BINARY_NAME in the
  5 +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
  6 +# work.
  7 +#
  8 +# Any new source files that you add to the application should be added here.
  9 +add_executable(${BINARY_NAME} WIN32
  10 + "flutter_window.cpp"
  11 + "main.cpp"
  12 + "utils.cpp"
  13 + "win32_window.cpp"
  14 + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
  15 + "Runner.rc"
  16 + "runner.exe.manifest"
  17 +)
  18 +
  19 +# Apply the standard set of build settings. This can be removed for applications
  20 +# that need different build settings.
  21 +apply_standard_settings(${BINARY_NAME})
  22 +
  23 +# Add preprocessor definitions for the build version.
  24 +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
  25 +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
  26 +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
  27 +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
  28 +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
  29 +
  30 +# Disable Windows macros that collide with C++ standard library functions.
  31 +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
  32 +
  33 +# Add dependency libraries and include directories. Add any application-specific
  34 +# dependencies here.
  35 +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
  36 +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib")
  37 +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
  38 +
  39 +# Run the Flutter tool portions of the build. This must not be removed.
  40 +add_dependencies(${BINARY_NAME} flutter_assemble)
  1 +// Microsoft Visual C++ generated resource script.
  2 +//
  3 +#pragma code_page(65001)
  4 +#include "resource.h"
  5 +
  6 +#define APSTUDIO_READONLY_SYMBOLS
  7 +/////////////////////////////////////////////////////////////////////////////
  8 +//
  9 +// Generated from the TEXTINCLUDE 2 resource.
  10 +//
  11 +#include "winres.h"
  12 +
  13 +/////////////////////////////////////////////////////////////////////////////
  14 +#undef APSTUDIO_READONLY_SYMBOLS
  15 +
  16 +/////////////////////////////////////////////////////////////////////////////
  17 +// English (United States) resources
  18 +
  19 +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
  20 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
  21 +
  22 +#ifdef APSTUDIO_INVOKED
  23 +/////////////////////////////////////////////////////////////////////////////
  24 +//
  25 +// TEXTINCLUDE
  26 +//
  27 +
  28 +1 TEXTINCLUDE
  29 +BEGIN
  30 + "resource.h\0"
  31 +END
  32 +
  33 +2 TEXTINCLUDE
  34 +BEGIN
  35 + "#include ""winres.h""\r\n"
  36 + "\0"
  37 +END
  38 +
  39 +3 TEXTINCLUDE
  40 +BEGIN
  41 + "\r\n"
  42 + "\0"
  43 +END
  44 +
  45 +#endif // APSTUDIO_INVOKED
  46 +
  47 +
  48 +/////////////////////////////////////////////////////////////////////////////
  49 +//
  50 +// Icon
  51 +//
  52 +
  53 +// Icon with lowest ID value placed first to ensure application icon
  54 +// remains consistent on all systems.
  55 +IDI_APP_ICON ICON "resources\\app_icon.ico"
  56 +
  57 +
  58 +/////////////////////////////////////////////////////////////////////////////
  59 +//
  60 +// Version
  61 +//
  62 +
  63 +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
  64 +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
  65 +#else
  66 +#define VERSION_AS_NUMBER 1,0,0,0
  67 +#endif
  68 +
  69 +#if defined(FLUTTER_VERSION)
  70 +#define VERSION_AS_STRING FLUTTER_VERSION
  71 +#else
  72 +#define VERSION_AS_STRING "1.0.0"
  73 +#endif
  74 +
  75 +VS_VERSION_INFO VERSIONINFO
  76 + FILEVERSION VERSION_AS_NUMBER
  77 + PRODUCTVERSION VERSION_AS_NUMBER
  78 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
  79 +#ifdef _DEBUG
  80 + FILEFLAGS VS_FF_DEBUG
  81 +#else
  82 + FILEFLAGS 0x0L
  83 +#endif
  84 + FILEOS VOS__WINDOWS32
  85 + FILETYPE VFT_APP
  86 + FILESUBTYPE 0x0L
  87 +BEGIN
  88 + BLOCK "StringFileInfo"
  89 + BEGIN
  90 + BLOCK "040904e4"
  91 + BEGIN
  92 + VALUE "CompanyName", "Next-gen Kaldi" "\0"
  93 + VALUE "FileDescription", "sherpa_onnx" "\0"
  94 + VALUE "FileVersion", VERSION_AS_STRING "\0"
  95 + VALUE "InternalName", "sherpa_onnx" "\0"
  96 + VALUE "LegalCopyright", "Copyright (C) 2024 Next-gen Kaldi. All rights reserved." "\0"
  97 + VALUE "OriginalFilename", "sherpa_onnx.exe" "\0"
  98 + VALUE "ProductName", "sherpa_onnx" "\0"
  99 + VALUE "ProductVersion", VERSION_AS_STRING "\0"
  100 + END
  101 + END
  102 + BLOCK "VarFileInfo"
  103 + BEGIN
  104 + VALUE "Translation", 0x409, 1252
  105 + END
  106 +END
  107 +
  108 +#endif // English (United States) resources
  109 +/////////////////////////////////////////////////////////////////////////////
  110 +
  111 +
  112 +
  113 +#ifndef APSTUDIO_INVOKED
  114 +/////////////////////////////////////////////////////////////////////////////
  115 +//
  116 +// Generated from the TEXTINCLUDE 3 resource.
  117 +//
  118 +
  119 +
  120 +/////////////////////////////////////////////////////////////////////////////
  121 +#endif // not APSTUDIO_INVOKED
  1 +#include "flutter_window.h"
  2 +
  3 +#include <optional>
  4 +
  5 +#include "flutter/generated_plugin_registrant.h"
  6 +
  7 +FlutterWindow::FlutterWindow(const flutter::DartProject& project)
  8 + : project_(project) {}
  9 +
  10 +FlutterWindow::~FlutterWindow() {}
  11 +
  12 +bool FlutterWindow::OnCreate() {
  13 + if (!Win32Window::OnCreate()) {
  14 + return false;
  15 + }
  16 +
  17 + RECT frame = GetClientArea();
  18 +
  19 + // The size here must match the window dimensions to avoid unnecessary surface
  20 + // creation / destruction in the startup path.
  21 + flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
  22 + frame.right - frame.left, frame.bottom - frame.top, project_);
  23 + // Ensure that basic setup of the controller was successful.
  24 + if (!flutter_controller_->engine() || !flutter_controller_->view()) {
  25 + return false;
  26 + }
  27 + RegisterPlugins(flutter_controller_->engine());
  28 + SetChildContent(flutter_controller_->view()->GetNativeWindow());
  29 +
  30 + flutter_controller_->engine()->SetNextFrameCallback([&]() {
  31 + this->Show();
  32 + });
  33 +
  34 + // Flutter can complete the first frame before the "show window" callback is
  35 + // registered. The following call ensures a frame is pending to ensure the
  36 + // window is shown. It is a no-op if the first frame hasn't completed yet.
  37 + flutter_controller_->ForceRedraw();
  38 +
  39 + return true;
  40 +}
  41 +
  42 +void FlutterWindow::OnDestroy() {
  43 + if (flutter_controller_) {
  44 + flutter_controller_ = nullptr;
  45 + }
  46 +
  47 + Win32Window::OnDestroy();
  48 +}
  49 +
  50 +LRESULT
  51 +FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
  52 + WPARAM const wparam,
  53 + LPARAM const lparam) noexcept {
  54 + // Give Flutter, including plugins, an opportunity to handle window messages.
  55 + if (flutter_controller_) {
  56 + std::optional<LRESULT> result =
  57 + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
  58 + lparam);
  59 + if (result) {
  60 + return *result;
  61 + }
  62 + }
  63 +
  64 + switch (message) {
  65 + case WM_FONTCHANGE:
  66 + flutter_controller_->engine()->ReloadSystemFonts();
  67 + break;
  68 + }
  69 +
  70 + return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
  71 +}
  1 +#ifndef RUNNER_FLUTTER_WINDOW_H_
  2 +#define RUNNER_FLUTTER_WINDOW_H_
  3 +
  4 +#include <flutter/dart_project.h>
  5 +#include <flutter/flutter_view_controller.h>
  6 +
  7 +#include <memory>
  8 +
  9 +#include "win32_window.h"
  10 +
  11 +// A window that does nothing but host a Flutter view.
  12 +class FlutterWindow : public Win32Window {
  13 + public:
  14 + // Creates a new FlutterWindow hosting a Flutter view running |project|.
  15 + explicit FlutterWindow(const flutter::DartProject& project);
  16 + virtual ~FlutterWindow();
  17 +
  18 + protected:
  19 + // Win32Window:
  20 + bool OnCreate() override;
  21 + void OnDestroy() override;
  22 + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
  23 + LPARAM const lparam) noexcept override;
  24 +
  25 + private:
  26 + // The project to run.
  27 + flutter::DartProject project_;
  28 +
  29 + // The Flutter instance hosted by this window.
  30 + std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
  31 +};
  32 +
  33 +#endif // RUNNER_FLUTTER_WINDOW_H_
  1 +#include <flutter/dart_project.h>
  2 +#include <flutter/flutter_view_controller.h>
  3 +#include <windows.h>
  4 +
  5 +#include "flutter_window.h"
  6 +#include "utils.h"
  7 +
  8 +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
  9 + _In_ wchar_t *command_line, _In_ int show_command) {
  10 + // Attach to console when present (e.g., 'flutter run') or create a
  11 + // new console when running with a debugger.
  12 + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
  13 + CreateAndAttachConsole();
  14 + }
  15 +
  16 + // Initialize COM, so that it is available for use in the library and/or
  17 + // plugins.
  18 + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
  19 +
  20 + flutter::DartProject project(L"data");
  21 +
  22 + std::vector<std::string> command_line_arguments =
  23 + GetCommandLineArguments();
  24 +
  25 + project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
  26 +
  27 + FlutterWindow window(project);
  28 + Win32Window::Point origin(10, 10);
  29 + Win32Window::Size size(1280, 720);
  30 + if (!window.Create(L"sherpa_onnx", origin, size)) {
  31 + return EXIT_FAILURE;
  32 + }
  33 + window.SetQuitOnClose(true);
  34 +
  35 + ::MSG msg;
  36 + while (::GetMessage(&msg, nullptr, 0, 0)) {
  37 + ::TranslateMessage(&msg);
  38 + ::DispatchMessage(&msg);
  39 + }
  40 +
  41 + ::CoUninitialize();
  42 + return EXIT_SUCCESS;
  43 +}
  1 +//{{NO_DEPENDENCIES}}
  2 +// Microsoft Visual C++ generated include file.
  3 +// Used by Runner.rc
  4 +//
  5 +#define IDI_APP_ICON 101
  6 +
  7 +// Next default values for new objects
  8 +//
  9 +#ifdef APSTUDIO_INVOKED
  10 +#ifndef APSTUDIO_READONLY_SYMBOLS
  11 +#define _APS_NEXT_RESOURCE_VALUE 102
  12 +#define _APS_NEXT_COMMAND_VALUE 40001
  13 +#define _APS_NEXT_CONTROL_VALUE 1001
  14 +#define _APS_NEXT_SYMED_VALUE 101
  15 +#endif
  16 +#endif
  1 +<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  2 +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  3 + <application xmlns="urn:schemas-microsoft-com:asm.v3">
  4 + <windowsSettings>
  5 + <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
  6 + </windowsSettings>
  7 + </application>
  8 + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
  9 + <application>
  10 + <!-- Windows 10 and Windows 11 -->
  11 + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
  12 + <!-- Windows 8.1 -->
  13 + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
  14 + <!-- Windows 8 -->
  15 + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
  16 + <!-- Windows 7 -->
  17 + <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
  18 + </application>
  19 + </compatibility>
  20 +</assembly>
  1 +#include "utils.h"
  2 +
  3 +#include <flutter_windows.h>
  4 +#include <io.h>
  5 +#include <stdio.h>
  6 +#include <windows.h>
  7 +
  8 +#include <iostream>
  9 +
  10 +void CreateAndAttachConsole() {
  11 + if (::AllocConsole()) {
  12 + FILE *unused;
  13 + if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
  14 + _dup2(_fileno(stdout), 1);
  15 + }
  16 + if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
  17 + _dup2(_fileno(stdout), 2);
  18 + }
  19 + std::ios::sync_with_stdio();
  20 + FlutterDesktopResyncOutputStreams();
  21 + }
  22 +}
  23 +
  24 +std::vector<std::string> GetCommandLineArguments() {
  25 + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
  26 + int argc;
  27 + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
  28 + if (argv == nullptr) {
  29 + return std::vector<std::string>();
  30 + }
  31 +
  32 + std::vector<std::string> command_line_arguments;
  33 +
  34 + // Skip the first argument as it's the binary name.
  35 + for (int i = 1; i < argc; i++) {
  36 + command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
  37 + }
  38 +
  39 + ::LocalFree(argv);
  40 +
  41 + return command_line_arguments;
  42 +}
  43 +
  44 +std::string Utf8FromUtf16(const wchar_t* utf16_string) {
  45 + if (utf16_string == nullptr) {
  46 + return std::string();
  47 + }
  48 + unsigned int target_length = ::WideCharToMultiByte(
  49 + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
  50 + -1, nullptr, 0, nullptr, nullptr)
  51 + -1; // remove the trailing null character
  52 + int input_length = (int)wcslen(utf16_string);
  53 + std::string utf8_string;
  54 + if (target_length == 0 || target_length > utf8_string.max_size()) {
  55 + return utf8_string;
  56 + }
  57 + utf8_string.resize(target_length);
  58 + int converted_length = ::WideCharToMultiByte(
  59 + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
  60 + input_length, utf8_string.data(), target_length, nullptr, nullptr);
  61 + if (converted_length == 0) {
  62 + return std::string();
  63 + }
  64 + return utf8_string;
  65 +}
  1 +#ifndef RUNNER_UTILS_H_
  2 +#define RUNNER_UTILS_H_
  3 +
  4 +#include <string>
  5 +#include <vector>
  6 +
  7 +// Creates a console for the process, and redirects stdout and stderr to
  8 +// it for both the runner and the Flutter library.
  9 +void CreateAndAttachConsole();
  10 +
  11 +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
  12 +// encoded in UTF-8. Returns an empty std::string on failure.
  13 +std::string Utf8FromUtf16(const wchar_t* utf16_string);
  14 +
  15 +// Gets the command line arguments passed in as a std::vector<std::string>,
  16 +// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
  17 +std::vector<std::string> GetCommandLineArguments();
  18 +
  19 +#endif // RUNNER_UTILS_H_
  1 +#include "win32_window.h"
  2 +
  3 +#include <dwmapi.h>
  4 +#include <flutter_windows.h>
  5 +
  6 +#include "resource.h"
  7 +
  8 +namespace {
  9 +
  10 +/// Window attribute that enables dark mode window decorations.
  11 +///
  12 +/// Redefined in case the developer's machine has a Windows SDK older than
  13 +/// version 10.0.22000.0.
  14 +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
  15 +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
  16 +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
  17 +#endif
  18 +
  19 +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
  20 +
  21 +/// Registry key for app theme preference.
  22 +///
  23 +/// A value of 0 indicates apps should use dark mode. A non-zero or missing
  24 +/// value indicates apps should use light mode.
  25 +constexpr const wchar_t kGetPreferredBrightnessRegKey[] =
  26 + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
  27 +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme";
  28 +
  29 +// The number of Win32Window objects that currently exist.
  30 +static int g_active_window_count = 0;
  31 +
  32 +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
  33 +
  34 +// Scale helper to convert logical scaler values to physical using passed in
  35 +// scale factor
  36 +int Scale(int source, double scale_factor) {
  37 + return static_cast<int>(source * scale_factor);
  38 +}
  39 +
  40 +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
  41 +// This API is only needed for PerMonitor V1 awareness mode.
  42 +void EnableFullDpiSupportIfAvailable(HWND hwnd) {
  43 + HMODULE user32_module = LoadLibraryA("User32.dll");
  44 + if (!user32_module) {
  45 + return;
  46 + }
  47 + auto enable_non_client_dpi_scaling =
  48 + reinterpret_cast<EnableNonClientDpiScaling*>(
  49 + GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
  50 + if (enable_non_client_dpi_scaling != nullptr) {
  51 + enable_non_client_dpi_scaling(hwnd);
  52 + }
  53 + FreeLibrary(user32_module);
  54 +}
  55 +
  56 +} // namespace
  57 +
  58 +// Manages the Win32Window's window class registration.
  59 +class WindowClassRegistrar {
  60 + public:
  61 + ~WindowClassRegistrar() = default;
  62 +
  63 + // Returns the singleton registrar instance.
  64 + static WindowClassRegistrar* GetInstance() {
  65 + if (!instance_) {
  66 + instance_ = new WindowClassRegistrar();
  67 + }
  68 + return instance_;
  69 + }
  70 +
  71 + // Returns the name of the window class, registering the class if it hasn't
  72 + // previously been registered.
  73 + const wchar_t* GetWindowClass();
  74 +
  75 + // Unregisters the window class. Should only be called if there are no
  76 + // instances of the window.
  77 + void UnregisterWindowClass();
  78 +
  79 + private:
  80 + WindowClassRegistrar() = default;
  81 +
  82 + static WindowClassRegistrar* instance_;
  83 +
  84 + bool class_registered_ = false;
  85 +};
  86 +
  87 +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
  88 +
  89 +const wchar_t* WindowClassRegistrar::GetWindowClass() {
  90 + if (!class_registered_) {
  91 + WNDCLASS window_class{};
  92 + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
  93 + window_class.lpszClassName = kWindowClassName;
  94 + window_class.style = CS_HREDRAW | CS_VREDRAW;
  95 + window_class.cbClsExtra = 0;
  96 + window_class.cbWndExtra = 0;
  97 + window_class.hInstance = GetModuleHandle(nullptr);
  98 + window_class.hIcon =
  99 + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
  100 + window_class.hbrBackground = 0;
  101 + window_class.lpszMenuName = nullptr;
  102 + window_class.lpfnWndProc = Win32Window::WndProc;
  103 + RegisterClass(&window_class);
  104 + class_registered_ = true;
  105 + }
  106 + return kWindowClassName;
  107 +}
  108 +
  109 +void WindowClassRegistrar::UnregisterWindowClass() {
  110 + UnregisterClass(kWindowClassName, nullptr);
  111 + class_registered_ = false;
  112 +}
  113 +
  114 +Win32Window::Win32Window() {
  115 + ++g_active_window_count;
  116 +}
  117 +
  118 +Win32Window::~Win32Window() {
  119 + --g_active_window_count;
  120 + Destroy();
  121 +}
  122 +
  123 +bool Win32Window::Create(const std::wstring& title,
  124 + const Point& origin,
  125 + const Size& size) {
  126 + Destroy();
  127 +
  128 + const wchar_t* window_class =
  129 + WindowClassRegistrar::GetInstance()->GetWindowClass();
  130 +
  131 + const POINT target_point = {static_cast<LONG>(origin.x),
  132 + static_cast<LONG>(origin.y)};
  133 + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
  134 + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
  135 + double scale_factor = dpi / 96.0;
  136 +
  137 + HWND window = CreateWindow(
  138 + window_class, title.c_str(), WS_OVERLAPPEDWINDOW,
  139 + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
  140 + Scale(size.width, scale_factor), Scale(size.height, scale_factor),
  141 + nullptr, nullptr, GetModuleHandle(nullptr), this);
  142 +
  143 + if (!window) {
  144 + return false;
  145 + }
  146 +
  147 + UpdateTheme(window);
  148 +
  149 + return OnCreate();
  150 +}
  151 +
  152 +bool Win32Window::Show() {
  153 + return ShowWindow(window_handle_, SW_SHOWNORMAL);
  154 +}
  155 +
  156 +// static
  157 +LRESULT CALLBACK Win32Window::WndProc(HWND const window,
  158 + UINT const message,
  159 + WPARAM const wparam,
  160 + LPARAM const lparam) noexcept {
  161 + if (message == WM_NCCREATE) {
  162 + auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
  163 + SetWindowLongPtr(window, GWLP_USERDATA,
  164 + reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
  165 +
  166 + auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
  167 + EnableFullDpiSupportIfAvailable(window);
  168 + that->window_handle_ = window;
  169 + } else if (Win32Window* that = GetThisFromHandle(window)) {
  170 + return that->MessageHandler(window, message, wparam, lparam);
  171 + }
  172 +
  173 + return DefWindowProc(window, message, wparam, lparam);
  174 +}
  175 +
  176 +LRESULT
  177 +Win32Window::MessageHandler(HWND hwnd,
  178 + UINT const message,
  179 + WPARAM const wparam,
  180 + LPARAM const lparam) noexcept {
  181 + switch (message) {
  182 + case WM_DESTROY:
  183 + window_handle_ = nullptr;
  184 + Destroy();
  185 + if (quit_on_close_) {
  186 + PostQuitMessage(0);
  187 + }
  188 + return 0;
  189 +
  190 + case WM_DPICHANGED: {
  191 + auto newRectSize = reinterpret_cast<RECT*>(lparam);
  192 + LONG newWidth = newRectSize->right - newRectSize->left;
  193 + LONG newHeight = newRectSize->bottom - newRectSize->top;
  194 +
  195 + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
  196 + newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
  197 +
  198 + return 0;
  199 + }
  200 + case WM_SIZE: {
  201 + RECT rect = GetClientArea();
  202 + if (child_content_ != nullptr) {
  203 + // Size and position the child window.
  204 + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
  205 + rect.bottom - rect.top, TRUE);
  206 + }
  207 + return 0;
  208 + }
  209 +
  210 + case WM_ACTIVATE:
  211 + if (child_content_ != nullptr) {
  212 + SetFocus(child_content_);
  213 + }
  214 + return 0;
  215 +
  216 + case WM_DWMCOLORIZATIONCOLORCHANGED:
  217 + UpdateTheme(hwnd);
  218 + return 0;
  219 + }
  220 +
  221 + return DefWindowProc(window_handle_, message, wparam, lparam);
  222 +}
  223 +
  224 +void Win32Window::Destroy() {
  225 + OnDestroy();
  226 +
  227 + if (window_handle_) {
  228 + DestroyWindow(window_handle_);
  229 + window_handle_ = nullptr;
  230 + }
  231 + if (g_active_window_count == 0) {
  232 + WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
  233 + }
  234 +}
  235 +
  236 +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
  237 + return reinterpret_cast<Win32Window*>(
  238 + GetWindowLongPtr(window, GWLP_USERDATA));
  239 +}
  240 +
  241 +void Win32Window::SetChildContent(HWND content) {
  242 + child_content_ = content;
  243 + SetParent(content, window_handle_);
  244 + RECT frame = GetClientArea();
  245 +
  246 + MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
  247 + frame.bottom - frame.top, true);
  248 +
  249 + SetFocus(child_content_);
  250 +}
  251 +
  252 +RECT Win32Window::GetClientArea() {
  253 + RECT frame;
  254 + GetClientRect(window_handle_, &frame);
  255 + return frame;
  256 +}
  257 +
  258 +HWND Win32Window::GetHandle() {
  259 + return window_handle_;
  260 +}
  261 +
  262 +void Win32Window::SetQuitOnClose(bool quit_on_close) {
  263 + quit_on_close_ = quit_on_close;
  264 +}
  265 +
  266 +bool Win32Window::OnCreate() {
  267 + // No-op; provided for subclasses.
  268 + return true;
  269 +}
  270 +
  271 +void Win32Window::OnDestroy() {
  272 + // No-op; provided for subclasses.
  273 +}
  274 +
  275 +void Win32Window::UpdateTheme(HWND const window) {
  276 + DWORD light_mode;
  277 + DWORD light_mode_size = sizeof(light_mode);
  278 + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
  279 + kGetPreferredBrightnessRegValue,
  280 + RRF_RT_REG_DWORD, nullptr, &light_mode,
  281 + &light_mode_size);
  282 +
  283 + if (result == ERROR_SUCCESS) {
  284 + BOOL enable_dark_mode = light_mode == 0;
  285 + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,
  286 + &enable_dark_mode, sizeof(enable_dark_mode));
  287 + }
  288 +}
  1 +#ifndef RUNNER_WIN32_WINDOW_H_
  2 +#define RUNNER_WIN32_WINDOW_H_
  3 +
  4 +#include <windows.h>
  5 +
  6 +#include <functional>
  7 +#include <memory>
  8 +#include <string>
  9 +
  10 +// A class abstraction for a high DPI-aware Win32 Window. Intended to be
  11 +// inherited from by classes that wish to specialize with custom
  12 +// rendering and input handling
  13 +class Win32Window {
  14 + public:
  15 + struct Point {
  16 + unsigned int x;
  17 + unsigned int y;
  18 + Point(unsigned int x, unsigned int y) : x(x), y(y) {}
  19 + };
  20 +
  21 + struct Size {
  22 + unsigned int width;
  23 + unsigned int height;
  24 + Size(unsigned int width, unsigned int height)
  25 + : width(width), height(height) {}
  26 + };
  27 +
  28 + Win32Window();
  29 + virtual ~Win32Window();
  30 +
  31 + // Creates a win32 window with |title| that is positioned and sized using
  32 + // |origin| and |size|. New windows are created on the default monitor. Window
  33 + // sizes are specified to the OS in physical pixels, hence to ensure a
  34 + // consistent size this function will scale the inputted width and height as
  35 + // as appropriate for the default monitor. The window is invisible until
  36 + // |Show| is called. Returns true if the window was created successfully.
  37 + bool Create(const std::wstring& title, const Point& origin, const Size& size);
  38 +
  39 + // Show the current window. Returns true if the window was successfully shown.
  40 + bool Show();
  41 +
  42 + // Release OS resources associated with window.
  43 + void Destroy();
  44 +
  45 + // Inserts |content| into the window tree.
  46 + void SetChildContent(HWND content);
  47 +
  48 + // Returns the backing Window handle to enable clients to set icon and other
  49 + // window properties. Returns nullptr if the window has been destroyed.
  50 + HWND GetHandle();
  51 +
  52 + // If true, closing this window will quit the application.
  53 + void SetQuitOnClose(bool quit_on_close);
  54 +
  55 + // Return a RECT representing the bounds of the current client area.
  56 + RECT GetClientArea();
  57 +
  58 + protected:
  59 + // Processes and route salient window messages for mouse handling,
  60 + // size change and DPI. Delegates handling of these to member overloads that
  61 + // inheriting classes can handle.
  62 + virtual LRESULT MessageHandler(HWND window,
  63 + UINT const message,
  64 + WPARAM const wparam,
  65 + LPARAM const lparam) noexcept;
  66 +
  67 + // Called when CreateAndShow is called, allowing subclass window-related
  68 + // setup. Subclasses should return false if setup fails.
  69 + virtual bool OnCreate();
  70 +
  71 + // Called when Destroy is called.
  72 + virtual void OnDestroy();
  73 +
  74 + private:
  75 + friend class WindowClassRegistrar;
  76 +
  77 + // OS callback called by message pump. Handles the WM_NCCREATE message which
  78 + // is passed when the non-client area is being created and enables automatic
  79 + // non-client DPI scaling so that the non-client area automatically
  80 + // responds to changes in DPI. All other messages are handled by
  81 + // MessageHandler.
  82 + static LRESULT CALLBACK WndProc(HWND const window,
  83 + UINT const message,
  84 + WPARAM const wparam,
  85 + LPARAM const lparam) noexcept;
  86 +
  87 + // Retrieves a class instance pointer for |window|
  88 + static Win32Window* GetThisFromHandle(HWND const window) noexcept;
  89 +
  90 + // Update the window frame's theme to match the system theme.
  91 + static void UpdateTheme(HWND const window);
  92 +
  93 + bool quit_on_close_ = false;
  94 +
  95 + // window handle for top level window.
  96 + HWND window_handle_ = nullptr;
  97 +
  98 + // window handle for hosted content.
  99 + HWND child_content_ = nullptr;
  100 +};
  101 +
  102 +#endif // RUNNER_WIN32_WINDOW_H_
@@ -6,6 +6,7 @@ export 'src/online_stream.dart'; @@ -6,6 +6,7 @@ export 'src/online_stream.dart';
6 export 'src/speaker_identification.dart'; 6 export 'src/speaker_identification.dart';
7 export 'src/vad.dart'; 7 export 'src/vad.dart';
8 export 'src/wave_reader.dart'; 8 export 'src/wave_reader.dart';
  9 +export 'src/wave_writer.dart';
9 import 'src/sherpa_onnx_bindings.dart'; 10 import 'src/sherpa_onnx_bindings.dart';
10 11
11 final DynamicLibrary _dylib = () { 12 final DynamicLibrary _dylib = () {
@@ -345,6 +345,12 @@ typedef SherpaOnnxReadWaveNative = Pointer<SherpaOnnxWave> Function( @@ -345,6 +345,12 @@ typedef SherpaOnnxReadWaveNative = Pointer<SherpaOnnxWave> Function(
345 345
346 typedef SherpaOnnxReadWave = SherpaOnnxReadWaveNative; 346 typedef SherpaOnnxReadWave = SherpaOnnxReadWaveNative;
347 347
  348 +typedef SherpaOnnxWriteWaveNative = Int32 Function(
  349 + Pointer<Float>, Int32, Int32, Pointer<Utf8>);
  350 +
  351 +typedef SherpaOnnxWriteWave = int Function(
  352 + Pointer<Float>, int, int, Pointer<Utf8>);
  353 +
348 typedef SherpaOnnxFreeWaveNative = Void Function(Pointer<SherpaOnnxWave>); 354 typedef SherpaOnnxFreeWaveNative = Void Function(Pointer<SherpaOnnxWave>);
349 355
350 typedef SherpaOnnxFreeWave = void Function(Pointer<SherpaOnnxWave>); 356 typedef SherpaOnnxFreeWave = void Function(Pointer<SherpaOnnxWave>);
@@ -448,6 +454,8 @@ class SherpaOnnxBindings { @@ -448,6 +454,8 @@ class SherpaOnnxBindings {
448 454
449 static SherpaOnnxReadWave? readWave; 455 static SherpaOnnxReadWave? readWave;
450 456
  457 + static SherpaOnnxWriteWave? writeWave;
  458 +
451 static SherpaOnnxFreeWave? freeWave; 459 static SherpaOnnxFreeWave? freeWave;
452 460
453 static void init(DynamicLibrary dynamicLibrary) { 461 static void init(DynamicLibrary dynamicLibrary) {
@@ -686,6 +694,11 @@ class SherpaOnnxBindings { @@ -686,6 +694,11 @@ class SherpaOnnxBindings {
686 .lookup<NativeFunction<SherpaOnnxReadWaveNative>>('SherpaOnnxReadWave') 694 .lookup<NativeFunction<SherpaOnnxReadWaveNative>>('SherpaOnnxReadWave')
687 .asFunction(); 695 .asFunction();
688 696
  697 + writeWave ??= dynamicLibrary
  698 + .lookup<NativeFunction<SherpaOnnxWriteWaveNative>>(
  699 + 'SherpaOnnxWriteWave')
  700 + .asFunction();
  701 +
689 freeWave ??= dynamicLibrary 702 freeWave ??= dynamicLibrary
690 .lookup<NativeFunction<SherpaOnnxFreeWaveNative>>('SherpaOnnxFreeWave') 703 .lookup<NativeFunction<SherpaOnnxFreeWaveNative>>('SherpaOnnxFreeWave')
691 .asFunction(); 704 .asFunction();
@@ -113,7 +113,7 @@ class CircularBuffer { @@ -113,7 +113,7 @@ class CircularBuffer {
113 } 113 }
114 114
115 class VoiceActivityDetector { 115 class VoiceActivityDetector {
116 - VoiceActivityDetector._({required this.ptr}); 116 + VoiceActivityDetector._({required this.ptr, required this.config});
117 117
118 // The user has to invoke VoiceActivityDetector.free() to avoid memory leak. 118 // The user has to invoke VoiceActivityDetector.free() to avoid memory leak.
119 factory VoiceActivityDetector( 119 factory VoiceActivityDetector(
@@ -144,7 +144,7 @@ class VoiceActivityDetector { @@ -144,7 +144,7 @@ class VoiceActivityDetector {
144 calloc.free(modelPtr); 144 calloc.free(modelPtr);
145 calloc.free(c); 145 calloc.free(c);
146 146
147 - return VoiceActivityDetector._(ptr: ptr); 147 + return VoiceActivityDetector._(ptr: ptr, config: config);
148 } 148 }
149 149
150 void free() { 150 void free() {
@@ -210,4 +210,5 @@ class VoiceActivityDetector { @@ -210,4 +210,5 @@ class VoiceActivityDetector {
210 } 210 }
211 211
212 Pointer<SherpaOnnxVoiceActivityDetector> ptr; 212 Pointer<SherpaOnnxVoiceActivityDetector> ptr;
  213 + final VadModelConfig config;
213 } 214 }
  1 +// Copyright (c) 2024 Xiaomi Corporation
  2 +import 'dart:ffi';
  3 +import 'dart:typed_data';
  4 +import 'package:ffi/ffi.dart';
  5 +
  6 +import './sherpa_onnx_bindings.dart';
  7 +
  8 +bool writeWave(
  9 + {required String filename,
  10 + required Float32List samples,
  11 + required int sampleRate}) {
  12 + final Pointer<Utf8> filenamePtr = filename.toNativeUtf8();
  13 +
  14 + final n = samples.length;
  15 + final Pointer<Float> p = calloc<Float>(n);
  16 +
  17 + final pList = p.asTypedList(n);
  18 + pList.setAll(0, samples);
  19 +
  20 + int ok =
  21 + SherpaOnnxBindings.writeWave?.call(p, n, sampleRate, filenamePtr) ?? 0;
  22 +
  23 + calloc.free(p);
  24 + calloc.free(filenamePtr);
  25 +
  26 + return ok == 1;
  27 +}
  1 +# The Flutter tooling requires that developers have CMake 3.10 or later
  2 +# installed. You should not increase this version, as doing so will cause
  3 +# the plugin to fail to compile for some customers of the plugin.
  4 +cmake_minimum_required(VERSION 3.10)
  5 +
  6 +# Project-level configuration.
  7 +set(PROJECT_NAME "sherpa_onnx")
  8 +project(${PROJECT_NAME} LANGUAGES CXX)
  9 +
  10 +# Invoke the build for native code shared with the other target platforms.
  11 +# This can be changed to accommodate different builds.
  12 +# add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_BINARY_DIR}/shared")
  13 +
  14 +# List of absolute paths to libraries that should be bundled with the plugin.
  15 +# This list could contain prebuilt libraries, or libraries created by an
  16 +# external build triggered from this build file.
  17 +set(sherpa_onnx_bundled_libraries
  18 + "${CMAKE_CURRENT_SOURCE_DIR}/libsherpa-onnx-c-api.so"
  19 + "${CMAKE_CURRENT_SOURCE_DIR}/libsherpa-onnx-core.so"
  20 + "${CMAKE_CURRENT_SOURCE_DIR}/libkaldi-decoder-core.so"
  21 + "${CMAKE_CURRENT_SOURCE_DIR}/libsherpa-onnx-kaldifst-core.so"
  22 + "${CMAKE_CURRENT_SOURCE_DIR}/libsherpa-onnx-fstfar.so"
  23 + "${CMAKE_CURRENT_SOURCE_DIR}/libsherpa-onnx-fst.so"
  24 + "${CMAKE_CURRENT_SOURCE_DIR}/libkaldi-native-fbank-core.so"
  25 + "${CMAKE_CURRENT_SOURCE_DIR}/libpiper_phonemize.so"
  26 + "${CMAKE_CURRENT_SOURCE_DIR}/libespeak-ng.so"
  27 + "${CMAKE_CURRENT_SOURCE_DIR}/libucd.so"
  28 + "${CMAKE_CURRENT_SOURCE_DIR}/libonnxruntime.so"
  29 + "${CMAKE_CURRENT_SOURCE_DIR}/libssentencepiece_core.so"
  30 + PARENT_SCOPE
  31 +)
@@ -39,4 +39,7 @@ flutter run --release -d macos @@ -39,4 +39,7 @@ flutter run --release -d macos
39 flutter create --platforms=windows,macos,linux . 39 flutter create --platforms=windows,macos,linux .
40 40
41 dart analyze 41 dart analyze
  42 +
  43 +FLUTTER_XCODE_ARCHS=arm64
  44 +FLUTTER_XCODE_ARCHS=x86_64
42 ``` 45 ```
@@ -50,6 +50,10 @@ flutter: @@ -50,6 +50,10 @@ flutter:
50 platforms: 50 platforms:
51 macos: 51 macos:
52 ffiPlugin: true 52 ffiPlugin: true
  53 + windows:
  54 + ffiPlugin: true
  55 + linux:
  56 + ffiPlugin: true
53 57
54 # To add assets to your plugin package, add an assets section, like this: 58 # To add assets to your plugin package, add an assets section, like this:
55 # assets: 59 # assets:
  1 +# The Flutter tooling requires that developers have a version of Visual Studio
  2 +# installed that includes CMake 3.14 or later. You should not increase this
  3 +# version, as doing so will cause the plugin to fail to compile for some
  4 +# customers of the plugin.
  5 +cmake_minimum_required(VERSION 3.14)
  6 +
  7 +# Project-level configuration.
  8 +set(PROJECT_NAME "sherpa_onnx")
  9 +project(${PROJECT_NAME} LANGUAGES CXX)
  10 +
  11 +# Invoke the build for native code shared with the other target platforms.
  12 +# This can be changed to accommodate different builds.
  13 +# add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_BINARY_DIR}/shared")
  14 +
  15 +# List of absolute paths to libraries that should be bundled with the plugin.
  16 +# This list could contain prebuilt libraries, or libraries created by an
  17 +# external build triggered from this build file.
  18 +set(sherpa_onnx_bundled_libraries
  19 + "${CMAKE_CURRENT_SOURCE_DIR}/sherpa-onnx-c-api.dll"
  20 + "${CMAKE_CURRENT_SOURCE_DIR}/sherpa-onnx-core.dll"
  21 + "${CMAKE_CURRENT_SOURCE_DIR}/kaldi-decoder-core.dll"
  22 + "${CMAKE_CURRENT_SOURCE_DIR}/sherpa-onnx-kaldifst-core.lib"
  23 + "${CMAKE_CURRENT_SOURCE_DIR}/sherpa-onnx-fstfar.lib"
  24 + "${CMAKE_CURRENT_SOURCE_DIR}/sherpa-onnx-fst.lib"
  25 + "${CMAKE_CURRENT_SOURCE_DIR}/kaldi-native-fbank-core.dll"
  26 + "${CMAKE_CURRENT_SOURCE_DIR}/piper_phonemize.dll"
  27 + "${CMAKE_CURRENT_SOURCE_DIR}/espeak-ng.dll"
  28 + "${CMAKE_CURRENT_SOURCE_DIR}/ucd.dll"
  29 + "${CMAKE_CURRENT_SOURCE_DIR}/onnxruntime.dll"
  30 + "${CMAKE_CURRENT_SOURCE_DIR}/ssentencepiece_core.dll"
  31 + PARENT_SCOPE
  32 +)