diff --git a/src/sound/i_soundinternal.h b/src/sound/i_soundinternal.h index aa91d73ae..47d2b923e 100644 --- a/src/sound/i_soundinternal.h +++ b/src/sound/i_soundinternal.h @@ -132,7 +132,7 @@ struct SoundDecoder virtual size_t read(char *buffer, size_t bytes) = 0; virtual TArray readAll(); - virtual bool seek(size_t ms_offset, bool ms) = 0; + virtual bool seek(size_t ms_offset, bool ms, bool mayrestart) = 0; virtual size_t getSampleOffset() = 0; virtual size_t getSampleLength() { return 0; } diff --git a/src/sound/mpg123_decoder.cpp b/src/sound/mpg123_decoder.cpp index 6ea6a1370..dccaa6600 100644 --- a/src/sound/mpg123_decoder.cpp +++ b/src/sound/mpg123_decoder.cpp @@ -190,23 +190,38 @@ size_t MPG123Decoder::read(char *buffer, size_t bytes) return amt; } -bool MPG123Decoder::seek(size_t ms_offset, bool ms) +bool MPG123Decoder::seek(size_t ms_offset, bool ms, bool mayrestart) { - int enc, channels; - long srate; + int enc, channels; + long srate; - if(mpg123_getformat(MPG123, &srate, &channels, &enc) == MPG123_OK) - { - size_t smp_offset = ms? (size_t)((double)ms_offset / 1000. * srate) : ms_offset; - if(mpg123_seek(MPG123, (off_t)smp_offset, SEEK_SET) >= 0) - { - Done = false; - return true; - } - } - return false; + if (!mayrestart || ms_offset > 0) + { + if (mpg123_getformat(MPG123, &srate, &channels, &enc) == MPG123_OK) + { + size_t smp_offset = ms ? (size_t)((double)ms_offset / 1000. * srate) : ms_offset; + if (mpg123_seek(MPG123, (off_t)smp_offset, SEEK_SET) >= 0) + { + Done = false; + return true; + } + } + return false; + } + else + { + // Restart the song instead of rewinding. A rewind seems to cause distortion when done repeatedly. + // offset is intentionally ignored here. + if (MPG123) + { + mpg123_close(MPG123); + mpg123_delete(MPG123); + MPG123 = 0; + } + Reader->Seek(0, SEEK_SET); + return open(Reader); + } } - size_t MPG123Decoder::getSampleOffset() { return mpg123_tell(MPG123); diff --git a/src/sound/mpg123_decoder.h b/src/sound/mpg123_decoder.h index fa20f0b3c..02af11a16 100644 --- a/src/sound/mpg123_decoder.h +++ b/src/sound/mpg123_decoder.h @@ -21,7 +21,7 @@ struct MPG123Decoder : public SoundDecoder virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); virtual size_t read(char *buffer, size_t bytes); - virtual bool seek(size_t ms_offset, bool ms); + virtual bool seek(size_t ms_offset, bool ms, bool mayrestart); virtual size_t getSampleOffset(); virtual size_t getSampleLength(); diff --git a/src/sound/musicformats/music_libsndfile.cpp b/src/sound/musicformats/music_libsndfile.cpp index fc159314f..e8a613278 100644 --- a/src/sound/musicformats/music_libsndfile.cpp +++ b/src/sound/musicformats/music_libsndfile.cpp @@ -326,16 +326,39 @@ bool SndFileSong::Read(SoundStream *stream, void *vbuff, int ilen, void *userdat } else { + // This looks a bit more complicated than necessary because libmpg123 will not read the full requested length for the last block in the file. if (currentpos + framestoread > song->Loop_End) { size_t endblock = (song->Loop_End - currentpos) * song->Channels * 2; - err = (song->Decoder->read(buff, endblock) != endblock); - buff = buff + endblock; - len -= endblock; - song->Decoder->seek(song->Loop_Start, false); + size_t endlen = song->Decoder->read(buff, endblock); + if (endlen != 0) + { + buff = buff + endblock; + len -= endblock; + song->Decoder->seek(song->Loop_Start, false, true); + } + else + { + song->CritSec.Leave(); + return false; + } + } + while (len > 0) + { + size_t readlen = song->Decoder->read(buff, len); + if (readlen == 0) + { + song->CritSec.Leave(); + return false; + } + buff += readlen; + len -= readlen; + if (len > 0) + { + song->Decoder->seek(song->Loop_Start, false, true); + } } - err |= song->Decoder->read(buff, len) != len; } song->CritSec.Leave(); - return !err; + return true; } diff --git a/src/sound/oalsound.cpp b/src/sound/oalsound.cpp index 946fba2b5..d9b53b0d5 100644 --- a/src/sound/oalsound.cpp +++ b/src/sound/oalsound.cpp @@ -215,7 +215,7 @@ class OpenALSoundStream : public SoundStream size_t got = self->Decoder->read((char*)ptr, length); if(got < (unsigned int)length) { - if(!self->Looping || !self->Decoder->seek(0, false)) + if(!self->Looping || !self->Decoder->seek(0, false, true)) return false; got += self->Decoder->read((char*)ptr+got, length-got); } @@ -364,7 +364,7 @@ public: virtual bool SetPosition(unsigned int ms_pos) { std::unique_lock lock(Renderer->StreamLock); - if(!Decoder->seek(ms_pos, true)) + if(!Decoder->seek(ms_pos, true, false)) return false; if(!Playing.load()) diff --git a/src/sound/sndfile_decoder.cpp b/src/sound/sndfile_decoder.cpp index fe808c983..b403b46ed 100644 --- a/src/sound/sndfile_decoder.cpp +++ b/src/sound/sndfile_decoder.cpp @@ -190,7 +190,7 @@ TArray SndFileDecoder::readAll() return output; } -bool SndFileDecoder::seek(size_t ms_offset, bool ms) +bool SndFileDecoder::seek(size_t ms_offset, bool ms, bool /*mayrestart*/) { size_t smp_offset = ms? (size_t)((double)ms_offset / 1000. * SndInfo.samplerate) : ms_offset; if(sf_seek(SndFile, smp_offset, SEEK_SET) < 0) diff --git a/src/sound/sndfile_decoder.h b/src/sound/sndfile_decoder.h index e5473dcb2..53fa6e6a9 100644 --- a/src/sound/sndfile_decoder.h +++ b/src/sound/sndfile_decoder.h @@ -17,8 +17,8 @@ struct SndFileDecoder : public SoundDecoder virtual size_t read(char *buffer, size_t bytes); virtual TArray readAll(); - virtual bool seek(size_t ms_offset, bool ms); - virtual size_t getSampleOffset(); + virtual bool seek(size_t ms_offset, bool ms, bool mayrestart); + virtual size_t getSampleOffset(); virtual size_t getSampleLength(); SndFileDecoder() : SndFile(0) { }