ModernLrc 1.2.0

dotnet add package ModernLrc --version 1.2.0
                    
NuGet\Install-Package ModernLrc -Version 1.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ModernLrc" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ModernLrc" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="ModernLrc" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ModernLrc --version 1.2.0
                    
#r "nuget: ModernLrc, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ModernLrc@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ModernLrc&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=ModernLrc&version=1.2.0
                    
Install as a Cake Tool

ModernLrc

Modern, performance-first LRC (lyrics) parser and writer for .NET 10. AOT-compatible, trim-safe, zero non-BCL runtime dependencies.

πŸ“– Full documentation, API reference, and topical guides: https://anthonyy232.github.io/ModernLrcParser (also browseable in the docs/ folder).

Features

  • Full LRC support β€” simple [mm:ss.xx]text, multi-timestamp [t1][t2]…, Enhanced LRC word timing <…>, Walaoke voice markers (M: / F: / D:) with state propagation.
  • Tolerant by default with rich diagnostics; opt-in LrcStrictness.Strict for fail-fast behaviour.
  • Span-first hand-rolled scanner β€” no regex, no backtracking, zero per-token transient allocations.
  • Every input shape β€” string, ReadOnlySpan<char>, ReadOnlyMemory<char>, TextReader, ReadOnlySpan<byte>, byte[], Stream, file paths; sync and async.
  • Zero-allocation writer via IBufferWriter<char> and UTF-8 byte sinks; the Write β†’ string, TextWriter, TryWrite, and Stream sinks pre-size their staging buffer to the document's estimated size so large renders don't accumulate copy-on-grow waste; atomic file write via temp + rename.
  • Encoding pipeline β€” BOM detection (UTF-8, UTF-16 LE/BE) β†’ caller override β†’ UTF-8 validation β†’ caller-supplied fallback.
  • Diagnostics catalogue β€” every recoverable concern surfaces as a stable diagnostic code (LRC0001–LRC0099) with line/column location.

Install

dotnet add package ModernLrc

Requirements

  • .NET 10 runtime (consumers).
  • .NET 10 SDK (pinned via global.json) only required to build from source.

Quick start

using ModernLrc;
using ModernLrc.Model;

// Parse
var result = LrcParser.Parse("[ti:Demo]\n[00:01.00]hello\n");
Console.WriteLine(result.Document.Metadata.Title); // Demo

// Author + write
var doc = new LrcDocumentBuilder()
    .WithTitle("My Song")
    .AddLine("00:12.00", "first line")
    .AddLine("00:14.20", "second line", LrcVoice.Female)
    .Build();
string lrc = LrcWriter.Write(doc);

Design notes

Offset model

Documents store the [offset:N] tag verbatim in Metadata.Offset. Line timestamps are NOT mutated at parse time. Apply the offset on demand:

var doc = LrcParser.Parse(text).Document;
TimeSpan effective = doc.GetEffectiveTime(line.Timestamp);
LrcLine? current = doc.FindLineAt(currentPlayhead);  // already factors offset in

This preserves round-trip fidelity β€” re-writing the document emits the original [offset:N] tag rather than collapsing it into mutated timestamps.

Round-trip fidelity applies to supported LRC syntax. If authored plain lyric text contains valid-looking LRC markers such as <00:01.00>, a later parse will treat those markers as syntax.

Encoding model

Byte/stream input uses this priority:

  1. BOM detection (UTF-8 EF BB BF, UTF-16 LE FF FE, UTF-16 BE FE FF).
  2. Caller-supplied LrcParseOptions.Encoding if set.
  3. UTF-8 validation (rejects malformed sequences).
  4. LrcParseOptions.FallbackEncoding (default Encoding.UTF8); emits LRC0010 at Error severity if it kicks in. Set FallbackEncoding = null to fail loudly instead.

For non-Unicode encodings without a BOM (Shift-JIS, GBK, Big5), provide the encoding explicitly β€” statistical detection is intentionally out of scope.

Whitespace and empty lines

