Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/6xingyv/accompanist-lyrics-core/llms.txt

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

ILyricsExporter is the interface every lyrics exporter implements. It exposes a single export method that takes a parsed SyncedLyrics object and serialises it into a target string format such as LRC, Enhanced LRC, or TTML. Implementing this interface is the standard way to add export support for any custom lyrics format.

Interface definition

interface ILyricsExporter {
    fun export(lyrics: SyncedLyrics): String
}

Methods

export

fun export(lyrics: SyncedLyrics): String
Converts the given SyncedLyrics object into a string representation of the target format.
lyrics
SyncedLyrics
required
The parsed lyrics data to export. Contains a list of ISyncedLine entries (either SyncedLine or KaraokeLine) along with optional metadata such as title and artists.
Returns a String containing the full serialised lyrics in the target format, ready to be written to a file or transmitted over a network.

Built-in exporters

All built-in exporters are object singletons — no instantiation is needed.

LrcExporter

Exports to standard LRC with line-level timestamps. Drops syllable timing data.

EnhancedLrcExporter

Exports to Enhanced LRC with <mm:ss.xx> syllable timestamps and background vocal blocks.

TTMLExporter

Exports to TTML/Apple Music XML, preserving syllable timing, phonetics, and span attributes.

Implementing a custom exporter

Implement ILyricsExporter as an object when your exporter holds no state, or as a class when it needs configuration.
object MyExporter : ILyricsExporter {
    override fun export(lyrics: SyncedLyrics): String {
        val sb = StringBuilder()
        for (line in lyrics.lines) {
            // build your format
        }
        return sb.toString()
    }
}

Handling line types

A SyncedLyrics.lines list contains entries of type ISyncedLine. The two concrete types are SyncedLine (plain timed text) and KaraokeLine (syllable-timed). Use a when block to handle both:
object MyExporter : ILyricsExporter {
    override fun export(lyrics: SyncedLyrics): String {
        val sb = StringBuilder()

        for (line in lyrics.lines) {
            when (line) {
                is KaraokeLine.MainKaraokeLine -> {
                    // Syllable-level timing available
                    sb.append(formatTimestamp(line.start))
                    for (syllable in line.syllables) {
                        sb.append(formatTimestamp(syllable.start))
                        sb.append(syllable.content)
                    }
                    sb.append("\n")
                }
                is SyncedLine -> {
                    // Line-level timing only
                    sb.append(formatTimestamp(line.start))
                    sb.append(line.content)
                    sb.append("\n")
                }
                else -> { /* handle other subtypes if needed */ }
            }
        }

        return sb.toString()
    }

    private fun formatTimestamp(ms: Int): String {
        val minutes = ms / 60000
        val seconds = (ms % 60000) / 1000
        val centiseconds = (ms % 1000) / 10
        return "[%02d:%02d.%02d]".format(minutes, seconds, centiseconds)
    }
}
line.start and line.end are both expressed in milliseconds as Int values. Convert to your format’s time unit inside the exporter — for example, divide by 10 for centiseconds (LRC) or format directly as HH:MM:SS.mmm for TTML.

Including metadata

SyncedLyrics carries optional metadata you can embed in format headers:
object MyExporter : ILyricsExporter {
    override fun export(lyrics: SyncedLyrics): String {
        val sb = StringBuilder()

        // Write header block
        if (lyrics.title.isNotEmpty()) sb.appendLine("[ti:${lyrics.title}]")
        lyrics.artists?.forEach { artist -> sb.appendLine("[ar:${artist.name}]") }

        // Write lyric lines …

        return sb.toString()
    }
}
Not all formats support both SyncedLine and KaraokeLine. If your target format only supports line-level timing, fall back to line.start for the timestamp and concatenate syllable.content values for the text — syllable boundaries will be lost but the output will remain valid.

Using an exporter

val lyrics: SyncedLyrics = AutoParser().parse(rawContent)

// Built-in exporter — object singleton, call directly
val lrcOutput: String = LrcExporter.export(lyrics)

// Custom exporter
val myOutput: String = MyExporter.export(lyrics)

See also

Build docs developers (and LLMs) love