aic-sdk 0.21.1

ai-coustics Speech Enhancement SDK
Documentation

aic-sdk - Rust Bindings for ai-coustics SDK

Rust wrapper for the ai-coustics Speech Enhancement SDK.

For comprehensive documentation, visit docs.ai-coustics.com.

[!NOTE] This SDK requires a license key. Generate your key at developers.ai-coustics.com.

[!WARNING] The bundled libraries were built with Rust 1.97.0-beta.4. Building your crate with that exact toolchain version fails to link, so use any other Rust version. This affects the default static linking only — the dynamic-linking and runtime-linking modes are unaffected (see Linking the native SDK).

Installation

Add to your project:

cargo add aic-sdk --features download-lib

Quick Start

use aic_sdk::{include_model, ProcessorConfig, Model, Processor};

// Embed model at compile time (or use Model::from_file to load at runtime)
static MODEL: &'static [u8] = include_model!("/path/to/model.aicmodel");

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Get your license key from the environment variable
    let license_key = std::env::var("AIC_SDK_LICENSE")?;

    // Load the embedded model (or download manually at https://artifacts.ai-coustics.io/)
    let model = Model::from_buffer(MODEL)?;

    // Get optimal configuration based on the selected model
    let config = ProcessorConfig::optimal(&model).with_num_channels(2);

    // Create a processor and initialize it
    let mut processor = Processor::new(&model, &license_key)?.with_config(&config)?;

    // Process audio (interleaved: channels × frames)
    let mut audio_buffer = vec![0.0f32; config.num_channels as usize * config.num_frames];
    processor.process_interleaved(&mut audio_buffer)?;

    Ok(())
}

Linking the native SDK

By default, aic-sdk-sys links the native AIC SDK statically. Two opt-in features link a shared libaic instead. They are mutually exclusive; enabling both (e.g. via --all-features) selects runtime-linking.

Feature Linking How libaic is located
(default) static, at build time AIC_LIB_PATH directory, or downloaded with download-lib
dynamic-linking dynamic, at build time same to link; then OS loader search at run time
runtime-linking dynamic, lazy on first use OS loader search by name, or aic_sdk::load_library(path)

In every mode, point the build at the SDK with AIC_LIB_PATH=/path/to/aic-sdk/lib, or enable download-lib to fetch it automatically:

AIC_SDK_LICENSE="" cargo run --example basic_usage \
  --features "dynamic-linking download-lib download-model"

[!NOTE] The dynamic-linking and runtime-linking modes are not affected by the toolchain link issue in the warning above; it applies to default static linking only.

Finding the library at run time

With dynamic-linking and runtime-linking, the OS dynamic loader must locate libaic when the program runs — download-lib only covers build time. Point the loader at the SDK lib directory, ship the library next to the binary, or install it system-wide:

  • Linux: LD_LIBRARY_PATH=/path/to/aic-sdk/lib, or an rpath (RUSTFLAGS="-C link-arg=-Wl,-rpath,\$ORIGIN" + ship libaic.so beside the binary).
  • macOS: DYLD_LIBRARY_PATH, @rpath/@loader_path, or a bundle layout.
  • Windows: put aic.dll next to the .exe or on PATH (the build-time import lib aic.lib and the runtime aic.dll may be in different directories).
  • Android: package libaic.so (arm64 only) into the APK under lib/arm64-v8a/.

runtime-linking loads libaic automatically on the first SDK call, by platform default name (libaic.so / libaic.dylib / aic.dll). To choose an exact file, call load_library first; if the library can't be found, that first call panics with a descriptive message:

unsafe { aic_sdk::load_library("/path/to/libaic.so")?; } // optional override

Usage

SDK Information

use aic_sdk;

// Get SDK version
println!("SDK version: {}", aic_sdk::get_sdk_version());

// Get compatible model version
println!("Compatible model version: {}", aic_sdk::get_compatible_model_version());

Loading Models

Download models and find available IDs at artifacts.ai-coustics.io.

Load from File

use aic_sdk::Model;

let model = Model::from_file("path/to/model.aicmodel")?;

Embed at Compile Time

use aic_sdk::{Model, include_model};

static MODEL: &'static [u8] = include_model!("/path/to/model.aicmodel");
let model = Model::from_buffer(MODEL)?;

Download from CDN

Enable the download-model feature:

cargo add aic-sdk --features download-lib,download-model
use aic_sdk::Model;

let model_path = Model::download("quail-vf-2.1-s-16khz", "./models")?;
let model = Model::from_file(&model_path)?;

Model Information

// Get model ID
let model_id = model.id();

// Get optimal sample rate for the model
let optimal_rate = model.optimal_sample_rate();

// Get optimal frame count for a specific sample rate
let optimal_frames = model.optimal_num_frames(48000);

Configuring the Processor

use aic_sdk::{Processor, ProcessorConfig};

// Get optimal configuration for the model
let config = ProcessorConfig::optimal(&model)
    .with_num_channels(1)
    .with_allow_variable_frames(false);
println!("{:?}", config);  // ProcessorConfig { sample_rate: 48000, num_channels: 1, num_frames: 480, allow_variable_frames: false }

// Or create from scratch
let config = ProcessorConfig {
    sample_rate: 48000,
    num_channels: 2,
    num_frames: 480,
    allow_variable_frames: false,
};

// Processor needs to be initialized before processing

