Initial release

This commit is contained in:
civ
2026-08-16 18:24:52 +07:00
commit 876886a39a
13244 changed files with 2353959 additions and 0 deletions
+202
View File
@@ -0,0 +1,202 @@
// ace-understand.cpp: audio understanding CLI (thin wrapper)
//
// Audio -> VAE encode -> FSQ tokenize -> LM understand -> metadata + lyrics
//
// Output: request JSON with metadata + lyrics, reusable as ace-lm or ace-synth input.
#include "audio-io.h"
#include "model-registry.h"
#include "model-store.h"
#include "pipeline-understand.h"
#include "request.h"
#include "version.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
static void usage(const char * prog) {
AceUnderstandParams d;
ace_understand_default_params(&d);
fprintf(stderr, "acestep.cpp %s\n\n", ACE_VERSION);
fprintf(stderr,
"Usage: %s --models <dir> --src-audio <file> [--request <json>] [options]\n"
"\n"
"Required:\n"
" --models <dir> Directory of GGUF model files\n"
" --src-audio <file> Source audio (WAV or MP3, any sample rate)\n"
"\n"
"Optional:\n"
" --request <json> Request JSON carrying model selection and\n"
" sampling params (lm_model, synth_model,\n"
" lm_temperature, lm_top_p, lm_top_k)\n"
"\n"
"When no --request is given, understand defaults apply\n"
"(temperature 0.3, top_p disabled).\n"
"\n"
"Output:\n"
" -o <json> Output JSON (default: stdout summary)\n"
"\n"
"Memory control:\n"
" --vae-chunk <N> Latent frames per tile (default: %d)\n"
" --vae-overlap <N> Overlap frames per side (default: %d)\n"
"\n"
"Debug:\n"
" --max-seq <N> KV cache size (default: %d)\n"
" --no-fsm Disable FSM constrained decoding\n"
" --no-fa Disable flash attention\n"
" --dump <dir> Dump tok_latents + tok_codes (skip LM)\n",
prog, d.vae_chunk, d.vae_overlap, d.max_seq);
}
int main(int argc, char ** argv) {
const char * models_dir = NULL;
const char * src_audio_path = NULL;
const char * request_path = NULL;
const char * output_path = NULL;
const char * dump_dir = NULL;
AceUnderstandParams params;
ace_understand_default_params(&params);
if (argc < 2) {
usage(argv[0]);
return 1;
}
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--models") && i + 1 < argc) {
models_dir = argv[++i];
} else if (!strcmp(argv[i], "--src-audio") && i + 1 < argc) {
src_audio_path = argv[++i];
} else if (!strcmp(argv[i], "--request") && i + 1 < argc) {
request_path = argv[++i];
} else if (!strcmp(argv[i], "-o") && i + 1 < argc) {
output_path = argv[++i];
} else if (!strcmp(argv[i], "--dump") && i + 1 < argc) {
dump_dir = argv[++i];
} else if (!strcmp(argv[i], "--max-seq") && i + 1 < argc) {
params.max_seq = atoi(argv[++i]);
} else if (!strcmp(argv[i], "--vae-chunk") && i + 1 < argc) {
params.vae_chunk = atoi(argv[++i]);
} else if (!strcmp(argv[i], "--vae-overlap") && i + 1 < argc) {
params.vae_overlap = atoi(argv[++i]);
} else if (!strcmp(argv[i], "--no-fsm")) {
params.use_fsm = false;
} else if (!strcmp(argv[i], "--no-fa")) {
params.use_fa = false;
} else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) {
usage(argv[0]);
return 0;
} else {
fprintf(stderr, "Unknown option: %s\n", argv[i]);
usage(argv[0]);
return 1;
}
}
if (!models_dir) {
fprintf(stderr, "[CLI] ERROR: --models required\n");
usage(argv[0]);
return 1;
}
if (!src_audio_path) {
fprintf(stderr, "[CLI] ERROR: --src-audio required\n");
usage(argv[0]);
return 1;
}
// Parse request JSON (if provided). Sampling params come from JSON.
// When no JSON, understand defaults apply (temperature=0.3 for transcription).
AceRequest req;
request_init(&req);
req.lm_temperature = 0.3f; // understand default: lower than generation
req.lm_top_p = 1.0f; // understand default: no nucleus sampling
if (request_path) {
if (!request_parse(&req, request_path)) {
return 1;
}
request_dump(&req, stderr);
}
// Scan the registry and resolve model paths. Empty lm_model / synth_model
// fall to the first entry of their bucket, matching server default behavior.
ModelRegistry registry;
if (!registry_scan(&registry, models_dir)) {
fprintf(stderr, "[Ace-Understand] FATAL: cannot scan --models %s\n", models_dir);
return 1;
}
if (registry.dit.empty() || registry.vae.empty()) {
fprintf(stderr, "[Ace-Understand] FATAL: understand pipeline needs DiT and VAE models under %s\n", models_dir);
return 1;
}
if (!dump_dir && registry.lm.empty()) {
fprintf(stderr, "[Ace-Understand] FATAL: understand pipeline needs an LM model under %s\n", models_dir);
return 1;
}
const ModelEntry * lm_entry =
dump_dir ? NULL : (req.lm_model.empty() ? &registry.lm[0] : registry_find(registry.lm, req.lm_model.c_str()));
if (!dump_dir && !lm_entry) {
fprintf(stderr, "[Ace-Understand] FATAL: lm_model '%s' not found in registry\n", req.lm_model.c_str());
return 1;
}
const ModelEntry * dit_entry =
req.synth_model.empty() ? &registry.dit[0] : registry_find(registry.dit, req.synth_model.c_str());
if (!dit_entry) {
fprintf(stderr, "[Ace-Understand] FATAL: synth_model '%s' not found in registry\n", req.synth_model.c_str());
return 1;
}
params.model_path = lm_entry ? lm_entry->path.c_str() : NULL;
params.dit_path = dit_entry->path.c_str();
params.vae_path = registry.vae[0].path.c_str();
params.dump_dir = dump_dir;
// load pipeline
ModelStore * store = store_create(EVICT_STRICT);
AceUnderstand * ctx = ace_understand_load(store, &params);
if (!ctx) {
store_free(store);
return 1;
}
// Read and resample audio to 48kHz stereo
int T_audio = 0;
float * planar = audio_read_48k(src_audio_path, &T_audio);
if (!planar) {
fprintf(stderr, "[Ace-Understand] FATAL: cannot read %s\n", src_audio_path);
ace_understand_free(ctx);
store_free(store);
return 1;
}
fprintf(stderr, "[Ace-Understand] %.2fs @ 48kHz\n", (float) T_audio / 48000.0f);
float * src_interleaved = audio_planar_to_interleaved(planar, T_audio);
free(planar);
int src_len = T_audio;
// run understand pipeline
AceRequest out;
int rc = ace_understand_generate(ctx, src_interleaved, src_len,
nullptr, 0, // src_latents (audio path)
&req, &out,
nullptr, nullptr, // latent_out, T_latent_out
NULL, NULL);
free(src_interleaved);
ace_understand_free(ctx);
store_free(store);
if (rc != 0) {
return 1;
}
// write output JSON
if (output_path) {
request_write(&out, output_path);
}
return 0;
}