Audio Classification
Transformers
ONNX
NeMo
Safetensors
anuvani_lid
audio
speech
language-identification
spoken-language-id
lid
indic
conformer
custom_code
Instructions to use CIIL/Anuvani-LID with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use CIIL/Anuvani-LID with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("audio-classification", model="CIIL/Anuvani-LID", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModelForAudioClassification model = AutoModelForAudioClassification.from_pretrained("CIIL/Anuvani-LID", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download mobile_inference/android/LiveLanguageId.kt from CIIL/Anuvani-LID: direct link, hf CLI and curl.
- Browser
- Download file 3.66 kB
-
https://huggingface.co/CIIL/Anuvani-LID/resolve/main/mobile_inference/android/LiveLanguageId.kt
- Command line
-
hf download hf://CIIL/Anuvani-LID/mobile_inference/android/LiveLanguageId.kt
-
curl -L -o LiveLanguageId.kt https://huggingface.co/CIIL/Anuvani-LID/resolve/main/mobile_inference/android/LiveLanguageId.kt
3.66 kB
| // Copyright (c) 2026, CIIL Anuvani-LID. Apache-2.0. | |
| // | |
| // Live language identification from the microphone: AudioRecord (16 kHz mono float) -> sherpa-onnx's Silero VAD -> | |
| // each finished stretch of speech is classified on a worker thread (so a slow classification never stalls the | |
| // recording) -> onSegment(start, end, scores) on that worker thread. Needs android.permission.RECORD_AUDIO. | |
| // A starting point: copy it into your app and change the package (docs/03-android.md, "Live microphone"). | |
| package com.example.anuvanilid | |
| import android.annotation.SuppressLint | |
| import android.media.AudioFormat | |
| import android.media.AudioRecord | |
| import android.media.MediaRecorder | |
| import com.k2fsa.sherpa.onnx.SileroVadModelConfig | |
| import com.k2fsa.sherpa.onnx.Vad | |
| import com.k2fsa.sherpa.onnx.VadModelConfig | |
| import java.util.concurrent.Executors | |
| import java.util.concurrent.TimeUnit | |
| class LiveLanguageId( | |
| private val identifier: LanguageIdentifier, | |
| vadModel: String, | |
| /** Called once per stretch of speech (seconds from start(); the most likely languages first). */ | |
| private val onSegment: (startS: Double, endS: Double, scores: List<LanguageScore>) -> Unit, | |
| /** Longest stretch passed to the model in one piece (seconds). */ | |
| maxSpeechSeconds: Float = 15f, | |
| ) : AutoCloseable { | |
| private val sampleRate = 16000 | |
| private val vad = Vad( | |
| config = VadModelConfig( | |
| sileroVadModelConfig = SileroVadModelConfig( | |
| model = vadModel, | |
| threshold = 0.5f, | |
| minSilenceDuration = 0.3f, | |
| minSpeechDuration = 0.25f, | |
| windowSize = 512, | |
| maxSpeechDuration = maxSpeechSeconds, | |
| ), | |
| sampleRate = sampleRate, | |
| numThreads = 1, | |
| ), | |
| ) | |
| private val worker = Executors.newSingleThreadExecutor() | |
| private var running = false | |
| private var thread: Thread? = null | |
| fun start() { | |
| check(!running) { "already started" } | |
| running = true | |
| thread = Thread { | |
| val minBuf = AudioRecord.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_FLOAT) | |
| val rec = AudioRecord( | |
| MediaRecorder.AudioSource.VOICE_RECOGNITION, sampleRate, AudioFormat.CHANNEL_IN_MONO, | |
| AudioFormat.ENCODING_PCM_FLOAT, maxOf(minBuf, 4 * sampleRate), | |
| ) | |
| val buf = FloatArray(512) | |
| rec.startRecording() | |
| while (running) { | |
| val n = rec.read(buf, 0, buf.size, AudioRecord.READ_BLOCKING) | |
| if (n <= 0) continue | |
| vad.acceptWaveform(if (n == buf.size) buf.copyOf() else buf.copyOf(n)) | |
| drain() | |
| } | |
| rec.stop() | |
| rec.release() | |
| vad.flush() | |
| drain() | |
| }.apply { name = "anuvani-lid-mic"; start() } | |
| } | |
| /** Stop recording; segments already queued are still classified. */ | |
| fun stop() { | |
| running = false | |
| thread?.join() | |
| thread = null | |
| } | |
| override fun close() { | |
| stop() | |
| worker.shutdown() | |
| worker.awaitTermination(60, TimeUnit.SECONDS) | |
| vad.release() | |
| } | |
| private fun drain() { | |
| while (!vad.empty()) { | |
| val seg = vad.front() | |
| vad.pop() | |
| val start = seg.start / sampleRate.toDouble() | |
| val end = (seg.start + seg.samples.size) / sampleRate.toDouble() | |
| val samples = seg.samples | |
| worker.execute { onSegment(start, end, identifier.classify(samples, sampleRate, topK = 3)) } | |
| } | |
| } | |
| } | |