Support line breaks and empty lines in .lrc files

Feature description:

When parsing .lrc files, retain a line without timestamp following one with a timestamp.
Display empty lines within the lyrics (after the first timestamp has occurred).
Not displaying tags at the start of the file [la:ger], etc. and lines without timestamp before the first timestamp would be a sensible solution imho.

Problem solved:

Line breaks are sometimes used in .lrc files to display 2 different lines at the same time.
Either when there’s a main singer and background vocals or when the original language + a translation are to be displayed at the same time, below one another.
Empty lines retain the verse structure of the lyrics (but it’s a matter of taste if they are to be displayed or not, so this could be optional).

Brought benefits:

It would mean that Symfonium can properly display lyrics with 2 lines of text per timestamp and retains the verse structure of the lyrics (when present in the file).

Other application solutions:

 
MusicBee handles .lrc files in the manner I described.
Tags and lines without timestamp at the start of the .lrc file are ignored, but after the first timestamp, line breaks and lines without timestamp are displayed at the same time as the previous timestamp (see screenshots).
 

Additional description and context:

 
I’ve added 4 screenshots that show how MusicBee and Symfonium handle 2 example songs + .lrc files. The first one with 2 interweaving vocals and the second one with original language + translation. I’ll provide the content of the .lrc files in a comment with formatting.

I admit that lyrics like these are fairly rare so I’d understand if you don’t want to do additional parsing to support them.
 

Screenshots / Mockup:

 

   

First .lrc file:

[0:10.308]Are you going to Scarborough Fair?
[0:17.568]Parsley, sage, rosemary and thyme;
[0:26.508]Remember me to one who lives there -
[0:34.008]She once was a true love of mine.

[0:43.848]Tell her to make me a cambric shirt:
(On the side of a hill in the deep forest green,)
[0:51.288]Parsley, sage, rosemary and thyme;
(Tracing a sparrow on snow-crested ground,)
[1:00.048]Without no seams nor needlework,
(Blankets and bedclothes, a child of the mountains)
[1:07.788]Then she'll be a true love of mine.
(Sleeps unaware of the clarion call)

[1:17.388]Tell her to find me an acre of land:
(On the side of a hill, a sprinkling of leaves.)
[1:24.708]Parsley, sage, rosemary and thyme;
(Washed is the ground with so many tears,)
[1:33.528]Between the salt water and the sea strand,
(A soldier cleans and polishes a gun.)
[1:40.728]Then she'll be a true love of mine.

