Introduce Mix_MusicDuration() to return music duration in seconds.
authorMatthias Gatto
Tue, 17 Dec 2019 21:55:00 +0300
changeset 1090543b77a3c0eb
parent 1089 6c766242c751
child 1091 8a09f3c0c340
Introduce Mix_MusicDuration() to return music duration in seconds.

Supports wav, vorbis, opus, mp3 (mpg123), flac, mod (modplug), timidity.
include/SDL_mixer.h
playmus.c
src/codecs/music_cmd.c
src/codecs/music_flac.c
src/codecs/music_fluidsynth.c
src/codecs/music_mad.c
src/codecs/music_mikmod.c
src/codecs/music_modplug.c
src/codecs/music_mpg123.c
src/codecs/music_nativemidi.c
src/codecs/music_ogg.c
src/codecs/music_opus.c
src/codecs/music_timidity.c
src/codecs/music_wav.c
src/music.c
src/music.h
     1.1 --- a/include/SDL_mixer.h	Tue Dec 17 21:54:04 2019 +0300
     1.2 +++ b/include/SDL_mixer.h	Tue Dec 17 21:55:00 2019 +0300
     1.3 @@ -618,6 +618,12 @@
     1.4  /* Stop music and set external music playback command */
     1.5  extern DECLSPEC int SDLCALL Mix_SetMusicCMD(const char *command);
     1.6  
     1.7 +/* Return music duration in seconds.
     1.8 +   If NULL is passed, returns duration of current playing music.
     1.9 +   Returns -1 on error.
    1.10 + */
    1.11 +extern DECLSPEC double SDLCALL Mix_MusicDuration(Mix_Music *music);
    1.12 +
    1.13  /* Synchro value is set by MikMod from modules while playing */
    1.14  extern DECLSPEC int SDLCALL Mix_SetSynchroValue(int value);
    1.15  extern DECLSPEC int SDLCALL Mix_GetSynchroValue(void);
     2.1 --- a/playmus.c	Tue Dec 17 21:54:04 2019 +0300
     2.2 +++ b/playmus.c	Tue Dec 17 21:55:00 2019 +0300
     2.3 @@ -251,7 +251,7 @@
     2.4          SDL_Log("Detected music type: %s", typ);
     2.5  
     2.6          /* Play and then exit */
     2.7 -        SDL_Log("Playing %s\n", argv[i]);
     2.8 +        SDL_Log("Playing %s, duration %f\n", argv[i], Mix_MusicDuration(music));
     2.9          Mix_FadeInMusic(music,looping,2000);
    2.10          while (!next_track && (Mix_PlayingMusic() || Mix_PausedMusic())) {
    2.11              if(interactive)
     3.1 --- a/src/codecs/music_cmd.c	Tue Dec 17 21:54:04 2019 +0300
     3.2 +++ b/src/codecs/music_cmd.c	Tue Dec 17 21:55:00 2019 +0300
     3.3 @@ -279,6 +279,7 @@
     3.4      MusicCMD_IsPlaying,
     3.5      NULL,   /* GetAudio */
     3.6      NULL,   /* Seek */
     3.7 +    NULL,   /* Duration */
     3.8      MusicCMD_Pause,
     3.9      MusicCMD_Resume,
    3.10      MusicCMD_Stop,
     4.1 --- a/src/codecs/music_flac.c	Tue Dec 17 21:54:04 2019 +0300
     4.2 +++ b/src/codecs/music_flac.c	Tue Dec 17 21:55:00 2019 +0300
     4.3 @@ -156,6 +156,7 @@
     4.4      SDL_AudioStream *stream;
     4.5      int loop;
     4.6      FLAC__uint64 pcm_pos;
     4.7 +    FLAC__uint64 full_length;
     4.8      SDL_bool loop_flag;
     4.9      FLAC__uint64 loop_start;
    4.10      FLAC__uint64 loop_end;
    4.11 @@ -578,6 +579,7 @@
    4.12          music->loop = 1;
    4.13      }
    4.14  
    4.15 +    music->full_length = full_length;
    4.16      music->freesrc = freesrc;
    4.17      return music;
    4.18  }
    4.19 @@ -679,6 +681,13 @@
    4.20      return 0;
    4.21  }
    4.22  
    4.23 +/* Return music duration in seconds */
    4.24 +static double FLAC_Duration(void *context)
    4.25 +{
    4.26 +    FLAC_Music *music = (FLAC_Music *)context;
    4.27 +    return (double)music->full_length / music->sample_rate;
    4.28 +}
    4.29 +
    4.30  /* Close the given FLAC_Music object */
    4.31  static void FLAC_Delete(void *context)
    4.32  {
    4.33 @@ -715,6 +724,7 @@
    4.34      NULL,   /* IsPlaying */
    4.35      FLAC_GetAudio,
    4.36      FLAC_Seek,
    4.37 +    FLAC_Duration,
    4.38      NULL,   /* Pause */
    4.39      NULL,   /* Resume */
    4.40      NULL,   /* Stop */
     5.1 --- a/src/codecs/music_fluidsynth.c	Tue Dec 17 21:54:04 2019 +0300
     5.2 +++ b/src/codecs/music_fluidsynth.c	Tue Dec 17 21:55:00 2019 +0300
     5.3 @@ -300,6 +300,7 @@
     5.4      FLUIDSYNTH_IsPlaying,
     5.5      FLUIDSYNTH_GetAudio,
     5.6      NULL,   /* Seek */
     5.7 +    NULL,   /* Duration */
     5.8      NULL,   /* Pause */
     5.9      NULL,   /* Resume */
    5.10      FLUIDSYNTH_Stop,
     6.1 --- a/src/codecs/music_mad.c	Tue Dec 17 21:54:04 2019 +0300
     6.2 +++ b/src/codecs/music_mad.c	Tue Dec 17 21:55:00 2019 +0300
     6.3 @@ -455,6 +455,7 @@
     6.4      NULL,   /* IsPlaying */
     6.5      MAD_GetAudio,
     6.6      MAD_Seek,
     6.7 +    NULL    /* Duration */
     6.8      NULL,   /* Pause */
     6.9      NULL,   /* Resume */
    6.10      NULL,   /* Stop */
     7.1 --- a/src/codecs/music_mikmod.c	Tue Dec 17 21:54:04 2019 +0300
     7.2 +++ b/src/codecs/music_mikmod.c	Tue Dec 17 21:55:00 2019 +0300
     7.3 @@ -482,6 +482,7 @@
     7.4      MIKMOD_IsPlaying,
     7.5      MIKMOD_GetAudio,
     7.6      MIKMOD_Seek,
     7.7 +    NULL,   /* Duration */
     7.8      NULL,   /* Pause */
     7.9      NULL,   /* Resume */
    7.10      MIKMOD_Stop,
     8.1 --- a/src/codecs/music_modplug.c	Tue Dec 17 21:54:04 2019 +0300
     8.2 +++ b/src/codecs/music_modplug.c	Tue Dec 17 21:55:00 2019 +0300
     8.3 @@ -39,6 +39,7 @@
     8.4      void (*ModPlug_Unload)(ModPlugFile* file);
     8.5      int  (*ModPlug_Read)(ModPlugFile* file, void* buffer, int size);
     8.6      void (*ModPlug_Seek)(ModPlugFile* file, int millisecond);
     8.7 +    int  (*ModPlug_GetLength)(ModPlugFile* file);
     8.8      void (*ModPlug_GetSettings)(ModPlug_Settings* settings);
     8.9      void (*ModPlug_SetSettings)(const ModPlug_Settings* settings);
    8.10      void (*ModPlug_SetMasterVolume)(ModPlugFile* file,unsigned int cvol);
    8.11 @@ -81,6 +82,7 @@
    8.12          FUNCTION_LOADER(ModPlug_Unload, void (*)(ModPlugFile* file))
    8.13          FUNCTION_LOADER(ModPlug_Read, int  (*)(ModPlugFile* file, void* buffer, int size))
    8.14          FUNCTION_LOADER(ModPlug_Seek, void (*)(ModPlugFile* file, int millisecond))
    8.15 +        FUNCTION_LOADER(ModPlug_GetLength, int (*)(ModPlugFile* file))
    8.16          FUNCTION_LOADER(ModPlug_GetSettings, void (*)(ModPlug_Settings* settings))
    8.17          FUNCTION_LOADER(ModPlug_SetSettings, void (*)(const ModPlug_Settings* settings))
    8.18          FUNCTION_LOADER(ModPlug_SetMasterVolume, void (*)(ModPlugFile* file,unsigned int cvol))
    8.19 @@ -264,6 +266,13 @@
    8.20      return 0;
    8.21  }
    8.22  
    8.23 +/* Return music duration in seconds */
    8.24 +static double MODPLUG_Duration(void *context)
    8.25 +{
    8.26 +    MODPLUG_Music *music = (MODPLUG_Music *)context;
    8.27 +    return modplug.ModPlug_GetLength(music->file) / 1000.0;
    8.28 +}
    8.29 +
    8.30  /* Close the given modplug stream */
    8.31  static void MODPLUG_Delete(void *context)
    8.32  {
    8.33 @@ -297,6 +306,7 @@
    8.34      NULL,   /* IsPlaying */
    8.35      MODPLUG_GetAudio,
    8.36      MODPLUG_Seek,
    8.37 +    MODPLUG_Duration,
    8.38      NULL,   /* Pause */
    8.39      NULL,   /* Resume */
    8.40      NULL,   /* Stop */
     9.1 --- a/src/codecs/music_mpg123.c	Tue Dec 17 21:54:04 2019 +0300
     9.2 +++ b/src/codecs/music_mpg123.c	Tue Dec 17 21:55:00 2019 +0300
     9.3 @@ -52,6 +52,7 @@
     9.4      int (*mpg123_read)(mpg123_handle *mh, unsigned char *outmemory, size_t outmemsize, size_t *done );
     9.5      int (*mpg123_replace_reader_handle)( mpg123_handle *mh, ssize_t (*r_read) (void *, void *, size_t), off_t (*r_lseek)(void *, off_t, int), void (*cleanup)(void*) );
     9.6      off_t (*mpg123_seek)( mpg123_handle *mh, off_t sampleoff, int whence );
     9.7 +    off_t (*mpg123_length)(mpg123_handle *mh);
     9.8      const char* (*mpg123_strerror)(mpg123_handle *mh);
     9.9  } mpg123_loader;
    9.10  
    9.11 @@ -99,6 +100,7 @@
    9.12          FUNCTION_LOADER(mpg123_read, int (*)(mpg123_handle *mh, unsigned char *outmemory, size_t outmemsize, size_t *done ))
    9.13          FUNCTION_LOADER(mpg123_replace_reader_handle, int (*)( mpg123_handle *mh, ssize_t (*r_read) (void *, void *, size_t), off_t (*r_lseek)(void *, off_t, int), void (*cleanup)(void*) ))
    9.14          FUNCTION_LOADER(mpg123_seek, off_t (*)( mpg123_handle *mh, off_t sampleoff, int whence ))
    9.15 +        FUNCTION_LOADER(mpg123_length, off_t (*)(mpg123_handle *mh))
    9.16          FUNCTION_LOADER(mpg123_strerror, const char* (*)(mpg123_handle *mh))
    9.17      }
    9.18      ++mpg123.loaded;
    9.19 @@ -132,6 +134,7 @@
    9.20      unsigned char *buffer;
    9.21      size_t buffer_size;
    9.22      long sample_rate;
    9.23 +    off_t total_length;
    9.24  } MPG123_Music;
    9.25  
    9.26  
    9.27 @@ -309,6 +312,8 @@
    9.28          return NULL;
    9.29      }
    9.30  
    9.31 +    music->total_length = mpg123.mpg123_length(music->handle);
    9.32 +
    9.33      music->freesrc = freesrc;
    9.34      return music;
    9.35  }
    9.36 @@ -415,6 +420,16 @@
    9.37      return 0;
    9.38  }
    9.39  
    9.40 +/* Return music duration in seconds */
    9.41 +static double MPG123_Duration(void *context)
    9.42 +{
    9.43 +    MPG123_Music *music = (MPG123_Music *)context;
    9.44 +    if (music->total_length < 0) {
    9.45 +        return -1.0;
    9.46 +    }
    9.47 +    return (double)music->total_length / music->sample_rate;
    9.48 +}
    9.49 +
    9.50  static void MPG123_Delete(void *context)
    9.51  {
    9.52      MPG123_Music *music = (MPG123_Music *)context;
    9.53 @@ -457,6 +472,7 @@
    9.54      NULL,   /* IsPlaying */
    9.55      MPG123_GetAudio,
    9.56      MPG123_Seek,
    9.57 +    MPG123_Duration,
    9.58      NULL,   /* Pause */
    9.59      NULL,   /* Resume */
    9.60      NULL,   /* Stop */
    10.1 --- a/src/codecs/music_nativemidi.c	Tue Dec 17 21:54:04 2019 +0300
    10.2 +++ b/src/codecs/music_nativemidi.c	Tue Dec 17 21:55:00 2019 +0300
    10.3 @@ -100,6 +100,7 @@
    10.4      NATIVEMIDI_IsPlaying,
    10.5      NULL,   /* GetAudio */
    10.6      NULL,   /* Seek */
    10.7 +    NULL,   /* Duration */
    10.8      NATIVEMIDI_Pause,
    10.9      NATIVEMIDI_Resume,
   10.10      NATIVEMIDI_Stop,
    11.1 --- a/src/codecs/music_ogg.c	Tue Dec 17 21:54:04 2019 +0300
    11.2 +++ b/src/codecs/music_ogg.c	Tue Dec 17 21:55:00 2019 +0300
    11.3 @@ -55,6 +55,11 @@
    11.4  #else
    11.5      int (*ov_time_seek)(OggVorbis_File *vf,double pos);
    11.6  #endif
    11.7 +#ifdef OGG_USE_TREMOR
    11.8 +    ogg_int64_t (*ov_time_total)(OggVorbis_File *vf, int i);
    11.9 +#else
   11.10 +    double (*ov_time_total)(OggVorbis_File *vf, int i);
   11.11 +#endif
   11.12      int (*ov_pcm_seek)(OggVorbis_File *vf, ogg_int64_t pos);
   11.13      ogg_int64_t (*ov_pcm_tell)(OggVorbis_File *vf);
   11.14  } vorbis_loader;
   11.15 @@ -97,9 +102,11 @@
   11.16  #ifdef OGG_USE_TREMOR
   11.17          FUNCTION_LOADER(ov_read, long (*)(OggVorbis_File *,char *,int,int *))
   11.18          FUNCTION_LOADER(ov_time_seek, int (*)(OggVorbis_File *,ogg_int64_t))
   11.19 +        FUNCTION_LOADER(ov_time_total, ogg_int64_t (*)(OggVorbis_File *, int))
   11.20  #else
   11.21          FUNCTION_LOADER(ov_read, long (*)(OggVorbis_File *,char *,int,int,int,int,int *))
   11.22          FUNCTION_LOADER(ov_time_seek, int (*)(OggVorbis_File *,double))
   11.23 +        FUNCTION_LOADER(ov_time_total, double (*)(OggVorbis_File *, int))
   11.24  #endif
   11.25          FUNCTION_LOADER(ov_pcm_seek, int (*)(OggVorbis_File *,ogg_int64_t))
   11.26          FUNCTION_LOADER(ov_pcm_tell, ogg_int64_t (*)(OggVorbis_File *))
   11.27 @@ -459,6 +466,17 @@
   11.28      return 0;
   11.29  }
   11.30  
   11.31 +/* Return music duration in seconds */
   11.32 +static double OGG_Duration(void *context)
   11.33 +{
   11.34 +    OGG_music *music = (OGG_music *)context;
   11.35 +#ifdef OGG_USE_TREMOR
   11.36 +    return vorbis.ov_time_total(&music->vf, -1) / 1000.0;
   11.37 +#else
   11.38 +    return vorbis.ov_time_total(&music->vf, -1);
   11.39 +#endif
   11.40 +}
   11.41 +
   11.42  /* Close the given OGG stream */
   11.43  static void OGG_Delete(void *context)
   11.44  {
   11.45 @@ -493,6 +511,7 @@
   11.46      NULL,   /* IsPlaying */
   11.47      OGG_GetAudio,
   11.48      OGG_Seek,
   11.49 +    OGG_Duration,
   11.50      NULL,   /* Pause */
   11.51      NULL,   /* Resume */
   11.52      NULL,   /* Stop */
    12.1 --- a/src/codecs/music_opus.c	Tue Dec 17 21:54:04 2019 +0300
    12.2 +++ b/src/codecs/music_opus.c	Tue Dec 17 21:55:00 2019 +0300
    12.3 @@ -120,6 +120,7 @@
    12.4      ogg_int64_t loop_start;
    12.5      ogg_int64_t loop_end;
    12.6      ogg_int64_t loop_len;
    12.7 +    ogg_int64_t full_length;
    12.8  } OPUS_music;
    12.9  
   12.10  
   12.11 @@ -339,6 +340,7 @@
   12.12          music->loop = 1;
   12.13      }
   12.14  
   12.15 +    music->full_length = full_length;
   12.16      music->freesrc = freesrc;
   12.17      return music;
   12.18  }
   12.19 @@ -449,6 +451,13 @@
   12.20      return 0;
   12.21  }
   12.22  
   12.23 +/* Return music duration in seconds */
   12.24 +static double OPUS_Duration(void *context)
   12.25 +{
   12.26 +    OPUS_music *music = (OPUS_music *)context;
   12.27 +    return music->full_length / 48000.0;
   12.28 +}
   12.29 +
   12.30  /* Close the given Opus stream */
   12.31  static void OPUS_Delete(void *context)
   12.32  {
   12.33 @@ -483,6 +492,7 @@
   12.34      NULL,   /* IsPlaying */
   12.35      OPUS_GetAudio,
   12.36      OPUS_Seek,
   12.37 +    OPUS_Duration,
   12.38      NULL,   /* Pause */
   12.39      NULL,   /* Resume */
   12.40      NULL,   /* Stop */
    13.1 --- a/src/codecs/music_timidity.c	Tue Dec 17 21:54:04 2019 +0300
    13.2 +++ b/src/codecs/music_timidity.c	Tue Dec 17 21:55:00 2019 +0300
    13.3 @@ -193,6 +193,7 @@
    13.4          return amount;
    13.5      }
    13.6  }
    13.7 +
    13.8  static int TIMIDITY_GetAudio(void *context, void *data, int bytes)
    13.9  {
   13.10      return music_pcm_getaudio(context, data, bytes, MIX_MAX_VOLUME, TIMIDITY_GetSome);
   13.11 @@ -205,6 +206,12 @@
   13.12      return 0;
   13.13  }
   13.14  
   13.15 +static double TIMIDITY_Duration(void *context)
   13.16 +{
   13.17 +    TIMIDITY_Music *music = (TIMIDITY_Music *)context;
   13.18 +    return Timidity_GetSongLength(music->song) / 1000.0;
   13.19 +}
   13.20 +
   13.21  static void TIMIDITY_Delete(void *context)
   13.22  {
   13.23      TIMIDITY_Music *music = (TIMIDITY_Music *)context;
   13.24 @@ -238,6 +245,7 @@
   13.25      NULL,   /* IsPlaying */
   13.26      TIMIDITY_GetAudio,
   13.27      TIMIDITY_Seek,
   13.28 +    TIMIDITY_Duration,
   13.29      NULL,   /* Pause */
   13.30      NULL,   /* Resume */
   13.31      NULL,   /* Stop */
    14.1 --- a/src/codecs/music_wav.c	Tue Dec 17 21:54:04 2019 +0300
    14.2 +++ b/src/codecs/music_wav.c	Tue Dec 17 21:55:00 2019 +0300
    14.3 @@ -579,6 +579,14 @@
    14.4      return 0;
    14.5  }
    14.6  
    14.7 +/* Return music duration in seconds */
    14.8 +static double WAV_Duration(void *context)
    14.9 +{
   14.10 +    WAV_Music *music = (WAV_Music *)context;
   14.11 +    Sint64 sample_size = music->spec.freq * music->samplesize;
   14.12 +    return (double)(music->stop - music->start) / sample_size;
   14.13 +}
   14.14 +
   14.15  /* Close the given WAV stream */
   14.16  static void WAV_Delete(void *context)
   14.17  {
   14.18 @@ -1088,6 +1096,7 @@
   14.19      NULL,   /* IsPlaying */
   14.20      WAV_GetAudio,
   14.21      WAV_Seek,   /* Seek */
   14.22 +    WAV_Duration,
   14.23      NULL,   /* Pause */
   14.24      NULL,   /* Resume */
   14.25      NULL,   /* Stop */
    15.1 --- a/src/music.c	Tue Dec 17 21:54:04 2019 +0300
    15.2 +++ b/src/music.c	Tue Dec 17 21:55:00 2019 +0300
    15.3 @@ -795,6 +795,35 @@
    15.4      return(retval);
    15.5  }
    15.6  
    15.7 +static double music_duration_int(Mix_Music *music)
    15.8 +{
    15.9 +    if (music->interface->Duration) {
   15.10 +        return music->interface->Duration(music->context);
   15.11 +    } else {
   15.12 +        Mix_SetError("Duration not implemented for music type");
   15.13 +        return -1;
   15.14 +    }
   15.15 +}
   15.16 +
   15.17 +double Mix_MusicDuration(Mix_Music *music)
   15.18 +{
   15.19 +    double retval;
   15.20 +
   15.21 +    Mix_LockAudio();
   15.22 +
   15.23 +    if (music) {
   15.24 +        retval = music_duration_int(music);
   15.25 +    } else if (music_playing) {
   15.26 +        retval = music_duration_int(music_playing);
   15.27 +    } else {
   15.28 +        Mix_SetError("music is NULL and no playing music");
   15.29 +        retval = -1;
   15.30 +    }
   15.31 +    Mix_UnlockAudio();
   15.32 +
   15.33 +    return(retval);
   15.34 +}
   15.35 +
   15.36  /* Set the music's initial volume */
   15.37  static void music_internal_initialize_volume(void)
   15.38  {
    16.1 --- a/src/music.h	Tue Dec 17 21:54:04 2019 +0300
    16.2 +++ b/src/music.h	Tue Dec 17 21:55:00 2019 +0300
    16.3 @@ -82,6 +82,9 @@
    16.4      /* Seek to a play position (in seconds) */
    16.5      int (*Seek)(void *music, double position);
    16.6  
    16.7 +    /* Get Music duration in ms */
    16.8 +    double (*Duration)(void *music);
    16.9 +
   16.10      /* Pause playing music */
   16.11      void (*Pause)(void *music);
   16.12