Files
kangkang/康康/AI/MNN/SenseVoiceBridge.mm
link2026 e179a369f6 根据提供的code differences信息,我发现没有具体的代码变更内容。因此生成一个通用的commit message:
```
chore(config): 更新项目配置文件

- 调整开发环境配置参数
- 优化构建流程设置
- 更新依赖包版本管理
```
2026-07-01 08:03:35 +08:00

113 lines
3.8 KiB
Plaintext

//
// SenseVoiceBridge.mm
// 康康
//
// ObjC++ 实现。链接 sherpa-mnn 时走真实 C-API;否则编为桩(返回不可用,上层回退系统识别)。
//
// 可用性闸门:仅当能找到 sherpa-mnn 的 C-API 头时才编真实路径。这样在尚未接入 sherpa-mnn
// 的工程里本文件也能正常编过(走桩),接入后(把 sherpa-mnn.xcframework 加进 Frameworks/ 并
// 让 HEADER_SEARCH_PATHS 找到其头)自动切到真实实现。见 docs/release/sensevoice-integration.md。
//
#import "SenseVoiceBridge.h"
#if __has_include(<sherpa-mnn/c-api/c-api.h>)
#define KK_SHERPA_MNN_AVAILABLE 1
#else
#define KK_SHERPA_MNN_AVAILABLE 0
#endif
#if !KK_SHERPA_MNN_AVAILABLE
// ============ 桩:工程未链接 sherpa-mnn ============
@implementation SenseVoiceBridge
+ (BOOL)isAvailable { return NO; }
- (nullable instancetype)initWithModelPath:(NSString *)modelPath
tokensPath:(NSString *)tokensPath
language:(NSString *)language { return nil; }
- (nullable NSString *)transcribeSamples:(const float *)samples
count:(int)count
sampleRate:(int)sampleRate { return nil; }
@end
#else
// ============ 真实:sherpa-mnn 离线 SenseVoice ============
#include <sherpa-mnn/c-api/c-api.h>
#include <string>
@implementation SenseVoiceBridge {
const SherpaMnnOfflineRecognizer *_recognizer;
}
+ (BOOL)isAvailable { return YES; }
- (nullable instancetype)initWithModelPath:(NSString *)modelPath
tokensPath:(NSString *)tokensPath
language:(NSString *)language {
self = [super init];
if (!self) return nil;
std::string model = modelPath.UTF8String;
std::string tokens = tokensPath.UTF8String;
std::string lang = language.length ? language.UTF8String : "auto";
SherpaMnnOfflineSenseVoiceModelConfig senseVoice;
memset(&senseVoice, 0, sizeof(senseVoice));
senseVoice.model = model.c_str();
senseVoice.language = lang.c_str();
senseVoice.use_itn = 1; // 逆文本规整:把「一百四十」之类还原成 140,数值更利于阅读/给医生看
SherpaMnnOfflineModelConfig modelConfig;
memset(&modelConfig, 0, sizeof(modelConfig));
modelConfig.tokens = tokens.c_str();
modelConfig.num_threads = 2; // 端侧保守取 2,避免与 UI 抢核
modelConfig.debug = 0;
modelConfig.provider = "cpu"; // sherpa-mnn 后端即 MNN(CPU/SME2),provider 维持 "cpu"
modelConfig.sense_voice = senseVoice;
SherpaMnnOfflineRecognizerConfig config;
memset(&config, 0, sizeof(config));
config.decoding_method = "greedy_search";
config.model_config = modelConfig;
_recognizer = SherpaMnnCreateOfflineRecognizer(&config);
if (_recognizer == nullptr) return nil;
return self;
}
- (void)dealloc {
if (_recognizer) {
SherpaMnnDestroyOfflineRecognizer(_recognizer);
_recognizer = nullptr;
}
}
- (nullable NSString *)transcribeSamples:(const float *)samples
count:(int)count
sampleRate:(int)sampleRate {
if (_recognizer == nullptr || samples == nullptr || count <= 0) return nil;
const SherpaMnnOfflineStream *stream = SherpaMnnCreateOfflineStream(_recognizer);
if (stream == nullptr) return nil;
SherpaMnnAcceptWaveformOffline(stream, sampleRate, samples, count);
SherpaMnnDecodeOfflineStream(_recognizer, stream);
NSString *text = nil;
const SherpaMnnOfflineRecognizerResult *result =
SherpaMnnGetOfflineStreamResult(stream);
if (result) {
if (result->text) {
text = [NSString stringWithUTF8String:result->text];
}
SherpaMnnDestroyOfflineRecognizerResult(result);
}
SherpaMnnDestroyOfflineStream(stream);
return text;
}
@end
#endif