Fixed bug 4849 - [PATCH] Expand OGG looping support
authorSam Lantinga <slouken@libsdl.org>
Fri, 01 Nov 2019 10:38:35 -0700
changeset 960493a943d944a
parent 959 e1919e398c29
child 961 ddfe5eeef0e1
Fixed bug 4849 - [PATCH] Expand OGG looping support

Michael Day

Attached is a patch for music_ogg.c which offers two improvements to the built-in OGG looping:

1. Valid tag/comment key names currently take the form of LOOPxxx. Add support for LOOP_xxx and LOOP-xxx as well.
2. Add support for loop points specified by time strings of the form HH:MM:SS.ss.

The impetus for this change is to help improve music looping for Doom source ports which expect users to use music files with tags based on the tag standard established by ZDoom. (See https://zdoom.org/wiki/Audio_loop)
music_ogg.c
     1.1 --- a/music_ogg.c	Tue Oct 08 01:50:02 2019 +0300
     1.2 +++ b/music_ogg.c	Fri Nov 01 10:38:35 2019 -0700
     1.3 @@ -223,6 +223,43 @@
     1.4      return 0;
     1.5  }
     1.6  
     1.7 +/* Parse time string of the form HH:MM:SS.mmm and return equivalent sample
     1.8 + * position */
     1.9 +static ogg_int64_t parse_time(char *time, long samplerate_hz)
    1.10 +{
    1.11 +    char *num_start, *p;
    1.12 +    ogg_int64_t result = 0;
    1.13 +    char c;
    1.14 +
    1.15 +    // Time is directly expressed as a sample position
    1.16 +    if (SDL_strchr(time, ':') == NULL)
    1.17 +    {
    1.18 +        return SDL_strtoull(time, NULL, 0);
    1.19 +    }
    1.20 +
    1.21 +    result = 0;
    1.22 +    num_start = time;
    1.23 +
    1.24 +    for (p = time; *p != '\0'; ++p)
    1.25 +    {
    1.26 +        if (*p == '.' || *p == ':')
    1.27 +        {
    1.28 +            c = *p; *p = '\0';
    1.29 +            result = result * 60 + SDL_atoi(num_start);
    1.30 +            num_start = p + 1;
    1.31 +            *p = c;
    1.32 +        }
    1.33 +
    1.34 +        if (*p == '.')
    1.35 +        {
    1.36 +            return result * samplerate_hz
    1.37 +            + (ogg_int64_t) (SDL_atof(p) * samplerate_hz);
    1.38 +        }
    1.39 +    }
    1.40 +
    1.41 +    return (result * 60 + SDL_atoi(num_start)) * samplerate_hz;
    1.42 +}
    1.43 +
    1.44  /* Load an OGG stream from an SDL_RWops object */
    1.45  static void *OGG_CreateFromRW(SDL_RWops *src, int freesrc)
    1.46  {
    1.47 @@ -231,6 +268,7 @@
    1.48      vorbis_comment *vc;
    1.49      int isLoopLength = 0, i;
    1.50      ogg_int64_t fullLength;
    1.51 +    long rate;
    1.52  
    1.53      music = (OGG_music *)SDL_calloc(1, sizeof *music);
    1.54      if (!music) {
    1.55 @@ -262,6 +300,7 @@
    1.56      }
    1.57  
    1.58      vc = vorbis.ov_comment(&music->vf, -1);
    1.59 +    rate = music->vi.rate;
    1.60      for (i = 0; i < vc->comments; i++) {
    1.61          char *param = SDL_strdup(vc->user_comments[i]);
    1.62          char *argument = param;
    1.63 @@ -272,14 +311,21 @@
    1.64              *(value++) = '\0';
    1.65          }
    1.66  
    1.67 +        /* Want to match LOOP-START, LOOP_START, etc. Remove - or _ from
    1.68 +         * string if it is present at position 4. */
    1.69 +
    1.70 +        if ((argument[4] == '_') || (argument[4] == '-')) {
    1.71 +            SDL_memmove(argument + 4, argument + 5, SDL_strlen(argument) - 4);
    1.72 +        }
    1.73 +
    1.74          if (SDL_strcasecmp(argument, "LOOPSTART") == 0)
    1.75 -            music->loop_start = SDL_strtoull(value, NULL, 0);
    1.76 +            music->loop_start = parse_time(value, rate);
    1.77          else if (SDL_strcasecmp(argument, "LOOPLENGTH") == 0) {
    1.78              music->loop_len = SDL_strtoull(value, NULL, 0);
    1.79              isLoopLength = 1;
    1.80          } else if (SDL_strcasecmp(argument, "LOOPEND") == 0) {
    1.81              isLoopLength = 0;
    1.82 -            music->loop_end = SDL_strtoull(value, NULL, 0);
    1.83 +            music->loop_end = parse_time(value, rate);
    1.84          }
    1.85          SDL_free(param);
    1.86      }