We are currently migrating Bugzilla to GitHub issues.
Any changes made to the bug tracker now will be lost, so please do not post new bugs or make changes to them.
When we're done, all bug URLs will redirect to their equivalent location on the new bug tracker.

Bug 4855 - [PATCH] Add looping support for FLAC
Summary: [PATCH] Add looping support for FLAC
Status: RESOLVED FIXED
Alias: None
Product: SDL_mixer
Classification: Unclassified
Component: misc (show other bugs)
Version: 2.0.4
Hardware: All All
: P2 normal
Assignee: Ryan C. Gordon
QA Contact: Sam Lantinga
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-11-11 14:32 UTC by Michael Day
Modified: 2019-11-17 19:41 UTC (History)
2 users (show)

See Also:


Attachments
Add looping to music_flac.c (10.07 KB, patch)
2019-11-11 14:32 UTC, Michael Day
Details | Diff
Version 2 - Add looping to music_flac.c (10.05 KB, patch)
2019-11-12 03:06 UTC, Michael Day
Details | Diff
Version 3 - Add looping to music_flac.c (10.30 KB, patch)
2019-11-12 14:13 UTC, Michael Day
Details | Diff
Replace str_to_int64 with `SDL_strtoull(x, NULL, 10)` (2.00 KB, patch)
2019-11-17 14:28 UTC, Vitaly Novichkov
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Michael Day 2019-11-11 14:32:46 UTC
Created attachment 4039 [details]
Add looping to music_flac.c

