Skip to content

feat: support word-by-word lyric parsing and highlighting - #736

Open
kt286 wants to merge 1 commit into
linuxdeepin:masterfrom
kt286:feat/word-lyrics
Open

feat: support word-by-word lyric parsing and highlighting#736
kt286 wants to merge 1 commit into
linuxdeepin:masterfrom
kt286:feat/word-lyrics

Conversation

@kt286

@kt286 kt286 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor
  • Add LyricWord struct for per-word timing data
  • Parser supports LRC word-by-word format (e.g. [00:27.94]持[00:28.26]有)
  • Fix centisecond parsing to correctly convert 1/100s to milliseconds
  • LyricPage/LyricRect add word-by-word rendering with per-word highlight
  • Presenter passes word timing data to QML layer
  • Add related unit tests

feat: 支持逐字歌词解析与高亮显示

  • 新增 LyricWord 结构体,用于存储逐字歌词的时间轴数据
  • 解析器支持 LRC 逐字格式(如 [00:27.94]持[00:28.26]有)
  • 修复厘秒解析,正确将百分之一秒转换为毫秒
  • LyricPage/LyricRect 添加逐字歌词渲染逻辑,支持逐字高亮
  • Presenter 传递逐字歌词数据到 QML 层
  • 添加相关单元测试

Summary by Sourcery

Add support for word-by-word timed lyrics, including parsing, data exposure, and UI highlighting.

New Features:

  • Support parsing LRC lyrics with multiple per-word timestamps into structured LyricWord data.
  • Expose per-line word timing information from LyricAnalysis through Presenter to the QML layer.
  • Render lyrics in QML with optional word-by-word highlighting based on the current playback position.

Bug Fixes:

  • Correct parsing of centisecond fractional timestamps so 1/100-second values are converted accurately to milliseconds.

Enhancements:

  • Maintain alignment between line-level lyrics and word-level timing when sorting by timestamp in LyricAnalysis.
  • Refine lyric text styling and opacity handling to work consistently for both line-based and word-by-word lyrics.

Tests:

  • Add unit tests covering multi-timestamp (word-by-word) lyric parsing, mixed formats, ordering by first timestamp, and boundary conditions for word timing access.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @kt286, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@sourcery-ai

sourcery-ai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds end-to-end support for word-by-word LRC lyrics: parsing multi-timestamp lines into per-word timing structures, exposing them via Presenter, and rendering them in QML with per-word highlighting based on playback position, along with unit tests and a centisecond parsing fix.

Sequence diagram for word-by-word lyric parsing and QML highlighting

sequenceDiagram
    participant Presenter
    participant LyricAnalysis
    participant LyricPage
    participant LyricRect

    Presenter->>LyricAnalysis: setFromFile(lrcPath)
    LyricAnalysis->>LyricAnalysis: parseLyric(str)
    LyricAnalysis-->>Presenter: allLyrics()
    Presenter->>LyricAnalysis: hasWordTiming(i)
    Presenter->>LyricAnalysis: getWordTiming(i)
    LyricAnalysis-->>Presenter: QVector<LyricWord>
    Presenter-->>LyricPage: getLyrics()

    LyricPage->>LyricPage: metaChange()
    LyricPage->>LyricRect: currentPosition, wordLyricsData

    LyricPage->>LyricPage: positionChange(position, length)
    LyricPage->>LyricRect: currentPosition

    LyricRect->>LyricRect: wordSung = modelData.time <= currentPosition
    LyricRect->>LyricRect: per-word highlight via Flow/Repeater
Loading

File-Level Changes

Change Details Files
Introduce a LyricWord data model and internal storage for per-word timing in LyricAnalysis, including parsing of multi-timestamp LRC lines and correct centisecond handling.
  • Add LyricWord struct to represent word-level time/text segments.
  • Replace direct QTime parsing with a parseTimeStamp helper that correctly handles mm:ss.xx and mm:ss.xxx formats.
  • Extend parseLyric to detect multi-timestamp lines, split them into LyricWord sequences, build full line text, and maintain a parallel m_wordLyrics vector.
  • Ensure lyrics and word timings remain aligned by sorting via index indirection.
  • Expose hasWordTiming and getWordTiming APIs for retrieving word-level timing per line.
