Skip to content

C examples

The C sample programs are available in sample/c/ in the TrulyNatural installation directory.

See ~/Sensory/TrulyNaturalSDK/7.7.0/sample/c/

You can build the sample code with CMake or with GNU Make.

Examples

live-spot.c
Shows how to run a wake word recognizer on live audio captured from the default audio source.
live-spot-stream.c
Runs a wake word recognizer on live audio captured using a custom audio stream, defined in alsa-stream.c.
live-segment.c
Runs a wake word recognizer on live audio, segments the speech following the wake word with a VAD, and then saves this audio snippet to a file.
push-audio.c
Runs a recognizer where the application pushes data through the recognition pipeline. Shows VAD audio processing for use with third-party recognizers such as keyword-to-search applications.
spot-data.c
Runs a small keyword spotter from code space. It uses a custom memory allocator to avoid calls to the system heap allocator, and reads audio data from code space to avoid file system use.
spot-data-stream.c
This example runs a wake word from code space with a custom audio stream, using pull mode processing with run. It is a reasonable starting point for running on a small device with an RTOS.
alsa-stream.c
Source for the fromAudioDevice Stream implementation for ALSA, used for live audio capture on Linux.
aqs-stream.c
Source for the fromAudioDevice Stream implementation for Audio Queue Services, used for live audio capture on macOS and iOS.
wmme-stream.c
Source for the fromAudioDevice Stream implementation for Windows Multimedia Extensions, used for live audio capture on Windows.
data-stream.c
This is the source for the fromAudioDevice Stream implementation for memory data, similar to fromMemory. It's used in the spot-data-stream.c example.
snsr-edit.c
Source for the snsr-edit command-line tool.
snsr-eval.c
Source for the snsr-eval command-line tool, and the snsr-eval-subset sample.
spot-convert.c
Source for the spot-convert command-line tool.
spot-enroll.c
Source for the spot-enroll command-line tool.
live-enroll.c
Source for the live-enroll command-line tool.

Build with CMake

We support building the code samples with CMake on Linux, macOS, and Windows. This requires CMake 3.10 or later and a compiler toolchain.

Open a terminal window and enter the commands below.

cd ~/Sensory/TrulyNaturalSDK/7.7.0/sample/c
cmake -S . -B build-sample
cmake --build build-sample --parallel --config Release
cmake --install build-sample

This installs the sample executables in the bin/ subdirectory.

CMakeLists.txt

These are the cmake configuration files used to build the sample code.

CMakeLists.txt
# Sensory Confidential
# Copyright (C)2024-2026 Sensory, Inc. https://sensory.com/
#
# TrulyNatural SDK sample code build configuration
#
# Configure, build, and install these samples with:
#
# cmake -S . -B build-sample
# cmake --build build-sample --parallel --config Release
# cmake --install build-sample
#
# Then find the sample executables in build-sample/bin/

cmake_minimum_required(VERSION 3.10.0)

if(POLICY CMP0177)
  cmake_policy(SET CMP0177 NEW)
endif()

project(SnsrSamples)

list(APPEND CMAKE_MODULE_PATH "~/Sensory/TrulyNaturalSDK/7.7.0")
include(SnsrLibrary)

add_subdirectory(src)
src/CMakeLists.txt
# Sensory Confidential
# Copyright (C)2024-2026 Sensory, Inc. https://sensory.com/
#
# This is not a stand-alone configuration. See by ../CMakeLists.txt

set(SAMPLE_BINARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../bin)
set(MODEL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../model)
set(SRC_GEN ${PROJECT_BINARY_DIR}/src)
set(SPT_HBG spot-hbg-enUS-1.4.0-m)
set(TPL_VAD tpl-vad-lvcsr-3.17.0)

add_executable(live-enroll live-enroll.c)
target_link_libraries(live-enroll SnsrLibraryOmitOSS)
install(TARGETS live-enroll DESTINATION ${SAMPLE_BINARY_DIR})

add_executable(live-segment live-segment.c)
target_link_libraries(live-segment SnsrLibraryOmitOSS)
install(TARGETS live-segment DESTINATION ${SAMPLE_BINARY_DIR})

add_executable(live-spot live-spot.c)
target_link_libraries(live-spot SnsrLibrary)
install(TARGETS live-spot DESTINATION ${SAMPLE_BINARY_DIR})

