mirror of
https://github.com/SFML/SFML.git
synced 2024-11-24 20:31:05 +08:00
Remove default empty state of sf::InputSoundFile
and sf::OutputSoundFile
This commit is contained in:
parent
53ade4baf1
commit
e9fadbbcb3
@ -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
|
||||
////////////////////////////////////////////////////////////
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
////////////////////////////////////////////////////////////
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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>);
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user