Lyric text is preserved verbatim, including whitespace-only lines and trailing whitespace. Apply display filters at the UI layer:

foreach (var line in doc.Lines)
{
    if (line is LrcPlainLine p && string.IsNullOrWhiteSpace(p.Text)) continue;
    Render(line);
}

Tolerantly accepted variants

Beyond the canonical [mm:ss.xx], the parser accepts these in tolerant mode (the default), each producing an informational diagnostic:

Variant Diagnostic Example
Three-digit fraction mm:ss.fff LRC0030 [00:01.500]
Colon fraction separator mm:ss:ff LRC0030 [00:01:50]
No fraction mm:ss LRC0030 [00:01]
Hours notation h:mm:ss.ff LRC0030 [1:02:33.45]
Comma decimal mm:ss,ff LRC0030 [01:23,45]
ID3 language prefix xxx\|\|... LRC0092 eng\|\|[ti:...]

Set LrcParseOptions.Strictness = LrcStrictness.Strict to throw LrcParseException on the first error-severity diagnostic. Tolerant mode collects everything for inspection on LrcParseResult.Diagnostics.

Comparison

ModernLrc compared to other .NET LRC parsers as of 2026-04-28.

Performance

Throughput on synthetic basic-LRC inputs (Small β‰ˆ 20 lines, Medium β‰ˆ 200 lines, Large β‰ˆ 2,000 lines). Lower is better. Ratio is relative to ModernLrc.

Parse
Library Small (Β΅s) Medium (Β΅s) Large (Β΅s) Large alloc
ModernLrc 1.1.0 1.89 (1.00Γ—) 15.3 (1.00Γ—) 154 (1.00Γ—) 324 KB
Opportunity.LrcParser 1.0.4 1.93 (1.02Γ—) 12.4 (0.81Γ—) 121 (0.78Γ—) 317 KB
Kfstorm.LrcParser 1.0.3 12.5 (6.63Γ—) 106 (6.92Γ—) 1,066 (6.91Γ—) 2,156 KB
LrcParser 2025.623.0 (karaoke-dev) 22.4 (11.9Γ—) 231 (15.1Γ—) 2,381 (15.4Γ—) 5,434 KB
SharpLyrics 1.0.0.2 (archived) 192 (101Γ—) 277 (18.1Γ—) 1,152 (7.47Γ—) 2,252 KB
Write
Library Small (Β΅s) Medium (Β΅s) Large (Β΅s) Large alloc
ModernLrc 1.1.0 2.76 (1.00Γ—) 23.1 (1.00Γ—) 367 (1.00Γ—) 399 KB
Opportunity.LrcParser 1.0.4 3.22 (1.17Γ—) 29.2 (1.26Γ—) 423 (1.15Γ—) 648 KB
LrcParser 2025.623.0 (karaoke-dev) 5.27 (1.91Γ—) 49.6 (2.14Γ—) 635 (1.73Γ—) 2,013 KB
Kfstorm.LrcParser 1.0.3 β€” β€” β€” (no write API)
SharpLyrics 1.0.0.2 (archived) β€” β€” β€” (no write API)

ModernLrc edges Opportunity.LrcParser on small inputs and trails by 19–27% on larger inputs; the gap is the cost of its broader feature surface (recovery, structured diagnostics, voice tracking, encoding detection, word-level timing, stable sort with reorder detection). Allocation is within 2% across all sizes. ModernLrc leads on write across every input size β€” 13–26% faster than Opportunity.LrcParser and ~1.7–2.1Γ— faster than karaoke-dev/LrcParser β€” while still emitting consecutive same-text lines as a collapsed [t1][t2]text group by default for round-trip fidelity.

Features