if (APPLE)
  add_executable(live-spot-stream live-spot-stream.c aqs-stream.c)
  target_link_libraries(live-spot-stream SnsrLibrary)
  install(TARGETS live-spot-stream DESTINATION ${SAMPLE_BINARY_DIR})
elseif (UNIX)
  add_executable(live-spot-stream live-spot-stream.c alsa-stream.c)
  target_link_libraries(live-spot-stream SnsrLibrary)
  install(TARGETS live-spot-stream DESTINATION ${SAMPLE_BINARY_DIR})
elseif (WIN32)
  add_executable(live-spot-stream live-spot-stream.c wmme-stream.c)
  target_link_libraries(live-spot-stream SnsrLibrary)
  install(TARGETS live-spot-stream DESTINATION ${SAMPLE_BINARY_DIR})
endif ()

add_executable(push-audio push-audio.c)
target_link_libraries(push-audio SnsrLibrary)
install(TARGETS push-audio DESTINATION ${SAMPLE_BINARY_DIR})

add_executable(snsr-edit snsr-edit.c)
target_link_libraries(snsr-edit SnsrLibrary)
install(TARGETS snsr-edit DESTINATION ${SAMPLE_BINARY_DIR})

add_custom_command(
  OUTPUT ${SRC_GEN}/${SPT_HBG}.c
  COMMAND snsr-edit
    -c spot_hbg_enUS
    -t ${MODEL_DIR}/${SPT_HBG}.snsr
  DEPENDS snsr-edit
)

add_custom_command(
  OUTPUT ${SRC_GEN}/${TPL_VAD}.c
  COMMAND snsr-edit
    -c tpl_vad_lvcsr
    -t ${MODEL_DIR}/${TPL_VAD}.snsr
  DEPENDS snsr-edit
)

add_executable(snsr-eval snsr-eval.c
               ${SRC_GEN}/${TPL_VAD}.c)
target_link_libraries(snsr-eval SnsrLibrary)
install(TARGETS snsr-eval DESTINATION ${SAMPLE_BINARY_DIR})

add_custom_command(
  OUTPUT ${SRC_GEN}/snsr-custom-init.c
  COMMAND snsr-edit
    -it ${MODEL_DIR}/${SPT_HBG}.snsr
  DEPENDS snsr-edit
)

add_executable(snsr-eval-subset snsr-eval.c
               ${SRC_GEN}/${TPL_VAD}.c
               ${SRC_GEN}/snsr-custom-init.c)
target_link_libraries(snsr-eval-subset SnsrLibrary)
target_compile_options(snsr-eval-subset PRIVATE -DSNSR_USE_SUBSET)
install(TARGETS snsr-eval-subset DESTINATION ${SAMPLE_BINARY_DIR})

add_executable(spot-convert spot-convert.c)
target_link_libraries(spot-convert SnsrLibraryOmitOSS)
install(TARGETS spot-convert DESTINATION ${SAMPLE_BINARY_DIR})

add_executable(spot-data spot-data.c data.c
              ${SRC_GEN}/${SPT_HBG}.c)
target_link_libraries(spot-data SnsrLibrary)
install(TARGETS spot-data DESTINATION ${SAMPLE_BINARY_DIR})

add_executable(spot-data-stream spot-data-stream.c data-stream.c data.c
               ${SRC_GEN}/${SPT_HBG}.c)
target_link_libraries(spot-data-stream SnsrLibrary)
install(TARGETS spot-data-stream DESTINATION ${SAMPLE_BINARY_DIR})

add_executable(spot-enroll spot-enroll.c)
target_link_libraries(spot-enroll SnsrLibraryOmitOSS)
install(TARGETS spot-enroll DESTINATION ${SAMPLE_BINARY_DIR})

Build with GNU Make

We support building the code samples with GNU Make on Linux and macOS only. This requires GNU Make 3.81 or later and a compiler toolchain.

Open a terminal window and enter the commands below.

cd ~/Sensory/TrulyNaturalSDK/7.7.0/sample/c
make -j all

This installs the sample executables in the bin/ subdirectory.

If you run make without arguments the Makefile lists all available targets:

