> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ai-coustics.com/llms.txt
> Use this file to discover all available pages before exploring further.

# C

> Build a C WAV-processing program with explicit SDK error checks and native cleanup.

**ai-coustics** exposes its native SDK through opaque C handles and checked return codes. This page uses SDK 0.25.0. Use the [C API reference](/reference/sdk/api/c/index) for functions, ownership rules and error codes. The [released header](https://github.com/ai-coustics/aic-sdk-c/blob/0.25.0/include/aic.h) provides the C declarations.

## Installation

Use a C11 compiler, CMake 3.24 or later and a supported macOS, Linux or Windows platform. This tutorial selects the GNU libc Linux archive; see the [compatibility matrix](/reference/sdk/compatibility-matrix). Release 0.25.0 adds musl archives for `x86_64` and `aarch64`. They ship `lib/libaic.a` only, with no musl `libaic.so`, so link the archive statically with a musl-native toolchain such as GCC on Alpine rather than following the shared-library CMake project below. On Windows, run build commands in a Visual Studio developer terminal with the C++ build tools installed.

Create an empty project directory:

```bash theme={null}
mkdir aic-c-quickstart
cd aic-c-quickstart
```

Download and extract the native SDK 0.25.0 archive. These POSIX commands select Apple Silicon, Intel Mac, Linux ARM64 or Linux x86-64 from the host architecture:

<CodeGroup>
  ```bash macOS / Linux theme={null}
  case "$(uname -s)-$(uname -m)" in
    Darwin-arm64) AIC_TARGET=aarch64-apple-darwin ;;
    Darwin-x86_64) AIC_TARGET=x86_64-apple-darwin ;;
    Linux-aarch64) AIC_TARGET=aarch64-unknown-linux-gnu ;;
    Linux-x86_64) AIC_TARGET=x86_64-unknown-linux-gnu ;;
    *) echo "Select a supported SDK archive from the releases page"; exit 1 ;;
  esac
  curl -fL -o sdk.tar.gz "https://github.com/ai-coustics/aic-sdk-c/releases/download/0.25.0/aic-sdk-${AIC_TARGET}-0.25.0.tar.gz"
  mkdir sdk
  tar -xzf sdk.tar.gz -C sdk
  ```

  ```powershell Windows PowerShell theme={null}
  # For an ARM64 build, replace x86_64 with aarch64.
  $AicTarget = "x86_64-pc-windows-msvc"
  Invoke-WebRequest "https://github.com/ai-coustics/aic-sdk-c/releases/download/0.25.0/aic-sdk-$AicTarget-0.25.0.zip" -OutFile sdk.zip
  Expand-Archive sdk.zip -DestinationPath sdk
  ```
</CodeGroup>

The project must now contain `sdk/include/aic.h` and `sdk/lib/`. Choose the archive for your compiler's target architecture when cross-compiling. [Release 0.25.0](https://github.com/ai-coustics/aic-sdk-c/releases/tag/0.25.0) includes all distributions.

## Quickstart

### Prepare the key, model and input

Generate an SDK key on the [developer platform](https://developers.ai-coustics.com). Set it in the terminal where you will run the program. Keep it out of source control and shared logs.

<CodeGroup>
  ```bash macOS / Linux theme={null}
  export AIC_SDK_LICENSE="YOUR_SDK_KEY"
  curl -fL -o model.aicmodel "https://artifacts.ai-coustics.io/models/quail-vf-2-2-l-16khz/v7/quail_vf_2_2_l_16khz_horgwub0_v14.aicmodel"
  ```

  ```powershell Windows PowerShell theme={null}
  $env:AIC_SDK_LICENSE = "YOUR_SDK_KEY"
  Invoke-WebRequest "https://artifacts.ai-coustics.io/models/quail-vf-2-2-l-16khz/v7/quail_vf_2_2_l_16khz_horgwub0_v14.aicmodel" -OutFile model.aicmodel
  ```
</CodeGroup>

Replace `YOUR_SDK_KEY` with your key. This pins a format-7 build of `quail-vf-2.2-l-16khz`. Quail Voice Focus isolates the primary speaker for speech-to-text (STT) input. Downloading the model in advance does not remove SDK key authorization requirements; see [authentication](/models/get-started/authenticate-apps).

Download the [noisy speech fixture](/assets/audio/noisy-speech.wav) and save it as `input.wav` in this project directory. It contains 56,080 mono PCM16 samples at 16 kHz (3.505 seconds). The [fixture guide](/guides/test-audio) includes attribution, checksums and the aligned clean reference.

For your own recording, export **mono, 16 kHz, signed 16-bit PCM WAV**, at most 60 seconds long. Renaming a file does not convert it.

### Add WAV file handling

The example uses [dr\_wav 0.14.4](https://github.com/mackron/dr_libs/tree/86cc48cbfd981fa00ea94905ac9d6df4b18d4e59) for file I/O. Download its pinned single header into the project directory:

<CodeGroup>
  ```bash macOS / Linux theme={null}
  curl -fL -o dr_wav.h "https://raw.githubusercontent.com/mackron/dr_libs/86cc48cbfd981fa00ea94905ac9d6df4b18d4e59/dr_wav.h"
  ```

  ```powershell Windows PowerShell theme={null}
  Invoke-WebRequest "https://raw.githubusercontent.com/mackron/dr_libs/86cc48cbfd981fa00ea94905ac9d6df4b18d4e59/dr_wav.h" -OutFile dr_wav.h
  ```
</CodeGroup>

Save this helper as `audio_file.h` alongside `dr_wav.h`. It checks input format and length, reads float samples and writes a 32-bit float WAV.

```c audio_file.h theme={null}
#ifndef AUDIO_FILE_H
#define AUDIO_FILE_H
#define DR_WAV_IMPLEMENTATION
#include "dr_wav.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

/* A short-file helper for this tutorial, independent of the SDK. */
static float* read_input(size_t* count) {
    drwav wav;
    if (!drwav_init_file(&wav, "input.wav", NULL)) {
        fprintf(stderr, "Cannot open input.wav as a WAV file.\n");
        return NULL;
    }
    if (wav.channels != 1 || wav.sampleRate != 16000 ||
        wav.translatedFormatTag != DR_WAVE_FORMAT_PCM || wav.bitsPerSample != 16 ||
        wav.totalPCMFrameCount == 0 || wav.totalPCMFrameCount > 960000) {
        fprintf(stderr, "Use a nonempty mono, 16 kHz, PCM16 input.wav, at most 60 seconds.\n");
        drwav_uninit(&wav);
        return NULL;
    }
    *count = (size_t)wav.totalPCMFrameCount;
    float* audio = (float*)malloc(*count * sizeof(float));
    if (!audio || drwav_read_pcm_frames_f32(&wav, *count, audio) != *count) {
        fprintf(stderr, "Cannot read all input samples.\n");
        free(audio);
        audio = NULL;
    }
    drwav_uninit(&wav);
    return audio;
}

static int write_output(const float* audio, size_t count) {
    drwav wav;
    drwav_data_format format = {drwav_container_riff, DR_WAVE_FORMAT_IEEE_FLOAT, 1, 16000, 32};
    for (size_t i = 0; i < count; ++i) {
        if (!isfinite(audio[i])) {
            fprintf(stderr, "Non-finite output sample; no output written.\n");
            return 0;
        }
    }
    if (!drwav_init_file_write(&wav, "enhanced.wav", &format, NULL)) {
        fprintf(stderr, "Cannot create enhanced.wav.\n");
        return 0;
    }
    drwav_uint64 written = drwav_write_pcm_frames(&wav, count, audio);
    drwav_result closed = drwav_uninit(&wav);
    if (written != count || closed != DRWAV_SUCCESS) {
        fprintf(stderr, "Cannot finish enhanced.wav; discard the incomplete file.\n");
        return 0;
    }
    return 1;
}
#endif
```

### Save the program

Save as `quickstart.c`:

```c quickstart.c theme={null}
#include "aic.h"
#include "audio_file.h"
#include <string.h>

#define CHECK(call) do { \
    enum AicErrorCode error = (call); \
    if (error != AIC_ERROR_CODE_SUCCESS) { \
        fprintf(stderr, "%s failed (SDK error %d).\n", #call, (int)error); \
        goto cleanup; \
    } \
} while (0)

int main(void) {
    const char* key = getenv("AIC_SDK_LICENSE");
    if (!key || !*key || strcmp(key, "YOUR_SDK_KEY") == 0) {
        fprintf(stderr, "Set AIC_SDK_LICENSE to your SDK key, then retry.\n");
        return 1;
    }
    int status = 1;
    size_t count = 0, block_size = 0, delay = 0, padded_count = 0;
    float* input = read_input(&count);
    float* output = NULL;
    struct AicModel* model = NULL;
    struct AicProcessor* processor = NULL;
    struct AicProcessorContext* context = NULL;
    if (!input) goto cleanup;
    CHECK(aic_model_create_from_file(&model, "model.aicmodel"));
    CHECK(aic_model_get_optimal_block_size(model, 16000, &block_size));
    CHECK(aic_processor_create(&processor, model, key, NULL));
    CHECK(aic_processor_initialize(processor, 16000, block_size, false));
    CHECK(aic_processor_context_create(&context, processor));
    CHECK(aic_processor_context_get_audio_delay(context, &delay));
    if (block_size == 0 || count > SIZE_MAX - delay ||
        count + delay > SIZE_MAX - (block_size - 1)) {
        fprintf(stderr, "Audio buffer size overflow.\n");
        goto cleanup;
    }
    padded_count = ((count + delay + block_size - 1) / block_size) * block_size;
    if (padded_count > SIZE_MAX / sizeof(float)) {
        fprintf(stderr, "Audio buffer size overflow.\n");
        goto cleanup;
    }
    output = (float*)calloc(padded_count, sizeof(float));
    if (!output) {
        fprintf(stderr, "Cannot allocate the processing buffer.\n");
        goto cleanup;
    }
    memcpy(output, input, count * sizeof(float));
    for (size_t start = 0; start < padded_count; start += block_size) {
        CHECK(aic_processor_process(processor, output + start, block_size));
    }
    /* Flush with silence, then omit the initial delay to align the files. */
    if (!write_output(output + delay, count)) goto cleanup;
    printf("SDK %s, model %s\n", aic_get_sdk_version(), aic_model_get_id(model));
    printf("Processed %zu samples at 16000 Hz; wrote enhanced.wav\n", count);
    status = 0;
cleanup:
    aic_processor_context_destroy(context);
    aic_processor_destroy(processor);
    aic_model_destroy(model);
    free(output);
    free(input);
    return status;
}
```

### Build and run

Save this complete project as `CMakeLists.txt`:

```cmake CMakeLists.txt theme={null}
cmake_minimum_required(VERSION 3.24)
project(aic_c_quickstart LANGUAGES C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
add_library(aic SHARED IMPORTED)
set(AIC_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/sdk")
if(WIN32)
  set_target_properties(aic PROPERTIES
    IMPORTED_LOCATION "${AIC_ROOT}/lib/aic.dll"
    IMPORTED_IMPLIB "${AIC_ROOT}/lib/aic.dll.lib")
elseif(APPLE)
  set_target_properties(aic PROPERTIES IMPORTED_LOCATION "${AIC_ROOT}/lib/libaic.dylib")
else()
  set_target_properties(aic PROPERTIES IMPORTED_LOCATION "${AIC_ROOT}/lib/libaic.so")
endif()
set_target_properties(aic PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${AIC_ROOT}/include")
add_executable(quickstart quickstart.c)
target_link_libraries(quickstart PRIVATE aic)
if(UNIX AND NOT APPLE)
  target_link_libraries(quickstart PRIVATE m)
endif()
if(WIN32)
  add_custom_command(TARGET quickstart POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different
    "$<TARGET_FILE:aic>" "$<TARGET_FILE_DIR:quickstart>")
endif()
```

Configure and build from `aic-c-quickstart`:

```bash theme={null}
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
```

Run from the project directory so the program can find `input.wav` and `model.aicmodel`:

<CodeGroup>
  ```bash macOS / Linux theme={null}
  ./build/quickstart
  ```

  ```powershell Windows PowerShell theme={null}
  .\build\Release\quickstart.exe
  ```
</CodeGroup>

### Check the result

A successful run exits with code zero, prints the native SDK version and resolved model ID, then reports `Processed N samples at 16000 Hz; wrote enhanced.wav`, where `N` is your input's sample count (`56080` for the supplied fixture). Open `enhanced.wav` in your audio editor: it should be mono, 16 kHz and the same duration as `input.wav`. The output is a **32-bit float WAV**; the input is PCM16.

The program pads the final block, flushes the delayed tail and removes the initial [processing delay](/reference/concepts/latency), preserving the original sample count. File writes are checked; discard any incomplete file if writing fails.

Listen to both files and compare them with the same STT settings to evaluate quality.

### Recover from an error

| Problem | Cause and retry |
| - | - |
| **Compiler, SDK header or library missing** | Install the compiler and check the selected archive or CMake download. Use the same target architecture for compiler and SDK. Rerun configure and build. |
| **Missing SDK key or SDK error 50** | Set `AIC_SDK_LICENSE` in this terminal to the complete SDK key, then run the program again. Error 50 is an invalid license format. |
| **SDK error 6 or 52** | Error 6 means processing is not allowed; error 52 means the license expired. Check account entitlement, key validity and [required connectivity](/models/get-started/authenticate-apps), then restart with a new processor. |
| **Input file rejected** | Save a nonempty mono, 16 kHz, PCM16 WAV of at most 60 seconds as `input.wav`, then retry. |
| **Model loading fails** | Keep `model.aicmodel` in the current directory. Re-download the exact compatible file above; error 100 means an invalid model, 101 an unsupported model version and 103 a filesystem error. |
| **SDK error 5** | The block size differs from initialization. Keep every call at the configured size and pad the final block as shown. |
| **Output write fails** | Check free space and write permission. Discard the incomplete `enhanced.wav`, fix the cause and retry. |

The CMake build uses the shared SDK library. Keep `sdk/lib` in place for this tutorial. On Windows, it copies `aic.dll` beside the executable; keep that DLL with your application. See the [C binding guide](/reference/sdk/language-bindings/c#integration-behavior) for deployment and static linking.

### Adapt this example

Keep one processor per stream and process blocks in order. Reset its context on a discontinuity or before unrelated audio. Downmix stereo or use a processor per channel. See [audio format](/reference/concepts/audio-format) and [streams and state](/reference/concepts/streams-and-state).

The helper reads a short recording into memory. For long files and live audio, use bounded buffers and keep file I/O, model loading and session teardown outside the audio callback. For human-listening enhancement, evaluate [Rook Multi Speaker](/models/perceptual-speech-enhancement/rook-multi-speaker).

## Integration behavior

| Concern | C SDK 0.25.0 behavior |
| - | - |
| **Audio buffer** | `aic_processor_process` accepts a writable contiguous buffer of mono `float` samples and modifies it in place. Use normalized float32 samples, nominally -1.0 to 1.0. |
| **Errors** | Check each `AicErrorCode` before using an output handle or value. `AIC_ERROR_CODE_SUCCESS` is zero. Creation failure leaves no usable object. |
| **Initialization** | Call `aic_processor_initialize` before processing. Choose the block size for the actual input rate with `aic_model_get_optimal_block_size`. The variable-block option defaults to the value your application passes; the tutorial passes `false`. |
| **State** | Use one processor per stream. Context reset clears enhancement state and buffers while retaining audio configuration. Do not reset between consecutive blocks. |
| **Context** | Create it with `aic_processor_context_create`. It provides parameter control, reset and delay queries. It has its own handle and must be destroyed separately. |
| **Concurrency** | Do not process or destroy the same processor concurrently. Context control functions have their own thread-safety contracts in the header. Use separate processors for separate simultaneous streams. |
| **Cleanup** | Destroy the context, processor and model handles when finished. The destroy functions accept `NULL`, so one cleanup path can handle partial construction. Destruction also closes the processor's session. |
| **Model lifetime** | A model loaded from a file is memory-mapped. Do not alter or delete that file while any dependent object remains alive. Model data has internal reference counting; processors keep their reference when the model handle is destroyed. |

Model creation, initialization and teardown allocate resources or may block. Run them outside the real-time audio callback. Keep input/output buffer storage alive throughout each synchronous processing call.

## Link and deploy

The tutorial uses a shared library to keep the build configuration small. Ship the matching `libaic.so`, `libaic.dylib` or `aic.dll` with your application and configure the platform's loader path. A successful local build does not ensure a relocated application can find its library.

Static archives are also available in the same [release](https://github.com/ai-coustics/aic-sdk-c/releases/tag/0.25.0). Static linking requires platform system libraries; on Windows, select the archive matching the MSVC runtime flavor. Follow the release's build guidance rather than mixing headers and binaries from different SDK versions.

For voice activity detection (VAD) or Tyto analysis, use the dedicated VAD or analyzer APIs and their corresponding model types. An enhancement model can also report speech through [`aic_energy_vad_context_create`](/reference/sdk/api/c/vad#aic_energy_vad_context_create). See the [released examples](https://github.com/ai-coustics/aic-sdk-c/tree/0.25.0/examples). Historical examples under `docs/0.24.0` describe that release; check [compatibility](/reference/sdk/compatibility-matrix) when upgrading.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.