Capability ModernLrc Opportunity karaoke-dev Kfstorm SharpLyrics
Basic LRC [mm:ss.xx]text βœ“ βœ“ βœ“ βœ“ βœ“
Enhanced LRC (word-level <…>) βœ“ βœ— βœ“ βœ— partialΒΉ
Walaoke voice markers (M:/F:/D:) βœ“ partialΒ² βœ— βœ— βœ—
ID3 metadata tags (ti, ar, al, …) βœ“ βœ“ βœ“ βœ“ βœ—
[offset:N] semantics βœ“ βœ“ βœ— βœ“ βœ—
ID3-style language prefix (eng\|\|…) βœ“ βœ— βœ— βœ— βœ—
Error recovery (continue past bad input) βœ“ partialΒ³ βœ— βœ— βœ—
Structured diagnostics with line/column βœ“ βœ— βœ— βœ— βœ—
Strict / lenient parse modes βœ“ βœ— βœ— βœ— βœ—
Round-trip parse β†’ write β†’ parse βœ“ βœ“ βœ“ βœ— βœ—
Sync write API βœ“ βœ“ βœ“ βœ— βœ—
Async parse / write βœ“ βœ— βœ— βœ— βœ—
Byte / Stream input + encoding detect βœ“ βœ— βœ— βœ— βœ—
IBufferWriter<byte/char> output βœ“ βœ— βœ— βœ— βœ—
TryParse / TryWrite non-throwing βœ“ βœ— βœ— βœ— βœ—
Span-first parser (no regex) βœ“ βœ“ βœ— βœ— βœ—
Native AOT compatible βœ“ βœ— βœ— βœ— βœ—
Trim-safe βœ“ βœ— βœ— βœ— βœ—
Nullable reference types βœ“ βœ— βœ“ βœ— βœ“
Target framework net10.0 netstandard1.0 netstandard2.1 portable PCL netstandard2.0
Last release 2026-04 2018-05 2025-06 2015-06 2023-11 (archived)

ΒΉ SharpLyrics has internal word-time extraction, but the public API surfaces only single-timestamp lines from a file path β€” no string input. Β² Opportunity.LrcParser exposes LineWithSpeaker for Speaker: text patterns, not the Walaoke M:/F:/D: voice protocol with state propagation. Β³ Opportunity.LrcParser collects parse exceptions in a list, but they are unstructured (no diagnostic codes or severity).

Methodology

  • Workload: synthetic basic-LRC inputs ([mm:ss.xx]text + ID3 metadata header), Small β‰ˆ 20 lines, Medium β‰ˆ 200 lines, Large β‰ˆ 2,000 lines. Inputs are stored as string constants β€” no file I/O on the timed path. SharpLyrics requires a file path, so its corpus is staged to a temp file in [GlobalSetup] (one-time cost outside the timed region).
  • Each parser called via the smallest possible idiomatic public entry point β€” no tuning.
  • Write benchmarks start from each library's own parsed model (parsed once in [GlobalSetup]) so the timed region measures serialisation only, not cross-model conversion.
  • Environment: BenchmarkDotNet 0.15.8, .NET 10.0.7 (SDK 10.0.202), Windows 11, AMD Ryzen 5 5600 (6 cores, 12 threads).
  • Numbers will drift across machines and SDK versions β€” treat ratios as the reliable signal, absolute numbers as a snapshot of one machine on one day.

Tools

Development hooks

pre-commit install
pre-commit run --all-files

The hook set runs common file hygiene checks and dotnet format.

Sample CLI

dotnet run --project samples/ModernLrc.Samples.Console -- parse path/to/song.lrc
dotnet run --project samples/ModernLrc.Samples.Console -- shift path/to/song.lrc 500

Benchmarks

dotnet run -c Release --project bench/ModernLrc.Benchmarks -- --filter '*'

Uses BenchmarkDotNet. Filter to a benchmark group with e.g. --filter ParseBenchmarks*.

License

MIT β€” see LICENSE.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on ModernLrc:

Repository Stars
Anthonyy232/Nagi
Rediscover your local music collection with Nagi, a music player focused on speed, simplicity, and privacy. Nagi is built with C# and WinUI 3 to offer a clean, native Fluent experience. It's beautiful, efficient, and respects your privacy.
Version Downloads Last Updated
1.2.0 111 5/3/2026
1.1.1 93 5/3/2026
1.1.0 104 4/28/2026
1.0.0 111 4/28/2026