% make
Make targets:

  make all      # build all executables in ./bin
  make clean    # remove build artifacts
  make debug    # build all with debugging enabled
  make help     # display this help message
  make test     # run enrollment and spotting tests

Building for macos from SDK root directory
../..

Run the sample tests:

% make -j -s test
Running test-enroll-0.
Running test-enroll-1.
Running test-enroll-2.
Running test-enroll-3.
Running test-convert-0.
Running test-data-0.
Running test-data-1.
Running test-subset-0.
./build/out/dsp-pc38-3.4.0-op10-prod-net.bin: OK
./build/out/dsp-pc38-3.4.0-op10-prod-search.bin: OK
./build/out/dsp-search-check.h: OK

Say the enrollment phrase (1/4) for "armadillo-1"
Recording:   1.88 s

Say the enrollment phrase (2/4) for "armadillo-1"
Recording:   1.71 s

Say the enrollment phrase (3/4) for "armadillo-1" with context,
  for example: "<phrase> will it rain tomorrow?"
Recording:   4.02 s

Say the enrollment phrase (4/4) for "armadillo-1" with context,
  for example: "<phrase> will it rain tomorrow?"
Recording:   2.91 s
Running test-push-0.
Running test-push-1.
SUCCESS: All tests passed.

Makefile

This is the make configuration file used to build and test the sample code.

Makefile
# Sensory Confidential
# Copyright (C)2015-2026 Sensory, Inc. https://sensory.com/
#
# TrulyNatural SDK GNU make build script

SNSR_ROOT := ../..


SNSR_EDIT = $(BIN_DIR)/snsr-edit
# This Makefile is meant to run on the target platform.
# Uncomment the following line if cross-compiling instead.
# SNSR_EDIT = $(TOOL_DIR)/snsr-edit

# OS-specific compiler defaults
OS_NAME := $(shell uname -s)

ifeq ($(OS_NAME),Linux)
# Linux
ARCH_NAME := $(shell $(CC) -dumpmachine)
OS_CFLAGS := -O3 -fPIC -DNDEBUG
OS_CFLAGS += -Wall -Werror
OS_CFLAGS += -fdata-sections -ffunction-sections
OS_LIBS   := -lsnsr -lasound -lpthread -lm -ldl -lstdc++
OS_LDFLAGS+= -Wl,--gc-sections
STATSIZE  := stat -c %s

else ifeq ($(OS_NAME),Darwin)
# macOS
ARCH_NAME := macos
ARCH := $(shell uname -m)
XCODE := /Applications/Xcode.app/Contents/Developer
SYSROOT := $(XCODE)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
CC := $(XCODE)/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
OS_ARCH   := -arch $(ARCH)
OS_CFLAGS := -O3 -fPIC -DNDEBUG
OS_CFLAGS += $(OS_ARCH)
OS_CFLAGS += -Wall -Werror
OS_CFLAGS += -isysroot $(SYSROOT)
OS_CFLAGS += -fdata-sections -ffunction-sections
OS_LDFLAGS+= -isysroot $(SYSROOT)
OS_LDFLAGS+= -dead_strip
OS_LDFLAGS+= $(OS_ARCH)
OS_LIBS   := -lsnsr -framework AudioToolbox -framework CoreFoundation
OS_LIBS   += -framework Foundation -framework Accelerate
OS_LIBS   += -lm -lstdc++
STATSIZE  := stat -f %z

else
$(error This operating system ($(OS_NAME)) is not supported)
endif

OS_CFLAGS  += -I$(SNSR_ROOT)/include
OS_LDFLAGS += -L$(SNSR_ROOT)/lib/$(ARCH_NAME)

TARGET_DIR := .
BIN_DIR    = $(TARGET_DIR)/bin
SRC_DIR    = $(TARGET_DIR)/src
OBJ_DIR    = $(BUILD_DIR)/obj
OUT_DIR    = $(BUILD_DIR)/out
BUILD_DIR  = $(TARGET_DIR)/build
TEST_DIR   = $(TARGET_DIR)/test

MODEL_DIR  = $(SNSR_ROOT)/model
DATA_DIR   = $(SNSR_ROOT)/data
TOOL_DIR   = $(SNSR_ROOT)/bin

