Remove default empty state of sf::InputSoundFile and sf::OutputSoundFile

This commit is contained in:
Chris Thrasher 2024-05-20 19:42:43 -06:00
parent 53ade4baf1
commit e9fadbbcb3
11 changed files with 167 additions and 194 deletions

View File

@ -33,6 +33,7 @@
#include <filesystem>
#include <memory>
#include <optional>
#include <vector>
#include <cstddef>
@ -64,10 +65,10 @@ public:
///
/// \param filename Path of the sound file to load
///
/// \return True if the file was successfully opened
/// \return Input sound file if the file was successfully opened, otherwise `std::nullopt`
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool openFromFile(const std::filesystem::path& filename);
[[nodiscard]] static std::optional<InputSoundFile> openFromFile(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Open a sound file in memory for reading
@ -78,10 +79,10 @@ public:
/// \param data Pointer to the file data in memory
/// \param sizeInBytes Size of the data to load, in bytes
///
/// \return True if the file was successfully opened
/// \return Input sound file if the file was successfully opened, otherwise `std::nullopt`
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool openFromMemory(const void* data, std::size_t sizeInBytes);
[[nodiscard]] static std::optional<InputSoundFile> openFromMemory(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Open a sound file from a custom stream for reading
@ -91,10 +92,10 @@ public:
///
/// \param stream Source stream to read from
///
/// \return True if the file was successfully opened
/// \return Input sound file if the file was successfully opened, otherwise `std::nullopt`
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool openFromStream(InputStream& stream);
[[nodiscard]] static std::optional<InputSoundFile> openFromStream(InputStream& stream);
////////////////////////////////////////////////////////////
/// \brief Get the total number of audio samples in the file
@ -211,6 +212,14 @@ public:
void close();
private:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Useful for implementing close()
///
////////////////////////////////////////////////////////////
InputSoundFile() = default;
////////////////////////////////////////////////////////////
/// \brief Deleter for input streams that only conditionally deletes
///
@ -228,6 +237,16 @@ private:
bool owned{true};
};
////////////////////////////////////////////////////////////
/// \brief Constructor from reader, stream, and attributes
///
////////////////////////////////////////////////////////////
InputSoundFile(std::unique_ptr<SoundFileReader>&& reader,
std::unique_ptr<InputStream, StreamDeleter>&& stream,
std::uint64_t sampleCount,
unsigned int sampleRate,
std::vector<SoundChannel>&& channelMap);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////

View File

@ -248,10 +248,10 @@ private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
InputSoundFile m_file; //!< The streamed music file
std::vector<std::int16_t> m_samples; //!< Temporary buffer of samples
std::recursive_mutex m_mutex; //!< Mutex protecting the data
Span<std::uint64_t> m_loopSpan; //!< Loop Range Specifier
std::optional<InputSoundFile> m_file; //!< The streamed music file
std::vector<std::int16_t> m_samples; //!< Temporary buffer of samples
std::recursive_mutex m_mutex; //!< Mutex protecting the data
Span<std::uint64_t> m_loopSpan; //!< Loop Range Specifier
};
} // namespace sf

View File

@ -34,6 +34,7 @@
#include <filesystem>
#include <memory>
#include <optional>
#include <vector>
#include <cstdint>
@ -58,13 +59,14 @@ public:
/// \param channelCount Number of channels in the sound
/// \param channelMap Map of position in sample frame to sound channel
///
/// \return True if the file was successfully opened
/// \return Output sound file if the file was successfully opened
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool openFromFile(const std::filesystem::path& filename,
unsigned int sampleRate,
unsigned int channelCount,
const std::vector<SoundChannel>& channelMap);
[[nodiscard]] static std::optional<OutputSoundFile> openFromFile(
const std::filesystem::path& filename,
unsigned int sampleRate,
unsigned int channelCount,
const std::vector<SoundChannel>& channelMap);
////////////////////////////////////////////////////////////
/// \brief Write audio samples to the file
@ -82,6 +84,12 @@ public:
void close();
private:
////////////////////////////////////////////////////////////
/// \brief Constructor from writer
///
////////////////////////////////////////////////////////////
explicit OutputSoundFile(std::unique_ptr<SoundFileWriter>&& writer);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////

View File

