Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/hitenpatel/songprint/llms.txt

Use this file to discover all available pages before exploring further.

SignatureGenerator and Pcm are written in pure common Kotlin — no java.io, no java.nio, no java.util.zip, and no platform-specific intrinsics. They can be included in any Kotlin Multiplatform shared module and will compile unchanged on any KMP target. RecognitionClient, on the other hand, uses java.net.HttpURLConnection and java.util.Base64, which means it is JVM-only. Understanding this split is the key to a clean KMP architecture with songprint.

What is KMP-compatible

SignatureGenerator

Pure common Kotlin. No JVM-only imports. Compiles on all KMP targets — Android, JVM, JS, and native. Safe to place in commonMain.

Pcm

Pure common Kotlin. No JVM-only imports. Compiles on all KMP targets. Safe to place in commonMain.

RecognitionClient

JVM-only. Uses java.net.HttpURLConnection and java.util.Base64. Only available in androidMain / jvmMain source sets.

Full library (JitPack JAR)

Published as a single JVM JAR. Depends on it from androidMain or jvmMain; do not add it to commonMain in a multiplatform module.

Adding the dependency

In a Kotlin Multiplatform module, scope the songprint dependency to the JVM/Android source sets where RecognitionClient is needed. SignatureGenerator and Pcm source files can be added to commonMain directly (see the architecture section below), but if you are pulling in the whole JitPack artifact it belongs in a JVM-side source set.
// shared/build.gradle.kts
repositories {
    maven("https://jitpack.io")
}

kotlin {
    androidTarget()
    jvm()
    // … other targets

    sourceSets {
        // The full library (including RecognitionClient) goes here.
        val androidMain by getting {
            dependencies {
                implementation("com.github.hitenpatel:songprint:0.1.0")
            }
        }
        // Or, if you also want it on desktop JVM:
        val jvmMain by getting {
            dependencies {
                implementation("com.github.hitenpatel:songprint:0.1.0")
            }
        }
    }
}
The cleanest approach is to keep all fingerprinting logic in commonMain and isolate the HTTP transport behind an interface or expect/actual declaration in the platform-specific source sets.
1

Define a recognition interface in commonMain

Expose a simple suspending interface that commonMain code can call without knowing about HTTP or the JVM.
// commonMain/RecognitionService.kt
interface RecognitionService {
    /** Returns raw JSON, or null on failure / no match. */
    suspend fun recognise(signature: ByteArray, numSamples: Int): String?
}
2

Run SignatureGenerator in commonMain

SignatureGenerator and Pcm compile without modification in commonMain.
// commonMain/Fingerprinter.kt
import io.github.hitenpatel.songprint.Pcm
import io.github.hitenpatel.songprint.SignatureGenerator

class Fingerprinter(private val service: RecognitionService) {

    suspend fun identify(rawPcmBytes: ByteArray, sampleRate: Int, channels: Int): String? {
        val samples  = Pcm.bytesToShorts(rawPcmBytes)
        val mono16k  = Pcm.resampleToMono16k(samples, sampleRate, channels)
        val signature = SignatureGenerator().generateSignature(mono16k)
        return service.recognise(signature, mono16k.size)
    }
}
3

Implement RecognitionService on Android / JVM

The actual (or concrete) implementation lives in androidMain or jvmMain, where RecognitionClient is available.
// androidMain/AndroidRecognitionService.kt
import io.github.hitenpatel.songprint.RecognitionClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class AndroidRecognitionService : RecognitionService {
    private val client = RecognitionClient()

    override suspend fun recognise(signature: ByteArray, numSamples: Int): String? =
        withContext(Dispatchers.IO) {
            client.recognise(signature, numSamples)
        }
}
4

Wire it together at the entry point

Inject the platform implementation when constructing Fingerprinter — from an Android ViewModel, for example.
// Android ViewModel or Activity
val fingerprinter = Fingerprinter(AndroidRecognitionService())
val json = fingerprinter.identify(rawBytes, sampleRate = 44100, channels = 2)

Example expect/actual structure

If you prefer expect/actual over an interface, the structure is similar. Declare the expectation in commonMain and fulfill it in each platform source set.
// commonMain/RecognitionApi.kt
expect suspend fun postSignature(signature: ByteArray, numSamples: Int): String?
// androidMain/RecognitionApi.android.kt
import io.github.hitenpatel.songprint.RecognitionClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

actual suspend fun postSignature(signature: ByteArray, numSamples: Int): String? =
    withContext(Dispatchers.IO) {
        RecognitionClient().recognise(signature, numSamples)
    }
// iosMain/RecognitionApi.ios.kt (stub — provide your own HTTP implementation)
actual suspend fun postSignature(signature: ByteArray, numSamples: Int): String? {
    // Use Ktor, NSURLSession, or another iOS-compatible HTTP client here.
    // Return the raw JSON response body, or null on failure.
    return null
}
songprint is published as a standard JVM JAR via JitPack. For iOS or native KMP targets, RecognitionClient is not available through the artifact. The fingerprinting core (SignatureGenerator and Pcm) can be used on those targets by copying the two source files directly into your shared module’s commonMain source set — they have no imports beyond the Kotlin standard library.

Build docs developers (and LLMs) love