From 2207af41e41141a0afc5cab57ed523679d1befdd Mon Sep 17 00:00:00 2001 From: Cobaltergeist Date: Sat, 30 Jul 2016 11:55:39 -0700 Subject: [PATCH] Fixed inconsistent seek behavior in SoundStream --- include/SFML/Audio/InputSoundFile.hpp | 18 +++++++++++++ include/SFML/Audio/Music.hpp | 7 +++-- include/SFML/Audio/SoundStream.hpp | 3 ++- src/SFML/Audio/InputSoundFile.cpp | 38 ++++++++++++++++++++++++--- src/SFML/Audio/Music.cpp | 12 +++------ src/SFML/Audio/SoundStream.cpp | 21 ++++++++++----- 6 files changed, 75 insertions(+), 24 deletions(-) diff --git a/include/SFML/Audio/InputSoundFile.hpp b/include/SFML/Audio/InputSoundFile.hpp index e59f2a2a8..983f74dd5 100644 --- a/include/SFML/Audio/InputSoundFile.hpp +++ b/include/SFML/Audio/InputSoundFile.hpp @@ -32,6 +32,7 @@ #include #include #include +#include namespace sf @@ -146,6 +147,22 @@ public: //////////////////////////////////////////////////////////// Time getDuration() const; + //////////////////////////////////////////////////////////// + /// \brief Get the read offset of the file in time + /// + /// \return Time position + /// + //////////////////////////////////////////////////////////// + Time getTimeOffset() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the read offset of the file in samples + /// + /// \return Sample position + /// + //////////////////////////////////////////////////////////// + Uint64 getSampleOffset() const; + //////////////////////////////////////////////////////////// /// \brief Change the current read position to the given sample offset /// @@ -203,6 +220,7 @@ private: SoundFileReader* m_reader; ///< Reader that handles I/O on the file's format InputStream* m_stream; ///< Input stream used to access the file's data bool m_streamOwned; ///< Is the stream internal or external? + Uint64 m_sampleOffset; ///< Sample Read Position Uint64 m_sampleCount; ///< Total number of samples in the file unsigned int m_channelCount; ///< Number of channels of the sound unsigned int m_sampleRate; ///< Number of samples per second diff --git a/include/SFML/Audio/Music.hpp b/include/SFML/Audio/Music.hpp index 1dc0e0c95..f2b9c535b 100644 --- a/include/SFML/Audio/Music.hpp +++ b/include/SFML/Audio/Music.hpp @@ -168,10 +168,9 @@ private: //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - InputSoundFile m_file; ///< The streamed music file - Time m_duration; ///< Music duration - std::vector m_samples; ///< Temporary buffer of samples - Mutex m_mutex; ///< Mutex protecting the data + InputSoundFile m_file; ///< The streamed music file + std::vector m_samples; ///< Temporary buffer of samples + Mutex m_mutex; ///< Mutex protecting the data }; } // namespace sf diff --git a/include/SFML/Audio/SoundStream.hpp b/include/SFML/Audio/SoundStream.hpp index a2d7cb988..6fbbb6db2 100644 --- a/include/SFML/Audio/SoundStream.hpp +++ b/include/SFML/Audio/SoundStream.hpp @@ -254,11 +254,12 @@ private: /// playing queue. /// /// \param bufferNum Number of the buffer to fill (in [0, BufferCount]) + /// \param immediateLoop Treat empty buffers as spent, and act on loops immediately /// /// \return True if the stream source has requested to stop, false otherwise /// //////////////////////////////////////////////////////////// - bool fillAndPushBuffer(unsigned int bufferNum); + bool fillAndPushBuffer(unsigned int bufferNum, bool immediateLoop = false); //////////////////////////////////////////////////////////// /// \brief Fill the audio buffers and put them all into the playing queue diff --git a/src/SFML/Audio/InputSoundFile.cpp b/src/SFML/Audio/InputSoundFile.cpp index 8487c539b..f71216203 100644 --- a/src/SFML/Audio/InputSoundFile.cpp +++ b/src/SFML/Audio/InputSoundFile.cpp @@ -41,6 +41,7 @@ InputSoundFile::InputSoundFile() : m_reader (NULL), m_stream (NULL), m_streamOwned (false), +m_sampleOffset (0), m_sampleCount (0), m_channelCount(0), m_sampleRate (0) @@ -195,15 +196,42 @@ unsigned int InputSoundFile::getSampleRate() const //////////////////////////////////////////////////////////// Time InputSoundFile::getDuration() const { + // Make sure we don't divide by 0 + if (m_channelCount == 0 || m_sampleRate == 0) + return Time::Zero; + return seconds(static_cast(m_sampleCount) / m_channelCount / m_sampleRate); } +//////////////////////////////////////////////////////////// +Time InputSoundFile::getTimeOffset() const +{ + // Make sure we don't divide by 0 + if (m_channelCount == 0 || m_sampleRate == 0) + return Time::Zero; + + return seconds(static_cast(m_sampleOffset) / m_channelCount / m_sampleRate); +} + + +//////////////////////////////////////////////////////////// +Uint64 InputSoundFile::getSampleOffset() const +{ + return m_sampleOffset; +} + + //////////////////////////////////////////////////////////// void InputSoundFile::seek(Uint64 sampleOffset) { if (m_reader) - m_reader->seek(sampleOffset); + { + // The reader handles an overrun gracefully, but we + // pre-check to keep our known position consistent + m_sampleOffset = std::min(sampleOffset, m_sampleCount); + m_reader->seek(m_sampleOffset); + } } @@ -217,10 +245,11 @@ void InputSoundFile::seek(Time timeOffset) //////////////////////////////////////////////////////////// Uint64 InputSoundFile::read(Int16* samples, Uint64 maxCount) { + Uint64 readSamples = 0; if (m_reader && samples && maxCount) - return m_reader->read(samples, maxCount); - else - return 0; + readSamples = m_reader->read(samples, maxCount); + m_sampleOffset += readSamples; + return readSamples; } @@ -238,6 +267,7 @@ void InputSoundFile::close() m_streamOwned = false; } m_stream = NULL; + m_sampleOffset = 0; // Reset the sound file attributes m_sampleCount = 0; diff --git a/src/SFML/Audio/Music.cpp b/src/SFML/Audio/Music.cpp index c40500c76..c920bbe77 100644 --- a/src/SFML/Audio/Music.cpp +++ b/src/SFML/Audio/Music.cpp @@ -36,8 +36,7 @@ namespace sf { //////////////////////////////////////////////////////////// Music::Music() : -m_file (), -m_duration() +m_file () { } @@ -105,7 +104,7 @@ bool Music::openFromStream(InputStream& stream) //////////////////////////////////////////////////////////// Time Music::getDuration() const { - return m_duration; + return m_file.getDuration(); } @@ -118,8 +117,8 @@ bool Music::onGetData(SoundStream::Chunk& data) data.samples = &m_samples[0]; data.sampleCount = static_cast(m_file.read(&m_samples[0], m_samples.size())); - // Check if we have reached the end of the audio file - return data.sampleCount == m_samples.size(); + // Check if we have stopped obtaining samples or reached the end of the audio file + return (data.sampleCount != 0) && (m_file.getSampleOffset() < m_file.getSampleCount()); } @@ -135,9 +134,6 @@ void Music::onSeek(Time timeOffset) //////////////////////////////////////////////////////////// void Music::initialize() { - // Compute the music duration - m_duration = m_file.getDuration(); - // Resize the internal buffer so that it can contain 1 second of audio samples m_samples.resize(m_file.getSampleRate() * m_file.getChannelCount()); diff --git a/src/SFML/Audio/SoundStream.cpp b/src/SFML/Audio/SoundStream.cpp index 901d17496..ae6224933 100644 --- a/src/SFML/Audio/SoundStream.cpp +++ b/src/SFML/Audio/SoundStream.cpp @@ -77,7 +77,9 @@ SoundStream::~SoundStream() void SoundStream::initialize(unsigned int channelCount, unsigned int sampleRate) { m_channelCount = channelCount; - m_sampleRate = sampleRate; + m_sampleRate = sampleRate; + m_samplesProcessed = 0; + m_isStreaming = false; // Deduce the format from the number of channels m_format = priv::AudioDevice::getFormatFromChannelCount(channelCount); @@ -127,11 +129,7 @@ void SoundStream::play() stop(); } - // Move to the beginning - onSeek(Time::Zero); - // Start updating the stream in a separate thread to avoid blocking the application - m_samplesProcessed = 0; m_isStreaming = true; m_threadStartState = Playing; m_thread.launch(); @@ -397,7 +395,7 @@ void SoundStream::streamData() //////////////////////////////////////////////////////////// -bool SoundStream::fillAndPushBuffer(unsigned int bufferNum) +bool SoundStream::fillAndPushBuffer(unsigned int bufferNum, bool immediateLoop) { bool requestStop = false; @@ -417,6 +415,13 @@ bool SoundStream::fillAndPushBuffer(unsigned int bufferNum) // If we previously had no data, try to fill the buffer once again if (!data.samples || (data.sampleCount == 0)) { + // If immediateLoop is specified, we have to immediately adjust the sample count + if (immediateLoop) + { + // We just tried to begin preloading at EOF: reset the sample count + m_samplesProcessed = 0; + m_endBuffers[bufferNum] = false; + } return fillAndPushBuffer(bufferNum); } } @@ -451,7 +456,9 @@ bool SoundStream::fillQueue() bool requestStop = false; for (int i = 0; (i < BufferCount) && !requestStop; ++i) { - if (fillAndPushBuffer(i)) + // Since no sound has been loaded yet, we can't schedule loop seeks preemptively, + // So if we start on EOF or Loop End, we let fillAndPushBuffer() adjust the sample count + if (fillAndPushBuffer(i, (i == 0))) requestStop = true; }