@ -65,51 +65,36 @@ void InputSoundFile::StreamDeleter::operator()(InputStream* ptr) const
////////////////////////////////////////////////////////////
bool InputSoundFile::openFromFile(const std::filesystem::path& filename)
std::optional<InputSoundFile> InputSoundFile::openFromFile(const std::filesystem::path& filename)
{
// If the file is already open, first close it
close();
// Find a suitable reader for the file type
auto reader = SoundFileFactory::createReaderFromFilename(filename);
if (!reader)
return false;
return std::nullopt;
// Wrap the file into a stream
auto file = std::make_unique<FileInputStream>();
// Open it
if (!file->open(filename))
return false;
return std::nullopt;
// Pass the stream to the reader
const auto info = reader->open(*file);
auto info = reader->open(*file);
if (!info)
return false;
return std::nullopt;
// Take ownership of successfully opened reader and stream
m_reader = std::move(reader);
m_stream = std::move(file);
// Retrieve the attributes of the open sound file
m_sampleCount = info->sampleCount;
m_sampleRate = info->sampleRate;
m_channelMap = info->channelMap;
return true;
return InputSoundFile(std::move(reader), std::move(file), info->sampleCount, info->sampleRate, std::move(info->channelMap));
}
////////////////////////////////////////////////////////////
bool InputSoundFile::openFromMemory(const void* data, std::size_t sizeInBytes)
std::optional<InputSoundFile> InputSoundFile::openFromMemory(const void* data, std::size_t sizeInBytes)
{
// If the file is already open, first close it
close();
// Find a suitable reader for the file type
auto reader = SoundFileFactory::createReaderFromMemory(data, sizeInBytes);
if (!reader)
return false;
return std::nullopt;
// Wrap the memory file into a stream
auto memory = std::make_unique<MemoryInputStream>();
@ -118,56 +103,35 @@ bool InputSoundFile::openFromMemory(const void* data, std::size_t sizeInBytes)
memory->open(data, sizeInBytes);
// Pass the stream to the reader
const auto info = reader->open(*memory);
auto info = reader->open(*memory);
if (!info)
return false;
return std::nullopt;
// Take ownership of successfully opened reader and stream
m_reader = std::move(reader);
m_stream = std::move(memory);
// Retrieve the attributes of the open sound file
m_sampleCount = info->sampleCount;
m_sampleRate = info->sampleRate;
m_channelMap = info->channelMap;
return true;
return InputSoundFile(std::move(reader), std::move(memory), info->sampleCount, info->sampleRate, std::move(info->channelMap));
}
////////////////////////////////////////////////////////////
bool InputSoundFile::openFromStream(InputStream& stream)
std::optional<InputSoundFile> InputSoundFile::openFromStream(InputStream& stream)
{
// If the file is already open, first close it
close();
// Find a suitable reader for the file type
auto reader = SoundFileFactory::createReaderFromStream(stream);
if (!reader)
return false;
return std::nullopt;
// Don't forget to reset the stream to its beginning before re-opening it
if (stream.seek(0) != 0)
{
err() << "Failed to open sound file from stream (cannot restart stream)" << std::endl;
return false;
return std::nullopt;
}
// Pass the stream to the reader
const auto info = reader->open(stream);
auto info = reader->open(stream);
if (!info)
return false;
return std::nullopt;
// Take ownership of reader and store a reference to the stream without taking ownership
m_reader = std::move(reader);
m_stream = {&stream, false};
// Retrieve the attributes of the open sound file
m_sampleCount = info->sampleCount;
m_sampleRate = info->sampleRate;
m_channelMap = info->channelMap;
return true;
return InputSoundFile(std::move(reader), {&stream, false}, info->sampleCount, info->sampleRate, std::move(info->channelMap));
}
@ -268,4 +232,19 @@ void InputSoundFile::close()
m_channelMap.clear();
}
////////////////////////////////////////////////////////////
InputSoundFile::InputSoundFile(std::unique_ptr<SoundFileReader>&& reader,
std::unique_ptr<InputStream, StreamDeleter>&& stream,
std::uint64_t sampleCount,
unsigned int sampleRate,
std::vector<SoundChannel>&& channelMap) :
m_reader(std::move(reader)),
m_stream(std::move(stream)),
m_sampleCount(sampleCount),
m_sampleRate(sampleRate),
m_channelMap(std::move(channelMap))
{
}
} // namespace sf

View File