# $(call audio-files,filename-prefix,index-list)
# e.g. $(call audio-files,armadillo-6-,0 1 2)
# returns a list of absolute paths to SDK enrollment test data
audio-files = $(addsuffix .wav,$(addprefix $(DATA_DIR)/enrollments/$1,$2))

TEST_DATA := $(call audio-files,armadillo-1-,0 1 2 3 4 5)
TEST_DATA += $(call audio-files,armadillo-6-,0 1 2 3 4 5)
TEST_DATA += $(call audio-files,jackalope-1-,0 1 2 3 4 5)
TEST_DATA += $(call audio-files,jackalope-4-,0 1 2 3 4 5)
TEST_DATA += $(call audio-files,terminator-2-,0 1 2 3 4 5)
TEST_DATA += $(call audio-files,terminator-6-,0 1 2 3 4 5)
TEST_DATA += $(call audio-files,armadillo-1-,0-c 1-c 2-c 3-c 4-c 5-c)
TEST_DATA += $(call audio-files,jackalope-1-,0-c 1-c 2-c 3-c 4-c 5-c)


UDT_MODEL   = $(MODEL_DIR)/udt-universal-3.67.1.0.snsr
UDT_MODEL_5 = $(MODEL_DIR)/udt-enUS-5.1.1.9.snsr
VTPL_MODEL  = $(MODEL_DIR)/tpl-spot-vad-3.13.0.snsr
HBG_MODEL_V = spot-hbg-enUS-1.4.0-m
HBG_MODEL   = $(MODEL_DIR)/$(HBG_MODEL_V).snsr
VG_MODEL    = $(MODEL_DIR)/spot-voicegenie-enUS-6.5.1-m.snsr
BASE_MODEL  = $(OUT_DIR)/enrolled-sv
VAD_MODEL_V = tpl-vad-lvcsr-3.17.0
VAD_MODEL   = $(MODEL_DIR)/$(VAD_MODEL_V).snsr


.PHONY: all clean debug help test
.PHONY: test-enroll-0 test-enroll-1 test-enroll-2 test-enroll-3
.PHONY: test-convert-0
.PHONY: test-push-0 test-push-1

define help
Make targets:

  make all      # build all executables in $(BIN_DIR)
  make clean    # remove build artifacts
  make debug    # build all with debugging enabled
  make help     # display this help message
  make test     # run enrollment and spotting tests

Building for $(ARCH_NAME) from SDK root directory
$(SNSR_ROOT)

endef

# Adjust test program verbosity
# Resolves to -v, unless make is run with the -s (silent) flag.
v = $(if $(findstring s,$(MAKEFLAGS)),,-v)

# Default target
help:; $(info $(help))

clean:
    rm -rf $(BIN_DIR) $(BUILD_DIR) $(OBJ_DIR) $(OUT_DIR) segmented-audio.wav
    rm -f $(SRC_DIR)/snsr-custom-init.c
    rm -f $(SRC_DIR)/$(HBG_MODEL_V).c $(SRC_DIR)/$(VAD_MODEL_V).c

debug: all
debug: CFLAGS=-O0 -g -UNDEBUG

test: test-enroll-0 test-enroll-1 test-enroll-2 test-enroll-3\
      test-convert-0 test-push-0 test-push-1 test-data-0 test-data-1\
      test-subset-0
    $(info SUCCESS: All tests passed.)

# End-to-end UDT enrollment test
test-enroll-0: $(BIN_DIR)/spot-enroll $(BIN_DIR)/snsr-eval | $(OUT_DIR)
    $(info Running $@.)
    $(BIN_DIR)/spot-enroll $v $v -t $(UDT_MODEL)\
      -o $(BASE_MODEL)-0.snsr\
      +armadillo-6 $(call audio-files,armadillo-6-,0 1 2 3)\
      +jackalope-4 $(call audio-files,jackalope-4-,0 1 2 3)\
      +terminator-2 $(call audio-files,terminator-2-,0 1 2 3)\
      +terminator-6 $(call audio-files,terminator-6-,0 1 2 3)\
      +armadillo-1 $(call audio-files,armadillo-1-,0 1)\
       -c $(call audio-files,armadillo-1-,0-c)\
       -c $(call audio-files,armadillo-1-,1-c)\
      +jackalope-1 $(call audio-files,jackalope-1-,0 1)\
       -c $(call audio-files,jackalope-1-,0-c)\
       -c $(call audio-files,jackalope-1-,1-c)
    $(BIN_DIR)/snsr-eval -t $(BASE_MODEL)-0.snsr $(TEST_DATA)\
      > $(OUT_DIR)/$@.txt
    diff $(OUT_DIR)/$@.txt $(TEST_DIR)/$@.txt\
      || (echo ERROR: $@ validation failed; exit 100)