// Option 1: Create and initialize in one step
let processor = Processor::new(&model, &license_key)?.with_config(&config)?;

// Option 2: Create first, then initialize separately
let mut processor = Processor::new(&model, &license_key)?;
processor.initialize(&config)?;

OpenTelemetry

By default, processor telemetry follows the SDK environment configuration, such as AIC_SDK_OTEL_ENABLE. Use OtelConfig when a single processor needs an explicit telemetry setting or session ID.

use aic_sdk::{OtelConfig, Processor};

let otel = OtelConfig::with_session_id("session-1");
let processor = Processor::with_otel_config(&model, &license_key, &otel)?
    .with_config(&config)?;

Processing Audio

// Interleaved processing (channels interleaved in single buffer)
// Format: [l, r, l, r, ...]
let mut audio_buffer = vec![0.0f32; config.num_channels as usize * config.num_frames];
processor.process_interleaved(&mut audio_buffer)?;

// Sequential processing (channels in sequence)
// Format: [l, l, ..., r, r, ...]
let mut audio_sequential = vec![0.0f32; config.num_channels as usize * config.num_frames];
processor.process_sequential(&mut audio_sequential)?;

// Planar processing (separate buffer per channel)
// Format: [[l, l, ...], [r, r, ...]]
let mut audio = vec![vec![0.0f32; config.num_frames]; config.num_channels as usize];
processor.process_planar(&mut audio)?;

Processor Context

The processor context provides thread-safe access to processor parameters and state. You can create multiple contexts and move them to any thread for concurrent parameter updates.

use aic_sdk::ProcessorParameter;

// Get processor context
let proc_ctx = processor.processor_context();

// Get output delay in samples
let delay = proc_ctx.output_delay();

// Reset processor state (clears internal buffers)
proc_ctx.reset()?;

// Set enhancement parameters
proc_ctx.set_parameter(ProcessorParameter::EnhancementLevel, 0.8)?;
proc_ctx.set_parameter(ProcessorParameter::Bypass, 0.0)?;

// Get parameter values
let level = proc_ctx.parameter(ProcessorParameter::EnhancementLevel)?;
println!("Enhancement level: {}", level);

Voice Activity Detection (VAD)

The VAD context provides thread-safe access to VAD parameters and state. You can create multiple contexts and move them to any thread for concurrent parameter updates.

use aic_sdk::VadParameter;

// Get VAD context from processor
let vad_ctx = processor.vad_context();

// Configure VAD parameters
vad_ctx.set_parameter(VadParameter::Sensitivity, 6.0)?;
vad_ctx.set_parameter(VadParameter::SpeechHoldDuration, 0.05)?;
vad_ctx.set_parameter(VadParameter::MinimumSpeechDuration, 0.0)?;

// Get parameter values
let sensitivity = vad_ctx.parameter(VadParameter::Sensitivity)?;
println!("VAD sensitivity: {}", sensitivity);

// Check for speech (after processing audio through the processor)
if vad_ctx.is_speech_detected() {
    println!("Speech detected!");
}

Working with the Analyzer

Instantiate an analyzer pair:

let (mut collector, mut analyzer) = aic_sdk::analyzer_pair(&model, &license_key)?;

Initialize the collector, similar to the processor initialization

let config = ProcessorConfig::optimal(&model);
collector.initialize(&config)?;

Buffer the audio using the Collector::buffer_* APIs. They mirror the Processor::process_* APIs. See the Processing Audio section for more details.

Analyze the buffered audio in a separate thread:

let result = analyzer.analyze_buffered()?;
println!("Risk score: {}", result.risk_score);

Async Processing

Enable the async feature to use [ProcessorAsync], which offloads processing to a background thread pool and returns a future. The implementation is runtime-agnostic and works on any executor (tokio, smol, async-std, ...). The pool defaults to one thread per logical CPU; override with the AIC_NUM_THREADS environment variable.

cargo add aic-sdk --features async
use aic_sdk::{Model, ProcessorAsync, ProcessorConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let license_key = std::env::var("AIC_SDK_LICENSE")?;
    let model = Model::from_file("path/to/model.aicmodel")?;
    let config = ProcessorConfig::optimal(&model).with_num_channels(2);

    let processor = ProcessorAsync::new(&model, &license_key)?
        .with_config(&config)
        .await?;

    // The async API takes ownership of the buffer and returns it back.
    let audio = vec![0.0f32; config.num_channels as usize * config.num_frames];
    let audio = processor.process_interleaved(audio).await?;
    Ok(())
}

Examples

See the example files for complete working examples:

Run examples with:

export AIC_SDK_LICENSE="your_license_key_here"
cargo run --example basic_usage --features download-lib,download-model

Documentation

License

This Rust wrapper is distributed under the Apache 2.0 license. The core C SDK is distributed under the proprietary AIC-SDK license.

Third-party notices

NOTICE.txt lists the third-party software distributed with the SDK, in two parts: the open-source code statically linked into the native libaic library, and the third-party Rust crates the bindings depend on. It is generated, not edited by hand. Regenerate with:

./scripts/generate-notice.sh   # requires: cargo install cargo-about

The libaic part is mirrored from the SDK release into aic-sdk-sys/NOTICE.libaic.txt and kept in sync by CI; update it from a release's NOTICE.txt whenever you bump aic-sdk-sys/checksum.txt.