You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Bun FFI调用Microsoft Speech C SDK失败,原生C脚本正常

问题:Bun FFI调用Microsoft Speech C SDK创建关键词识别器失败

背景

我有一个可正常运行的C脚本,能通过Microsoft Speech C SDK检测音频文件中的关键词。由于官方JS SDK未实现本地关键词检测功能,官方建议使用C/C++ SDK结合Node封装调用,因此尝试通过Bun FFI实现相同逻辑。

原生C脚本(可正常运行)

编译命令

gcc main.c -o main -I./speechsdk/include/c_api/ -L./speechsdk/lib/x64/ -lMicrosoft.CognitiveServices.Speech.core -Wl,-rpath=./speechsdk/lib/x64/ -g

C代码

#include "./speechsdk/include/c_api/speechapi_c.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define CHECK_RESULT(result, message) \
    if ((result) != SPX_NOERROR) { \
        fprintf(stderr, message ": %lu\n", (result)); \
        exit(-1); \
    }

int main() {
    SPXAUDIOCONFIGHANDLE audioConfig;
    AZACHR audioConfigResult =
         audio_config_create_audio_input_from_wav_file_name(&audioConfig,
                                                                             "./file.wav");
    CHECK_RESULT(audioConfigResult, "Failed to create audio config.");
    printf("Created audio config.\n");

    SPXKEYWORDHANDLE keywordModel;
    AZACHR keywordModelResult = keyword_recognition_model_create_from_file(
         "./keyword_models/hey_bumblebee.table", &keywordModel);
    CHECK_RESULT(keywordModelResult, "Failed to create keyword model.");
    printf("Created keyword model.\n");

    SPXRECOHANDLE recognizer;
    AZACHR recognizerResult =
         recognizer_create_keyword_recognizer_from_audio_config(&recognizer,
                                                                                  audioConfig);
    CHECK_RESULT(recognizerResult, "Failed to create recognizer.");
    printf("Created recognizer.\n");

    SPXRESULTHANDLE resultHandle = NULL;
    AZACHR recognizeKeywordResult = recognizer_recognize_keyword_once(
         recognizer, keywordModel, &resultHandle);
    CHECK_RESULT(recognizeKeywordResult, "Failed to to start recognition.");

    Result_Reason reason;
    AZACHR reasonResult = result_get_reason(resultHandle, &reason);
    CHECK_RESULT(reasonResult, "Failed to get result reason.");

    if (reason == ResultReason_RecognizedKeyword) {
        char textBuffer[256];
        AZACHR textResult =
             result_get_text(resultHandle, textBuffer, sizeof(textBuffer));
        CHECK_RESULT(textResult, "Failed to get recognized text.");
        printf("Recognized: \"%s\"\n", textBuffer);
    } else if (reason == ResultReason_NoMatch) {
        Result_NoMatchReason noMatchReason;
        AZACHR noMatchReasonResult =
             result_get_no_match_reason(resultHandle, &noMatchReason);
        CHECK_RESULT(noMatchReasonResult, "Failed to get no match reason.");
        printf("No match. Reason: %d.\n", noMatchReason);
    } else if (reason == ResultReason_Canceled) {
        Result_CancellationReason cancellationReason;
        Result_CancellationErrorCode cancelationCode;
        AZACHR canceledReasonResult =
             result_get_reason_canceled(resultHandle, &cancellationReason);
        CHECK_RESULT(canceledReasonResult, "Failed to get canceled reason.");
        AZACHR canceledCodeResult =
             result_get_canceled_error_code(resultHandle, &cancelationCode);
        CHECK_RESULT(canceledCodeResult, "Failed to get canceled error code.");
        printf("Canceled. Reason: %d. Code: %d.\n", cancellationReason,
                 cancelationCode);
    } else {
        printf("Unknown.\n");
    }
    return 0;
}

Bun FFI调用代码(创建识别器失败)

运行时创建audioConfig和keywordModel返回SPX_NOERROR(0),表示成功,但创建recognizer时返回无对应错误码的大数值,导致失败。相关代码如下:

import { dlopen, FFIType, CString, ptr } from "bun:ffi";

const cwd = process.cwd();