# End-to-end UDT enrollment test, using adapted enrollment contexts
test-enroll-1: $(BIN_DIR)/spot-enroll $(BIN_DIR)/snsr-eval | $(OUT_DIR)
    $(info Running $@.)
    $(BIN_DIR)/spot-enroll $v -t $(UDT_MODEL)\
      -a $(OUT_DIR)/armadillo-6.snsr\
      -o $(OUT_DIR)/enrolled-armadillo-6.snsr\
      +armadillo-6 $(call audio-files,armadillo-6-,0 1 2 3)
    $(BIN_DIR)/spot-enroll $v -t $(UDT_MODEL)\
      -a $(OUT_DIR)/jackalope-4.snsr\
      -o $(OUT_DIR)/enrolled-jackalope-4.snsr\
      +jackalope-4 $(call audio-files,jackalope-4-,0 1 2 3)
    $(BIN_DIR)/spot-enroll $v -t $(UDT_MODEL)\
      -a $(OUT_DIR)/terminator-2.snsr\
      -o $(OUT_DIR)/enrolled-terminator-2.snsr\
      +terminator-2 $(call audio-files,terminator-2-,0 1 2 3)
    $(BIN_DIR)/spot-enroll $v -t $(UDT_MODEL)\
      -a $(OUT_DIR)/terminator-6.snsr\
      -o $(OUT_DIR)/enrolled-terminator-6.snsr\
      +terminator-6 $(call audio-files,terminator-6-,0 1 2 3)
    $(BIN_DIR)/spot-enroll $v -t $(UDT_MODEL)\
      -a $(OUT_DIR)/armadillo-1.snsr\
      -o $(OUT_DIR)/enrolled-armadillo.snsr\
      +armadillo-1 $(call audio-files,armadillo-1-,0 1)\
       -c $(call audio-files,armadillo-1-,0-c)\
       -c $(call audio-files,armadillo-1-,1-c)
    $(BIN_DIR)/spot-enroll $v -t $(UDT_MODEL)\
      -a $(OUT_DIR)/jackalope-1.snsr\
      -o $(OUT_DIR)/enrolled-jackalope-1.snsr\
      +jackalope-1 $(call audio-files,jackalope-1-,0 1)\
       -c $(call audio-files,jackalope-1-,0-c)\
       -c $(call audio-files,jackalope-1-,1-c)
    $(BIN_DIR)/spot-enroll $v -t $(UDT_MODEL)\
      -t $(OUT_DIR)/armadillo-6.snsr\
      -t $(OUT_DIR)/jackalope-4.snsr\
      -t $(OUT_DIR)/terminator-2.snsr\
      -t $(OUT_DIR)/terminator-6.snsr\
      -t $(OUT_DIR)/armadillo-1.snsr\
      -t $(OUT_DIR)/jackalope-1.snsr\
      -o $(BASE_MODEL)-1.snsr
    $(BIN_DIR)/snsr-eval -t $(BASE_MODEL)-1.snsr $(TEST_DATA)\
      > $(OUT_DIR)/$@.txt
    diff $(OUT_DIR)/$@.txt $(TEST_DIR)/$@.txt >/dev/null\
      || diff $(OUT_DIR)/$@.txt $(TEST_DIR)/$@-alt.txt >/dev/null\
      || (echo ERROR: $@ validation failed; exit 101)

# Live end-to-end UDT enrollment test.
test-enroll-2: $(BIN_DIR)/live-enroll $(BIN_DIR)/snsr-eval | $(OUT_DIR)
    $(info Running $@.)
    $(BIN_DIR)/live-enroll $v $v -t $(UDT_MODEL)\
      -o $(BASE_MODEL)-2.snsr\
      +armadillo-1 $(call audio-files,armadillo-1-,0 1 0-c 1-c)
    $(BIN_DIR)/snsr-eval -t $(BASE_MODEL)-2.snsr $(TEST_DATA)\
      > $(OUT_DIR)/$@.txt
    diff $(OUT_DIR)/$@.txt $(TEST_DIR)/$@.txt\
      || (echo ERROR: $@ validation failed; exit 102)