Attached is a patch for music_flac.c which adds looping support for FLAC files. It mirrors the behavior and interface of the OGG looping. When loop tags LOOPSTART, LOOPEND and/or LOOPLENGTH are present in the FLAC Vorbis comment metadata, the module will use those specified values when looping the file. (It also accepts LOOP-* and LOOP_*.) Loop points can be expressed as raw PCM sample positions or as time strings of the form HH:MM:SS.ss.
Comment 1 Ozkan Sezer 2019-11-11 15:09:51 UTC
Is http://hg.libsdl.org/SDL_mixer/rev/b2b18baf65d9 (i.e. bug #4853)
needed for this too?
Comment 2 Michael Day 2019-11-11 17:37:36 UTC
(In reply to Ozkan Sezer from comment #1)
> Is http://hg.libsdl.org/SDL_mixer/rev/b2b18baf65d9 (i.e. bug #4853)
> needed for this too?

This looks like a sensible addition. When I get a chance I'll update the patch to incorporate this. Thanks!
Comment 3 Michael Day 2019-11-12 03:04:47 UTC
Question: Could we accomplish the same thing by telling strtoull that the number is base 10? Right now the code passes 0 for the base argument which tells strtoull to determine the base from the prefix. If there's a leading 0, it will interpret the number as octal which would likely be wrong. If you explicitly specify that it's base 10 however, it should correctly parse the leading zeros.

We simply change the function calls from:

SDL_strtoull(value, NULL, 0)

to:

SDL_strtoull(value, NULL, 10)

I tested it out a bit and it seems to work as expected. Also, strtoull appears to correctly handle whitespace and trailing non-numeric characters. For example, if the number string is " 000100abc" and is specified as decimal it will interpret that as 100.

See my updated patch v2 for code implemented this way. Also I cleaned up some formatting so it matches the SDL style.
Comment 4 Michael Day 2019-11-12 03:06:20 UTC
Created attachment 4040 [details]
Version 2 - Add looping to music_flac.c
Comment 5 Michael Day 2019-11-12 14:13:51 UTC
Created attachment 4043 [details]
Version 3 - Add looping to music_flac.c

I found a bug which could cause the first repeat instance to be glitchy. This is now fixed in Version 3 of the patch.
Comment 6 Sam Lantinga 2019-11-17 06:12:43 UTC
Patch added, thanks!
https://hg.libsdl.org/SDL_mixer/rev/e5b4ff96668b
Comment 7 Ozkan Sezer 2019-11-17 07:31:43 UTC
Should we remove str_to_int64() stuff in music_ogg.c and
replace them with SDL_strtoull(value, NULL, 10) as it is
done here (see comment #3) ?
Comment 8 Vitaly Novichkov 2019-11-17 13:31:52 UTC
> Should we remove str_to_int64() stuff in music_ogg.c and
replace them with SDL_strtoull(value, NULL, 10) as it is
done here (see comment #3) ?

I should verify this, and yeah, if it will work, then, give `(ogg_int64_t)SDL_strtoull(value, NULL, 10)`
Comment 9 Vitaly Novichkov 2019-11-17 13:43:32 UTC
Okay, look the difference:

A sample code:
```
#include <SDL2/SDL.h>
#include <stdio.h>

static const char *ku[] =
{
    "123461",
    "000324234",
    "heck312rg28342",
    "314135135000310135",
    "00000000000000000000013513",
    "000003303333305ff1"
};

static Uint64 str_to_int64(const char *param)
{
    char *copy = SDL_strdup(param);
    char *front = copy;
    char *back;
    Uint64 ret;

    /* Find digit between of 1 and 9 at begin */
    while (*front != '\0' && (*front < '1' || *front > '9')) {
        front++;
    }
    /* Find any non-digit character or NULL */
    back = front;
    while (*back != '\0' && (*back >= '0' && *back <= '9')) {
        back++;
    }
    *back = '\0';
    ret = SDL_strtoull(front, NULL, 0);
    SDL_free(copy);
    return ret;
}

int main()
{
    size_t i = 0;
    for(i = 0; i < sizeof(ku) / sizeof(const char*); i++)
        printf("%s:\n -> %lu\n -> %lu\n", ku[i], SDL_strtoull(ku[i], NULL, 10), str_to_int64(ku[i]));
    return 0;
}
```

and it's output:
```
123461:
 -> 123461
 -> 123461
000324234:
 -> 324234
 -> 324234
heck312rg28342:
 -> 0
 -> 312
314135135000310135:
 -> 314135135000310135
 -> 314135135000310135
00000000000000000000013513:
 -> 13513
 -> 13513
000003303333305ff1:
 -> 3303333305
 -> 3303333305
```

The SDL_strtoull() fails to parse the `heck312rg28342` and it can't extract "312" from off the garbage and returns 0. Shouldn't be critical as the sort of garbage like this shouldn't be valid at all.
Comment 10 Ozkan Sezer 2019-11-17 14:01:19 UTC
(In reply to Vitaly Novichkov from comment #9)
> The SDL_strtoull() fails to parse the `heck312rg28342` and it can't extract
> "312" from off the garbage and returns 0. Shouldn't be critical as the sort
> of garbage like this shouldn't be valid at all.

OK, we leave this as is.  For ogg side: Should we revert 
https://hg.libsdl.org/SDL_mixer/rev/b2b18baf65d9 and use
the 10-base with strtoull method here?
Comment 11 Vitaly Novichkov 2019-11-17 14:28:33 UTC
Created attachment 4055 [details]
Replace str_to_int64 with `SDL_strtoull(x, NULL, 10)`

Yes, it can. I made a test on my side, and it works fine. So, the workaround is no more needed! Thanks for finding a true solution! :)
Comment 12 Ozkan Sezer 2019-11-17 14:51:13 UTC
(In reply to Vitaly Novichkov from comment #11)
> Created attachment 4055 [details]
> Replace str_to_int64 with `SDL_strtoull(x, NULL, 10)`
> 
> Yes, it can. I made a test on my side, and it works fine. So, the workaround
> is no more needed! Thanks for finding a true solution! :)

Applied your patch: https://hg.libsdl.org/SDL_mixer/rev/48f61cd20cdd
Thanks.
Comment 13 Vitaly Novichkov 2019-11-17 16:02:08 UTC
Okay, just now I have found in the code one of C90 violations:
```
for (int i = 0; i < vc->num_comments; ++i) {
```
The `int i` is incompatible with C90. It's the feature of C11.
Comment 14 Vitaly Novichkov 2019-11-17 16:13:09 UTC
Another case:

This code
```
        if (is_loop_length) {
            music->loop_end = music->loop_start + music->loop_len;
        } else {
            music->loop_len = music->loop_end - music->loop_start;
        }
```
should be placed after vorbis comments fetching.
Comment 15 Ozkan Sezer 2019-11-17 16:16:41 UTC
(In reply to Vitaly Novichkov from comment #13)
> Okay, just now I have found in the code one of C90 violations:
> for (int i = 0; i < vc->num_comments; ++i) {

Fixed: https://hg.libsdl.org/SDL_mixer/rev/229b6f0fa03f
Comment 16 Ozkan Sezer 2019-11-17 16:17:49 UTC
(In reply to Vitaly Novichkov from comment #14)
> Another case:
> 
> This code
> ```
>         if (is_loop_length) {
>             music->loop_end = music->loop_start + music->loop_len;
>         } else {
>             music->loop_len = music->loop_end - music->loop_start;
>         }
> ```
> should be placed after vorbis comments fetching.

Michael Day: that one is for you.
Comment 17 Vitaly Novichkov 2019-11-17 16:21:47 UTC
I meant, place this code after loop, not inside the loop
Comment 18 Michael Day 2019-11-17 19:41:49 UTC
(In reply to Vitaly Novichkov from comment #14)
> Another case:
> 
> This code
> ```
>         if (is_loop_length) {
>             music->loop_end = music->loop_start + music->loop_len;
>         } else {
>             music->loop_len = music->loop_end - music->loop_start;
>         }
> ```
> should be placed after vorbis comments fetching.

Firstly thank you for taking the time to review this code. I will watch out for C99-isms in the future. :)

Regarding the if (is_loop_length) block, I currently have this located at the end of vorbis comment case in the flac_music_metadata_cb callback. As far as I can tell I *am* running this code after fetching all of the vorbis comments. I'm confused about where exactly you want me to move it. Can you clarify please? Thanks!