Skip to content

snsr-edit.c

This is the source code for the snsr-edit command-line tool.

Instructions

See snsr-edit.

Code

Available in this TrulyNatural SDK installation at ~/Sensory/TrulyNaturalSDK/7.6.1/sample/c/src/snsr-edit.c

snsr-edit.c

/* Sensory Confidential
 * Copyright (C)2016-2025 Sensory, Inc. https://sensory.com/
 *
 * TrulyHandsfree SDK command-line model editing utility.
 *------------------------------------------------------------------------------
 */

#include <snsr.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TASK_VERSION "~0.8.0 || 1.0.0"

#define DEFAULT_INIT_FILENAME  "snsr-custom-init.c";


static void
fatal(SnsrSession s, int rc, const char *format, ...)
{
  va_list a;
  fprintf(stderr, "ERROR: ");
  va_start(a, format);
  vfprintf(stderr, format, a);
  va_end(a);
  fprintf(stderr, "\n");
  snsrRelease(s);
  exit(rc);
}


static const char *usageDetail =
  "Settings are strings used as keys to query or change task behavior.\n"
  "Most frequently used are operating-point for wake words and command sets,\n"
  "leading-silence and trailing-silence for VAD templates,\n"
  "partial-result-interval for LVCSR and STT, and stt-profile for STT models.\n"
  "Refer to the " SNSR_NAME " SDK documentation for a complete list and\n"
  "descriptions of all supported settings.\n";

static void
usage(const char *name)
{
  SnsrSession s;
  const char *libInfo;

  fprintf(stderr, "Edits/modifies " SNSR_NAME " SDK .snsr model files.\n\n");

  fprintf(stderr,
          "usage: %s -t task [options]\n"
          " options:\n"
          "  -C tag-identifier   : emit C source to load model into RAM\n"
          "  -c tag-identifier   : emit C source to run model from code space\n"
          "  -e setting filename : extract task setting/slot into filename\n"
          "  -f setting filename : load filename into task setting/slot\n"
          "  -g setting value    : load string into task setting\n"
          "  -i                  : emit custom initialization code\n"
          "  -o out              : output filename\n"
          "  -p                  : prune unused settings to reduce model size\n"
          "  -q setting          : query a task setting\n"
          "  -s setting=value    : override a task setting\n"
          "  -t task             : specify task filename (required)\n"
          "  -v [-v [-v]]        : increase verbosity\n", name);
  fprintf(stderr, "\n%s", usageDetail);

  snsrNew(&s);
  snsrGetString(s, SNSR_LIBRARY_INFO, &libInfo);
  fprintf(stderr, "\n%s\n", libInfo);
  snsrRelease(s);
  exit(199);
}


/* Report command-line argument errors.
 */
static void
quitOnError(SnsrSession s)
{
  SnsrRC r = snsrRC(s);
  if (r == SNSR_RC_NO_MODEL)
    fatal(s, r, "set -t task before -f, -q, or -s options");
  if (r != SNSR_RC_OK) fatal(s, r, "%s", snsrErrorDetail(s));
}


/* Report model license keys.
 */
static void
reportModelLicense(SnsrSession s, const char *modelfile, int verbose)
{
  const char *msg = NULL;

  if (verbose > 1) {
    snsrGetString(s, SNSR_MODEL_LICENSE_EXPIRES, &msg);
    if (msg)
      fprintf(stderr, "\"%s\": %s.\n", modelfile, msg);
  }
  msg = NULL;
  snsrGetString(s, SNSR_MODEL_LICENSE_WARNING, &msg);
  if (msg)
    fprintf(stderr, "WARNING for model \"%s\": %s.\n", modelfile, msg);
}


