---
source_path: "api/overview.md"
canonical_url: "https://doc.sensory.com/tnl/7.8/api/overview/"
---

# API overview

This is a brief overview of the [API design goals](https://doc.sensory.com/tnl/7.8/api/overview.md#design-goals),
the SDK's [conceptual model](https://doc.sensory.com/tnl/7.8/api/overview.md#conceptual-model), and the two supported [audio processing modes](https://doc.sensory.com/tnl/7.8/api/overview.md#processing-modes).

## Design goals

The TrulyNatural SDK API is a result of these design goals:

* Pure C implementation.
    * Lowest common denominator, widest toolchain availability.
    * No C++ runtime overhead.
    * Fast.
* Simple API.
    * Small footprint: limited number of functions and data types.
    * Generic, independent of the inference task.
    * Fundamental data types only: floating point, integer, strings, streams, and opaque object instance handles.
    * Make it easier to provide bindings for languages other than C.
* Flexible configuration.
    * Hide complexity,
    * but still allow for fine-grained configuration if needed.
    * Settings indexed by string names, documented settings define public API.
* One self-contained model per task.
    * Model includes a flow graph that specifies how various low-level
      internal modules (feature extractors, acoustic models, etc.)
      connect and interact.
    * Includes all required module configurations.
* Run on a wide variety of platforms, including ones without file system support.

There is a significant downside to these design choices: Discoverability is very limited. You
cannot determine model behavior from function or method names alone. You must refer to the
[model type](https://doc.sensory.com/tnl/7.8/models/types/index.md#model-types) documentation for expected task behavior and [available settings](https://doc.sensory.com/tnl/7.8/api/setting-keys/index.md#setting-keys).

## Conceptual model

This library uses a [dataflow][] approach to evaluate speech
recognition tasks.
It uses [inversion of control][]: The SDK invokes [event](https://doc.sensory.com/tnl/7.8/api/setting-keys/events.md#events)
handlers to report [results](https://doc.sensory.com/tnl/7.8/api/setting-keys/results.md#results) and control task flow.

The API contains two primary data types: [Session](https://doc.sensory.com/tnl/7.8/api/inference.md#session) used for model [inference](https://doc.sensory.com/tnl/7.8/api/inference.md#inference),
and a [Stream](https://doc.sensory.com/tnl/7.8/api/io.md#stream) abstraction for [input and output](https://doc.sensory.com/tnl/7.8/api/io.md#input-and-output).

Sessions hold the entire state of a model instance, and use streams for
all input and output. There is, for example, a single [load](https://doc.sensory.com/tnl/7.8/api/inference.md#load)
function to load a model into a session, but this supports loading
from a [named file](https://doc.sensory.com/tnl/7.8/api/io.md#fromfilename), an open [FILE *](https://doc.sensory.com/tnl/7.8/api/io.md#fromfile) handle,
a [memory](https://doc.sensory.com/tnl/7.8/api/io.md#frommemory) segment, from the [code](https://doc.sensory.com/tnl/7.8/api/io.md#fromcode) segment, and
from compressed [assets](https://doc.sensory.com/tnl/7.8/api/io.md#fromasset) on Android.

Models (`snsr` files) define flow pipelines and session behavior. These
contain the serialized content[^1] of the session flow graph, including all
binary models and configurations. Think of these as hierarchical key-value databases.
Once loaded into a [Session](https://doc.sensory.com/tnl/7.8/api/inference.md#session), you can query or change the [setting keys](https://doc.sensory.com/tnl/7.8/api/setting-keys/index.md#setting-keys) with
generic [getter](https://doc.sensory.com/tnl/7.8/api/inference.md#getters) and [setter](https://doc.sensory.com/tnl/7.8/api/inference.md#setters) functions.

[^1]: Similar in concept to [protocol buffers][], but with streamed unpacking into
native data structures in RAM, no need for accessor functions, and additional features
such as conversion to [code](https://doc.sensory.com/tnl/7.8/api/inference.md#fm_source) for running from the text segment.

## Processing modes

Two modes are available for audio processing:

* **Pull** mode, where the [run](https://doc.sensory.com/tnl/7.8/api/inference.md#run) function reads audio from the configured
  input stream. This blocks on read until new data are available. The [run](https://doc.sensory.com/tnl/7.8/api/inference.md#run)
  function returns only when the stream runs out of data (for example the end of a file),
  an [event](https://doc.sensory.com/tnl/7.8/api/setting-keys/events.md#events) handler tells it to stop, or an error occurs.
* **Push** mode, where the application repeatedly calls the [push](https://doc.sensory.com/tnl/7.8/api/inference.md#push) function
  with small chunks of the audio data. The [push](https://doc.sensory.com/tnl/7.8/api/inference.md#push) function returns once it has processed
  or buffered these data. The application eventually calls [stop](https://doc.sensory.com/tnl/7.8/api/inference.md#stop) to flush and process
  any buffered data.

Model evaluation typically follows this recipe:

<!-- tab: pull-mode -->

**Pull mode**

1. Create a new session instance with [new](https://doc.sensory.com/tnl/7.8/api/inference.md#new).
2. [Load](https://doc.sensory.com/tnl/7.8/api/inference.md#load) a task model into the instance.
3. [Set](https://doc.sensory.com/tnl/7.8/api/inference.md#setters) the [input](https://doc.sensory.com/tnl/7.8/api/setting-keys/runtime.md#-audio-pcm) source stream.
4. [Register](https://doc.sensory.com/tnl/7.8/api/inference.md#sethandler) one or more [event](https://doc.sensory.com/tnl/7.8/api/setting-keys/events.md#events) handlers.
5. Enter the main loop by calling [run](https://doc.sensory.com/tnl/7.8/api/inference.md#run).
   The library will process the input streams and
   invoke event handlers at appropriate times. The main loop continues
   until a terminating condition is reached, such as an event returning
   an error code.
6. [Release](https://doc.sensory.com/tnl/7.8/api/heap.md#release) the session instance.

**Also see these related items:** [Your first program](https://doc.sensory.com/tnl/7.8/getting-started/your-first-program.md#your-first-program), [live-spot.c](https://doc.sensory.com/tnl/7.8/api/sample/c/live-spot.md#live-spotc), [evalUDT.java](https://doc.sensory.com/tnl/7.8/api/sample/java/evalUDT.md#evaludtjava), and
[hello_world.py](https://doc.sensory.com/tnl/7.8/api/sample/python/hello_world.md#hello_worldpy).
<!-- /tab -->

<!-- tab: push-mode -->

**Push mode**

1. Create a new session instance with [new](https://doc.sensory.com/tnl/7.8/api/inference.md#new).
2. [Load](https://doc.sensory.com/tnl/7.8/api/inference.md#load) a task model into the instance.
3. [Register](https://doc.sensory.com/tnl/7.8/api/inference.md#sethandler) one or more [event](https://doc.sensory.com/tnl/7.8/api/setting-keys/events.md#events) handlers.
4. Process audio segments by calling [push](https://doc.sensory.com/tnl/7.8/api/inference.md#push) repeatedly.
   This will invoke event handlers before `push` returns.
5. Call [stop](https://doc.sensory.com/tnl/7.8/api/inference.md#stop) once to flush any buffered audio.
6. [Release](https://doc.sensory.com/tnl/7.8/api/heap.md#release) the session instance.

**Also see these related items:** [push-audio.c](https://doc.sensory.com/tnl/7.8/api/sample/c/push-audio.md#push-audioc) and [stt_push.py](https://doc.sensory.com/tnl/7.8/api/sample/python/stt_push.md#stt-push-py).
<!-- /tab -->

## Language bindings

This version of the TrulyNatural SDK supports three language bindings: [C][],
[Java][], and [Python][]. C is the native API; Java and Python are generated
wrappers with idiomatic naming and error handling on top of the same session and
stream model.

### C

The C binding exposes the native API directly. Functions use the `snsr` prefix
and pass opaque handles explicitly, for example
`snsrSetHandler(s, key, callback)`.

The C binding uses a *latched-error* model: every function returns
[SnsrRC](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), and once a session enters an error state, every subsequent call
short-circuits with the same code until [clearRC](https://doc.sensory.com/tnl/7.8/api/inference.md#clearrc) (or [reset](https://doc.sensory.com/tnl/7.8/api/inference.md#reset)) is called.
Read the latched code with [rC](https://doc.sensory.com/tnl/7.8/api/inference.md#rc) and the human-readable detail with
[errorDetail](https://doc.sensory.com/tnl/7.8/api/inference.md#errordetail).

The C binding uses [reference counting](https://doc.sensory.com/tnl/7.8/api/heap.md#memory-management) on every
[Session](https://doc.sensory.com/tnl/7.8/api/inference.md#session), [Stream](https://doc.sensory.com/tnl/7.8/api/io.md#stream), and [Callback](https://doc.sensory.com/tnl/7.8/api/inference.md#callback) handle (and on the C string returned
by [getString](https://doc.sensory.com/tnl/7.8/api/inference.md#getters)); manage lifetimes with [retain](https://doc.sensory.com/tnl/7.8/api/heap.md#retain) and [release](https://doc.sensory.com/tnl/7.8/api/heap.md#release).

### Java

A C function `snsrXxx(SnsrSession s, ...)` becomes a Java method
`Session.xxx(...)`: the `Snsr` prefix and the `SnsrSession` first argument are
absorbed into the receiver. For example,
`snsrSetHandler(s, key, c)` ↔ `s.setHandler(key, c)`.

The Java binding does not surface latched errors to callers. Each Java
method either completes successfully or throws an exception describing
the failure; subsequent method calls on the same session start fresh.
Six methods that perform I/O — [Session.load](https://doc.sensory.com/tnl/7.8/api/inference.md#load), [Session.run](https://doc.sensory.com/tnl/7.8/api/inference.md#run),
and [Stream.copy](https://doc.sensory.com/tnl/7.8/api/io.md#stream-copy), [Stream.getDelim](https://doc.sensory.com/tnl/7.8/api/io.md#stream-getDelim),
[Stream.open](https://doc.sensory.com/tnl/7.8/api/io.md#stream-open), [Stream.read](https://doc.sensory.com/tnl/7.8/api/io.md#stream-read),
[Stream.skip](https://doc.sensory.com/tnl/7.8/api/io.md#stream-skip), [Stream.write](https://doc.sensory.com/tnl/7.8/api/io.md#stream-write) — declare
`throws java.io.IOException` and so are checked. All other exceptions
are unchecked subclasses of `java.lang.RuntimeException`. The mapping
from [SnsrRC](https://doc.sensory.com/tnl/7.8/api/inference.md#rc) to Java exception class is part of the binding's
contract:

| Java exception class                  | Thrown for [SnsrRC](https://doc.sensory.com/tnl/7.8/api/inference.md#rc) codes such as                                                                                                                                                                                          | Typical cause                                                                                                                                          |
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `java.io.IOException` (checked)       | [EOF](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_eof), [STREAM](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), [STREAM_END](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_stream_end), [NOT_OPEN](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), [BUFFER_OVERRUN](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), [BUFFER_UNDERRUN](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), [DELIM_NOT_FOUND](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), [LIBRARY_TOO_OLD](https://doc.sensory.com/tnl/7.8/api/inference.md#rc)   | Stream I/O failed or reached end-of-data. Only thrown from the six methods that declare `throws IOException`; identical conditions outside those methods raise `RuntimeException`. |
| `java.lang.OutOfMemoryError`          | [NO_MEMORY](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), [NOT_ENOUGH_SPACE](https://doc.sensory.com/tnl/7.8/api/inference.md#rc)                                                                                                                                                              | Allocation failed.                                                                                                                                     |
| `java.lang.IllegalArgumentException`  | [INVALID_ARG](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), [INVALID_HANDLE](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), [INCORRECT_SETTING_TYPE](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_incorrect_setting_type), [SETTING_IS_READ_ONLY](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), [FORMAT_NOT_SUPPORTED](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), [VERSION_MISMATCH](https://doc.sensory.com/tnl/7.8/api/inference.md#rc) | The caller passed a value that is the wrong type, the wrong format, or otherwise unacceptable.                                                          |
| `java.lang.IndexOutOfBoundsException` | [SETTING_NOT_FOUND](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), [SETTING_NOT_AVAILABLE](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), [VALUE_NOT_SET](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), [ARG_OUT_OF_RANGE](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), [NAME_NOT_UNIQUE](https://doc.sensory.com/tnl/7.8/api/inference.md#rc), [ITERATION_LIMIT](https://doc.sensory.com/tnl/7.8/api/inference.md#rc) | A lookup by name, index, or port missed; or a numeric / iteration limit was exceeded.                                                                  |
| `java.lang.RuntimeException`          | All other non-OK codes — `ERROR`, `NOT_IMPLEMENTED`, `CONFIGURATION_*`, `ELEMENT_*`, `LICENSE_*`, `NO_MODEL`, `NOT_INITIALIZED`, `NOT_SUPPORTED`, `TIMED_OUT`, and so on.                                                       | Misconfiguration, license issue, internal API violation, or the catch-all bucket.                                                                      |

Callbacks that need to control the run loop without raising an exception
may return any of [OK](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_ok), [STREAM_END](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_stream_end), [STOP](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_stop),
[SKIP](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_skip), [REPEAT](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_repeat), or [TIMED_OUT](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_timed_out); any
other return value from a callback is translated into an exception by the
same mapping.

Java methods that have no out-parameters in C return the [Session](https://doc.sensory.com/tnl/7.8/api/inference.md#session)
instance instead, so callers chain freely:

```java
s.load(input).setHandler(KEY, listener).run();
```

When an exception is thrown, the underlying [SnsrRC](https://doc.sensory.com/tnl/7.8/api/inference.md#rc) code remains
available on the [Session](https://doc.sensory.com/tnl/7.8/api/inference.md#session) (or [Stream](https://doc.sensory.com/tnl/7.8/api/io.md#stream)) for the duration of the
`catch` block: call [rC](https://doc.sensory.com/tnl/7.8/api/inference.md#rc) to read it programmatically, or
[errorDetail](https://doc.sensory.com/tnl/7.8/api/inference.md#errordetail) for the human-readable message. (The exception's
`getMessage()` is set to the same `errorDetail` text.)
This is useful when the exception class alone is too coarse — for
example, distinguishing a missing setting from an unsupported one
when both surface as `IndexOutOfBoundsException`:

```java
try {
    s.set(KEY, value).run();
} catch (IndexOutOfBoundsException e) {
    if (s.rC() == SnsrRC.SETTING_NOT_FOUND) {
        // Treat as a config-file typo, fall back to a default.
    } else {
        throw e;
    }
}
```

The handler does not need to do anything to "reset" the session — as
above, the next method call on the same session starts fresh.

The Java binding uses standard garbage collection. Explicit `retain` /
`release` are not exposed in Java and are not needed; [Session.release()](https://doc.sensory.com/tnl/7.8/api/inference.md#session-release)
is provided for callers who want to free native resources promptly without
waiting for GC, but is otherwise optional.

### Python

The [Python][] binding follows the Java naming recipe with [PEP 8][]
snake_case: drop the `snsr`/`Snsr` prefix and the session/stream receiver, then
convert camelCase to snake_case (`snsrSetInt` → `set_int`,
`snsrGetString` → `get_string`). Class methods use the type name instead of a
prefix (`snsrStreamFromAudioFile` → `Stream.from_audio_file`). A few names
differ on purpose: `snsrSet(s, "+i+…")` → `apply("+i+…")` (not `set_*`),
`snsrStreamFromFileName` → `Stream.from_filename`, and stream status uses
properties (`s.rc`, `s.error_detail`) instead of `snsrStreamRC` /
`snsrStreamErrorDetail`. Look up signatures on the **Python** tabs throughout
this reference (setting keys and enums include Python automatically).

The [Python][] binding also avoids a latched-error session state. Any call that
would return a non-OK [SnsrRC](https://doc.sensory.com/tnl/7.8/api/inference.md#rc) in C raises `snsr.Error` instead; read the
detail string from `Error.message` (there is no [clearRC](https://doc.sensory.com/tnl/7.8/api/inference.md#clearrc) or session
[errorDetail](https://doc.sensory.com/tnl/7.8/api/inference.md#errordetail) on the Python surface). [run](https://doc.sensory.com/tnl/7.8/api/inference.md#run) is special: it returns an
[RC](https://doc.sensory.com/tnl/7.8/api/inference.md#rc) on success (for example when a handler returns [STOP](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_stop)) and
raises `snsr.Error` only for true failures. Callbacks may return [OK](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_ok),
[STREAM_END](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_stream_end), [STOP](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_stop), [SKIP](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_skip), [REPEAT](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_repeat),
or [TIMED_OUT](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_timed_out); any other code from a handler is raised as
`snsr.Error`. Install the wheel from the SDK installer, not PyPI — see
[Integrate with your build § Python](https://doc.sensory.com/tnl/7.8/api/build-system.md#build-python).

| Python | Thrown for [SnsrRC](https://doc.sensory.com/tnl/7.8/api/inference.md#rc) codes such as | Typical cause |
| ------ | ------------------------------------- | --------------- |
| `snsr.Error` | All non-OK codes (same set as the Java `RuntimeException` and `IOException` rows above, plus stream and configuration failures) | Any API or handler failure; `Error.message` matches C [errorDetail](https://doc.sensory.com/tnl/7.8/api/inference.md#errordetail). |
| Normal return from [run](https://doc.sensory.com/tnl/7.8/api/inference.md#run) | [OK](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_ok), [STOP](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_stop), [STREAM_END](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_stream_end), … | Handler stopped the loop or the input stream ended without an error. |

**Context managers.** `with snsr.Stream(...) as s:` calls [open](https://doc.sensory.com/tnl/7.8/api/io.md#stream-open) on
enter and [close](https://doc.sensory.com/tnl/7.8/api/io.md#stream-close) on exit. `with snsr.Session() as s:` does
**not** open the session (construction already initializes it); it only
[release](https://doc.sensory.com/tnl/7.8/api/heap.md#release)s on exit. That differs from `with open(...)` on a file and
from Java, which has no context-manager support.

The [Python][] binding does not expose [retain](https://doc.sensory.com/tnl/7.8/api/heap.md#retain) or [release](https://doc.sensory.com/tnl/7.8/api/heap.md#release) to callers.
Use `with snsr.Session() as s` or `with snsr.Stream(...) as s` to free native
resources promptly; otherwise handles are released when Python finalizes the
objects. Session [getters](https://doc.sensory.com/tnl/7.8/api/inference.md#getters) that return a [Stream](https://doc.sensory.com/tnl/7.8/api/io.md#stream) (for example
`get_stream` in Python) yield a retained handle (same ownership rules as the
C API, documented on [Memory management](https://doc.sensory.com/tnl/7.8/api/heap.md#memory-management)).

**Ctrl+C during blocking calls.** Python installs its own `SIGINT` handler, so
`^C` while [run](https://doc.sensory.com/tnl/7.8/api/inference.md#run) (or another call) is blocked in native code only takes
effect after the call returns — the process appears hung. Before a long-running
SDK call, restore the default handler:

```python
import signal

signal.signal(signal.SIGINT, signal.SIG_DFL)
```

Then `^C` terminates the process immediately. Programs that return
[STOP](https://doc.sensory.com/tnl/7.8/api/inference.md#rc_stop) from a result handler still exit normally after a spot without
keyboard input.

### Kotlin on Android

Kotlin Android apps use the **Java binding** unchanged. The same
`com.sensory.speech.snsr` `@aar` artifact, the same [Session](https://doc.sensory.com/tnl/7.8/api/inference.md#session) and [Stream](https://doc.sensory.com/tnl/7.8/api/io.md#stream)
classes, and the same [Listener](https://doc.sensory.com/tnl/7.8/api/inference.md#listener) interface — called from Kotlin source.
There is no separate Kotlin SDK; the **Java** tabs on [Inference](https://doc.sensory.com/tnl/7.8/api/inference.md#inference) and
[I/O](https://doc.sensory.com/tnl/7.8/api/io.md#input-and-output) are the reference for Kotlin callers too.

The same notes apply to desktop Kotlin against the JAR coordinates in
[Integrate with your build § Java](https://doc.sensory.com/tnl/7.8/api/build-system.md#build-java); only Android-specific items
below are platform-specific.

A few interop points are worth knowing because the call site looks slightly
different from Java:

* **Lambdas for [Listener](https://doc.sensory.com/tnl/7.8/api/inference.md#listener).** [Listener](https://doc.sensory.com/tnl/7.8/api/inference.md#listener) is a Java single-abstract-method
  interface, so Kotlin converts a lambda to it directly:

    ```kotlin
    session.setHandler(Snsr.RESULT_EVENT) { ses, _ ->
        println("Spotted \"${ses.getString(Snsr.RES_TEXT)}\".")
        SnsrRC.STOP
    }
    ```

    Kotlin's own `interface` types would require `fun interface` for the same
    conversion, but Kotlin → Java SAM interop is unconditional. Listener
    parameters arrive as Kotlin **platform types** (`SnsrSession!`, `String!`)
    because the Java SDK has no nullability annotations; treat them as
    non-null.

* **Checked `IOException`.** The six methods listed above declare
  `throws java.io.IOException`. The Kotlin compiler does **not** enforce Java
  checked exceptions — these methods still throw `IOException` at runtime,
  but callers receive no compile-time warning. Wrap [Session.load](https://doc.sensory.com/tnl/7.8/api/inference.md#load),
  [Session.run](https://doc.sensory.com/tnl/7.8/api/inference.md#run), and [Stream](https://doc.sensory.com/tnl/7.8/api/io.md#stream) I/O in `try`/`catch` (or `runCatching`)
  even though Kotlin lets you omit it.

* **`release()` is not `close()`.** [Session](https://doc.sensory.com/tnl/7.8/api/inference.md#session) and [Stream](https://doc.sensory.com/tnl/7.8/api/io.md#stream) expose
  `release()`; they do not implement `java.lang.AutoCloseable`. The Kotlin
  `use { }` helper does **not** apply. Call `release()` explicitly, or write
  an app-side extension function — there is no SDK-shipped Kotlin extension.

* **Threading.** [run](https://doc.sensory.com/tnl/7.8/api/inference.md#run) blocks for the lifetime of the recognition session.
  Use a **dedicated single thread** (e.g. `newSingleThreadContext("snsr")` or
  a plain `Thread`) — the same worker-thread pattern as the Java Android
  samples. Do not run [run](https://doc.sensory.com/tnl/7.8/api/inference.md#run) on `Dispatchers.IO`; that pool is sized for
  short blocking I/O, not for an open-ended pull loop.

For end-to-end Kotlin code, see the **Kotlin** sub-tab in
[Your first program](https://doc.sensory.com/tnl/7.8/getting-started/your-first-program.md#your-first-program) § The program.

**Also see these related items:** [Android examples](https://doc.sensory.com/tnl/7.8/api/sample/android/index.md#android-examples), [Integrate with your build § Android](https://doc.sensory.com/tnl/7.8/api/build-system.md#build-android).

<!-- Reference definitions from includes/links.md -->
[C]: https://en.wikipedia.org/wiki/C_(programming_language) "C programming language"
[dataflow]: https://en.wikipedia.org/wiki/Flow-based_programming "Flow-based programming"
[inversion of control]: https://en.wikipedia.org/wiki/Inversion_of_control
[Java]: https://en.wikipedia.org/wiki/Java_(programming_language) "Java programming language"
[PEP 8]: https://peps.python.org/pep-0008/ "Style Guide for Python Code"
[protocol buffers]: https://en.wikipedia.org/wiki/Protocol_Buffers
[Python]: https://en.wikipedia.org/wiki/Python_(programming_language) "Python programming language"

<!-- Abbreviation definitions from includes/abbreviations.md -->
*[API]: Application Programming Interface
*[RAM]: Random Access Memory
*[SDK]: Software Development Kit
*[STT]: Speech To Text: transformers with language model and CTC decoding
*[TNL]: TrulyNatural, Sensory's large-vocabulary speech recognition technology
