# HG changeset patch # User Emmanuel Gil Peyrot # Date 1371539045 -7200 # Parent dae7bb0a66b137f3d5ed48dbeae843f87490a86d Add loop points support to Mix_Music, currently only for the Wavestream and Vorbis decoders. diff --git a/SDL_mixer.h b/SDL_mixer.h --- a/SDL_mixer.h +++ b/SDL_mixer.h @@ -587,7 +587,7 @@ /* Set the current position in the music stream. This returns 0 if successful, or -1 if it failed or isn't implemented. This function is only implemented for MOD music formats (set pattern - order number) and for OGG, FLAC, MP3_MAD, and MODPLUG music (set + order number) and for WAVE, Ogg, FLAC, MP3_MAD, and ModPlug music (set position in seconds), at the moment. */ extern DECLSPEC int SDLCALL Mix_SetMusicPosition(double position); @@ -610,6 +610,13 @@ extern DECLSPEC const char* SDLCALL Mix_GetSoundFonts(void); extern DECLSPEC int SDLCALL Mix_EachSoundFont(int (*function)(const char*, void*), void *data); +/* Set start and end loop points, use 0 to disable + This returns 0 if successful, or -1 if it failed or isn't implemented. + The start loop point is implemented for every backend implementing seek. + The end loop point, only for the WAVE and Ogg ones. +*/ +extern DECLSPEC int SDLCALL Mix_SetLoopPoints(Mix_Music *music, double start, double end); + /* Get the Mix_Chunk currently associated with a mixer channel Returns NULL if it's an invalid channel, or there's no chunk associated. */ diff --git a/dynamic_ogg.c b/dynamic_ogg.c --- a/dynamic_ogg.c +++ b/dynamic_ogg.c @@ -65,6 +65,13 @@ SDL_UnloadObject(vorbis.handle); return -1; } + vorbis.ov_pcm_tell = + (ogg_int64_t (*)(OggVorbis_File *)) + SDL_LoadFunction(vorbis.handle, "ov_pcm_tell"); + if ( vorbis.ov_pcm_tell == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } vorbis.ov_read = #ifdef OGG_USE_TREMOR (long (*)(OggVorbis_File *,char *,int,int *)) @@ -120,6 +127,7 @@ vorbis.ov_info = ov_info; vorbis.ov_open_callbacks = ov_open_callbacks; vorbis.ov_pcm_total = ov_pcm_total; + vorbis.ov_pcm_tell = ov_pcm_tell; vorbis.ov_read = ov_read; vorbis.ov_time_seek = ov_time_seek; } diff --git a/dynamic_ogg.h b/dynamic_ogg.h --- a/dynamic_ogg.h +++ b/dynamic_ogg.h @@ -33,6 +33,7 @@ vorbis_info *(*ov_info)(OggVorbis_File *vf,int link); int (*ov_open_callbacks)(void *datasource, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks); ogg_int64_t (*ov_pcm_total)(OggVorbis_File *vf,int i); + ogg_int64_t (*ov_pcm_tell)(OggVorbis_File *vf); #ifdef OGG_USE_TREMOR long (*ov_read)(OggVorbis_File *vf,char *buffer,int length, int *bitstream); #else diff --git a/music.c b/music.c --- a/music.c +++ b/music.c @@ -122,6 +122,7 @@ int fade_step; int fade_steps; int error; + double loop_start; }; #ifdef MID_MUSIC #ifdef USE_TIMIDITY_MIDI @@ -212,7 +213,7 @@ Mix_Fading current_fade; --music_loops; current_fade = music_playing->fading; - music_internal_play(music_playing, 0.0); + music_internal_play(music_playing, music_playing->loop_start); music_playing->fading = current_fade; } else @@ -620,6 +621,8 @@ } music->error = 1; + music->loop_start = 0.0; + switch (type) { #ifdef WAV_MUSIC case MUS_WAV: @@ -1049,6 +1052,11 @@ int retval = 0; switch (music_playing->type) { +#ifdef WAV_MUSIC + case MUS_WAV: + WAVStream_jump_to_time(music_playing->data.wave, position); + break; +#endif #ifdef MODPLUG_MUSIC case MUS_MODPLUG: modplug_jump_to_time(music_playing->data.modplug, position); @@ -1605,3 +1613,37 @@ return 1; } #endif + +int Mix_SetLoopPoints(Mix_Music *music, double start, double end) +{ + int ret = 0; + + /* 0.0 disables the loop point */ + if ( start < 0.0 || end < 0.0 || ( end && start >= end ) ) { + Mix_SetError("Invalid value for loop points"); + return -1; + } + + switch (music->type) { +#ifdef WAV_MUSIC + case MUS_WAV: + ret = WAVStream_set_end_point(music->data.wave, end); + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + ret = OGG_set_end_point(music->data.ogg, end); + break; +#endif + default: + /* Music type not supporting end loop point */ + if (end) + ret = -1; + break; + } + + if ( ret == 0 ) + music->loop_start = start; + + return ret; +} diff --git a/music_ogg.c b/music_ogg.c --- a/music_ogg.c +++ b/music_ogg.c @@ -96,6 +96,8 @@ SDL_free(music); return(NULL); } + + music->loop_stop = 0; } else { SDL_OutOfMemory(); return(NULL); @@ -122,6 +124,8 @@ int len; char data[4096]; SDL_AudioCVT *cvt; + vorbis_info *vi; + ogg_int64_t pos; #ifdef OGG_USE_TREMOR len = vorbis.ov_read(&music->vf, data, sizeof(data), §ion); @@ -134,11 +138,9 @@ } return; } + vi = vorbis.ov_info(&music->vf, -1); cvt = &music->cvt; if ( section != music->section ) { - vorbis_info *vi; - - vi = vorbis.ov_info(&music->vf, -1); SDL_BuildAudioCVT(cvt, AUDIO_S16, vi->channels, vi->rate, mixer.format,mixer.channels,mixer.freq); if ( cvt->buf ) { @@ -148,6 +150,12 @@ music->section = section; } if ( cvt->buf ) { + pos = vorbis.ov_pcm_tell(&music->vf); + if ( music->loop_stop && pos > music->loop_stop ) { + music->playing = 0; + /* the format is hardcoded to AUDIO_S16, hence 2 */ + len = (pos - music->loop_stop) * vi->channels * 2; + } SDL_memcpy(cvt->buf, data, len); if ( cvt->needed ) { cvt->len = len; @@ -222,4 +230,12 @@ #endif } +/* Set or unset loop points to a stream */ +int OGG_set_end_point(OGG_music *music, double end) +{ + vorbis_info *vi = vorbis.ov_info(&music->vf, -1); + music->loop_stop = (ogg_int64_t)(vi->rate * end); + return 0; +} + #endif /* OGG_MUSIC */ diff --git a/music_ogg.h b/music_ogg.h --- a/music_ogg.h +++ b/music_ogg.h @@ -41,6 +41,7 @@ SDL_AudioCVT cvt; int len_available; Uint8 *snd_available; + ogg_int64_t loop_stop; } OGG_music; /* Initialize the Ogg Vorbis player, with the given mixer settings @@ -72,4 +73,7 @@ /* Jump (seek) to a given position (time is in seconds) */ extern void OGG_jump_to_time(OGG_music *music, double time); +/* Set or unset loop points to a stream */ +extern int OGG_set_end_point(OGG_music *music, double end); + #endif /* OGG_MUSIC */ diff --git a/playmus.c b/playmus.c --- a/playmus.c +++ b/playmus.c @@ -64,7 +64,7 @@ void Usage(char *argv0) { - fprintf(stderr, "Usage: %s [-i] [-l] [-8] [-r rate] [-c channels] [-b buffers] [-v N] [-rwops] \n", argv0); + fprintf(stderr, "Usage: %s [-i] [-l [-s start]] [-e end] [-8] [-r rate] [-c channels] [-b buffers] [-v N] [-rwops] \n", argv0); } void Menu(void) @@ -114,6 +114,8 @@ int audio_buffers; int audio_volume = MIX_MAX_VOLUME; int looping = 0; + double loop_start = 0.0; + double loop_end = 0.0; int interactive = 0; int rwops = 0; int i; @@ -148,6 +150,14 @@ if ( strcmp(argv[i], "-l") == 0 ) { looping = -1; } else + if ( (strcmp(argv[i], "-s") == 0) && argv[i+1] ) { + ++i; + loop_start = atof(argv[i]); + } else + if ( (strcmp(argv[i], "-e") == 0) && argv[i+1] ) { + ++i; + loop_end = atof(argv[i]); + } else if ( strcmp(argv[i], "-i") == 0 ) { interactive = 1; } else @@ -212,6 +222,14 @@ CleanUp(2); } + if ( loop_start || loop_end ) { + if ( Mix_SetLoopPoints(music, loop_start, loop_end) < 0 ) { + fprintf(stderr, "Couldn't set looppoints for %s: %s\n", + argv[i], SDL_GetError()); + CleanUp(3); + } + } + /* Play and then exit */ printf("Playing %s\n", argv[i]); Mix_FadeInMusic(music,looping,2000); diff --git a/wavestream.c b/wavestream.c --- a/wavestream.c +++ b/wavestream.c @@ -142,6 +142,9 @@ SDL_free(wave); return(NULL); } + wave->loop_stop = 0; + wave->samplesize = wavespec.channels * SDL_AUDIO_BITSIZE(wavespec.format) >> 3; + wave->byterate = wavespec.freq * wave->samplesize; SDL_BuildAudioCVT(&wave->cvt, wavespec.format, wavespec.channels, wavespec.freq, mixer.format, mixer.channels, mixer.freq); @@ -164,8 +167,9 @@ { Sint64 pos; Sint64 left = 0; + long stop = music->loop_stop? music->loop_stop: music->stop; - if ( music && ((pos=SDL_RWtell(music->src)) < music->stop) ) { + if ( music && ((pos=SDL_RWtell(music->src)) < stop) ) { if ( music->cvt.needed ) { int original_len; @@ -182,8 +186,8 @@ } music->cvt.len = original_len; } - if ( (music->stop - pos) < original_len ) { - left = (original_len - (music->stop - pos)); + if ( (stop - pos) < original_len ) { + left = (original_len - (stop - pos)); original_len -= (int)left; left = (int)((double)left*music->cvt.len_ratio); } @@ -202,8 +206,8 @@ SDL_MixAudio(stream, music->cvt.buf, music->cvt.len_cvt, wavestream_volume); } else { Uint8 *data; - if ( (music->stop - pos) < len ) { - left = (len - (music->stop - pos)); + if ( (stop - pos) < len ) { + left = (len - (stop - pos)); len -= (int)left; } data = SDL_stack_alloc(Uint8, len); @@ -243,14 +247,40 @@ int WAVStream_Active(void) { int active; + long stop = music->loop_stop? music->loop_stop: music->stop; active = 0; - if ( music && (SDL_RWtell(music->src) < music->stop) ) { + if ( music && (SDL_RWtell(music->src) < stop) ) { active = 1; } return(active); } +/* Jump (seek) to a given position (time is in seconds) */ +int WAVStream_jump_to_time(WAVStream *wave, double position) +{ + long start = (long)(position * wave->byterate) & ~(wave->samplesize-1); + + SDL_RWseek(wave->src, wave->start + start, RW_SEEK_SET); + + return 0; +} + +/* Set or unset loop points to a stream */ +int WAVStream_set_end_point(WAVStream *wave, double end) +{ + long new_end = (long)(end * wave->byterate); + + if ( end < 0.0 || new_end > wave->stop ) { + Mix_SetError("Invalid value for end point"); + return -1; + } + + wave->loop_stop = new_end & ~(wave->samplesize-1); + + return 0; +} + static int ReadChunk(SDL_RWops *src, Chunk *chunk, int read_data) { chunk->magic = SDL_ReadLE32(src); diff --git a/wavestream.h b/wavestream.h --- a/wavestream.h +++ b/wavestream.h @@ -31,6 +31,9 @@ long start; long stop; SDL_AudioCVT cvt; + long samplesize; + long byterate; + long loop_stop; } WAVStream; /* Initialize the WAVStream player, with the given mixer settings @@ -58,3 +61,9 @@ /* Return non-zero if a stream is currently playing */ extern int WAVStream_Active(void); + +/* Jump (seek) to a given position (time is in seconds) */ +extern int WAVStream_jump_to_time(WAVStream *wave, double position); + +/* Set or unset loop points to a stream */ +extern int WAVStream_set_end_point(WAVStream *wave, double end);