Fixed inconsistent seek behavior in SoundStream
This commit is contained in:
parent
2df9abf341
commit
2207af41e4
@ -32,6 +32,7 @@
|
|||||||
#include <SFML/System/NonCopyable.hpp>
|
#include <SFML/System/NonCopyable.hpp>
|
||||||
#include <SFML/System/Time.hpp>
|
#include <SFML/System/Time.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
||||||
namespace sf
|
namespace sf
|
||||||
@ -146,6 +147,22 @@ public:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
Time getDuration() const;
|
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
|
/// \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
|
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
|
InputStream* m_stream; ///< Input stream used to access the file's data
|
||||||
bool m_streamOwned; ///< Is the stream internal or external?
|
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
|
Uint64 m_sampleCount; ///< Total number of samples in the file
|
||||||
unsigned int m_channelCount; ///< Number of channels of the sound
|
unsigned int m_channelCount; ///< Number of channels of the sound
|
||||||
unsigned int m_sampleRate; ///< Number of samples per second
|
unsigned int m_sampleRate; ///< Number of samples per second
|
||||||
|
@ -169,7 +169,6 @@ private:
|
|||||||
// Member data
|
// Member data
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
InputSoundFile m_file; ///< The streamed music file
|
InputSoundFile m_file; ///< The streamed music file
|
||||||
Time m_duration; ///< Music duration
|
|
||||||
std::vector<Int16> m_samples; ///< Temporary buffer of samples
|
std::vector<Int16> m_samples; ///< Temporary buffer of samples
|
||||||
Mutex m_mutex; ///< Mutex protecting the data
|
Mutex m_mutex; ///< Mutex protecting the data
|
||||||
};
|
};
|
||||||
|
@ -254,11 +254,12 @@ private:
|
|||||||
/// playing queue.
|
/// playing queue.
|
||||||
///
|
///
|
||||||
/// \param bufferNum Number of the buffer to fill (in [0, BufferCount])
|
/// \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
|
/// \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
|
/// \brief Fill the audio buffers and put them all into the playing queue
|
||||||
|
@ -41,6 +41,7 @@ InputSoundFile::InputSoundFile() :
|
|||||||
m_reader (NULL),
|
m_reader (NULL),
|
||||||
m_stream (NULL),
|
m_stream (NULL),
|
||||||
m_streamOwned (false),
|
m_streamOwned (false),
|
||||||
|
m_sampleOffset (0),
|
||||||
m_sampleCount (0),
|
m_sampleCount (0),
|
||||||
m_channelCount(0),
|
m_channelCount(0),
|
||||||
m_sampleRate (0)
|
m_sampleRate (0)
|
||||||
@ -195,15 +196,42 @@ unsigned int InputSoundFile::getSampleRate() const
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
Time InputSoundFile::getDuration() 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<float>(m_sampleCount) / m_channelCount / m_sampleRate);
|
return seconds(static_cast<float>(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<float>(m_sampleOffset) / m_channelCount / m_sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
Uint64 InputSoundFile::getSampleOffset() const
|
||||||
|
{
|
||||||
|
return m_sampleOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void InputSoundFile::seek(Uint64 sampleOffset)
|
void InputSoundFile::seek(Uint64 sampleOffset)
|
||||||
{
|
{
|
||||||
if (m_reader)
|
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 InputSoundFile::read(Int16* samples, Uint64 maxCount)
|
||||||
{
|
{
|
||||||
|
Uint64 readSamples = 0;
|
||||||
if (m_reader && samples && maxCount)
|
if (m_reader && samples && maxCount)
|
||||||
return m_reader->read(samples, maxCount);
|
readSamples = m_reader->read(samples, maxCount);
|
||||||
else
|
m_sampleOffset += readSamples;
|
||||||
return 0;
|
return readSamples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -238,6 +267,7 @@ void InputSoundFile::close()
|
|||||||
m_streamOwned = false;
|
m_streamOwned = false;
|
||||||
}
|
}
|
||||||
m_stream = NULL;
|
m_stream = NULL;
|
||||||
|
m_sampleOffset = 0;
|
||||||
|
|
||||||
// Reset the sound file attributes
|
// Reset the sound file attributes
|
||||||
m_sampleCount = 0;
|
m_sampleCount = 0;
|
||||||
|
@ -36,8 +36,7 @@ namespace sf
|
|||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
Music::Music() :
|
Music::Music() :
|
||||||
m_file (),
|
m_file ()
|
||||||
m_duration()
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -105,7 +104,7 @@ bool Music::openFromStream(InputStream& stream)
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
Time Music::getDuration() const
|
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.samples = &m_samples[0];
|
||||||
data.sampleCount = static_cast<std::size_t>(m_file.read(&m_samples[0], m_samples.size()));
|
data.sampleCount = static_cast<std::size_t>(m_file.read(&m_samples[0], m_samples.size()));
|
||||||
|
|
||||||
// Check if we have reached the end of the audio file
|
// Check if we have stopped obtaining samples or reached the end of the audio file
|
||||||
return data.sampleCount == m_samples.size();
|
return (data.sampleCount != 0) && (m_file.getSampleOffset() < m_file.getSampleCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -135,9 +134,6 @@ void Music::onSeek(Time timeOffset)
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void Music::initialize()
|
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
|
// Resize the internal buffer so that it can contain 1 second of audio samples
|
||||||
m_samples.resize(m_file.getSampleRate() * m_file.getChannelCount());
|
m_samples.resize(m_file.getSampleRate() * m_file.getChannelCount());
|
||||||
|
|
||||||
|
@ -78,6 +78,8 @@ void SoundStream::initialize(unsigned int channelCount, unsigned int sampleRate)
|
|||||||
{
|
{
|
||||||
m_channelCount = channelCount;
|
m_channelCount = channelCount;
|
||||||
m_sampleRate = sampleRate;
|
m_sampleRate = sampleRate;
|
||||||
|
m_samplesProcessed = 0;
|
||||||
|
m_isStreaming = false;
|
||||||
|
|
||||||
// Deduce the format from the number of channels
|
// Deduce the format from the number of channels
|
||||||
m_format = priv::AudioDevice::getFormatFromChannelCount(channelCount);
|
m_format = priv::AudioDevice::getFormatFromChannelCount(channelCount);
|
||||||
@ -127,11 +129,7 @@ void SoundStream::play()
|
|||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move to the beginning
|
|
||||||
onSeek(Time::Zero);
|
|
||||||
|
|
||||||
// Start updating the stream in a separate thread to avoid blocking the application
|
// Start updating the stream in a separate thread to avoid blocking the application
|
||||||
m_samplesProcessed = 0;
|
|
||||||
m_isStreaming = true;
|
m_isStreaming = true;
|
||||||
m_threadStartState = Playing;
|
m_threadStartState = Playing;
|
||||||
m_thread.launch();
|
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;
|
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 we previously had no data, try to fill the buffer once again
|
||||||
if (!data.samples || (data.sampleCount == 0))
|
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);
|
return fillAndPushBuffer(bufferNum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -451,7 +456,9 @@ bool SoundStream::fillQueue()
|
|||||||
bool requestStop = false;
|
bool requestStop = false;
|
||||||
for (int i = 0; (i < BufferCount) && !requestStop; ++i)
|
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;
|
requestStop = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user