@ -34,6 +34,8 @@
#include <mutex>
#include <ostream>
#include <cassert>
namespace sf
{
@ -52,7 +54,8 @@ bool Music::openFromFile(const std::filesystem::path& filename)
stop();
// Open the underlying sound file
if (!m_file.openFromFile(filename))
m_file = sf::InputSoundFile::openFromFile(filename);
if (!m_file)
return false;
// Perform common initializations
@ -69,7 +72,8 @@ bool Music::openFromMemory(const void* data, std::size_t sizeInBytes)
stop();
// Open the underlying sound file
if (!m_file.openFromMemory(data, sizeInBytes))
m_file = sf::InputSoundFile::openFromMemory(data, sizeInBytes);
if (!m_file)
return false;
// Perform common initializations
@ -86,7 +90,8 @@ bool Music::openFromStream(InputStream& stream)
stop();
// Open the underlying sound file
if (!m_file.openFromStream(stream))
m_file = sf::InputSoundFile::openFromStream(stream);
if (!m_file)
return false;
// Perform common initializations
@ -99,7 +104,8 @@ bool Music::openFromStream(InputStream& stream)
////////////////////////////////////////////////////////////
Time Music::getDuration() const
{
return m_file.getDuration();
assert(m_file && "Music::getDuration() Cannot get duration until music is opened");
return m_file->getDuration();
}
@ -116,7 +122,8 @@ void Music::setLoopPoints(TimeSpan timePoints)
Span<std::uint64_t> samplePoints{timeToSamples(timePoints.offset), timeToSamples(timePoints.length)};
// Check our state. This averts a divide-by-zero. GetChannelCount() is cheap enough to use often
if (getChannelCount() == 0 || m_file.getSampleCount() == 0)
assert(m_file && "Music::setLoopPoints() Cannot set loop points unit music is opened");
if (getChannelCount() == 0 || m_file->getSampleCount() == 0)
{
err() << "Music is not in a valid state to assign Loop Points." << std::endl;
return;
@ -129,7 +136,7 @@ void Music::setLoopPoints(TimeSpan timePoints)
samplePoints.length -= (samplePoints.length % getChannelCount());
// Validate
if (samplePoints.offset >= m_file.getSampleCount())
if (samplePoints.offset >= m_file->getSampleCount())
{
err() << "LoopPoints offset val must be in range [0, Duration)." << std::endl;
return;
@ -141,7 +148,7 @@ void Music::setLoopPoints(TimeSpan timePoints)
}
// Clamp End Point
samplePoints.length = std::min(samplePoints.length, m_file.getSampleCount() - samplePoints.offset);
samplePoints.length = std::min(samplePoints.length, m_file->getSampleCount() - samplePoints.offset);
// If this change has no effect, we can return without touching anything
if (samplePoints.offset == m_loopSpan.offset && samplePoints.length == m_loopSpan.length)
@ -172,10 +179,12 @@ void Music::setLoopPoints(TimeSpan timePoints)
////////////////////////////////////////////////////////////
bool Music::onGetData(SoundStream::Chunk& data)
{
assert(m_file && "Music::onGetData() Cannot perform operation until music is opened");
const std::lock_guard lock(m_mutex);
std::size_t toFill = m_samples.size();
std::uint64_t currentOffset = m_file.getSampleOffset();
std::uint64_t currentOffset = m_file->getSampleOffset();
const std::uint64_t loopEnd = m_loopSpan.offset + m_loopSpan.length;
// If the loop end is enabled and imminent, request less data.
@ -186,11 +195,11 @@ bool Music::onGetData(SoundStream::Chunk& data)
// Fill the chunk parameters
data.samples = m_samples.data();
data.sampleCount = static_cast<std::size_t>(m_file.read(m_samples.data(), toFill));
data.sampleCount = static_cast<std::size_t>(m_file->read(m_samples.data(), toFill));
currentOffset += data.sampleCount;
// Check if we have stopped obtaining samples or reached either the EOF or the loop end point
return (data.sampleCount != 0) && (currentOffset < m_file.getSampleCount()) &&
return (data.sampleCount != 0) && (currentOffset < m_file->getSampleCount()) &&
(currentOffset != loopEnd || m_loopSpan.length == 0);
}
@ -198,28 +207,32 @@ bool Music::onGetData(SoundStream::Chunk& data)
////////////////////////////////////////////////////////////
void Music::onSeek(Time timeOffset)
{
assert(m_file && "Music::onSeek() Cannot perform operation until music is opened");
const std::lock_guard lock(m_mutex);
m_file.seek(timeOffset);
m_file->seek(timeOffset);
}
////////////////////////////////////////////////////////////
std::optional<std::uint64_t> Music::onLoop()
{
assert(m_file && "Music::onLoop() Cannot perform operation until music is opened");
// Called by underlying SoundStream so we can determine where to loop.
const std::lock_guard lock(m_mutex);
const std::uint64_t currentOffset = m_file.getSampleOffset();
const std::uint64_t currentOffset = m_file->getSampleOffset();
if (getLoop() && (m_loopSpan.length != 0) && (currentOffset == m_loopSpan.offset + m_loopSpan.length))
{
// Looping is enabled, and either we're at the loop end, or we're at the EOF
// when it's equivalent to the loop end (loop end takes priority). Send us to loop begin
m_file.seek(m_loopSpan.offset);
return static_cast<std::int64_t>(m_file.getSampleOffset());
m_file->seek(m_loopSpan.offset);
return static_cast<std::int64_t>(m_file->getSampleOffset());
}
else if (getLoop() && (currentOffset >= m_file.getSampleCount()))
else if (getLoop() && (currentOffset >= m_file->getSampleCount()))
{
// If we're at the EOF, reset to 0
m_file.seek(0);
m_file->seek(0);
return 0;
}
return std::nullopt;
@ -231,13 +244,13 @@ void Music::initialize()
{
// Compute the music positions
m_loopSpan.offset = 0;
m_loopSpan.length = m_file.getSampleCount();
m_loopSpan.length = m_file->getSampleCount();
// 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());
// Initialize the stream
SoundStream::initialize(m_file.getChannelCount(), m_file.getSampleRate(), m_file.getChannelMap());
SoundStream::initialize(m_file->getChannelCount(), m_file->getSampleRate(), m_file->getChannelMap());
}
////////////////////////////////////////////////////////////

View File

@ -33,27 +33,24 @@
namespace sf
{
////////////////////////////////////////////////////////////
bool OutputSoundFile::openFromFile(const std::filesystem::path& filename,
unsigned int sampleRate,
unsigned int channelCount,
const std::vector<SoundChannel>& channelMap)
std::optional<OutputSoundFile> OutputSoundFile::openFromFile(
const std::filesystem::path& filename,
unsigned int sampleRate,
unsigned int channelCount,
const std::vector<SoundChannel>& channelMap)
{
// If the file is already open, first close it
close();
// Find a suitable writer for the file type
m_writer = SoundFileFactory::createWriterFromFilename(filename);
if (!m_writer)
return false;
auto writer = SoundFileFactory::createWriterFromFilename(filename);
if (!writer)
return std::nullopt;
// Pass the stream to the reader
if (!m_writer->open(filename, sampleRate, channelCount, channelMap))
if (!writer->open(filename, sampleRate, channelCount, channelMap))
{
close();
return false;
return std::nullopt;
}
return true;
return OutputSoundFile(std::move(writer));
}
@ -72,4 +69,10 @@ void OutputSoundFile::close()
m_writer.reset();
}
////////////////////////////////////////////////////////////
OutputSoundFile::OutputSoundFile(std::unique_ptr<SoundFileWriter>&& writer) : m_writer(std::move(writer))
{
}
} // namespace sf

View File

@ -70,9 +70,8 @@ SoundBuffer::~SoundBuffer()
////////////////////////////////////////////////////////////
std::optional<SoundBuffer> SoundBuffer::loadFromFile(const std::filesystem::path& filename)
{
InputSoundFile file;
if (file.openFromFile(filename))
return initialize(file);
if (auto file = InputSoundFile::openFromFile(filename))
return initialize(*file);
else
return std::nullopt;
}
@ -81,9 +80,8 @@ std::optional<SoundBuffer> SoundBuffer::loadFromFile(const std::filesystem::path
////////////////////////////////////////////////////////////
std::optional<SoundBuffer> SoundBuffer::loadFromMemory(const void* data, std::size_t sizeInBytes)
{
InputSoundFile file;
if (file.openFromMemory(data, sizeInBytes))
return initialize(file);
if (auto file = InputSoundFile::openFromMemory(data, sizeInBytes))
return initialize(*file);
else
return std::nullopt;
}
@ -92,9 +90,8 @@ std::optional<SoundBuffer> SoundBuffer::loadFromMemory(const void* data, std::si
////////////////////////////////////////////////////////////
std::optional<SoundBuffer> SoundBuffer::loadFromStream(InputStream& stream)
{
InputSoundFile file;
if (file.openFromStream(stream))
return initialize(file);
if (auto file = InputSoundFile::openFromStream(stream))
return initialize(*file);
else
return std::nullopt;
}
@ -136,11 +133,10 @@ std::optional<SoundBuffer> SoundBuffer::loadFromSamples(
bool SoundBuffer::saveToFile(const std::filesystem::path& filename) const
{
// Create the sound file in write mode
OutputSoundFile file;
if (file.openFromFile(filename, getSampleRate(), getChannelCount(), getChannelMap()))
if (auto file = OutputSoundFile::openFromFile(filename, getSampleRate(), getChannelCount(), getChannelMap()))
{
// Write the samples to the opened file
file.write(m_samples.data(), m_samples.size());
file->write(m_samples.data(), m_samples.size());
return true;
}

View File

@ -15,37 +15,25 @@ TEST_CASE("[Audio] sf::InputSoundFile")
{
SECTION("Type traits")
{
STATIC_CHECK(!std::is_default_constructible_v<sf::InputSoundFile>);
STATIC_CHECK(!std::is_copy_constructible_v<sf::InputSoundFile>);
STATIC_CHECK(!std::is_copy_assignable_v<sf::InputSoundFile>);
STATIC_CHECK(std::is_nothrow_move_constructible_v<sf::InputSoundFile>);
STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::InputSoundFile>);
}
SECTION("Construction")
{
const sf::InputSoundFile inputSoundFile;
CHECK(inputSoundFile.getSampleCount() == 0);
CHECK(inputSoundFile.getChannelCount() == 0);
CHECK(inputSoundFile.getSampleRate() == 0);
CHECK(inputSoundFile.getDuration() == sf::Time::Zero);
CHECK(inputSoundFile.getTimeOffset() == sf::Time::Zero);
CHECK(inputSoundFile.getSampleOffset() == 0);
}
SECTION("openFromFile()")
{
sf::InputSoundFile inputSoundFile;
SECTION("Invalid file")
{
CHECK(!inputSoundFile.openFromFile("does/not/exist.wav"));
CHECK(!sf::InputSoundFile::openFromFile("does/not/exist.wav"));
}
SECTION("Valid file")
{
SECTION("flac")
{
REQUIRE(inputSoundFile.openFromFile("Audio/ding.flac"));
const auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.flac").value();
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
@ -56,7 +44,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("mp3")
{
REQUIRE(inputSoundFile.openFromFile("Audio/ding.mp3"));
const auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.mp3").value();
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
@ -67,7 +55,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("ogg")
{
REQUIRE(inputSoundFile.openFromFile("Audio/doodle_pop.ogg"));
const auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/doodle_pop.ogg").value();
CHECK(inputSoundFile.getSampleCount() == 2'116'992);
CHECK(inputSoundFile.getChannelCount() == 2);
CHECK(inputSoundFile.getSampleRate() == 44'100);
@ -78,7 +66,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("wav")
{
REQUIRE(inputSoundFile.openFromFile("Audio/killdeer.wav"));
const auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/killdeer.wav").value();
CHECK(inputSoundFile.getSampleCount() == 112'941);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 22'050);
@ -91,9 +79,8 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("openFromMemory()")
{
const auto memory = loadIntoMemory("Audio/killdeer.wav");
sf::InputSoundFile inputSoundFile;
REQUIRE(inputSoundFile.openFromMemory(memory.data(), memory.size()));
const auto memory = loadIntoMemory("Audio/killdeer.wav");
const auto inputSoundFile = sf::InputSoundFile::openFromMemory(memory.data(), memory.size()).value();
CHECK(inputSoundFile.getSampleCount() == 112'941);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 22'050);
@ -104,12 +91,11 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("openFromStream()")
{
sf::InputSoundFile inputSoundFile;
sf::FileInputStream stream;
SECTION("Invalid stream")
{
CHECK(!inputSoundFile.openFromStream(stream));
CHECK(!sf::InputSoundFile::openFromStream(stream));
}
SECTION("Valid stream")
@ -117,7 +103,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("flac")
{
REQUIRE(stream.open("Audio/ding.flac"));
REQUIRE(inputSoundFile.openFromStream(stream));
const auto inputSoundFile = sf::InputSoundFile::openFromStream(stream).value();
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
@ -129,7 +115,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("mp3")
{
REQUIRE(stream.open("Audio/ding.mp3"));
REQUIRE(inputSoundFile.openFromStream(stream));
const auto inputSoundFile = sf::InputSoundFile::openFromStream(stream).value();
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 44'100);
@ -141,7 +127,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("ogg")
{
REQUIRE(stream.open("Audio/doodle_pop.ogg"));
REQUIRE(inputSoundFile.openFromStream(stream));
const auto inputSoundFile = sf::InputSoundFile::openFromStream(stream).value();
CHECK(inputSoundFile.getSampleCount() == 2'116'992);
CHECK(inputSoundFile.getChannelCount() == 2);
CHECK(inputSoundFile.getSampleRate() == 44'100);
@ -153,7 +139,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("wav")
{
REQUIRE(stream.open("Audio/killdeer.wav"));
REQUIRE(inputSoundFile.openFromStream(stream));
const auto inputSoundFile = sf::InputSoundFile::openFromStream(stream).value();
CHECK(inputSoundFile.getSampleCount() == 112'941);
CHECK(inputSoundFile.getChannelCount() == 1);
CHECK(inputSoundFile.getSampleRate() == 22'050);
@ -166,11 +152,9 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("seek(std::uint64_t)")
{
sf::InputSoundFile inputSoundFile;
SECTION("flac")
{
REQUIRE(inputSoundFile.openFromFile("Audio/ding.flac"));
auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.flac").value();
inputSoundFile.seek(1'000);
CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(22'675));
CHECK(inputSoundFile.getSampleOffset() == 1'000);
@ -178,7 +162,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("mp3")
{
REQUIRE(inputSoundFile.openFromFile("Audio/ding.mp3"));
auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.mp3").value();
inputSoundFile.seek(1'000);
CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(22'675));
CHECK(inputSoundFile.getSampleOffset() == 1'000);
@ -186,7 +170,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("ogg")
{
REQUIRE(inputSoundFile.openFromFile("Audio/doodle_pop.ogg"));
auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/doodle_pop.ogg").value();
inputSoundFile.seek(1'000);
CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(11'337));
CHECK(inputSoundFile.getSampleOffset() == 1'000);
@ -194,7 +178,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("wav")
{
REQUIRE(inputSoundFile.openFromFile("Audio/killdeer.wav"));
auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/killdeer.wav").value();
inputSoundFile.seek(1'000);
CHECK(inputSoundFile.getTimeOffset() == sf::microseconds(45'351));
CHECK(inputSoundFile.getSampleOffset() == 1'000);
@ -203,8 +187,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("seek(Time)")
{
sf::InputSoundFile inputSoundFile;
REQUIRE(inputSoundFile.openFromFile("Audio/ding.flac"));
auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.flac").value();
inputSoundFile.seek(sf::milliseconds(100));
CHECK(inputSoundFile.getSampleCount() == 87'798);
CHECK(inputSoundFile.getChannelCount() == 1);
@ -216,21 +199,15 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("read()")
{
sf::InputSoundFile inputSoundFile;
std::array<std::int16_t, 4> samples{};
SECTION("Unloaded file")
{
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 0);
}
REQUIRE(inputSoundFile.openFromFile("Audio/ding.flac"));
auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.flac").value();
SECTION("Null address")
{
CHECK(inputSoundFile.read(nullptr, 10) == 0);
}
std::array<std::int16_t, 4> samples{};
SECTION("Zero count")
{
CHECK(inputSoundFile.read(samples.data(), 0) == 0);
@ -240,7 +217,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
{
SECTION("flac")
{
REQUIRE(inputSoundFile.openFromFile("Audio/ding.flac"));
inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.flac").value();
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
CHECK(samples == std::array<std::int16_t, 4>{0, 1, -1, 4});
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
@ -249,7 +226,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("mp3")
{
REQUIRE(inputSoundFile.openFromFile("Audio/ding.mp3"));
inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.mp3").value();
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
CHECK(samples == std::array<std::int16_t, 4>{0, -2, 0, 2});
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
@ -258,7 +235,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("ogg")
{
REQUIRE(inputSoundFile.openFromFile("Audio/doodle_pop.ogg"));
inputSoundFile = sf::InputSoundFile::openFromFile("Audio/doodle_pop.ogg").value();
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
CHECK(samples == std::array<std::int16_t, 4>{-827, -985, -1168, -1319});
CHECK(inputSoundFile.read(samples.data(), samples.size()) == 4);
@ -274,8 +251,7 @@ TEST_CASE("[Audio] sf::InputSoundFile")
SECTION("close()")
{
sf::InputSoundFile inputSoundFile;
REQUIRE(inputSoundFile.openFromFile("Audio/ding.flac"));
auto inputSoundFile = sf::InputSoundFile::openFromFile("Audio/ding.flac").value();
inputSoundFile.close();
CHECK(inputSoundFile.getSampleCount() == 0);
CHECK(inputSoundFile.getChannelCount() == 0);

View File

@ -37,7 +37,6 @@ TEST_CASE("[Audio] sf::Music", runAudioDeviceTests())
SECTION("Construction")
{
const sf::Music music;
CHECK(music.getDuration() == sf::Time::Zero);
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
CHECK(length == sf::Time::Zero);
@ -55,7 +54,6 @@ TEST_CASE("[Audio] sf::Music", runAudioDeviceTests())
SECTION("Invalid file")
{
REQUIRE(!music.openFromFile("does/not/exist.wav"));
CHECK(music.getDuration() == sf::Time::Zero);
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
CHECK(length == sf::Time::Zero);
@ -89,7 +87,6 @@ TEST_CASE("[Audio] sf::Music", runAudioDeviceTests())
SECTION("Invalid buffer")
{
REQUIRE(!music.openFromMemory(memory.data(), memory.size()));
CHECK(music.getDuration() == sf::Time::Zero);
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
CHECK(length == sf::Time::Zero);
@ -124,7 +121,6 @@ TEST_CASE("[Audio] sf::Music", runAudioDeviceTests())
SECTION("Invalid stream")
{
CHECK(!music.openFromStream(stream));
CHECK(music.getDuration() == sf::Time::Zero);
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
CHECK(length == sf::Time::Zero);
@ -178,32 +174,15 @@ TEST_CASE("[Audio] sf::Music", runAudioDeviceTests())
SECTION("setLoopPoints()")
{
sf::Music music;
SECTION("No file")
{
music.setLoopPoints({sf::Time::Zero, sf::Time::Zero});
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::Time::Zero);
CHECK(length == sf::Time::Zero);
CHECK(music.getChannelCount() == 0);
CHECK(music.getSampleRate() == 0);
CHECK(music.getStatus() == sf::Music::Status::Stopped);
CHECK(music.getPlayingOffset() == sf::Time::Zero);
CHECK(!music.getLoop());
}
SECTION("Loaded file")
{
REQUIRE(music.openFromFile("Audio/killdeer.wav"));
music.setLoopPoints({sf::seconds(1), sf::seconds(2)});
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::seconds(1));
CHECK(length == sf::seconds(2));
CHECK(music.getChannelCount() == 1);
CHECK(music.getSampleRate() == 22050);
CHECK(music.getStatus() == sf::Music::Status::Stopped);
CHECK(music.getPlayingOffset() == sf::Time::Zero);
CHECK(!music.getLoop());
}
REQUIRE(music.openFromFile("Audio/killdeer.wav"));
music.setLoopPoints({sf::seconds(1), sf::seconds(2)});
const auto [offset, length] = music.getLoopPoints();
CHECK(offset == sf::seconds(1));
CHECK(length == sf::seconds(2));
CHECK(music.getChannelCount() == 1);
CHECK(music.getSampleRate() == 22050);
CHECK(music.getStatus() == sf::Music::Status::Stopped);
CHECK(music.getPlayingOffset() == sf::Time::Zero);
CHECK(!music.getLoop());
}
}

View File

@ -2,6 +2,7 @@
#include <type_traits>
static_assert(!std::is_default_constructible_v<sf::OutputSoundFile>);
static_assert(!std::is_copy_constructible_v<sf::OutputSoundFile>);
static_assert(!std::is_copy_assignable_v<sf::OutputSoundFile>);
static_assert(std::is_nothrow_move_constructible_v<sf::OutputSoundFile>);

View File

@ -8,7 +8,6 @@
int main()
{
// Audio
[[maybe_unused]] const sf::InputSoundFile inputSoundFile;
[[maybe_unused]] const sf::SoundBufferRecorder soundBufferRecorder;
[[maybe_unused]] const sf::Music music;