2024-07-04 10:49:09 +08:00
|
|
|
// Copyright (c) 2024 Xiaomi Corporation
|
|
|
|
|
import 'dart:convert';
|
|
|
|
|
import 'dart:ffi';
|
|
|
|
|
|
|
|
|
|
import 'package:ffi/ffi.dart';
|
|
|
|
|
|
|
|
|
|
import './feature_config.dart';
|
|
|
|
|
import './offline_stream.dart';
|
|
|
|
|
import './sherpa_onnx_bindings.dart';
|
2024-07-10 21:48:23 +08:00
|
|
|
import './utils.dart';
|
2024-07-04 10:49:09 +08:00
|
|
|
|
|
|
|
|
class OfflineTransducerModelConfig {
|
|
|
|
|
const OfflineTransducerModelConfig({
|
|
|
|
|
this.encoder = '',
|
|
|
|
|
this.decoder = '',
|
|
|
|
|
this.joiner = '',
|
|
|
|
|
});
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
factory OfflineTransducerModelConfig.fromJson(Map<String, dynamic> json) {
|
|
|
|
|
return OfflineTransducerModelConfig(
|
|
|
|
|
encoder: json['encoder'] as String? ?? '',
|
|
|
|
|
decoder: json['decoder'] as String? ?? '',
|
|
|
|
|
joiner: json['joiner'] as String? ?? '',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
@override
|
|
|
|
|
String toString() {
|
|
|
|
|
return 'OfflineTransducerModelConfig(encoder: $encoder, decoder: $decoder, joiner: $joiner)';
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
Map<String, dynamic> toJson() => {
|
|
|
|
|
'encoder': encoder,
|
|
|
|
|
'decoder': decoder,
|
|
|
|
|
'joiner': joiner,
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
final String encoder;
|
|
|
|
|
final String decoder;
|
|
|
|
|
final String joiner;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class OfflineParaformerModelConfig {
|
|
|
|
|
const OfflineParaformerModelConfig({this.model = ''});
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
factory OfflineParaformerModelConfig.fromJson(Map<String, dynamic> json) {
|
|
|
|
|
return OfflineParaformerModelConfig(
|
|
|
|
|
model: json['model'] as String? ?? '',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
@override
|
|
|
|
|
String toString() {
|
|
|
|
|
return 'OfflineParaformerModelConfig(model: $model)';
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
Map<String, dynamic> toJson() => {
|
|
|
|
|
'model': model,
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
final String model;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class OfflineNemoEncDecCtcModelConfig {
|
|
|
|
|
const OfflineNemoEncDecCtcModelConfig({this.model = ''});
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
factory OfflineNemoEncDecCtcModelConfig.fromJson(Map<String, dynamic> json) {
|
|
|
|
|
return OfflineNemoEncDecCtcModelConfig(
|
|
|
|
|
model: json['model'] as String? ?? '',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
@override
|
|
|
|
|
String toString() {
|
|
|
|
|
return 'OfflineNemoEncDecCtcModelConfig(model: $model)';
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
Map<String, dynamic> toJson() => {
|
|
|
|
|
'model': model,
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
final String model;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-03 15:59:38 +08:00
|
|
|
class OfflineDolphinModelConfig {
|
|
|
|
|
const OfflineDolphinModelConfig({this.model = ''});
|
|
|
|
|
|
|
|
|
|
factory OfflineDolphinModelConfig.fromJson(Map<String, dynamic> json) {
|
|
|
|
|
return OfflineDolphinModelConfig(
|
|
|
|
|
model: json['model'] as String? ?? '',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
String toString() {
|
|
|
|
|
return 'OfflineDolphinModelConfig(model: $model)';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Map<String, dynamic> toJson() => {
|
|
|
|
|
'model': model,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
final String model;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
class OfflineWhisperModelConfig {
|
|
|
|
|
const OfflineWhisperModelConfig(
|
|
|
|
|
{this.encoder = '',
|
|
|
|
|
this.decoder = '',
|
|
|
|
|
this.language = '',
|
|
|
|
|
this.task = '',
|
|
|
|
|
this.tailPaddings = -1});
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
factory OfflineWhisperModelConfig.fromJson(Map<String, dynamic> json) {
|
|
|
|
|
return OfflineWhisperModelConfig(
|
|
|
|
|
encoder: json['encoder'] as String? ?? '',
|
|
|
|
|
decoder: json['decoder'] as String? ?? '',
|
|
|
|
|
language: json['language'] as String? ?? '',
|
|
|
|
|
task: json['task'] as String? ?? '',
|
|
|
|
|
tailPaddings: json['tailPaddings'] as int? ?? -1,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
@override
|
|
|
|
|
String toString() {
|
|
|
|
|
return 'OfflineWhisperModelConfig(encoder: $encoder, decoder: $decoder, language: $language, task: $task, tailPaddings: $tailPaddings)';
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
Map<String, dynamic> toJson() => {
|
|
|
|
|
'encoder': encoder,
|
|
|
|
|
'decoder': decoder,
|
|
|
|
|
'language': language,
|
|
|
|
|
'task': task,
|
|
|
|
|
'tailPaddings': tailPaddings,
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
final String encoder;
|
|
|
|
|
final String decoder;
|
|
|
|
|
final String language;
|
|
|
|
|
final String task;
|
|
|
|
|
final int tailPaddings;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-17 15:17:08 +08:00
|
|
|
class OfflineFireRedAsrModelConfig {
|
2025-02-25 01:43:48 -05:00
|
|
|
const OfflineFireRedAsrModelConfig({this.encoder = '', this.decoder = ''});
|
|
|
|
|
|
|
|
|
|
factory OfflineFireRedAsrModelConfig.fromJson(Map<String, dynamic> json) {
|
|
|
|
|
return OfflineFireRedAsrModelConfig(
|
|
|
|
|
encoder: json['encoder'] as String? ?? '',
|
|
|
|
|
decoder: json['decoder'] as String? ?? '',
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-02-17 15:17:08 +08:00
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
String toString() {
|
|
|
|
|
return 'OfflineFireRedAsrModelConfig(encoder: $encoder, decoder: $decoder)';
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
Map<String, dynamic> toJson() => {
|
|
|
|
|
'encoder': encoder,
|
|
|
|
|
'decoder': decoder,
|
|
|
|
|
};
|
|
|
|
|
|
2025-02-17 15:17:08 +08:00
|
|
|
final String encoder;
|
|
|
|
|
final String decoder;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-27 12:04:12 +08:00
|
|
|
class OfflineMoonshineModelConfig {
|
|
|
|
|
const OfflineMoonshineModelConfig(
|
|
|
|
|
{this.preprocessor = '',
|
|
|
|
|
this.encoder = '',
|
|
|
|
|
this.uncachedDecoder = '',
|
|
|
|
|
this.cachedDecoder = ''});
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
factory OfflineMoonshineModelConfig.fromJson(Map<String, dynamic> json) {
|
|
|
|
|
return OfflineMoonshineModelConfig(
|
|
|
|
|
preprocessor: json['preprocessor'] as String? ?? '',
|
|
|
|
|
encoder: json['encoder'] as String? ?? '',
|
|
|
|
|
uncachedDecoder: json['uncachedDecoder'] as String? ?? '',
|
|
|
|
|
cachedDecoder: json['cachedDecoder'] as String? ?? '',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-27 12:04:12 +08:00
|
|
|
@override
|
|
|
|
|
String toString() {
|
|
|
|
|
return 'OfflineMoonshineModelConfig(preprocessor: $preprocessor, encoder: $encoder, uncachedDecoder: $uncachedDecoder, cachedDecoder: $cachedDecoder)';
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
Map<String, dynamic> toJson() => {
|
|
|
|
|
'preprocessor': preprocessor,
|
|
|
|
|
'encoder': encoder,
|
|
|
|
|
'uncachedDecoder': uncachedDecoder,
|
|
|
|
|
'cachedDecoder': cachedDecoder,
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-27 12:04:12 +08:00
|
|
|
final String preprocessor;
|
|
|
|
|
final String encoder;
|
|
|
|
|
final String uncachedDecoder;
|
|
|
|
|
final String cachedDecoder;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
class OfflineTdnnModelConfig {
|
|
|
|
|
const OfflineTdnnModelConfig({this.model = ''});
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
factory OfflineTdnnModelConfig.fromJson(Map<String, dynamic> json) {
|
|
|
|
|
return OfflineTdnnModelConfig(
|
|
|
|
|
model: json['model'] as String? ?? '',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
@override
|
|
|
|
|
String toString() {
|
|
|
|
|
return 'OfflineTdnnModelConfig(model: $model)';
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
Map<String, dynamic> toJson() => {
|
|
|
|
|
'model': model,
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
final String model;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-21 21:48:12 +08:00
|
|
|
class OfflineSenseVoiceModelConfig {
|
|
|
|
|
const OfflineSenseVoiceModelConfig({
|
|
|
|
|
this.model = '',
|
|
|
|
|
this.language = '',
|
|
|
|
|
this.useInverseTextNormalization = false,
|
|
|
|
|
});
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
factory OfflineSenseVoiceModelConfig.fromJson(Map<String, dynamic> json) {
|
|
|
|
|
return OfflineSenseVoiceModelConfig(
|
|
|
|
|
model: json['model'] as String? ?? '',
|
|
|
|
|
language: json['language'] as String? ?? '',
|
|
|
|
|
useInverseTextNormalization:
|
|
|
|
|
json['useInverseTextNormalization'] as bool? ?? false,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-21 21:48:12 +08:00
|
|
|
@override
|
|
|
|
|
String toString() {
|
|
|
|
|
return 'OfflineSenseVoiceModelConfig(model: $model, language: $language, useInverseTextNormalization: $useInverseTextNormalization)';
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
Map<String, dynamic> toJson() => {
|
|
|
|
|
'model': model,
|
|
|
|
|
'language': language,
|
|
|
|
|
'useInverseTextNormalization': useInverseTextNormalization,
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-21 21:48:12 +08:00
|
|
|
final String model;
|
|
|
|
|
final String language;
|
|
|
|
|
final bool useInverseTextNormalization;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
class OfflineLMConfig {
|
|
|
|
|
const OfflineLMConfig({this.model = '', this.scale = 1.0});
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
factory OfflineLMConfig.fromJson(Map<String, dynamic> json) {
|
|
|
|
|
return OfflineLMConfig(
|
|
|
|
|
model: json['model'] as String? ?? '',
|
|
|
|
|
scale: (json['scale'] as num?)?.toDouble() ?? 1.0,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
@override
|
|
|
|
|
String toString() {
|
|
|
|
|
return 'OfflineLMConfig(model: $model, scale: $scale)';
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
Map<String, dynamic> toJson() => {
|
|
|
|
|
'model': model,
|
|
|
|
|
'scale': scale,
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
final String model;
|
|
|
|
|
final double scale;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class OfflineModelConfig {
|
|
|
|
|
const OfflineModelConfig({
|
|
|
|
|
this.transducer = const OfflineTransducerModelConfig(),
|
|
|
|
|
this.paraformer = const OfflineParaformerModelConfig(),
|
|
|
|
|
this.nemoCtc = const OfflineNemoEncDecCtcModelConfig(),
|
|
|
|
|
this.whisper = const OfflineWhisperModelConfig(),
|
|
|
|
|
this.tdnn = const OfflineTdnnModelConfig(),
|
2024-07-21 21:48:12 +08:00
|
|
|
this.senseVoice = const OfflineSenseVoiceModelConfig(),
|
2024-10-27 12:04:12 +08:00
|
|
|
this.moonshine = const OfflineMoonshineModelConfig(),
|
2025-02-17 15:17:08 +08:00
|
|
|
this.fireRedAsr = const OfflineFireRedAsrModelConfig(),
|
2025-04-03 15:59:38 +08:00
|
|
|
this.dolphin = const OfflineDolphinModelConfig(),
|
2024-07-04 10:49:09 +08:00
|
|
|
required this.tokens,
|
|
|
|
|
this.numThreads = 1,
|
|
|
|
|
this.debug = true,
|
|
|
|
|
this.provider = 'cpu',
|
|
|
|
|
this.modelType = '',
|
|
|
|
|
this.modelingUnit = '',
|
|
|
|
|
this.bpeVocab = '',
|
|
|
|
|
this.telespeechCtc = '',
|
|
|
|
|
});
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
factory OfflineModelConfig.fromJson(Map<String, dynamic> json) {
|
|
|
|
|
return OfflineModelConfig(
|
|
|
|
|
transducer: json['transducer'] != null
|
|
|
|
|
? OfflineTransducerModelConfig.fromJson(
|
|
|
|
|
json['transducer'] as Map<String, dynamic>)
|
|
|
|
|
: const OfflineTransducerModelConfig(),
|
|
|
|
|
paraformer: json['paraformer'] != null
|
|
|
|
|
? OfflineParaformerModelConfig.fromJson(
|
|
|
|
|
json['paraformer'] as Map<String, dynamic>)
|
|
|
|
|
: const OfflineParaformerModelConfig(),
|
|
|
|
|
nemoCtc: json['nemoCtc'] != null
|
|
|
|
|
? OfflineNemoEncDecCtcModelConfig.fromJson(
|
|
|
|
|
json['nemoCtc'] as Map<String, dynamic>)
|
|
|
|
|
: const OfflineNemoEncDecCtcModelConfig(),
|
|
|
|
|
whisper: json['whisper'] != null
|
|
|
|
|
? OfflineWhisperModelConfig.fromJson(
|
|
|
|
|
json['whisper'] as Map<String, dynamic>)
|
|
|
|
|
: const OfflineWhisperModelConfig(),
|
|
|
|
|
tdnn: json['tdnn'] != null
|
|
|
|
|
? OfflineTdnnModelConfig.fromJson(
|
|
|
|
|
json['tdnn'] as Map<String, dynamic>)
|
|
|
|
|
: const OfflineTdnnModelConfig(),
|
|
|
|
|
senseVoice: json['senseVoice'] != null
|
|
|
|
|
? OfflineSenseVoiceModelConfig.fromJson(
|
|
|
|
|
json['senseVoice'] as Map<String, dynamic>)
|
|
|
|
|
: const OfflineSenseVoiceModelConfig(),
|
|
|
|
|
moonshine: json['moonshine'] != null
|
|
|
|
|
? OfflineMoonshineModelConfig.fromJson(
|
|
|
|
|
json['moonshine'] as Map<String, dynamic>)
|
|
|
|
|
: const OfflineMoonshineModelConfig(),
|
|
|
|
|
fireRedAsr: json['fireRedAsr'] != null
|
|
|
|
|
? OfflineFireRedAsrModelConfig.fromJson(
|
|
|
|
|
json['fireRedAsr'] as Map<String, dynamic>)
|
|
|
|
|
: const OfflineFireRedAsrModelConfig(),
|
2025-04-03 15:59:38 +08:00
|
|
|
dolphin: json['dolphin'] != null
|
|
|
|
|
? OfflineDolphinModelConfig.fromJson(
|
|
|
|
|
json['dolphin'] as Map<String, dynamic>)
|
|
|
|
|
: const OfflineDolphinModelConfig(),
|
2025-02-25 01:43:48 -05:00
|
|
|
tokens: json['tokens'] as String,
|
|
|
|
|
numThreads: json['numThreads'] as int? ?? 1,
|
|
|
|
|
debug: json['debug'] as bool? ?? true,
|
|
|
|
|
provider: json['provider'] as String? ?? 'cpu',
|
|
|
|
|
modelType: json['modelType'] as String? ?? '',
|
|
|
|
|
modelingUnit: json['modelingUnit'] as String? ?? '',
|
|
|
|
|
bpeVocab: json['bpeVocab'] as String? ?? '',
|
|
|
|
|
telespeechCtc: json['telespeechCtc'] as String? ?? '',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
@override
|
|
|
|
|
String toString() {
|
2025-04-03 15:59:38 +08:00
|
|
|
return 'OfflineModelConfig(transducer: $transducer, paraformer: $paraformer, nemoCtc: $nemoCtc, whisper: $whisper, tdnn: $tdnn, senseVoice: $senseVoice, moonshine: $moonshine, fireRedAsr: $fireRedAsr, dolphin: $dolphin, tokens: $tokens, numThreads: $numThreads, debug: $debug, provider: $provider, modelType: $modelType, modelingUnit: $modelingUnit, bpeVocab: $bpeVocab, telespeechCtc: $telespeechCtc)';
|
2024-07-04 10:49:09 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
Map<String, dynamic> toJson() => {
|
|
|
|
|
'transducer': transducer.toJson(),
|
|
|
|
|
'paraformer': paraformer.toJson(),
|
|
|
|
|
'nemoCtc': nemoCtc.toJson(),
|
|
|
|
|
'whisper': whisper.toJson(),
|
|
|
|
|
'tdnn': tdnn.toJson(),
|
|
|
|
|
'senseVoice': senseVoice.toJson(),
|
|
|
|
|
'moonshine': moonshine.toJson(),
|
|
|
|
|
'fireRedAsr': fireRedAsr.toJson(),
|
2025-04-03 15:59:38 +08:00
|
|
|
'dolphin': dolphin.toJson(),
|
2025-02-25 01:43:48 -05:00
|
|
|
'tokens': tokens,
|
|
|
|
|
'numThreads': numThreads,
|
|
|
|
|
'debug': debug,
|
|
|
|
|
'provider': provider,
|
|
|
|
|
'modelType': modelType,
|
|
|
|
|
'modelingUnit': modelingUnit,
|
|
|
|
|
'bpeVocab': bpeVocab,
|
|
|
|
|
'telespeechCtc': telespeechCtc,
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
final OfflineTransducerModelConfig transducer;
|
|
|
|
|
final OfflineParaformerModelConfig paraformer;
|
|
|
|
|
final OfflineNemoEncDecCtcModelConfig nemoCtc;
|
|
|
|
|
final OfflineWhisperModelConfig whisper;
|
|
|
|
|
final OfflineTdnnModelConfig tdnn;
|
2024-07-21 21:48:12 +08:00
|
|
|
final OfflineSenseVoiceModelConfig senseVoice;
|
2024-10-27 12:04:12 +08:00
|
|
|
final OfflineMoonshineModelConfig moonshine;
|
2025-02-17 15:17:08 +08:00
|
|
|
final OfflineFireRedAsrModelConfig fireRedAsr;
|
2025-04-03 15:59:38 +08:00
|
|
|
final OfflineDolphinModelConfig dolphin;
|
2024-07-04 10:49:09 +08:00
|
|
|
|
|
|
|
|
final String tokens;
|
|
|
|
|
final int numThreads;
|
|
|
|
|
final bool debug;
|
|
|
|
|
final String provider;
|
|
|
|
|
final String modelType;
|
|
|
|
|
final String modelingUnit;
|
|
|
|
|
final String bpeVocab;
|
|
|
|
|
final String telespeechCtc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class OfflineRecognizerConfig {
|
|
|
|
|
const OfflineRecognizerConfig({
|
|
|
|
|
this.feat = const FeatureConfig(),
|
|
|
|
|
required this.model,
|
|
|
|
|
this.lm = const OfflineLMConfig(),
|
|
|
|
|
this.decodingMethod = 'greedy_search',
|
|
|
|
|
this.maxActivePaths = 4,
|
|
|
|
|
this.hotwordsFile = '',
|
|
|
|
|
this.hotwordsScore = 1.5,
|
|
|
|
|
this.ruleFsts = '',
|
|
|
|
|
this.ruleFars = '',
|
2024-08-08 10:43:31 +08:00
|
|
|
this.blankPenalty = 0.0,
|
2024-07-04 10:49:09 +08:00
|
|
|
});
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
factory OfflineRecognizerConfig.fromJson(Map<String, dynamic> json) {
|
|
|
|
|
return OfflineRecognizerConfig(
|
|
|
|
|
feat: json['feat'] != null
|
|
|
|
|
? FeatureConfig.fromJson(json['feat'] as Map<String, dynamic>)
|
|
|
|
|
: const FeatureConfig(),
|
|
|
|
|
model: OfflineModelConfig.fromJson(json['model'] as Map<String, dynamic>),
|
|
|
|
|
lm: json['lm'] != null
|
|
|
|
|
? OfflineLMConfig.fromJson(json['lm'] as Map<String, dynamic>)
|
|
|
|
|
: const OfflineLMConfig(),
|
|
|
|
|
decodingMethod: json['decodingMethod'] as String? ?? 'greedy_search',
|
|
|
|
|
maxActivePaths: json['maxActivePaths'] as int? ?? 4,
|
|
|
|
|
hotwordsFile: json['hotwordsFile'] as String? ?? '',
|
|
|
|
|
hotwordsScore: (json['hotwordsScore'] as num?)?.toDouble() ?? 1.5,
|
|
|
|
|
ruleFsts: json['ruleFsts'] as String? ?? '',
|
|
|
|
|
ruleFars: json['ruleFars'] as String? ?? '',
|
|
|
|
|
blankPenalty: (json['blankPenalty'] as num?)?.toDouble() ?? 0.0,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
@override
|
|
|
|
|
String toString() {
|
2024-08-08 10:43:31 +08:00
|
|
|
return 'OfflineRecognizerConfig(feat: $feat, model: $model, lm: $lm, decodingMethod: $decodingMethod, maxActivePaths: $maxActivePaths, hotwordsFile: $hotwordsFile, hotwordsScore: $hotwordsScore, ruleFsts: $ruleFsts, ruleFars: $ruleFars, blankPenalty: $blankPenalty)';
|
2024-07-04 10:49:09 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
Map<String, dynamic> toJson() => {
|
|
|
|
|
'feat': feat.toJson(),
|
|
|
|
|
'model': model.toJson(),
|
|
|
|
|
'lm': lm.toJson(),
|
|
|
|
|
'decodingMethod': decodingMethod,
|
|
|
|
|
'maxActivePaths': maxActivePaths,
|
|
|
|
|
'hotwordsFile': hotwordsFile,
|
|
|
|
|
'hotwordsScore': hotwordsScore,
|
|
|
|
|
'ruleFsts': ruleFsts,
|
|
|
|
|
'ruleFars': ruleFars,
|
|
|
|
|
'blankPenalty': blankPenalty,
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
final FeatureConfig feat;
|
|
|
|
|
final OfflineModelConfig model;
|
|
|
|
|
final OfflineLMConfig lm;
|
|
|
|
|
final String decodingMethod;
|
|
|
|
|
|
|
|
|
|
final int maxActivePaths;
|
|
|
|
|
|
|
|
|
|
final String hotwordsFile;
|
|
|
|
|
|
|
|
|
|
final double hotwordsScore;
|
|
|
|
|
|
|
|
|
|
final String ruleFsts;
|
|
|
|
|
final String ruleFars;
|
2024-08-08 10:43:31 +08:00
|
|
|
|
|
|
|
|
final double blankPenalty;
|
2024-07-04 10:49:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class OfflineRecognizerResult {
|
|
|
|
|
OfflineRecognizerResult(
|
2024-08-16 19:21:59 -04:00
|
|
|
{required this.text,
|
|
|
|
|
required this.tokens,
|
|
|
|
|
required this.timestamps,
|
|
|
|
|
required this.lang,
|
|
|
|
|
required this.emotion,
|
|
|
|
|
required this.event});
|
2024-07-04 10:49:09 +08:00
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
factory OfflineRecognizerResult.fromJson(Map<String, dynamic> json) {
|
|
|
|
|
return OfflineRecognizerResult(
|
|
|
|
|
text: json['text'] as String? ?? '',
|
|
|
|
|
tokens: (json['tokens'] as List?)?.map((e) => e as String).toList() ?? [],
|
|
|
|
|
timestamps: (json['timestamps'] as List?)
|
|
|
|
|
?.map((e) => (e as num).toDouble())
|
|
|
|
|
.toList() ??
|
|
|
|
|
[],
|
|
|
|
|
lang: json['lang'] as String? ?? '',
|
|
|
|
|
emotion: json['emotion'] as String? ?? '',
|
|
|
|
|
event: json['event'] as String? ?? '',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
@override
|
|
|
|
|
String toString() {
|
2024-08-16 19:21:59 -04:00
|
|
|
return 'OfflineRecognizerResult(text: $text, tokens: $tokens, timestamps: $timestamps, lang: $lang, emotion: $emotion, event: $event)';
|
2024-07-04 10:49:09 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-25 01:43:48 -05:00
|
|
|
Map<String, dynamic> toJson() => {
|
|
|
|
|
'text': text,
|
|
|
|
|
'tokens': tokens,
|
|
|
|
|
'timestamps': timestamps,
|
|
|
|
|
'lang': lang,
|
|
|
|
|
'emotion': emotion,
|
|
|
|
|
'event': event,
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
final String text;
|
|
|
|
|
final List<String> tokens;
|
|
|
|
|
final List<double> timestamps;
|
2024-08-16 19:21:59 -04:00
|
|
|
final String lang;
|
|
|
|
|
final String emotion;
|
|
|
|
|
final String event;
|
2024-07-04 10:49:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class OfflineRecognizer {
|
2024-12-31 18:07:52 +08:00
|
|
|
OfflineRecognizer.fromPtr({required this.ptr, required this.config});
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
OfflineRecognizer._({required this.ptr, required this.config});
|
|
|
|
|
|
|
|
|
|
void free() {
|
|
|
|
|
SherpaOnnxBindings.destroyOfflineRecognizer?.call(ptr);
|
|
|
|
|
ptr = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The user is responsible to call the OfflineRecognizer.free()
|
|
|
|
|
/// method of the returned instance to avoid memory leak.
|
|
|
|
|
factory OfflineRecognizer(OfflineRecognizerConfig config) {
|
|
|
|
|
final c = calloc<SherpaOnnxOfflineRecognizerConfig>();
|
|
|
|
|
|
|
|
|
|
c.ref.feat.sampleRate = config.feat.sampleRate;
|
|
|
|
|
c.ref.feat.featureDim = config.feat.featureDim;
|
|
|
|
|
|
|
|
|
|
// transducer
|
|
|
|
|
c.ref.model.transducer.encoder =
|
|
|
|
|
config.model.transducer.encoder.toNativeUtf8();
|
|
|
|
|
c.ref.model.transducer.decoder =
|
|
|
|
|
config.model.transducer.decoder.toNativeUtf8();
|
|
|
|
|
c.ref.model.transducer.joiner =
|
|
|
|
|
config.model.transducer.joiner.toNativeUtf8();
|
|
|
|
|
|
|
|
|
|
// paraformer
|
|
|
|
|
c.ref.model.paraformer.model = config.model.paraformer.model.toNativeUtf8();
|
|
|
|
|
|
|
|
|
|
// nemoCtc
|
|
|
|
|
c.ref.model.nemoCtc.model = config.model.nemoCtc.model.toNativeUtf8();
|
|
|
|
|
|
|
|
|
|
// whisper
|
|
|
|
|
c.ref.model.whisper.encoder = config.model.whisper.encoder.toNativeUtf8();
|
|
|
|
|
|
|
|
|
|
c.ref.model.whisper.decoder = config.model.whisper.decoder.toNativeUtf8();
|
|
|
|
|
|
|
|
|
|
c.ref.model.whisper.language = config.model.whisper.language.toNativeUtf8();
|
|
|
|
|
|
|
|
|
|
c.ref.model.whisper.task = config.model.whisper.task.toNativeUtf8();
|
|
|
|
|
|
|
|
|
|
c.ref.model.whisper.tailPaddings = config.model.whisper.tailPaddings;
|
|
|
|
|
|
|
|
|
|
c.ref.model.tdnn.model = config.model.tdnn.model.toNativeUtf8();
|
|
|
|
|
|
2024-07-21 21:48:12 +08:00
|
|
|
c.ref.model.senseVoice.model = config.model.senseVoice.model.toNativeUtf8();
|
|
|
|
|
|
|
|
|
|
c.ref.model.senseVoice.language =
|
|
|
|
|
config.model.senseVoice.language.toNativeUtf8();
|
|
|
|
|
|
|
|
|
|
c.ref.model.senseVoice.useInverseTextNormalization =
|
|
|
|
|
config.model.senseVoice.useInverseTextNormalization ? 1 : 0;
|
|
|
|
|
|
2024-10-27 12:04:12 +08:00
|
|
|
c.ref.model.moonshine.preprocessor =
|
|
|
|
|
config.model.moonshine.preprocessor.toNativeUtf8();
|
|
|
|
|
c.ref.model.moonshine.encoder =
|
|
|
|
|
config.model.moonshine.encoder.toNativeUtf8();
|
|
|
|
|
c.ref.model.moonshine.uncachedDecoder =
|
|
|
|
|
config.model.moonshine.uncachedDecoder.toNativeUtf8();
|
|
|
|
|
c.ref.model.moonshine.cachedDecoder =
|
|
|
|
|
config.model.moonshine.cachedDecoder.toNativeUtf8();
|
|
|
|
|
|
2025-02-17 15:17:08 +08:00
|
|
|
// FireRedAsr
|
2025-02-25 01:43:48 -05:00
|
|
|
c.ref.model.fireRedAsr.encoder =
|
|
|
|
|
config.model.fireRedAsr.encoder.toNativeUtf8();
|
|
|
|
|
c.ref.model.fireRedAsr.decoder =
|
|
|
|
|
config.model.fireRedAsr.decoder.toNativeUtf8();
|
2025-02-17 15:17:08 +08:00
|
|
|
|
2025-04-03 15:59:38 +08:00
|
|
|
c.ref.model.dolphin.model = config.model.dolphin.model.toNativeUtf8();
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
c.ref.model.tokens = config.model.tokens.toNativeUtf8();
|
|
|
|
|
|
|
|
|
|
c.ref.model.numThreads = config.model.numThreads;
|
|
|
|
|
c.ref.model.debug = config.model.debug ? 1 : 0;
|
|
|
|
|
c.ref.model.provider = config.model.provider.toNativeUtf8();
|
|
|
|
|
c.ref.model.modelType = config.model.modelType.toNativeUtf8();
|
|
|
|
|
c.ref.model.modelingUnit = config.model.modelingUnit.toNativeUtf8();
|
|
|
|
|
c.ref.model.bpeVocab = config.model.bpeVocab.toNativeUtf8();
|
|
|
|
|
c.ref.model.telespeechCtc = config.model.telespeechCtc.toNativeUtf8();
|
|
|
|
|
|
|
|
|
|
c.ref.lm.model = config.lm.model.toNativeUtf8();
|
|
|
|
|
c.ref.lm.scale = config.lm.scale;
|
|
|
|
|
|
|
|
|
|
c.ref.decodingMethod = config.decodingMethod.toNativeUtf8();
|
|
|
|
|
c.ref.maxActivePaths = config.maxActivePaths;
|
|
|
|
|
|
|
|
|
|
c.ref.hotwordsFile = config.hotwordsFile.toNativeUtf8();
|
|
|
|
|
c.ref.hotwordsScore = config.hotwordsScore;
|
|
|
|
|
|
|
|
|
|
c.ref.ruleFsts = config.ruleFsts.toNativeUtf8();
|
|
|
|
|
c.ref.ruleFars = config.ruleFars.toNativeUtf8();
|
|
|
|
|
|
2024-08-08 10:43:31 +08:00
|
|
|
c.ref.blankPenalty = config.blankPenalty;
|
|
|
|
|
|
2024-07-04 10:49:09 +08:00
|
|
|
final ptr = SherpaOnnxBindings.createOfflineRecognizer?.call(c) ?? nullptr;
|
|
|
|
|
|
|
|
|
|
calloc.free(c.ref.ruleFars);
|
|
|
|
|
calloc.free(c.ref.ruleFsts);
|
|
|
|
|
calloc.free(c.ref.hotwordsFile);
|
|
|
|
|
calloc.free(c.ref.decodingMethod);
|
|
|
|
|
calloc.free(c.ref.lm.model);
|
|
|
|
|
calloc.free(c.ref.model.telespeechCtc);
|
|
|
|
|
calloc.free(c.ref.model.bpeVocab);
|
|
|
|
|
calloc.free(c.ref.model.modelingUnit);
|
|
|
|
|
calloc.free(c.ref.model.modelType);
|
|
|
|
|
calloc.free(c.ref.model.provider);
|
|
|
|
|
calloc.free(c.ref.model.tokens);
|
2025-04-03 15:59:38 +08:00
|
|
|
calloc.free(c.ref.model.dolphin.model);
|
2025-02-17 15:17:08 +08:00
|
|
|
calloc.free(c.ref.model.fireRedAsr.decoder);
|
|
|
|
|
calloc.free(c.ref.model.fireRedAsr.encoder);
|
2024-10-27 12:04:12 +08:00
|
|
|
calloc.free(c.ref.model.moonshine.cachedDecoder);
|
|
|
|
|
calloc.free(c.ref.model.moonshine.uncachedDecoder);
|
|
|
|
|
calloc.free(c.ref.model.moonshine.encoder);
|
|
|
|
|
calloc.free(c.ref.model.moonshine.preprocessor);
|
2024-07-21 21:48:12 +08:00
|
|
|
calloc.free(c.ref.model.senseVoice.language);
|
|
|
|
|
calloc.free(c.ref.model.senseVoice.model);
|
2024-07-04 10:49:09 +08:00
|
|
|
calloc.free(c.ref.model.tdnn.model);
|
|
|
|
|
calloc.free(c.ref.model.whisper.task);
|
|
|
|
|
calloc.free(c.ref.model.whisper.language);
|
|
|
|
|
calloc.free(c.ref.model.whisper.decoder);
|
|
|
|
|
calloc.free(c.ref.model.whisper.encoder);
|
|
|
|
|
calloc.free(c.ref.model.nemoCtc.model);
|
|
|
|
|
calloc.free(c.ref.model.paraformer.model);
|
|
|
|
|
calloc.free(c.ref.model.transducer.encoder);
|
|
|
|
|
calloc.free(c.ref.model.transducer.decoder);
|
|
|
|
|
calloc.free(c.ref.model.transducer.joiner);
|
|
|
|
|
calloc.free(c);
|
|
|
|
|
|
|
|
|
|
return OfflineRecognizer._(ptr: ptr, config: config);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The user has to invoke stream.free() on the returned instance
|
|
|
|
|
/// to avoid memory leak
|
|
|
|
|
OfflineStream createStream() {
|
|
|
|
|
final p = SherpaOnnxBindings.createOfflineStream?.call(ptr) ?? nullptr;
|
|
|
|
|
return OfflineStream(ptr: p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void decode(OfflineStream stream) {
|
|
|
|
|
SherpaOnnxBindings.decodeOfflineStream?.call(ptr, stream.ptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OfflineRecognizerResult getResult(OfflineStream stream) {
|
|
|
|
|
final json =
|
|
|
|
|
SherpaOnnxBindings.getOfflineStreamResultAsJson?.call(stream.ptr) ??
|
|
|
|
|
nullptr;
|
|
|
|
|
if (json == nullptr) {
|
2024-08-16 19:21:59 -04:00
|
|
|
return OfflineRecognizerResult(
|
|
|
|
|
text: '',
|
|
|
|
|
tokens: [],
|
|
|
|
|
timestamps: [],
|
|
|
|
|
lang: '',
|
|
|
|
|
emotion: '',
|
|
|
|
|
event: '');
|
2024-07-04 10:49:09 +08:00
|
|
|
}
|
|
|
|
|
|
2024-07-10 21:48:23 +08:00
|
|
|
final parsedJson = jsonDecode(toDartString(json));
|
2024-07-04 10:49:09 +08:00
|
|
|
|
|
|
|
|
SherpaOnnxBindings.destroyOfflineStreamResultJson?.call(json);
|
|
|
|
|
|
|
|
|
|
return OfflineRecognizerResult(
|
|
|
|
|
text: parsedJson['text'],
|
|
|
|
|
tokens: List<String>.from(parsedJson['tokens']),
|
2024-08-16 19:21:59 -04:00
|
|
|
timestamps: List<double>.from(parsedJson['timestamps']),
|
|
|
|
|
lang: parsedJson['lang'],
|
|
|
|
|
emotion: parsedJson['emotion'],
|
|
|
|
|
event: parsedJson['event']);
|
2024-07-04 10:49:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Pointer<SherpaOnnxOfflineRecognizer> ptr;
|
|
|
|
|
OfflineRecognizerConfig config;
|
|
|
|
|
}
|