---
source_path: "api/sample/c/index.md"
canonical_url: "https://doc.sensory.com/tnl/7.8/api/sample/c/"
---

# C examples

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

See _~/Sensory/TrulyNaturalSDK/7.9.0-pre.0/sample/c/_

You can build the [sample code](https://doc.sensory.com/tnl/7.8/api/sample/c/index.md#c-examples-list) with [CMake](https://doc.sensory.com/tnl/7.8/api/sample/c/index.md#examples-cmake)
or with [GNU Make](https://doc.sensory.com/tnl/7.8/api/sample/c/index.md#examples-make).

## Examples

[live-spot.c](https://doc.sensory.com/tnl/7.8/api/sample/c/live-spot.md#live-spotc)

- Shows how to run a wake word recognizer on live audio captured from
  the default audio source. For a shorter teaching version you type yourself,
  see [Your first program](https://doc.sensory.com/tnl/7.8/getting-started/your-first-program.md#your-first-program).

[live-spot-stream.c](https://doc.sensory.com/tnl/7.8/api/sample/c/live-spot-stream.md#live-spot-streamc)

- Runs a wake word recognizer on live audio captured using a custom
  audio stream, defined in [alsa-stream.c](https://doc.sensory.com/tnl/7.8/api/sample/c/alsa-stream.md#alsa-streamc).

[live-segment.c](https://doc.sensory.com/tnl/7.8/api/sample/c/live-segment.md#live-segmentc)

- 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](https://doc.sensory.com/tnl/7.8/api/sample/c/push-audio.md#push-audioc)

- 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](https://doc.sensory.com/tnl/7.8/api/sample/c/spot-data.md#spot-datac)

- Runs a small keyword spotter from code space.
  It uses a [custom memory allocator](https://doc.sensory.com/tnl/7.8/api/library-config.md#alloctlsf) to avoid calls to
  the system heap allocator, and reads audio data from code space
  to avoid file system use.

[spot-data-stream.c](https://doc.sensory.com/tnl/7.8/api/sample/c/spot-data-stream.md#spot-data-streamc)

- This example runs a wake word from code space with a
  [custom audio stream](https://doc.sensory.com/tnl/7.8/api/sample/c/data-stream.md#data-streamc), using pull mode processing
  with [run](https://doc.sensory.com/tnl/7.8/api/inference.md#run). It is a reasonable starting point for running on a
  small device with an RTOS.

[alsa-stream.c](https://doc.sensory.com/tnl/7.8/api/sample/c/alsa-stream.md#alsa-streamc)

- Source for the [fromAudioDevice](https://doc.sensory.com/tnl/7.8/api/io.md#fromaudiodevice) [Stream](https://doc.sensory.com/tnl/7.8/api/io.md#stream) implementation
  for [ALSA][], used for live audio capture on Linux.

[aqs-stream.c](https://doc.sensory.com/tnl/7.8/api/sample/c/aqs-stream.md#aqs-streamc)

- Source for the [fromAudioDevice](https://doc.sensory.com/tnl/7.8/api/io.md#fromaudiodevice) [Stream](https://doc.sensory.com/tnl/7.8/api/io.md#stream) implementation
  for [Audio Queue Services][], used for live audio capture on macOS and iOS.

[wmme-stream.c](https://doc.sensory.com/tnl/7.8/api/sample/c/wmme-stream.md#wmme-streamc)

- Source for the [fromAudioDevice](https://doc.sensory.com/tnl/7.8/api/io.md#fromaudiodevice) [Stream](https://doc.sensory.com/tnl/7.8/api/io.md#stream) implementation
  for [Windows Multimedia Extensions][], used for live audio capture on Windows.

[data-stream.c](https://doc.sensory.com/tnl/7.8/api/sample/c/data-stream.md#data-streamc)

- This is the source for the [fromAudioDevice](https://doc.sensory.com/tnl/7.8/api/io.md#fromaudiodevice) [Stream](https://doc.sensory.com/tnl/7.8/api/io.md#stream) implementation
  for memory data, similar to [fromMemory](https://doc.sensory.com/tnl/7.8/api/io.md#frommemory). It's used in the [spot-data-stream.c](https://doc.sensory.com/tnl/7.8/api/sample/c/spot-data-stream.md#spot-data-streamc)
  example.

[snsr-edit.c](https://doc.sensory.com/tnl/7.8/api/sample/c/snsr-edit.md#snsr-editc)

- Source for the [snsr-edit](https://doc.sensory.com/tnl/7.8/tools/snsr-edit.md#snsr-edit) command-line tool.

[snsr-eval.c](https://doc.sensory.com/tnl/7.8/api/sample/c/snsr-eval.md#snsr-evalc)

- Source for the [snsr-eval](https://doc.sensory.com/tnl/7.8/tools/snsr-eval.md#snsr-eval) command-line tool, and the `snsr-eval-subset` sample.

[spot-convert.c](https://doc.sensory.com/tnl/7.8/api/sample/c/spot-convert.md#spot-convertc)

- Source for the [spot-convert](https://doc.sensory.com/tnl/7.8/tools/spot-convert.md#spot-convert) command-line tool.

[spot-enroll.c](https://doc.sensory.com/tnl/7.8/api/sample/c/spot-enroll.md#spot-enrollc)

- Source for the [spot-enroll](https://doc.sensory.com/tnl/7.8/tools/spot-enroll.md#spot-enroll) command-line tool.

[live-enroll.c](https://doc.sensory.com/tnl/7.8/api/sample/c/live-enroll.md#live-enrollc)

- Source for the [live-enroll](https://doc.sensory.com/tnl/7.8/tools/live-enroll.md#live-enroll) command-line tool.

## Build with CMake

Build 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.

```sh
cd ~/Sensory/TrulyNaturalSDK/7.9.0-pre.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.

### Details: CMakeLists.txt

```cmake
# 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 "$ENV{HOME}/Sensory/TrulyNaturalSDK/7.9.0-pre.0")
include(SnsrLibrary)

add_subdirectory(src)
```

### Details: src/CMakeLists.txt

```cmake
# 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

Build 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.

```sh
cd ~/Sensory/TrulyNaturalSDK/7.9.0-pre.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:

```console
% 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:

```console
% 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.

### Details: Makefile

```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 $@

```

<!-- Reference definitions from includes/links.md -->
[ALSA]: http://www.alsa-project.org/main/index.php/ALSA_Library_API "Advanced Linux Sound Architecture"
[Audio Queue Services]: https://developer.apple.com/documentation/audiotoolbox/audio-queue-services "Audio Toolbox, Core Audio"
[CMake]: https://cmake.org/ "CMake: A Powerful Software Build System"
[GNU Make]: https://www.gnu.org/software/make/ "GNU Make build automation tool"
[Windows Multimedia Extensions]: https://learn.microsoft.com/en-us/windows/win32/api/mmeapi/nf-mmeapi-waveinopen "waveInOpen"

<!-- Abbreviation definitions from includes/abbreviations.md -->
*[ALSA]: Advanced Linux Sound Architecture
*[API]: Application Programming Interface
*[AQS]: Audio Queue Services, Apple's audio capture API on Darwin / macOS
*[RTOS]: Real-Time Operating System
*[TNL]: TrulyNatural, Sensory's large-vocabulary speech recognition technology
*[VAD]: Voice Activity Detector
*[WMME]: Windows Multimedia Extensions, the audio capture API on Windows