[1:50.662]Tell her to reap it in a sickle of leather:
(War bellows blazing in scarlet battalions,)
[1:58.222]Parsley, sage, rosemary and thyme;
(Generals order their soldiers to kill)
[2:07.042]And gather it all in a bunch of heather,
(And to fight for a cause they've long ago forgotten.)
[2:14.842]Then she'll be a true love of mine.

[2:24.622]Are you going to Scarborough Fair?
[2:32.062]Parsley, sage, rosemary and thyme;
[2:41.242]Remember me to one who lives there -
[2:48.982]She once was a true love of mine.

Second .lrc file:

[0:00.000]Haritsumeta yumi no
The stretching bow
[0:06.696]Furueru tsuru yo
Of its trembling string 
[0:11.988]Tsuki no hikari ni zawameku
Disturbed in the moonlight
[0:18.276]Omae no kokoro
Is your heart

[0:24.216]Togisumasareta
The well-sharpened blade
[0:30.044]Yaiba no utsukushii
Has a beautiful tip
[0:35.044]Sono kissaki ni yoku nita
That resembles to it 
[0:41.504]Sonata no yokogao
Is your profile

[0:47.444]Kanashimi to ikari ni
In the grief and anger
[0:52.930]Hisomu makoto no kokoro wo
Your true heart lurks within
[0:59.024]Shiru wa mori no sei-i
You’re understood by the Spirits of the Forest

[1:05.384]Mononoke-tachi dake
Only the Mononoke
[1:11.144]Mononoke-tachi dake
Only the Mononoke

@itm have you decided what you’ll do ? :slight_smile:

Yes, lms will support line breaks

[0:00.000] [0:01.000] [0:02.000]Haritsumeta yumi no
The stretching bow

So this should also have the multilines on all the time stamps?

Interesting, I have never encountered that case in the wild. However I’d limit it to repeating the following line and that only if it’s not an empty line. Otherwise the resulting formatting could turn ugly quickly.

Not sure to understand what you say.

The multi timestamp is frequent to avoid repeating the same text over and over and the timestamps can be at different positions in the lyrics with other text in the middle

For example

[1][5][10] Hello
[2] Tata
[3] Titi
[8] Plip
[12] Plop

Is a reduced version of

[1] Hello
[2] Tata
[3] Titi
[5] Hello
[8] Plip
[10] Hello
[12] Plop

so I guess if we go multi line the example from before should repeat all the lines at all the timestamps and it’s more complicated to code :wink:

I meant only repeating these 2 lines

[0:00.000] [0:01.000] [0:02.000]Haritsumeta yumi no
The stretching bow

and not the empty line below them as well.

Also I meant not to repeat the empty line in a case like this:

[0:00.000] [0:01.000] [0:02.000]Haritsumeta yumi no

I just tried it and MusicBee displays:

[0:00.000] [0:03.000] [0:05.000]Haritsumeta yumi no
The stretching bow
[0:06.696]Furueru tsuru yo
Of its trembling string
[0:11.988]Tsuki no hikari ni zawameku

like this:


So it does not repeat the 2nd part of the multi-line (only the line with multiple timestamps) and instead only displays it at the same time as the first occurence.
Probably to avoid this:

After thinking about it while watching TV I’m not sure it’s worth the effort to repeat both parts of multi-lines. Multi-lines are already rare and if you deliberately create them I’d guess you’re smart enough not to use repeated lines as part of a multi-line which compounds the required parsing effort.

I know, though I’m not really a fan of them as I don’t know a single piece of software that allows to easily (re-)sync those repeated lines (except for applying a global offset to all timestamps). Couple bytes saved and a lot of headache won imho. Also they mess with the lyrics structure when you view them in a text editor. For example if the first line of each verse is the same but the rest differs, you get one repeated line and then a bunch of incomplete verses. It’s more efficient but it bothers me visually.

I’d imagine something like this:

  1. Check if the line starts with multiple timestamps.
  2. Check if the following line does not contain a timestamp and is also not only a newline.
  3. Group the repeated line and the following line together if it’s text.
  4. Insert both of them at all timestamps contained in the repeated line.

Keeping the correct order becomes tricky by supporting multi-lines and empty lines tho as you have to treat all lines between 2 lines that start with timestamps (including the first timestamp line and excluding the second one) as one group to insert other lines before/after to avoid possibly breaking other multi-lines.

I’m probably overlooking a couple other cases that complicate it further tho so I don’t think it’s worth the effort. If multi-lines are displayed as well as in MusicBee that would be good enough in my opinion, considering how rare they are.

Maybe ask ChatGPT to sum this up, turned out as a bit of a wall of text.

Edit: After thinking about it some more I realized that empty lines without timestamps lead to problems with repeated lines as you cannot know if the empty line is supposed to be before (end of verse) or after (start of verse) an inserted line:

[0:00.000][0:20.333]Haritsumeta yumi no
[0:18.276]Omae no kokoro
Is your heart

[0:24.216]Togisumasareta
The well-sharpened blade

Could either lead to:

[0:00.000]Haritsumeta yumi no
[0:18.276]Omae no kokoro
Is your heart
[0:20.333]Haritsumeta yumi no

[0:24.216]Togisumasareta
The well-sharpened blade

Or to

[0:00.000]Haritsumeta yumi no
[0:18.276]Omae no kokoro
Is your heart

[0:20.333]Haritsumeta yumi no
[0:24.216]Togisumasareta
The well-sharpened blade

In a perfect world a dual approach might work, where empty lines are kept if the lyrics do not contain repeated lines (no insertions needed) and removed if they do, which would avoid this problem. I’ll probably add some more thoughts when I wake up tomorrow, so brace yourselves. :upside_down_face:

For lms, I made these choices regarding mixing lines with timestamps with lines without timestamp:

  • keep multi lines for each timestamp entry (even for repeated timestamps, that’s not that complicated to code)
  • strip the ending lines that are empty
  • strip all the first lines that have no timestamp
1 Like

You still need to parse the [offset:] to get the offset :slight_smile:

For empty lines people often want them (including in non synced) but after a few discussions I only leave one so if there’s 3 empty lines only 1 is kept.

Same for multi lines.

For @655321 a line without a timestamp can only be related to the previous timesamp and not a future one.

Yes, “tags” are not part of that and always taken into account (even in the middle of the file)
I do not skip any empty line for unsynchronized lyrics, I was just talking about multi lines for a single timestamp (or a set of timestamps)

Well he just asked to keep them :wink:

I decided to strip empty trailing lines because some lyrics files end with multiple empty lines.

I do speak about that:

[00:01.00]first line

Second line

[00:02.00]last line


Result will be:

1000: "first line\n\nSecond line"
2000: "last line"

If you see the initial screenshots those are kept and @655321 seems to want them.

This is IMO related to handling the lines with or without a timestamp, if you do support them then why remove some and keep others.

Specially why 1000: "first line\n\nSecond line" ? You keep one but not the other that’s no consistent no?

And same if you keep them for unsynced lyrics. I always think consistency > everything from user and support POV.

That’s sensible. (Honestly I’d even remove consecutive spaces and leading + trailing whitespace in each line).

That’s how MusicBee handles it. Of course properly handling repeated multi-lines would be better but you said that’s complicated to code.

Within the lyrics (to separate verses), not trailing or leading empty lines.

In the example I would have expected

1000: "first line\n\nSecond line\n"
2000: "last line"

To keep empty lines separating lyrics lines but it’s fine to strip trailing empty lines at the end of the lyrics and reduce repeated empty lines within them to one.
But that only works well when there are no repeated lines mixed in.

What I meant is that this:

[la:ger]
[offset:+100]
some random text

[0:18.276]Omae no kokoro
Is your heart


[0:24.216]Togisumasareta

The well-sharpened blade


Should end up like this:

18376: "Omae no kokoro\nIs your heart\n"
24316: "Togisumasareta\n\nThe well-sharpened blade"

Apply the offset, do not display tags, empty lines and text without timestamps before the first line with a timestamp at the start of the .lrc file, reduce consecutive empty lines to one empty line, remove trailing empty lines but keep (single) empty lines within the lyrics.

That would be sensible and consistent imho.

That’s what I’ll do except 24316: "Togisumasareta\n\nThe well-sharpened blade" will be 24316: "Togisumasareta\n\nThe well-sharpened blade\n"

This is not that complicated, it just require a larger refactor

1 Like

Unfortunately, it seems quite common to have empty lines at the end of lyrics files :frowning:
It really seems to be something that is not deliberate

If you trim to 1 empty line this does not matter, specially if it’s synced and multiline.

From a display perspective 1 \n on the very last line of the lyrics does not change anything at all, it’s just a little more padding.

Many people aren’t even aware of the fact that you can have newlines at the end of a text file as most text editors only indicate them by increasing the line number on the left (if the editor even has that, looking at Notepad).

The problem with repeated multi-lines I had in mind yesterday was this:

[0:01.00]repeat me
repeat me too
[0:02.00]some line1
[0:03.00]some line2
[0:04.00]repeat me
repeat me too

[0:05.00]repeat me
repeat me too
[0:06.00]some line4

Would yield:

100: "repeat me\nrepeat me too"
200: "some line1"
300: "some line2"
400: "repeat me\nrepeat me too\n"
500: "repeat me\nrepeat me too"
600: "some line4"

However if it was saved like this instead:

[0:01.00][0:04.00][0:05.00]repeat me
repeat me too
[0:02.00]some line1
[0:03.00]some line2

[0:06.00]some line4

It would yield:

100: "repeat me\nrepeat me too"
200: "some line1"
300: "some line2\n"
400: "repeat me\nrepeat me too"
500: "repeat me\nrepeat me too"
600: "some line4"

But that can be solved by saving it like this (which admittedly saves fewer characters):

[0:01.00][0:05.00]repeat me
repeat me too
[0:02.00]some line1
[0:03.00]some line2
[0:04.00]repeat me
repeat me too

[0:06.00]some line4

Which would once again correctly yield:

100: "repeat me\nrepeat me too"
200: "some line1"
300: "some line2"
400: "repeat me\nrepeat me too\n"
500: "repeat me\nrepeat me too"
600: "some line4"

So I don’t see a problem with (properly created) repeated multi-lines any more, or rather if there are problems, it’s the fault of whoever created them poorly. :grinning:

1 Like