src/libdmusic/core/lyricanalysis.h
src/libdmusic/core/lyricanalysis.cpp
Expose word-level lyric timing through Presenter so the QML layer can consume per-line word timestamps and text.
  • Update Presenter::getLyrics to iterate by index and include hasWordTiming flags.
  • When a line has word timing, convert QVector into a QVariantList of {time,text} maps and attach to the lyric item as "words".
  • Preserve existing time/lyric fields while augmenting the payload with word timing metadata.
src/libdmusic/presenter.cpp
Add QML-side data flow and rendering for per-word lyrics, including word-level highlight state driven by playback position.
  • Add currentPosition and wordLyricsData properties to LyricPage and LyricRect to carry playback position and per-line word arrays.
  • In metaChange, split Presenter.getLyrics result into a ListModel with primitive fields and a separate JS array for wordLyricsData due to ListModel nested-data limitations.
  • Bind LyricRect.currentPosition and wordLyricsData from LyricPage so each lyric line can access its word segments and playback position.
  • Track currentPosition in positionChange to update word highlight as playback advances.
src/music-player/lyric/LyricPage.qml
src/music-player/lyric/LyricRect.qml
Implement dual rendering paths in LyricRect for whole-line and word-by-word lyrics, with distance-based opacity and animated per-word highlighting.
  • Add per-item properties (lineIndex, hasWordTiming, lineWords, distanceOpacity) to compute whether a line uses word timing and its base opacity based on distance from current index.
  • Refactor the existing Text color logic to reuse distanceOpacity for both light and dark themes and handle invalid parent cases safely.
  • Add a Flow+Repeater block for word-by-word lyrics, rendering each LyricWord.text as a Text element, with wordSung state based on currentPosition >= word time.
  • Apply palette.highlight to sung words in the current line, a faded color for unsung words, and distanceOpacity-based colors for non-current lines.
  • Add a short ColorAnimation Behavior on color to smooth highlight transitions.
  • Keep the original single Text path for lines without word timing and hide it when word timing is present.
src/music-player/lyric/LyricRect.qml
Add unit tests to validate multi-timestamp lyric parsing, mixed formats, sorting behavior, centisecond conversion, and word-timing boundary cases.
  • Add tests that parse a sample multi-timestamp LRC line, verifying combined line text, hasWordTiming, and word count.
  • Add tests asserting individual LyricWord.time and text values match expected centisecond-to-millisecond conversions and parsed segments.
  • Add tests covering simple lyrics with no word timing, mixed ordinary and word-by-word lines, and multiple word-by-word lines sorted by first timestamp.
  • Add boundary tests ensuring invalid indices for word timing return empty arrays and false from hasWordTiming.
  • Update SPDX copyright headers to include 2026 in touched files.
tests/libdmusic-test/test_lyricanalysis.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@kt286
kt286 force-pushed the feat/word-lyrics branch from 7fb4187 to c64fff6 Compare August 2, 2026 13:20
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: kt286

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kt286
kt286 force-pushed the feat/word-lyrics branch from c64fff6 to b1fc343 Compare August 2, 2026 13:26
- Add LyricWord struct for per-word timing data
- Parser supports LRC word-by-word format (e.g. [00:27.94]持[00:28.26]有)
- Fix centisecond parsing to correctly convert 1/100s to milliseconds
- LyricPage/LyricRect add word-by-word rendering with per-word highlight
- Presenter passes word timing data to QML layer
- Add related unit tests

feat: 支持逐字歌词解析与高亮显示

- 新增 LyricWord 结构体,用于存储逐字歌词的时间轴数据
- 解析器支持 LRC 逐字格式(如 [00:27.94]持[00:28.26]有)
- 修复厘秒解析,正确将百分之一秒转换为毫秒
- LyricPage/LyricRect 添加逐字歌词渲染逻辑,支持逐字高亮
- Presenter 传递逐字歌词数据到 QML 层
- 添加相关单元测试
@kt286
kt286 force-pushed the feat/word-lyrics branch from b1fc343 to 3c8bc3f Compare August 4, 2026 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants