Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions tools/omnivoice/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,26 @@ target_include_directories(omnivoice-test-abi-c PRIVATE
${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(omnivoice-test-abi-c PRIVATE omnivoice_lib)

# omnivoice-test-diarizer-metadata : metadata-only GGUF contract test for the
# vendored diarizer reader. It writes tiny GGUF files, so it does not need the
# pyannote model artifact.
add_executable(omnivoice-test-diarizer-metadata tests/diarizer-metadata.c)
set_target_properties(omnivoice-test-diarizer-metadata PROPERTIES
C_STANDARD 99
C_STANDARD_REQUIRED ON
C_EXTENSIONS ON)
if(MSVC)
target_compile_options(omnivoice-test-diarizer-metadata PRIVATE /W4 /WX)
else()
target_compile_options(omnivoice-test-diarizer-metadata PRIVATE -Wall -Werror -pedantic)
endif()
target_include_directories(omnivoice-test-diarizer-metadata PRIVATE
${OMNIVOICE_VOICE_CLASSIFIER_INCLUDE_DIRS})
target_link_libraries(omnivoice-test-diarizer-metadata PRIVATE eliza_voice_classifiers)
if(BUILD_TESTING)
add_test(NAME omnivoice-test-diarizer-metadata COMMAND omnivoice-test-diarizer-metadata)
endif()

# omnivoice-dac-parity : focused CPU numerical parity harness for
# dac_conv_t1d's ggml_conv_transpose_1d migration (elizaOS/eliza#7660).
# The default run uses deterministic synthetic DAC block shapes; pass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
#define DIAR_LINEAR0_OUT 128
#define DIAR_LINEAR1_OUT 128
#define DIAR_LEAKY_ALPHA 0.01f
#define DIAR_GGUF_CONVERTER_EPOCH 2
#define DIAR_LSTM_GATE_ORDER "IFGO"

/* Cached pointers + buffer struct for one diarizer session. */
struct voice_diarizer_session {
Expand Down Expand Up @@ -223,12 +225,11 @@ static void lstm_run_dir(const float *x_dot_W,
gate_buf[g] = acc;
}

/* Apply nonlinearities. Gate order I, O, F, C (ONNX IOFC layout —
* pyannote-3 segmentation GGUF, #9460). */
/* Apply nonlinearities. Gate order I, F, G, O. */
const float *gi = gate_buf + 0 * H;
const float *go = gate_buf + 1 * H;
const float *gf = gate_buf + 2 * H;
const float *gg = gate_buf + 3 * H;
const float *gf = gate_buf + 1 * H;
const float *gg = gate_buf + 2 * H;
const float *go = gate_buf + 3 * H;
for (int j = 0; j < H; ++j) {
const float i_t = sigmoidf(gi[j]);
const float f_t = sigmoidf(gf[j]);
Expand Down Expand Up @@ -393,6 +394,33 @@ int voice_diarizer_open(const char *gguf, voice_diarizer_handle *out) {
meta.sample_rate != VOICE_CLASSIFIER_SAMPLE_RATE_HZ) return -EINVAL;
if (meta.num_classes != 0 &&
meta.num_classes != VOICE_DIARIZER_NUM_CLASSES) return -EINVAL;
if (meta.window_samples != 0 &&
meta.window_samples != DIAR_WINDOW_SAMPLES) return -EINVAL;
if (meta.frames_per_window != 0 &&
meta.frames_per_window != DIAR_FRAMES_PER_WINDOW) return -EINVAL;
if (meta.lstm_layers != 0 &&
meta.lstm_layers != DIAR_LSTM_LAYERS) return -EINVAL;
if (meta.lstm_hidden != 0 &&
meta.lstm_hidden != DIAR_LSTM_HIDDEN) return -EINVAL;
if (meta.linear0_out != 0 &&
meta.linear0_out != DIAR_LINEAR0_OUT) return -EINVAL;
if (meta.linear1_out != 0 &&
meta.linear1_out != DIAR_LINEAR1_OUT) return -EINVAL;
if (meta.converter_epoch < DIAR_GGUF_CONVERTER_EPOCH) {
fprintf(stderr,
"[voice_diarizer] stale GGUF converter epoch %d; need >= %d with LSTM gates packed as %s\n",
meta.converter_epoch,
DIAR_GGUF_CONVERTER_EPOCH,
DIAR_LSTM_GATE_ORDER);
return -EINVAL;
}
if (strcmp(meta.lstm_gate_order, DIAR_LSTM_GATE_ORDER) != 0) {
fprintf(stderr,
"[voice_diarizer] unsupported LSTM gate order '%s'; expected %s\n",
meta.lstm_gate_order[0] ? meta.lstm_gate_order : "<missing>",
DIAR_LSTM_GATE_ORDER);
return -EINVAL;
}

struct voice_diarizer_session *s =
(struct voice_diarizer_session *)calloc(1, sizeof(*s));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,17 @@ static int vc_gguf_load_state_cb(const char *key,
free(str);
return 1;
}
if (vc_gguf_key_eq(key, s->want_prefix, "lstm_gate_order")) {
if (type != VC_GGUF_TYPE_STRING) return -1;
char *str = NULL;
const int rc = vc_gguf_read_string(f, &str);
if (rc != 0) return -1;
const size_t n = sizeof(s->out->lstm_gate_order) - 1;
strncpy(s->out->lstm_gate_order, str, n);
s->out->lstm_gate_order[n] = '\0';
free(str);
return 1;
}
/* Wav2Small-specific uint32 keys: read into the matching field
* and let the per-head opener validate against its expectation. */
struct {
Expand All @@ -239,6 +250,13 @@ static int vc_gguf_load_state_cb(const char *key,
{ "ffn_dim", &s->out->ffn_dim },
{ "num_layers", &s->out->num_layers },
{ "num_heads", &s->out->num_heads },
{ "converter_epoch", &s->out->converter_epoch },
{ "window_samples", &s->out->window_samples },
{ "frames_per_window", &s->out->frames_per_window },
{ "lstm_layers", &s->out->lstm_layers },
{ "lstm_hidden", &s->out->lstm_hidden },
{ "linear0_out", &s->out->linear0_out },
{ "linear1_out", &s->out->linear1_out },
};
for (size_t i = 0; i < sizeof(u32_extras) / sizeof(u32_extras[0]); ++i) {
if (vc_gguf_key_eq(key, s->want_prefix, u32_extras[i].suffix)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ typedef struct voice_gguf_metadata {
int ffn_dim;
int num_layers;
int num_heads;
/* Diarizer-specific metadata. Older published GGUFs leave these
* zero/empty; the diarizer opener treats missing gate metadata as legacy
* and validates explicit metadata against the reader's compiled contract.
*/
int converter_epoch;
int window_samples;
int frames_per_window;
int lstm_layers;
int lstm_hidden;
int linear0_out;
int linear1_out;
char lstm_gate_order[16];
} voice_gguf_metadata_t;

/* GGUF GGML tensor data types — we only support F32 in the per-head
Expand Down
Loading
Loading