# Test old UDT model
test-enroll-3: $(BIN_DIR)/spot-enroll $(BIN_DIR)/snsr-eval | $(OUT_DIR)
    $(info Running $@.)
    $(BIN_DIR)/spot-enroll $v $v -t $(UDT_MODEL_5)\
      -o $(BASE_MODEL)-3.snsr\
      +armadillo-1 $(call audio-files,armadillo-1-,0 1 2 3)
    $(BIN_DIR)/snsr-eval -t $(BASE_MODEL)-3.snsr $(TEST_DATA)\
      > $(OUT_DIR)/$@.txt
    diff $(OUT_DIR)/$@.txt $(TEST_DIR)/$@.txt\
      || (echo ERROR: $@ validation failed; exit 103)

# Validate DSP conversion
test-convert-0: $(BIN_DIR)/spot-convert | $(OUT_DIR)
    $(info Running $@.)
    $(BIN_DIR)/spot-convert -t $(HBG_MODEL) -p $(OUT_DIR)/dsp pc38
    tail -10 $(OUT_DIR)/dsp-pc38-3.4.0-op10-prod-search.h\
      > $(OUT_DIR)/dsp-search-check.h
    shasum -c $(TEST_DIR)/dsp-checksum.txt

# Push audio samples instead of the default pull
# Uses test-enroll-0 models
test-push-0: test-enroll-0 $(BIN_DIR)/push-audio | $(OUT_DIR)
    $(info Running $@.)
    $(BIN_DIR)/push-audio $(BASE_MODEL)-0.snsr\
      $(call audio-files,jackalope-4-,0)\
      > $(OUT_DIR)/$@.txt
    diff $(OUT_DIR)/$@.txt $(TEST_DIR)/$@.txt\
      || (echo ERROR: $@ validation failed; exit 104)

# Push audio samples instead of the default pull
# Uses test-enroll-0 models and the tpl-spot-vad-*.snsr template
test-push-1: test-enroll-0 $(BIN_DIR)/push-audio $(SNSR_EDIT) | $(OUT_DIR)
    $(info Running $@.)
    $(SNSR_EDIT) -t $(VTPL_MODEL)\
      -f 0 $(BASE_MODEL)-0.snsr -o $(OUT_DIR)/spot-vad.snsr
    $(BIN_DIR)/push-audio $(OUT_DIR)/spot-vad.snsr\
      $(call audio-files,armadillo-1-,1-c)\
      > $(OUT_DIR)/$@.txt
    diff $(OUT_DIR)/$@.txt $(TEST_DIR)/$@.txt\
      || (echo ERROR: $@ validation failed; exit 105)

test-data-0: $(BIN_DIR)/spot-data | $(OUT_DIR)
    $(info Running $@.)
    $(BIN_DIR)/spot-data > $(OUT_DIR)/$@.txt
    diff $(OUT_DIR)/$@.txt $(TEST_DIR)/$@.txt\
      || (echo ERROR: $@ validation failed; exit 104)

test-data-1: $(BIN_DIR)/spot-data-stream | $(OUT_DIR)
    $(info Running $@.)
    $(BIN_DIR)/spot-data-stream > $(OUT_DIR)/$@.txt
    diff $(OUT_DIR)/$@.txt $(TEST_DIR)/$@.txt\
      || (echo ERROR: $@ validation failed; exit 104)

test-subset-0: $(BIN_DIR)/snsr-eval-subset $(BIN_DIR)/snsr-eval | $(OUT_DIR)
    $(info Running $@.)
    test $(shell $(STATSIZE) $(BIN_DIR)/snsr-eval-subset) -lt \
         $(shell $(STATSIZE) $(BIN_DIR)/snsr-eval) ||\
      (echo ERROR: $@ size validation failed; exit 105)
    $(BIN_DIR)/snsr-eval-subset -t $(HBG_MODEL) /dev/null ||\
      (echo ERROR: $@ validation failed; exit 106)
    $(BIN_DIR)/snsr-eval-subset -t $(VG_MODEL) /dev/null 2>&1 |\
      grep SNSR_USE_SUBSET >/dev/null ||\
      (echo ERROR: $@ validation failed; exit 107)