// Enum derived from the SDK
enum ResultReason {
    NoMatch = 0,
    Canceled = 1,
    RecognizingSpeech = 2,
    RecognizedSpeech = 3,
    RecognizingIntent = 4,
    RecognizedIntent = 5,
    TranslatingSpeech = 6,
    TranslatedSpeech = 7,
    SynthesizingAudio = 8,
    SynthesizingAudioComplete = 9,
    RecognizingKeyword = 10,
    RecognizedKeyword = 11,
    SynthesizingAudioStart = 12,
}

const speechSdkPath = `${cwd}/speechsdk/lib/x64/libMicrosoft.CognitiveServices.Speech.core.so`;

// Load the Speech SDK shared library
const speechSdk = dlopen(speechSdkPath, {
    audio_config_create_audio_input_from_wav_file_name: {
        args: [FFIType.cstring, FFIType.ptr],
        returns: FFIType.u64_fast,
    },
    keyword_recognition_model_create_from_file: {
        args: [FFIType.cstring, FFIType.ptr],
        returns: FFIType.u64_fast,
    },
    recognizer_create_keyword_recognizer_from_audio_config: {
        args: [FFIType.ptr, FFIType.ptr],
        returns: FFIType.u64_fast,
    },
    recognizer_recognize_keyword_once: {
        args: [FFIType.ptr, FFIType.ptr, FFIType.ptr],
        returns: FFIType.u64_fast,
    },
    result_get_reason: {
        args: [FFIType.ptr, FFIType.ptr],
        returns: FFIType.u64_fast,
    },
    result_get_text: {
        args: [FFIType.ptr, FFIType.ptr, FFIType.u32],
        returns: FFIType.u64_fast,
    },
});

const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();

const checkResult = (result: number | bigint) => {
    if (result !== 0) {
        throw new Error(`Error: ${result}`);
    }
};

// Replace these with the actual file paths
const audioFilePath = textEncoder.encode(`${cwd}/file.wav`);
const keywordModelFilePath = textEncoder.encode(
    `${cwd}/keyword_models/hey_bumblebee.table`
);

// Create audio config
console.log(
    `Creating audio config from file: ${textDecoder.decode(audioFilePath)}`
);
const audioConfig = ptr(new Uint8Array(8));
const audioConfigResult =
    speechSdk.symbols.audio_config_create_audio_input_from_wav_file_name(
        audioFilePath,
        audioConfig
    );
checkResult(audioConfigResult);
console.log("Created audio config.");

// Create keyword model
console.log(
    `Creating keyword model from file: ${textDecoder.decode(
        keywordModelFilePath
    )}`
);
const keywordModel = ptr(new Uint8Array(8));
const keywordModelResult =
    speechSdk.symbols.keyword_recognition_model_create_from_file(
        keywordModelFilePath,
        keywordModel
    );
checkResult(keywordModelResult);
console.log("Created keyword model.");

// Create recognizer
console.log("Creating recognizer...");
const recognizer = ptr(new Uint8Array(8));
const recognizerResult =
    speechSdk.symbols.recognizer_create_keyword_recognizer_from_audio_config(
        recognizer,
        audioConfig
    );
console.log();
checkResult(recognizerResult!);
console.log("Created recognizer.");

// // Start recognition
// console.log("Starting recognition...");
// const resultHandle = ptr(new Uint8Array(16));
// const recognizeKeywordResult =
//  speechSdk.symbols.recognizer_recognize_keyword_once(
//      recognizer,
//      keywordModel,
//      resultHandle
//  );
// checkResult(recognizeKeywordResult);
// console.log("Recognition finished.");
//
// // Get result reason
// console.log("Getting result reason...");
// const reason = ptr(new Uint8Array(8));
// const reasonResult = speechSdk.symbols.result_get_reason(resultHandle, reason);
// checkResult(reasonResult);
// console.log("Got result reason:", new CString(reason));
//
// // Check the reason and handle accordingly
// if (reason === ResultReason.RecognizedKeyword) {
//  // Assuming a buffer size, adjust as needed
//  const textBuffer = new Uint8Array(256);
//  const textResult = speechSdk.symbols.result_get_text(
//      resultHandle,
//      textBuffer,
//      textBuffer.length
//  );
//  checkResult(textResult);
//  console.log("Recognized:", new CString(ptr(textBuffer)));
// } else {
//  console.error("Recognition failed:", reason);
// }

疑问

不确定是否是符号类型映射错误导致创建识别器失败,寻求解决办法。


内容的提问来源于stack exchange,提问作者ChevCast

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 20:27:05