Remove default empty state of sf::SoundBuffer

This commit is contained in:
Chris Thrasher 2024-05-18 15:37:06 -06:00
parent 1a40f01957
commit add6422e6b
9 changed files with 87 additions and 100 deletions

View File

@ -13,9 +13,7 @@
void playSound()
{
// Load a sound buffer from a wav file
sf::SoundBuffer buffer;
if (!buffer.loadFromFile("resources/killdeer.wav"))
return;
const auto buffer = sf::SoundBuffer::loadFromFile("resources/killdeer.wav").value();
// Display sound information
std::cout << "killdeer.wav:" << '\n'

View File

@ -49,9 +49,7 @@ int main()
window.setVerticalSyncEnabled(true);
// Load the sounds used in the game
sf::SoundBuffer ballSoundBuffer;
if (!ballSoundBuffer.loadFromFile(resourcesDir() / "ball.wav"))
return EXIT_FAILURE;
const auto ballSoundBuffer = sf::SoundBuffer::loadFromFile(resourcesDir() / "ball.wav").value();
sf::Sound ballSound(ballSoundBuffer);
// Create the SFML logo texture:

View File

@ -274,12 +274,7 @@ private:
///
/// Usage example:
/// \code
/// sf::SoundBuffer buffer;
/// if (!buffer.loadFromFile("sound.wav"))
/// {
/// // Handle error...
/// }
///
/// const auto buffer = sf::SoundBuffer::loadFromFile("sound.wav").value();
/// sf::Sound sound(buffer);
/// sound.play();
/// \endcode

View File

@ -34,6 +34,7 @@
#include <SFML/System/Time.hpp>
#include <filesystem>
#include <optional>
#include <unordered_set>
#include <vector>
@ -54,12 +55,6 @@ class InputStream;
class SFML_AUDIO_API SoundBuffer
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
SoundBuffer() = default;
////////////////////////////////////////////////////////////
/// \brief Copy constructor
///
@ -82,12 +77,12 @@ public:
///
/// \param filename Path of the sound file to load
///
/// \return True if loading succeeded, false if it failed
/// \return Sound buffer if loading succeeded, `std::nullopt` if it failed
///
/// \see loadFromMemory, loadFromStream, loadFromSamples, saveToFile
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromFile(const std::filesystem::path& filename);
[[nodiscard]] static std::optional<SoundBuffer> loadFromFile(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Load the sound buffer from a file in memory
@ -98,12 +93,12 @@ public:
/// \param data Pointer to the file data in memory
/// \param sizeInBytes Size of the data to load, in bytes
///
/// \return True if loading succeeded, false if it failed
/// \return Sound buffer if loading succeeded, `std::nullopt` if it failed
///
/// \see loadFromFile, loadFromStream, loadFromSamples
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromMemory(const void* data, std::size_t sizeInBytes);
[[nodiscard]] static std::optional<SoundBuffer> loadFromMemory(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Load the sound buffer from a custom stream
@ -113,12 +108,12 @@ public:
///
/// \param stream Source stream to read from
///
/// \return True if loading succeeded, false if it failed
/// \return Sound buffer if loading succeeded, `std::nullopt` if it failed
///
/// \see loadFromFile, loadFromMemory, loadFromSamples
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromStream(InputStream& stream);
[[nodiscard]] static std::optional<SoundBuffer> loadFromStream(InputStream& stream);
////////////////////////////////////////////////////////////
/// \brief Load the sound buffer from an array of audio samples
@ -131,12 +126,13 @@ public:
/// \param sampleRate Sample rate (number of samples to play per second)
/// \param channelMap Map of position in sample frame to sound channel
///
/// \return True if loading succeeded, false if it failed
/// \return Sound buffer if loading succeeded, `std::nullopt` if it failed
///
/// \see loadFromFile, loadFromMemory, saveToFile
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromSamples(const std::int16_t* samples,
[[nodiscard]] static std::optional<SoundBuffer> loadFromSamples(
const std::int16_t* samples,
std::uint64_t sampleCount,
unsigned int channelCount,
unsigned int sampleRate,
@ -247,6 +243,12 @@ public:
private:
friend class Sound;
////////////////////////////////////////////////////////////
/// \brief Construct from vector of samples
///
////////////////////////////////////////////////////////////
explicit SoundBuffer(std::vector<std::int16_t>&& samples);
////////////////////////////////////////////////////////////
/// \brief Initialize the internal state after loading a new sound
///
@ -255,7 +257,7 @@ private:
/// \return True on successful initialization, false on failure
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool initialize(InputSoundFile& file);
[[nodiscard]] static std::optional<SoundBuffer> initialize(InputSoundFile& file);
////////////////////////////////////////////////////////////
/// \brief Update the internal buffer with the cached audio samples
@ -341,14 +343,8 @@ private:
///
/// Usage example:
/// \code
/// // Declare a new sound buffer
/// sf::SoundBuffer buffer;
///
/// // Load it from a file
/// if (!buffer.loadFromFile("sound.wav"))
/// {
/// // error...
/// }
/// // Load a new sound buffer from a file
/// const auto buffer = sf::SoundBuffer::loadFromFile("sound.wav").value();
///
/// // Create a sound source bound to the buffer
/// sf::Sound sound1(buffer);

View File

@ -98,7 +98,7 @@ private:
// Member data
////////////////////////////////////////////////////////////
std::vector<std::int16_t> m_samples; //!< Temporary sample buffer to hold the recorded data
SoundBuffer m_buffer; //!< Sound buffer that will contain the recorded data
std::optional<SoundBuffer> m_buffer; //!< Sound buffer that will contain the recorded data
};
} // namespace sf

View File

@ -68,40 +68,41 @@ SoundBuffer::~SoundBuffer()
////////////////////////////////////////////////////////////
bool SoundBuffer::loadFromFile(const std::filesystem::path& filename)
std::optional<SoundBuffer> SoundBuffer::loadFromFile(const std::filesystem::path& filename)
{
InputSoundFile file;
if (file.openFromFile(filename))
return initialize(file);
else
return false;
return std::nullopt;
}
////////////////////////////////////////////////////////////
bool SoundBuffer::loadFromMemory(const void* data, std::size_t sizeInBytes)
std::optional<SoundBuffer> SoundBuffer::loadFromMemory(const void* data, std::size_t sizeInBytes)
{
InputSoundFile file;
if (file.openFromMemory(data, sizeInBytes))
return initialize(file);
else
return false;
return std::nullopt;
}
////////////////////////////////////////////////////////////
bool SoundBuffer::loadFromStream(InputStream& stream)
std::optional<SoundBuffer> SoundBuffer::loadFromStream(InputStream& stream)
{
InputSoundFile file;
if (file.openFromStream(stream))
return initialize(file);
else
return false;
return std::nullopt;
}
////////////////////////////////////////////////////////////
bool SoundBuffer::loadFromSamples(const std::int16_t* samples,
std::optional<SoundBuffer> SoundBuffer::loadFromSamples(
const std::int16_t* samples,
std::uint64_t sampleCount,
unsigned int channelCount,
unsigned int sampleRate,
@ -110,10 +111,12 @@ bool SoundBuffer::loadFromSamples(const std::int16_t* samples,
if (samples && sampleCount && channelCount && sampleRate && !channelMap.empty())
{
// Copy the new audio samples
m_samples.assign(samples, samples + sampleCount);
SoundBuffer soundBuffer(std::vector<std::int16_t>(samples, samples + sampleCount));
// Update the internal buffer with the new samples
return update(channelCount, sampleRate, channelMap);
if (!soundBuffer.update(channelCount, sampleRate, channelMap))
return std::nullopt;
return soundBuffer;
}
else
{
@ -124,7 +127,7 @@ bool SoundBuffer::loadFromSamples(const std::int16_t* samples,
<< "channels: " << channelCount << ", "
<< "samplerate: " << sampleRate << ")" << std::endl;
return false;
return std::nullopt;
}
}
@ -206,21 +209,30 @@ SoundBuffer& SoundBuffer::operator=(const SoundBuffer& right)
////////////////////////////////////////////////////////////
bool SoundBuffer::initialize(InputSoundFile& file)
SoundBuffer::SoundBuffer(std::vector<std::int16_t>&& samples) : m_samples(std::move(samples))
{
}
////////////////////////////////////////////////////////////
std::optional<SoundBuffer> SoundBuffer::initialize(InputSoundFile& file)
{
// Retrieve the sound parameters
const std::uint64_t sampleCount = file.getSampleCount();
// Read the samples from the provided file
m_samples.resize(static_cast<std::size_t>(sampleCount));
if (file.read(m_samples.data(), sampleCount) == sampleCount)
std::vector<std::int16_t> samples(static_cast<std::size_t>(sampleCount));
if (file.read(samples.data(), sampleCount) == sampleCount)
{
// Update the internal buffer with the new samples
return update(file.getChannelCount(), file.getSampleRate(), file.getChannelMap());
SoundBuffer soundBuffer(std::move(samples));
if (!soundBuffer.update(file.getChannelCount(), file.getSampleRate(), file.getChannelMap()))
return std::nullopt;
return soundBuffer;
}
else
{
return false;
return std::nullopt;
}
}

View File

@ -48,7 +48,7 @@ SoundBufferRecorder::~SoundBufferRecorder()
bool SoundBufferRecorder::onStart()
{
m_samples.clear();
m_buffer = SoundBuffer();
m_buffer.reset();
return true;
}
@ -69,7 +69,12 @@ void SoundBufferRecorder::onStop()
if (m_samples.empty())
return;
if (!m_buffer.loadFromSamples(m_samples.data(), m_samples.size(), getChannelCount(), getSampleRate(), getChannelMap()))
m_buffer = sf::SoundBuffer::loadFromSamples(m_samples.data(),
m_samples.size(),
getChannelCount(),
getSampleRate(),
getChannelMap());
if (!m_buffer)
err() << "Failed to stop capturing audio data" << std::endl;
}
@ -77,7 +82,8 @@ void SoundBufferRecorder::onStop()
////////////////////////////////////////////////////////////
const SoundBuffer& SoundBufferRecorder::getBuffer() const
{
return m_buffer;
assert(m_buffer && "SoundBufferRecorder::getBuffer() Cannot return reference to null buffer");
return *m_buffer;
}
} // namespace sf

View File

@ -25,8 +25,7 @@ TEST_CASE("[Audio] sf::Sound", runAudioDeviceTests())
STATIC_CHECK(std::has_virtual_destructor_v<sf::Sound>);
}
sf::SoundBuffer soundBuffer;
REQUIRE(soundBuffer.loadFromFile("Audio/ding.flac"));
const auto soundBuffer = sf::SoundBuffer::loadFromFile("Audio/ding.flac").value();
SECTION("Construction")
{
@ -52,8 +51,8 @@ TEST_CASE("[Audio] sf::Sound", runAudioDeviceTests())
SECTION("Assignment")
{
const sf::SoundBuffer emptySoundBuffer;
sf::Sound soundCopy(emptySoundBuffer);
const sf::SoundBuffer otherSoundBuffer = sf::SoundBuffer::loadFromFile("Audio/ding.flac").value();
sf::Sound soundCopy(otherSoundBuffer);
soundCopy = sound;
CHECK(&soundCopy.getBuffer() == &soundBuffer);
CHECK(!soundCopy.getLoop());
@ -64,7 +63,7 @@ TEST_CASE("[Audio] sf::Sound", runAudioDeviceTests())
SECTION("Set/get buffer")
{
const sf::SoundBuffer otherSoundBuffer;
const sf::SoundBuffer otherSoundBuffer = sf::SoundBuffer::loadFromFile("Audio/ding.flac").value();
sf::Sound sound(soundBuffer);
sound.setBuffer(otherSoundBuffer);
CHECK(&sound.getBuffer() == &otherSoundBuffer);

View File

@ -14,6 +14,7 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
{
SECTION("Type traits")
{
STATIC_CHECK(!std::is_default_constructible_v<sf::SoundBuffer>);
STATIC_CHECK(std::is_copy_constructible_v<sf::SoundBuffer>);
STATIC_CHECK(std::is_copy_assignable_v<sf::SoundBuffer>);
STATIC_CHECK(std::is_move_constructible_v<sf::SoundBuffer>);
@ -22,20 +23,9 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
STATIC_CHECK(!std::is_nothrow_move_assignable_v<sf::SoundBuffer>);
}
SECTION("Construction")
{
const sf::SoundBuffer soundBuffer;
CHECK(soundBuffer.getSamples() == nullptr);
CHECK(soundBuffer.getSampleCount() == 0);
CHECK(soundBuffer.getSampleRate() == 44100);
CHECK(soundBuffer.getChannelCount() == 1);
CHECK(soundBuffer.getDuration() == sf::Time::Zero);
}
SECTION("Copy semantics")
{
sf::SoundBuffer soundBuffer;
REQUIRE(soundBuffer.loadFromFile("Audio/ding.flac"));
const auto soundBuffer = sf::SoundBuffer::loadFromFile("Audio/ding.flac").value();
SECTION("Construction")
{
@ -49,7 +39,7 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
SECTION("Assignment")
{
sf::SoundBuffer soundBufferCopy;
sf::SoundBuffer soundBufferCopy = sf::SoundBuffer::loadFromFile("Audio/doodle_pop.ogg").value();
soundBufferCopy = soundBuffer;
CHECK(soundBufferCopy.getSamples() != nullptr);
CHECK(soundBufferCopy.getSampleCount() == 87798);
@ -61,16 +51,14 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
SECTION("loadFromFile()")
{
sf::SoundBuffer soundBuffer;
SECTION("Invalid filename")
{
CHECK(!soundBuffer.loadFromFile("does/not/exist.wav"));
CHECK(!sf::SoundBuffer::loadFromFile("does/not/exist.wav"));
}
SECTION("Valid file")
{
REQUIRE(soundBuffer.loadFromFile("Audio/ding.flac"));
const auto soundBuffer = sf::SoundBuffer::loadFromFile("Audio/ding.flac").value();
CHECK(soundBuffer.getSamples() != nullptr);
CHECK(soundBuffer.getSampleCount() == 87798);
CHECK(soundBuffer.getSampleRate() == 44100);
@ -81,19 +69,17 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
SECTION("loadFromMemory()")
{
sf::SoundBuffer soundBuffer;
SECTION("Invalid memory")
{
CHECK(!soundBuffer.loadFromMemory(nullptr, 0));
CHECK(!sf::SoundBuffer::loadFromMemory(nullptr, 0));
constexpr std::array<std::byte, 5> memory{};
CHECK(!soundBuffer.loadFromMemory(memory.data(), memory.size()));
CHECK(!sf::SoundBuffer::loadFromMemory(memory.data(), memory.size()));
}
SECTION("Valid memory")
{
const auto memory = loadIntoMemory("Audio/ding.flac");
REQUIRE(soundBuffer.loadFromMemory(memory.data(), memory.size()));
const auto soundBuffer = sf::SoundBuffer::loadFromMemory(memory.data(), memory.size()).value();
CHECK(soundBuffer.getSamples() != nullptr);
CHECK(soundBuffer.getSampleCount() == 87798);
CHECK(soundBuffer.getSampleRate() == 44100);
@ -105,17 +91,16 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
SECTION("loadFromStream()")
{
sf::FileInputStream stream;
sf::SoundBuffer soundBuffer;
SECTION("Invalid stream")
{
CHECK(!soundBuffer.loadFromStream(stream));
CHECK(!sf::SoundBuffer::loadFromStream(stream));
}
SECTION("Valid stream")
{
REQUIRE(stream.open("Audio/ding.flac"));
REQUIRE(soundBuffer.loadFromStream(stream));
const auto soundBuffer = sf::SoundBuffer::loadFromStream(stream).value();
CHECK(soundBuffer.getSamples() != nullptr);
CHECK(soundBuffer.getSampleCount() == 87798);
CHECK(soundBuffer.getSampleRate() == 44100);
@ -129,13 +114,11 @@ TEST_CASE("[Audio] sf::SoundBuffer", runAudioDeviceTests())
const auto filename = std::filesystem::temp_directory_path() / "ding.flac";
{
sf::SoundBuffer soundBuffer;
REQUIRE(soundBuffer.loadFromFile("Audio/ding.flac"));
const auto soundBuffer = sf::SoundBuffer::loadFromFile("Audio/ding.flac").value();
REQUIRE(soundBuffer.saveToFile(filename));
}
sf::SoundBuffer soundBuffer;
REQUIRE(soundBuffer.loadFromFile(filename));
const auto soundBuffer = sf::SoundBuffer::loadFromFile(filename).value();
CHECK(soundBuffer.getSamples() != nullptr);
CHECK(soundBuffer.getSampleCount() == 87798);
CHECK(soundBuffer.getSampleRate() == 44100);