# aic-sdk-wasm - WebAssembly Bindings for ai-coustics SDK

WebAssembly wrapper for the ai-coustics Speech Enhancement SDK. This package allows you to run ai-coustics models directly in the browser.

For comprehensive documentation, visit [docs.ai-coustics.com](https://docs.ai-coustics.com).

> [!NOTE]
> This SDK requires a license key. Generate your key at [developers.ai-coustics.io](https://developers.ai-coustics.io).

## Installation

```bash
npm install @ai-coustics/aic-sdk-wasm
```

## Quick Start

```javascript
import init, { Model, Processor } from "@ai-coustics/aic-sdk-wasm";

// Initialize the WASM module
await init();

// Load a model
const response = await fetch("https://artifacts.ai-coustics.io/models/quail-vf-l-16khz/v1/quail_vf_l_16khz_jc5pk1aa_v17.aicmodel");
const modelBytes = new Uint8Array(await response.arrayBuffer());
const model = Model.fromBytes(modelBytes);

// Get optimal configuration
const sampleRate = model.getOptimalSampleRate();
const numFrames = model.getOptimalNumFrames(sampleRate);
const numChannels = 1;

// Create and initialize processor
const processor = new Processor(model, "YOUR_LICENSE_KEY");
processor.initialize(sampleRate, numChannels, numFrames, false);

// Process audio (Float32Array)
const audioBuffer = new Float32Array(numChannels * numFrames);
// ... fill buffer with audio data ...
processor.processInterleaved(audioBuffer);
```

## Usage

### SDK Information

```javascript
import { getVersion, getCompatibleModelVersion } from "@ai-coustics/aic-sdk-wasm";

// Get SDK version
console.log(`SDK version: ${getVersion()}`);

// Get compatible model version
console.log(`Compatible model version: ${getCompatibleModelVersion()}`);
```

### Loading Models

Download models and find available IDs at [artifacts.ai-coustics.io](https://artifacts.ai-coustics.io/).

Since browsers cannot access the file system directly, you must load the model file as a byte array (Uint8Array).

```javascript
// Fetch the .aicmodel file
const response = await fetch("https://artifacts.ai-coustics.io/models/quail-vf-l-16khz/v1/quail_vf_l_16khz_jc5pk1aa_v17.aicmodel");
const bytes = new Uint8Array(await response.arrayBuffer());

// Create Model instance
const model = Model.fromBytes(bytes);
```

### Model Information

```javascript
// Get model ID
const modelId = model.getId();

// Get optimal sample rate for the model
const optimalRate = model.getOptimalSampleRate();

// Get optimal frame count for a specific sample rate
const optimalFrames = model.getOptimalNumFrames(48000);
```

### Configuring the Processor

```javascript
// Create processor
const processor = new Processor(model, "YOUR_LICENSE_KEY");

// Initialize with audio settings
processor.initialize(
  sampleRate,           // Sample rate in Hz (8000 - 192000)
  numChannels,          // Number of audio channels
  numFrames,            // Samples per channel per processing call
  allowVariableFrames   // Allow variable frame sizes (default: false)
);
```

### Processing Audio

```javascript
// Interleaved audio: [L0, R0, L1, R1, ...]
const buffer = new Float32Array(numChannels * numFrames);
processor.processInterleaved(buffer);

// Sequential audio: [L0, L1, ..., R0, R1, ...]
processor.processSequential(buffer);

// Planar audio: Not typically used in JS/WASM context as cleanly as native, 
// but supported if you pack data into a single Float32Array
// processor.processPlanar(buffer, numChannels);
```

### Processor Context

```javascript
import { ProcessorParameter } from "@ai-coustics/aic-sdk-wasm";

// Get processor context
const procCtx = processor.getProcessorContext();

// Get output delay in samples
const delay = procCtx.getOutputDelay();

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

// Set enhancement parameters
procCtx.setParameter(ProcessorParameter.EnhancementLevel, 0.8);
procCtx.setParameter(ProcessorParameter.Bypass, 0.0);

// Get parameter values
const level = procCtx.getParameter(ProcessorParameter.EnhancementLevel);
console.log(`Enhancement level: ${level}`);
```

### Voice Activity Detection (VAD)

```javascript
import { VadParameter } from "@ai-coustics/aic-sdk-wasm";

// Get VAD context from processor
const vadCtx = processor.getVadContext();

// Configure VAD parameters
vadCtx.setParameter(VadParameter.Sensitivity, 6.0);
vadCtx.setParameter(VadParameter.SpeechHoldDuration, 0.03);
vadCtx.setParameter(VadParameter.MinimumSpeechDuration, 0.0);

// Get parameter values
const sensitivity = vadCtx.getParameter(VadParameter.Sensitivity);
console.log(`VAD sensitivity: ${sensitivity}`);

// Check for speech (after processing audio through the processor)
if (vadCtx.isSpeechDetected()) {
  console.log("Speech detected!");
}
```

### Analysis

For analysis-only models (e.g. Tyto) use the `Analyzer` class instead of `Processor`. The analyzer buffers audio in the audio thread via `bufferInterleaved` / `bufferSequential` / `bufferPlanar` and runs the analysis model on demand via `analyze()`.

```javascript
import { Analyzer } from "@ai-coustics/aic-sdk-wasm";

const analysisModel = Model.fromBytes(analysisModelBytes);
const sampleRate = analysisModel.getOptimalSampleRate();
const numFrames = analysisModel.getOptimalNumFrames(sampleRate);

const analyzer = new Analyzer(analysisModel, "YOUR_LICENSE_KEY");
analyzer.initialize(sampleRate, 1, numFrames, false);

// From the audio path (any of the three layouts):
analyzer.bufferInterleaved(audioChunk, numChannels, numFrames);
// analyzer.bufferSequential(audioChunk, numChannels, num);
// analyzer.bufferPlanar(audioChunk, numChannels);

// From any context:
const result = analyzer.analyze();
console.log(result.riskScore, result.speakerReverb, result.speakerLoudness,
            result.interferingSpeech, result.mediaSpeech, result.noise,
            result.packetLoss);
result.free();

// Optional: clear buffered audio on stream discontinuity.
analyzer.reset();

// Optional: refresh a JWT-form license without reconstructing the analyzer.
// analyzer.updateBearerToken(newJwt);

analyzer.free();
analysisModel.free();
```

Passing an analysis model to `new Processor(...)` throws `ModelTypeUnsupported`; conversely, passing an enhancement/VAD model to `new Analyzer(...)` throws the same. Pick the class that matches the loaded model's type.

## Memory Management

WebAssembly memory should be manually freed when objects are no longer needed. This does not need to be done if the browser supports [weak references](https://wasm-bindgen.github.io/wasm-bindgen/reference/weak-references.html).


```javascript
// When finished with the VAD
vadCtx.free();

// When finished with the processor
processor.free();

// When finished with the model (and you won't create new processors from it)
model.free();
```

## Examples

See the [`examples/`](examples/) directory for complete working examples, including:
- **index.html**: Full interactive demo with file input, parameter controls, and real-time VAD visualization.
- **benchmark.html**: Performance benchmark tool to measure processing latency.
- **basic.html**: Demonstrates all core SDK APIs.

## Documentation

- **Full Documentation**: [docs.ai-coustics.com](https://docs.ai-coustics.com)
- **Available Models**: [artifacts.ai-coustics.io](https://artifacts.ai-coustics.io)
