From e77ab3fbdb638a65acdb8f8ad70025151aa12704 Mon Sep 17 00:00:00 2001 From: Matthias Gatto Date: Tue, 17 Dec 2019 21:55:00 +0300 Subject: [PATCH] Introduce Mix_MusicDuration() to return music duration in seconds. Supports wav, vorbis, opus, mp3 (mpg123), flac, mod (modplug), timidity. --- include/SDL_mixer.h | 6 ++++++ playmus.c | 2 +- src/codecs/music_cmd.c | 1 + src/codecs/music_flac.c | 10 ++++++++++ src/codecs/music_fluidsynth.c | 1 + src/codecs/music_mad.c | 1 + src/codecs/music_mikmod.c | 1 + src/codecs/music_modplug.c | 10 ++++++++++ src/codecs/music_mpg123.c | 16 ++++++++++++++++ src/codecs/music_nativemidi.c | 1 + src/codecs/music_ogg.c | 19 +++++++++++++++++++ src/codecs/music_opus.c | 10 ++++++++++ src/codecs/music_timidity.c | 8 ++++++++ src/codecs/music_wav.c | 9 +++++++++ src/music.c | 29 +++++++++++++++++++++++++++++ src/music.h | 3 +++ 16 files changed, 126 insertions(+), 1 deletion(-) diff --git a/include/SDL_mixer.h b/include/SDL_mixer.h index 52a29280..e4118bc8 100644 --- a/include/SDL_mixer.h +++ b/include/SDL_mixer.h @@ -618,6 +618,12 @@ extern DECLSPEC int SDLCALL Mix_PlayingMusic(void); /* Stop music and set external music playback command */ extern DECLSPEC int SDLCALL Mix_SetMusicCMD(const char *command); +/* Return music duration in seconds. + If NULL is passed, returns duration of current playing music. + Returns -1 on error. + */ +extern DECLSPEC double SDLCALL Mix_MusicDuration(Mix_Music *music); + /* Synchro value is set by MikMod from modules while playing */ extern DECLSPEC int SDLCALL Mix_SetSynchroValue(int value); extern DECLSPEC int SDLCALL Mix_GetSynchroValue(void); diff --git a/playmus.c b/playmus.c index fb9fbf6e..4d89199f 100644 --- a/playmus.c +++ b/playmus.c @@ -251,7 +251,7 @@ int main(int argc, char *argv[]) SDL_Log("Detected music type: %s", typ); /* Play and then exit */ - SDL_Log("Playing %s\n", argv[i]); + SDL_Log("Playing %s, duration %f\n", argv[i], Mix_MusicDuration(music)); Mix_FadeInMusic(music,looping,2000); while (!next_track && (Mix_PlayingMusic() || Mix_PausedMusic())) { if(interactive) diff --git a/src/codecs/music_cmd.c b/src/codecs/music_cmd.c index b3981783..e7eb734e 100644 --- a/src/codecs/music_cmd.c +++ b/src/codecs/music_cmd.c @@ -279,6 +279,7 @@ Mix_MusicInterface Mix_MusicInterface_CMD = MusicCMD_IsPlaying, NULL, /* GetAudio */ NULL, /* Seek */ + NULL, /* Duration */ MusicCMD_Pause, MusicCMD_Resume, MusicCMD_Stop, diff --git a/src/codecs/music_flac.c b/src/codecs/music_flac.c index 99705c5e..4142eaaf 100644 --- a/src/codecs/music_flac.c +++ b/src/codecs/music_flac.c @@ -156,6 +156,7 @@ typedef struct { SDL_AudioStream *stream; int loop; FLAC__uint64 pcm_pos; + FLAC__uint64 full_length; SDL_bool loop_flag; FLAC__uint64 loop_start; FLAC__uint64 loop_end; @@ -578,6 +579,7 @@ static void *FLAC_CreateFromRW(SDL_RWops *src, int freesrc) music->loop = 1; } + music->full_length = full_length; music->freesrc = freesrc; return music; } @@ -679,6 +681,13 @@ static int FLAC_Seek(void *context, double position) return 0; } +/* Return music duration in seconds */ +static double FLAC_Duration(void *context) +{ + FLAC_Music *music = (FLAC_Music *)context; + return (double)music->full_length / music->sample_rate; +} + /* Close the given FLAC_Music object */ static void FLAC_Delete(void *context) { @@ -715,6 +724,7 @@ Mix_MusicInterface Mix_MusicInterface_FLAC = NULL, /* IsPlaying */ FLAC_GetAudio, FLAC_Seek, + FLAC_Duration, NULL, /* Pause */ NULL, /* Resume */ NULL, /* Stop */ diff --git a/src/codecs/music_fluidsynth.c b/src/codecs/music_fluidsynth.c index 91596296..ad4463f4 100644 --- a/src/codecs/music_fluidsynth.c +++ b/src/codecs/music_fluidsynth.c @@ -300,6 +300,7 @@ Mix_MusicInterface Mix_MusicInterface_FLUIDSYNTH = FLUIDSYNTH_IsPlaying, FLUIDSYNTH_GetAudio, NULL, /* Seek */ + NULL, /* Duration */ NULL, /* Pause */ NULL, /* Resume */ FLUIDSYNTH_Stop, diff --git a/src/codecs/music_mad.c b/src/codecs/music_mad.c index e2ff7856..e646d3c8 100644 --- a/src/codecs/music_mad.c +++ b/src/codecs/music_mad.c @@ -455,6 +455,7 @@ Mix_MusicInterface Mix_MusicInterface_MAD = NULL, /* IsPlaying */ MAD_GetAudio, MAD_Seek, + NULL /* Duration */ NULL, /* Pause */ NULL, /* Resume */ NULL, /* Stop */ diff --git a/src/codecs/music_mikmod.c b/src/codecs/music_mikmod.c index 562d2619..d267dceb 100644 --- a/src/codecs/music_mikmod.c +++ b/src/codecs/music_mikmod.c @@ -482,6 +482,7 @@ Mix_MusicInterface Mix_MusicInterface_MIKMOD = MIKMOD_IsPlaying, MIKMOD_GetAudio, MIKMOD_Seek, + NULL, /* Duration */ NULL, /* Pause */ NULL, /* Resume */ MIKMOD_Stop, diff --git a/src/codecs/music_modplug.c b/src/codecs/music_modplug.c index a8a09401..7e1aefe1 100644 --- a/src/codecs/music_modplug.c +++ b/src/codecs/music_modplug.c @@ -39,6 +39,7 @@ typedef struct { void (*ModPlug_Unload)(ModPlugFile* file); int (*ModPlug_Read)(ModPlugFile* file, void* buffer, int size); void (*ModPlug_Seek)(ModPlugFile* file, int millisecond); + int (*ModPlug_GetLength)(ModPlugFile* file); void (*ModPlug_GetSettings)(ModPlug_Settings* settings); void (*ModPlug_SetSettings)(const ModPlug_Settings* settings); void (*ModPlug_SetMasterVolume)(ModPlugFile* file,unsigned int cvol); @@ -81,6 +82,7 @@ static int MODPLUG_Load(void) FUNCTION_LOADER(ModPlug_Unload, void (*)(ModPlugFile* file)) FUNCTION_LOADER(ModPlug_Read, int (*)(ModPlugFile* file, void* buffer, int size)) FUNCTION_LOADER(ModPlug_Seek, void (*)(ModPlugFile* file, int millisecond)) + FUNCTION_LOADER(ModPlug_GetLength, int (*)(ModPlugFile* file)) FUNCTION_LOADER(ModPlug_GetSettings, void (*)(ModPlug_Settings* settings)) FUNCTION_LOADER(ModPlug_SetSettings, void (*)(const ModPlug_Settings* settings)) FUNCTION_LOADER(ModPlug_SetMasterVolume, void (*)(ModPlugFile* file,unsigned int cvol)) @@ -264,6 +266,13 @@ static int MODPLUG_Seek(void *context, double position) return 0; } +/* Return music duration in seconds */ +static double MODPLUG_Duration(void *context) +{ + MODPLUG_Music *music = (MODPLUG_Music *)context; + return modplug.ModPlug_GetLength(music->file) / 1000.0; +} + /* Close the given modplug stream */ static void MODPLUG_Delete(void *context) { @@ -297,6 +306,7 @@ Mix_MusicInterface Mix_MusicInterface_MODPLUG = NULL, /* IsPlaying */ MODPLUG_GetAudio, MODPLUG_Seek, + MODPLUG_Duration, NULL, /* Pause */ NULL, /* Resume */ NULL, /* Stop */ diff --git a/src/codecs/music_mpg123.c b/src/codecs/music_mpg123.c index 653a2f9e..e9e73b58 100644 --- a/src/codecs/music_mpg123.c +++ b/src/codecs/music_mpg123.c @@ -52,6 +52,7 @@ typedef struct { int (*mpg123_read)(mpg123_handle *mh, unsigned char *outmemory, size_t outmemsize, size_t *done ); 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*) ); off_t (*mpg123_seek)( mpg123_handle *mh, off_t sampleoff, int whence ); + off_t (*mpg123_length)(mpg123_handle *mh); const char* (*mpg123_strerror)(mpg123_handle *mh); } mpg123_loader; @@ -99,6 +100,7 @@ static int MPG123_Load(void) FUNCTION_LOADER(mpg123_read, int (*)(mpg123_handle *mh, unsigned char *outmemory, size_t outmemsize, size_t *done )) 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*) )) FUNCTION_LOADER(mpg123_seek, off_t (*)( mpg123_handle *mh, off_t sampleoff, int whence )) + FUNCTION_LOADER(mpg123_length, off_t (*)(mpg123_handle *mh)) FUNCTION_LOADER(mpg123_strerror, const char* (*)(mpg123_handle *mh)) } ++mpg123.loaded; @@ -132,6 +134,7 @@ typedef struct unsigned char *buffer; size_t buffer_size; long sample_rate; + off_t total_length; } MPG123_Music; @@ -309,6 +312,8 @@ static void *MPG123_CreateFromRW(SDL_RWops *src, int freesrc) return NULL; } + music->total_length = mpg123.mpg123_length(music->handle); + music->freesrc = freesrc; return music; } @@ -415,6 +420,16 @@ static int MPG123_Seek(void *context, double secs) return 0; } +/* Return music duration in seconds */ +static double MPG123_Duration(void *context) +{ + MPG123_Music *music = (MPG123_Music *)context; + if (music->total_length < 0) { + return -1.0; + } + return (double)music->total_length / music->sample_rate; +} + static void MPG123_Delete(void *context) { MPG123_Music *music = (MPG123_Music *)context; @@ -457,6 +472,7 @@ Mix_MusicInterface Mix_MusicInterface_MPG123 = NULL, /* IsPlaying */ MPG123_GetAudio, MPG123_Seek, + MPG123_Duration, NULL, /* Pause */ NULL, /* Resume */ NULL, /* Stop */ diff --git a/src/codecs/music_nativemidi.c b/src/codecs/music_nativemidi.c index 0ba52a01..bdf0da62 100644 --- a/src/codecs/music_nativemidi.c +++ b/src/codecs/music_nativemidi.c @@ -100,6 +100,7 @@ Mix_MusicInterface Mix_MusicInterface_NATIVEMIDI = NATIVEMIDI_IsPlaying, NULL, /* GetAudio */ NULL, /* Seek */ + NULL, /* Duration */ NATIVEMIDI_Pause, NATIVEMIDI_Resume, NATIVEMIDI_Stop, diff --git a/src/codecs/music_ogg.c b/src/codecs/music_ogg.c index d29bdf4d..bab2554a 100644 --- a/src/codecs/music_ogg.c +++ b/src/codecs/music_ogg.c @@ -54,6 +54,11 @@ typedef struct { int (*ov_time_seek)(OggVorbis_File *vf,ogg_int64_t pos); #else int (*ov_time_seek)(OggVorbis_File *vf,double pos); +#endif +#ifdef OGG_USE_TREMOR + ogg_int64_t (*ov_time_total)(OggVorbis_File *vf, int i); +#else + double (*ov_time_total)(OggVorbis_File *vf, int i); #endif int (*ov_pcm_seek)(OggVorbis_File *vf, ogg_int64_t pos); ogg_int64_t (*ov_pcm_tell)(OggVorbis_File *vf); @@ -97,9 +102,11 @@ static int OGG_Load(void) #ifdef OGG_USE_TREMOR FUNCTION_LOADER(ov_read, long (*)(OggVorbis_File *,char *,int,int *)) FUNCTION_LOADER(ov_time_seek, int (*)(OggVorbis_File *,ogg_int64_t)) + FUNCTION_LOADER(ov_time_total, ogg_int64_t (*)(OggVorbis_File *, int)) #else FUNCTION_LOADER(ov_read, long (*)(OggVorbis_File *,char *,int,int,int,int,int *)) FUNCTION_LOADER(ov_time_seek, int (*)(OggVorbis_File *,double)) + FUNCTION_LOADER(ov_time_total, double (*)(OggVorbis_File *, int)) #endif FUNCTION_LOADER(ov_pcm_seek, int (*)(OggVorbis_File *,ogg_int64_t)) FUNCTION_LOADER(ov_pcm_tell, ogg_int64_t (*)(OggVorbis_File *)) @@ -459,6 +466,17 @@ static int OGG_Seek(void *context, double time) return 0; } +/* Return music duration in seconds */ +static double OGG_Duration(void *context) +{ + OGG_music *music = (OGG_music *)context; +#ifdef OGG_USE_TREMOR + return vorbis.ov_time_total(&music->vf, -1) / 1000.0; +#else + return vorbis.ov_time_total(&music->vf, -1); +#endif +} + /* Close the given OGG stream */ static void OGG_Delete(void *context) { @@ -493,6 +511,7 @@ Mix_MusicInterface Mix_MusicInterface_OGG = NULL, /* IsPlaying */ OGG_GetAudio, OGG_Seek, + OGG_Duration, NULL, /* Pause */ NULL, /* Resume */ NULL, /* Stop */ diff --git a/src/codecs/music_opus.c b/src/codecs/music_opus.c index d35f7a54..f4e86f9a 100644 --- a/src/codecs/music_opus.c +++ b/src/codecs/music_opus.c @@ -120,6 +120,7 @@ typedef struct { ogg_int64_t loop_start; ogg_int64_t loop_end; ogg_int64_t loop_len; + ogg_int64_t full_length; } OPUS_music; @@ -339,6 +340,7 @@ static void *OPUS_CreateFromRW(SDL_RWops *src, int freesrc) music->loop = 1; } + music->full_length = full_length; music->freesrc = freesrc; return music; } @@ -449,6 +451,13 @@ static int OPUS_Seek(void *context, double time) return 0; } +/* Return music duration in seconds */ +static double OPUS_Duration(void *context) +{ + OPUS_music *music = (OPUS_music *)context; + return music->full_length / 48000.0; +} + /* Close the given Opus stream */ static void OPUS_Delete(void *context) { @@ -483,6 +492,7 @@ Mix_MusicInterface Mix_MusicInterface_Opus = NULL, /* IsPlaying */ OPUS_GetAudio, OPUS_Seek, + OPUS_Duration, NULL, /* Pause */ NULL, /* Resume */ NULL, /* Stop */ diff --git a/src/codecs/music_timidity.c b/src/codecs/music_timidity.c index d728292a..8ca5aa62 100644 --- a/src/codecs/music_timidity.c +++ b/src/codecs/music_timidity.c @@ -193,6 +193,7 @@ static int TIMIDITY_GetSome(void *context, void *data, int bytes, SDL_bool *done return amount; } } + static int TIMIDITY_GetAudio(void *context, void *data, int bytes) { return music_pcm_getaudio(context, data, bytes, MIX_MAX_VOLUME, TIMIDITY_GetSome); @@ -205,6 +206,12 @@ static int TIMIDITY_Seek(void *context, double position) return 0; } +static double TIMIDITY_Duration(void *context) +{ + TIMIDITY_Music *music = (TIMIDITY_Music *)context; + return Timidity_GetSongLength(music->song) / 1000.0; +} + static void TIMIDITY_Delete(void *context) { TIMIDITY_Music *music = (TIMIDITY_Music *)context; @@ -238,6 +245,7 @@ Mix_MusicInterface Mix_MusicInterface_TIMIDITY = NULL, /* IsPlaying */ TIMIDITY_GetAudio, TIMIDITY_Seek, + TIMIDITY_Duration, NULL, /* Pause */ NULL, /* Resume */ NULL, /* Stop */ diff --git a/src/codecs/music_wav.c b/src/codecs/music_wav.c index 25044987..ec302078 100644 --- a/src/codecs/music_wav.c +++ b/src/codecs/music_wav.c @@ -579,6 +579,14 @@ static int WAV_Seek(void *context, double position) return 0; } +/* Return music duration in seconds */ +static double WAV_Duration(void *context) +{ + WAV_Music *music = (WAV_Music *)context; + Sint64 sample_size = music->spec.freq * music->samplesize; + return (double)(music->stop - music->start) / sample_size; +} + /* Close the given WAV stream */ static void WAV_Delete(void *context) { @@ -1088,6 +1096,7 @@ Mix_MusicInterface Mix_MusicInterface_WAV = NULL, /* IsPlaying */ WAV_GetAudio, WAV_Seek, /* Seek */ + WAV_Duration, NULL, /* Pause */ NULL, /* Resume */ NULL, /* Stop */ diff --git a/src/music.c b/src/music.c index ee2988f3..de63486e 100644 --- a/src/music.c +++ b/src/music.c @@ -795,6 +795,35 @@ int Mix_SetMusicPosition(double position) return(retval); } +static double music_duration_int(Mix_Music *music) +{ + if (music->interface->Duration) { + return music->interface->Duration(music->context); + } else { + Mix_SetError("Duration not implemented for music type"); + return -1; + } +} + +double Mix_MusicDuration(Mix_Music *music) +{ + double retval; + + Mix_LockAudio(); + + if (music) { + retval = music_duration_int(music); + } else if (music_playing) { + retval = music_duration_int(music_playing); + } else { + Mix_SetError("music is NULL and no playing music"); + retval = -1; + } + Mix_UnlockAudio(); + + return(retval); +} + /* Set the music's initial volume */ static void music_internal_initialize_volume(void) { diff --git a/src/music.h b/src/music.h index b36fec03..efbf9224 100644 --- a/src/music.h +++ b/src/music.h @@ -82,6 +82,9 @@ typedef struct /* Seek to a play position (in seconds) */ int (*Seek)(void *music, double position); + /* Get Music duration in ms */ + double (*Duration)(void *music); + /* Pause playing music */ void (*Pause)(void *music);