# Create a rule for building name from source, in $(BIN_DIR)
# $(call add-target-rule,name,source1.c source2.c ...)
add-target-rule = $(eval $(call emit-target-rule,$1,$2))
define emit-target-rule
all: $$(BIN_DIR)/$(strip $1)
$$(BIN_DIR)/$(strip $1): $$(addprefix $$(OBJ_DIR)/,$(2:.c=.o)) | $$(BIN_DIR)
    $$(CC) $$(OS_LDFLAGS) $$(LDFLAGS) -o $$@ $$^ $$(OS_LIBS) $$(LIBS)
endef

# Command-line application targets
$(call add-target-rule, spot-convert, spot-convert.c)
$(call add-target-rule, snsr-edit,    snsr-edit.c)
$(call add-target-rule, spot-enroll,  spot-enroll.c)
$(call add-target-rule, snsr-eval,    snsr-eval.c tpl-vad-lvcsr-3.17.0.c)
$(call add-target-rule, snsr-eval-subset,\
       snsr-eval-subset.c snsr-custom-init.c tpl-vad-lvcsr-3.17.0.c)
$(call add-target-rule, live-enroll,  live-enroll.c)
$(call add-target-rule, live-segment, live-segment.c)
$(call add-target-rule, live-spot,    live-spot.c)
$(call add-target-rule, push-audio,    push-audio.c)
$(call add-target-rule, spot-data,\
       spot-data.c spot-hbg-enUS-1.4.0-m.c data.c)
$(call add-target-rule, spot-data-stream,\
       spot-data-stream.c data-stream.c spot-hbg-enUS-1.4.0-m.c data.c)

ifeq ($(OS_NAME),Linux)
# The custom stream sample uses ALSA on Linux.
$(call add-target-rule, live-spot-stream, live-spot-stream.c alsa-stream.c)

else ifeq ($(OS_NAME),Darwin)
# The custom stream sample uses AQS on macOS.
$(call add-target-rule, live-spot-stream, live-spot-stream.c aqs-stream.c)
endif

# Build object files from C sources
$(OBJ_DIR)/%.o : $(SRC_DIR)/%.c  | $(OBJ_DIR)
    $(CC) -c $(OS_CFLAGS) $(CFLAGS) -o $@ $<

# spot-enroll doesn't use OSS modules
$(OBJ_DIR)/spot-enroll.o : $(SRC_DIR)/spot-enroll.c  | $(OBJ_DIR)
    $(CC) -DSNSR_OMIT_OSS_COMPONENTS -c $(OS_CFLAGS) $(CFLAGS) -o $@ $<

# Create $(SRC_DIR)/snsr-custom-init.c using snsr-edit,
# limit support to those modules needed for $(HBG_MODEL)
$(SRC_DIR)/snsr-custom-init.c: $(SNSR_EDIT)
    $(SNSR_EDIT) -o $@ -vit $(HBG_MODEL)

# Create $(SRC_DIR)/spot-hbg-enUS-*.c from the snsr model
$(SRC_DIR)/$(HBG_MODEL_V).c: $(SNSR_EDIT)
    $(SNSR_EDIT) -o $@ -c spot_hbg_enUS -vt $(HBG_MODEL)

# Create $(SRC_DIR)/tpl-vad-lvcsr-*.c from the snsr model
$(SRC_DIR)/$(VAD_MODEL_V).c: $(SNSR_EDIT)
    $(SNSR_EDIT) -o $@ -c tpl_vad_lvcsr -vt $(VAD_MODEL)

# Build snsr-eval-subset object files with -DSNSR_USE_SUBSET
$(OBJ_DIR)/snsr-eval-subset.o: $(SRC_DIR)/snsr-eval.c | $(OBJ_DIR)
    $(CC) -c  $(OS_CFLAGS) $(CFLAGS) -DSNSR_USE_SUBSET -o $@ $<

# Create output directories
$(BIN_DIR) $(BUILD_DIR) $(OBJ_DIR) $(OUT_DIR):
    mkdir -p $@