int
main(int argc, char *argv[])
{
  SnsrRC r;
  SnsrSession s;
  int customInit = 0, o, prune = 0, verbose = 0, useRAM = 0;
  const char *msg = NULL, *out = NULL, *task = NULL, *tag = NULL;
  char *outPath = NULL;
  extern char *optarg;
  extern int optind;
#ifdef SNSR_USE_SECURITY_CHIP
  uint32_t *securityChipComms(uint32_t *in);
  snsrConfig(SNSR_CONFIG_SECURITY_CHIP, securityChipComms);
#endif

  if (argc == 1) usage(argv[0]);
  r = snsrNew(&s);
  if (r != SNSR_RC_OK)
    fatal(s, r, "%s", s? snsrErrorDetail(s): snsrRCMessage(r));
  snsrSetString(s, SNSR_PREPARE_SUBSET_INIT, NULL);
  snsrSetString(s, SNSR_PRUNE_SETTINGS, "no");

  while ((o = getopt(argc, argv, "aC:c:e:f:g:io:pq:s:t:v?")) >= 0) {
    switch (o) {
    case 'C':
      useRAM = 1;
    case 'c':
      tag = optarg;
      break;
    case 'e':
      if (optind >= argc) usage(argv[0]);
      {
        SnsrStream slot, out;
        snsrGetStream(s, optarg, &slot);
        quitOnError(s);
        out = snsrStreamFromFileName(argv[optind], "w");
        snsrRetain(out);
        if (verbose > 1)
          printf("Saving setting \"%s\" into \"%s\".\n", optarg, argv[optind]);
        snsrStreamCopy(out, slot, SIZE_MAX);
        if (snsrStreamRC(out) != SNSR_RC_EOF)
          fatal(s, snsrStreamRC(out), "%s", snsrStreamErrorDetail(out));
        snsrRelease(out);
        optind++;
      }
      break;
    case 'f':
      if (optind >= argc) usage(argv[0]);
      if (verbose > 1)
        printf("Loading \"%s\" into setting \"%s\".\n", argv[optind], optarg);
      snsrSetStream(s, optarg, snsrStreamFromFileName(argv[optind++], "r"));
      quitOnError(s);
      reportModelLicense(s, argv[optind - 1], verbose);
      break;
    case 'g':
      if (optind >= argc) usage(argv[0]);
      if (verbose > 1)
        printf("Loading \"%s\" into setting \"%s\".\n", argv[optind], optarg);
      snsrSetStream(s, optarg, snsrStreamFromString(argv[optind++]));
      quitOnError(s);
      break;
    case 'i':
      customInit = 1;
      if (!out) out = DEFAULT_INIT_FILENAME;
      break;
    case 'o':
      out = optarg;
      break;
    case 'p':
      prune = 1;
      break;
    case 'q': {
      const char *strVal = NULL;
      if (snsrGetString(s, optarg, &strVal) == SNSR_RC_OK) {
        const char *q = strVal && strchr(strVal, ' ')? "\"": "";
        printf("%s = %s%s%s\n", optarg, q, strVal, q);
      }
      quitOnError(s);
      break;
    }
    case 's':
      if (verbose > 2)
        printf("Applying setting \"%s\".\n", optarg);
      snsrSet(s, optarg);
      quitOnError(s);
      break;
    case 't':
      if (verbose > 1)
        printf("Loading \"%s\" as the template model.\n", optarg);
      snsrLoad(s, snsrStreamFromFileName(optarg, "r"));
      if (!task) task = optarg;
      quitOnError(s);
      reportModelLicense(s, optarg, verbose);
      break;
    case 'v': verbose++; break;
    case '?':
    default:  usage(argv[0]);
    }
  }
  r = snsrRequire(s, SNSR_TASK_VERSION, TASK_VERSION);
  if (r == SNSR_RC_NO_MODEL || optind != argc) usage(argv[0]);

  /* Report application license status */
  if (verbose > 1) {
    snsrGetString(s, SNSR_LICENSE_EXPIRES, &msg);
    if (msg) fprintf(stderr, "\"%s\": %s.\n", argv[0], msg);
  }
  msg = NULL;
  snsrGetString(s, SNSR_LICENSE_WARNING, &msg);
  if (msg) fprintf(stderr, "WARNING for \"%s\": %s.\n", argv[0], msg);

  snsrSetString(s, SNSR_MODEL_NAME, task);

  if (tag) {
    SnsrDataFormat fmt = SNSR_FM_SOURCE;
    if (!out) {
      char *t;
      if (!(out = outPath = malloc(strlen(task) + 3)))
        fatal(s, SNSR_RC_NO_MEMORY, "%s", snsrRCMessage(SNSR_RC_NO_MEMORY));
      strcpy(outPath, task);
      if ((t = strrchr(outPath, '.'))) *t = '\0';
      strcat(outPath, ".c");
      if ((t = strrchr(outPath, '/')) || (t = strrchr(outPath, '\\'))) {
        *t = '\0';
        out = t + 1;
      }
    }
    snsrSetString(s, SNSR_TAG_IDENTIFIER, tag);
    if (useRAM)     fmt = SNSR_FM_SOURCE_RAM;
    else if (prune) fmt = SNSR_FM_SOURCE_PRUNED;
    r = snsrSave(s, fmt, snsrStreamFromFileName(out, "w"));

  } else if (customInit) {
    r = snsrSave(s, SNSR_FM_SUBSET_INIT, snsrStreamFromFileName(out, "w"));

  } else if (out) {
    r = snsrSave(s, prune? SNSR_FM_CONFIG_PRUNED: SNSR_FM_CONFIG,
                 snsrStreamFromFileName(out, "w"));
  }

  if (r != SNSR_RC_OK) fatal(s, r, "%s", snsrErrorDetail(s));
  if (out && verbose > 0)
    printf("Output written to \"%s\".\n", out);
  free(outPath);
  snsrRelease(s);
  snsrTearDown();
  return 0;
}