Bindings

Kotlin

Kotlin binding for the Rapidly SDK, for Android apps via Maven Central.

The Kotlin binding wraps the Rapidly SDK's native C engine in an idiomatic Kotlin class, distributed via Maven Central as an Android library. The same engine runs natively on Android. See Overview for what's inside the engine and the supported Android targets.

Install

Requirements: Android minSdk 26 (Android 8.0). The library bundles the arm64-v8a ABI.

In app/build.gradle.kts:

dependencies {
    implementation("io.rapidly:rapidly-sdk:1.0")
}

Alternative: .aar from GitHub Release

To vendor the binary directly instead of pulling from Maven Central, download rapidly-sdk-1.0.aar from the GitHub Release into your app's libs/ folder, then:

dependencies {
    implementation(files("libs/rapidly-sdk-1.0.aar"))
}

Basic usage

Import the binding:

import io.rapidly.engine.RapidlyEngine

Models ship as .rapidly files. On Android, place them in your app's assets/ folder and copy to context.cacheDir on first use, then pass the filesystem path:

val modelFile = File(context.cacheDir, "speech-denoise-32ms.v1.0.rapidly")
if (!modelFile.exists()) {
    context.assets.open("speech-denoise-32ms.v1.0.rapidly").use { input ->
        modelFile.outputStream().use { output -> input.copyTo(output) }
    }
}

Create an engine by specifying the model file path, channel count, and sample rate:

val engine = RapidlyEngine.create(
    modelFilepath = modelFile.absolutePath,
    numOfChannels = 2,
    sampleRate = 48000.0,
) ?: error("Unable to create the Rapidly engine.")

create is a factory that returns null only when the model file is missing or unreadable. License coverage never causes a null result.

Audio buffers must be direct so the JNI path can read them without copying:

val inputBuffer = ByteBuffer.allocateDirect(numOfSamples * 2 * 4)
    .order(ByteOrder.nativeOrder())
    .asFloatBuffer()

// Interleaved format (L1, R1, L2, R2, ...)
engine.addAudioInterleaved(inputBuffer, numOfSamples)

Retrieve processed audio. Processing introduces latency, so query the number of available samples first:

val pending = engine.getNumOfPendingSamples()
engine.getAudioInterleaved(outputBuffer, pending)

When finished, call close() to release the underlying processor:

engine.close()

To capture the processing tail caused by model latency, add silent audio (zeros) after your input stream ends.

Threading

Drive each engine from a single thread. RapidlyEngine implements AutoCloseable, so for short offline jobs wrap construction in a use block to release native resources automatically:

RapidlyEngine.create(modelFilepath, numOfChannels = 1, sampleRate = 48000.0)
    ?.use { engine -> /* ... */ }

For real-time audio, call close() explicitly from your audio-stop path. The JVM has no deterministic finalizer, so a forgotten close leaks the native processor until process exit.

See One vs multiple engines for the universal threading rules and the stop-then-close sequence.

Licensing

Call RapidlyEngine.addLicense before constructing any engine. See Pricing for licensing options.

if (!RapidlyEngine.addLicense("lk_...")) {
    error("Rapidly license could not be verified.")
}

Android specifics

Audio buffers passed to addAudio* and getAudio* must be direct FloatBuffer instances created from ByteBuffer.allocateDirect(...).order(ByteOrder.nativeOrder()).asFloatBuffer(). The JNI path reads and writes directly into the native buffer, avoiding a copy across the JVM boundary.

Model files must be on the filesystem. Ship them in your app's assets/ folder and copy to context.cacheDir on first launch.

The library ships only the arm64-v8a ABI. Emulator testing requires a physical arm64 device or an arm64 emulator image.

Types

RapidlyEngine

public class RapidlyEngine : AutoCloseable

The Kotlin-native audio engine. Implements AutoCloseable for use with use { }.

RapidlyEngine.ProcessorInfo

public data class ProcessorInfo(
    val sampleRate: Double,        // Sample rate the model was trained on
    val numOfModelChannels: Int,   // Number of channels used internally
    val latencyInSamples: Int,     // Maximum processing latency
)

The processor automatically converts sample rates and channel formats to match the model.

RapidlyEngine.Parameters

public enum class Parameters(public val value: Int) {
    MAXIMUM_ATTENUATION(0x0001),
    SENSITIVITY(0x0002),
    MASK_EXTRAPOLATION(0x0003),
    FIRST_MODEL_SENSITIVITY(0x1000),
}

For stem-separation models with multiple internal models, address an individual stem by offsetting FIRST_MODEL_SENSITIVITY.value with the model index.

Methods

Licensing

addLicense

public companion object {
    @JvmStatic
    public fun addLicense(licenseString: String): Boolean
}

Adds a license key to remove audio watermarking from the output.

ParameterDescription
licenseStringLicense string issued by Rapidly.

Returns: true if the license is valid, false otherwise.

Engine lifecycle

create(modelFilepath:numOfChannels:sampleRate:)

public companion object {
    @JvmStatic
    public fun create(
        modelFilepath: String,
        numOfChannels: Int,
        sampleRate: Double,
    ): RapidlyEngine?
}

Creates an engine and loads a single model file.

ParameterDescription
modelFilepathFilesystem path to the .rapidly model file.
numOfChannelsNumber of audio channels to process.
sampleRateSample rate of the input audio.

Returns: A valid instance on success, null when the model file is missing or unreadable.

create(modelFilepaths:numOfChannels:sampleRate:)

public companion object {
    @JvmStatic
    public fun create(
        modelFilepaths: List<String>,
        numOfChannels: Int,
        sampleRate: Double,
    ): RapidlyEngine?
}

Creates a processor that performs stem separation, loading a set of model files. The number of stems is derived from modelFilepaths.size.

close

public override fun close()

Releases the underlying processor and frees its resources. Safe to call multiple times. After close, all audio methods become no-ops and read methods return false or zero.

resetProcessorState

public fun resetProcessorState()

Resets the processor state and clears all internal delay lines. Call this when starting a new audio stream.

Audio I/O

addAudio

public fun addAudio(
    pcmChannels: Array<FloatBuffer>,
    numOfSamples: Int,
)

Adds audio from separate per-channel direct buffers.

addAudioInterleaved

public fun addAudioInterleaved(
    pcmChannels: FloatBuffer,
    numOfSamples: Int,
)

Adds audio from an interleaved direct buffer (L1, R1, L2, R2, ...).

getNumOfPendingSamples

public fun getNumOfPendingSamples(): Int

Returns the number of processed samples available for retrieval.

getAudio

public fun getAudio(
    pcmChannels: Array<FloatBuffer>,
    numOfSamples: Int,
): Boolean

Retrieves processed audio into separate per-channel direct buffers.

getAudioInterleaved

public fun getAudioInterleaved(
    pcmChannels: FloatBuffer,
    numOfSamples: Int,
): Boolean

Retrieves processed audio into an interleaved direct buffer.

Parameters

getParameterValue

public fun getParameterValue(parameter: Parameters): Float

Returns the current value of a parameter.

setParameter

public fun setParameter(parameter: Parameters, value: Float)

Sets a parameter value.

Processor info

getProcessorInfo

public fun getProcessorInfo(): ProcessorInfo

Returns a ProcessorInfo describing the processor.

Output busses

The Kotlin wrapper exposes engine-wide and per-stem-model controls through Parameters. For introspection of named output busses and per-bus gain control, use the C engine's bus methods through JNI; see the equivalent functions on the C / C++ page.