CCNSV × DSpotter

Cyberon Linux / Android integration guide

先找出喚醒詞,
再確認是誰說的。

DSpotter KWS 負責找到 trigger phrase;CNSV VID 負責訓練或驗證說話者。 兩套 engine 共用音訊,由應用程式在命中時串起結果。

2 handlesDSpotter 與 CNSV 各自初始化、重置與釋放
1 audio stream同一份 16 kHz/mono/PCM16 同時餵給兩邊
1 hit windowDSpotter timing 轉成 CNSV ring buffer 的回溯區間

01 / interaction

兩套獨立 API,一條應用層資料流

Cyberon 沒有合併的 VID+KWS API。Sample app 持有兩個 handle,將每批 PCM 同時送入兩套 engine。

INPUT

PCM audio

16,000 Hz、單聲道、signed 16-bit。Linux 使用 SHORT*,Android 使用 short[]

DUAL FEED

兩邊都持續收音

DSpotter偵測 trigger phrase
CNSV累積 ring buffer
ON HIT

訓練或驗證

DSpotter 回傳命中區間;CNSV 擷取該段音訊,執行 TrainSpeakerGetResult

02 / lifecycle

從初始化到下一次喚醒

以下順序對應 Linux sample。Android 的初始化先後相反,但開始處理前同樣需要兩個有效 handle。

準備 license 與模型

初始化需要 Cyberon license、DSpotter command pack、CNSV pretrained model。驗證模式另載入已註冊的 speaker models。

建立 CNSV handle

Linux sample 設 10 秒 ring buffer、最多 10 位 speaker、1 個 ONNX thread,並使用 batch mode。數值 10 是 sample 設定,不是 SDK 保證的硬上限。

hCNSV = CNSVApi_Init(
    license, 10, 10, svModel,
    1, FALSE, &err, NULL, NULL
);
CNSVApi_SetThreshold(hCNSV, threshold);

建立 DSpotter handle

先從 pack 取得 group 數並選擇要啟用的 group,再建立 KWS engine。建立後可以列舉 pack 內的 commands。

int groups = DSpotterGetNumGroup(packBin);
// enableGroup[0..groups-1] = TRUE
hKWS = DSpotterInitMultiWithPackBin(
    packBin, enableGroup, 500,
    NULL, 0, &err, license, NULL
);

每一批音訊同時餵給兩邊

nNumSample 是 sample 數,不是 byte 數。應用程式必須維持相同的連續音訊與順序。

int kwsRet = DSpotterAddSample(hKWS, pcm, sampleCount);
int svRet  = CNSVApi_AddSample(hCNSV, pcm, sampleCount);

if (kwsRet == DSPOTTER_SUCCESS) {
    // retrieve result and verify speaker
}

把 KWS 命中區間交給 CNSV

DSpotter 回傳 word duration、end silence 與 network latency。Sample 將它們直接組成 CNSV 的回溯座標。

nStart = wordDura + endSil + latency nEnd = endSil + latency
DSpotterGetUTF8Result(hKWS, &cmd, result,
    &wordDura, &endSil, &latency,
    &confidence, &sgDiff, &fil);

// Enrollment
CNSVApi_TrainSpeaker(hCNSV, nStart, nEnd, &speakerModel);

// Verification
CNSVApi_GetResult(hCNSV, nStart, nEnd,
    &speakerId, speakerName, &score);

完成一次事件,再繼續偵測

每次 hit 處理後清除 CNSV 的音訊狀態,並讓 DSpotter 進入下一輪。程式結束時分別釋放兩個 handle。

CNSVApi_Reset(hCNSV);
DSpotterContinue(hKWS);

// shutdown
CNSVApi_Release(hCNSV);
DSpotterRelease(hKWS);

03 / audio contract

音訊格式與 buffer 語意

CNSV 與 DSpotter sample 使用同一種格式,所以能直接 dual-feed,不需要 resample 或型別轉換。

項目契約整合提醒
Sample rate16,000 HzAndroid recognizer 由 DSpotterGetSampleRate() 建 recorder
ChannelMono不要直接餵 interleaved stereo
Sample typeSigned PCM16Linux SHORT*;Android short[]
Input countSample 數Linux sample 使用 bytes / sizeof(SHORT)
Frame sizeAPI 未規定固定大小Android demo 自選 30 ms=480 samples=960 bytes
nStart/nEnd相對最新 sample 的回溯距離不是絕對 timestamp
BATCH MODE · SAMPLE USED

先存音訊,命中後才推論

CNSVApi_AddSample() 只累積資料,通常回 CNSVApi_Err_NeedMoreSample。Inference 在 TrainSpeakerGetResultGetBestResult 發生。

STREAMING MODE · AVAILABLE

輸入期間增量推論

若初始化開啟 streaming,CNSV 可在 AddSample 期間執行 inference;觸發 inference 時回 success。目前 Linux 與 Android demo 都沒有採用此模式。

04 / public surface

API 責任地圖

實作時以交付的 header 或 Java JNI facade 為準;Programming Guide 主要用來確認 CNSV 的行為語意。

CNSV VID

17 支 public APIs

  • Lifecycle: InitResetReleaseGetVersion
  • Audio/result: AddSampleGetResultGetBestResultSetThreshold
  • Training: TrainSpeaker
  • Registry: speaker 的 add、remove、count、get model
  • Persistence: SaveSpeakerToFileLoadSpeakerFromFile
DSPOTTER KWS

KWS、結果與 tuning

  • Lifecycle/model: init、reset、release、group、version
  • Commands/audio: command enumeration、sample rate、add sample
  • Results: UTF-8/UTF-16、NoWait、N-best、continue
  • Tuning: confidence、SGDiff、end silence、energy、per-command
  • Utilities: AGC callback、combine pack bins
平台CNSVDSpotter主要差異
Linux x86-64C API/libCNSV.soC API/libDSpotter.so提供 UTF-16 result 與 command APIs
Android arm64-v8aJava/JNI/long handleJava/JNI/long handle保留 deprecated tuning,另有 result ID mapping setter
Android armeabi-v7a有 CNSV native library交付物中沒有 DSpotter native library不能視為完整 KWS+VID 組合

05 / verify before production

四個不能從 sample 擴大推論的地方

DOCUMENT MISMATCH

Android PDF 與 JNI facade 不同版

PDF 的 Init 帶 VAD runtime/model,並列出 SetNormalize 與 ByID APIs;實際 CNSV.java 使用 ONNX thread、streaming flag 與 ByIndex APIs。

UNDOCUMENTED UNIT

DSpotter timing 單位未寫明

Sample 把 timing 當成 CNSV sample offsets,但 DSpotter header 沒有正式定義單位、範圍或跨版本穩定性。

RETURN VALUE

TrainSpeaker() 成功值有衝突

Guide 寫 success 為 0;Linux sample 將正值當成累積 reference-vector 數量。正式 wrapper 應先向 vendor 確認。

CAPACITY & LICENSE

10 位 speaker 只是 demo 設定

SDK 還有 exceed-max-speaker 與 license-count-exceeded 錯誤。生產上限、thread safety 與 instance 數量都需要正式規格。

Evidence map

本教學依據

Official guides:Windows/Linux CNSV Programming Guide §3;Android CNSV Programming Guide §3;Android CNSVDemo User Guide §1、§3。

Linux source:CNSVApi.hDSpotterApi.hDSpotterApi_Const.hCNSVDemo.c

Android source:CNSV.javaDSpotter.javaSVTrainer.javaSVRecognizer.java

範圍:文件與原始碼分析;沒有宣稱已完成 live microphone、準確率、延遲或 thread-safety 驗證。