From 1a40f0195788185d56eca04687a8cd793c90b2fc Mon Sep 17 00:00:00 2001 From: binary1248 Date: Fri, 17 May 2024 03:59:35 +0200 Subject: [PATCH] Replaced SoundFileReaderWav implementation with miniaudio (dr_)wav decoder. --- src/SFML/Audio/CMakeLists.txt | 2 +- src/SFML/Audio/MiniaudioUtils.cpp | 50 +++ src/SFML/Audio/MiniaudioUtils.hpp | 7 +- src/SFML/Audio/SoundFileReaderWav.cpp | 477 +++++++------------------- src/SFML/Audio/SoundFileReaderWav.hpp | 24 +- 5 files changed, 186 insertions(+), 374 deletions(-) diff --git a/src/SFML/Audio/CMakeLists.txt b/src/SFML/Audio/CMakeLists.txt index 258116ce8..a4bd1ed36 100644 --- a/src/SFML/Audio/CMakeLists.txt +++ b/src/SFML/Audio/CMakeLists.txt @@ -84,7 +84,7 @@ sfml_add_library(Audio target_compile_definitions(sfml-audio PRIVATE OV_EXCLUDE_STATIC_CALLBACKS FLAC__NO_DLL) # disable miniaudio features we do not use -target_compile_definitions(sfml-audio PRIVATE MA_NO_DECODING MA_NO_ENCODING MA_NO_RESOURCE_MANAGER MA_NO_GENERATION) +target_compile_definitions(sfml-audio PRIVATE MA_NO_MP3 MA_NO_FLAC MA_NO_ENCODING MA_NO_RESOURCE_MANAGER MA_NO_GENERATION) # setup dependencies target_link_libraries(sfml-audio diff --git a/src/SFML/Audio/MiniaudioUtils.cpp b/src/SFML/Audio/MiniaudioUtils.cpp index 8ea706494..44cd82e8a 100644 --- a/src/SFML/Audio/MiniaudioUtils.cpp +++ b/src/SFML/Audio/MiniaudioUtils.cpp @@ -184,6 +184,56 @@ ma_channel MiniaudioUtils::soundChannelToMiniaudioChannel(SoundChannel soundChan } +//////////////////////////////////////////////////////////// +SoundChannel MiniaudioUtils::miniaudioChannelToSoundChannel(ma_channel soundChannel) +{ + switch (soundChannel) + { + case MA_CHANNEL_NONE: + return SoundChannel::Unspecified; + case MA_CHANNEL_MONO: + return SoundChannel::Mono; + case MA_CHANNEL_FRONT_LEFT: + return SoundChannel::FrontLeft; + case MA_CHANNEL_FRONT_RIGHT: + return SoundChannel::FrontRight; + case MA_CHANNEL_FRONT_CENTER: + return SoundChannel::FrontCenter; + case MA_CHANNEL_FRONT_LEFT_CENTER: + return SoundChannel::FrontLeftOfCenter; + case MA_CHANNEL_FRONT_RIGHT_CENTER: + return SoundChannel::FrontRightOfCenter; + case MA_CHANNEL_LFE: + return SoundChannel::LowFrequencyEffects; + case MA_CHANNEL_BACK_LEFT: + return SoundChannel::BackLeft; + case MA_CHANNEL_BACK_RIGHT: + return SoundChannel::BackRight; + case MA_CHANNEL_BACK_CENTER: + return SoundChannel::BackCenter; + case MA_CHANNEL_SIDE_LEFT: + return SoundChannel::SideLeft; + case MA_CHANNEL_SIDE_RIGHT: + return SoundChannel::SideRight; + case MA_CHANNEL_TOP_CENTER: + return SoundChannel::TopCenter; + case MA_CHANNEL_TOP_FRONT_LEFT: + return SoundChannel::TopFrontLeft; + case MA_CHANNEL_TOP_FRONT_RIGHT: + return SoundChannel::TopFrontRight; + case MA_CHANNEL_TOP_FRONT_CENTER: + return SoundChannel::TopFrontCenter; + case MA_CHANNEL_TOP_BACK_LEFT: + return SoundChannel::TopBackLeft; + case MA_CHANNEL_TOP_BACK_RIGHT: + return SoundChannel::TopBackRight; + default: + assert(soundChannel == MA_CHANNEL_TOP_BACK_CENTER); + return SoundChannel::TopBackCenter; + } +} + + //////////////////////////////////////////////////////////// Time MiniaudioUtils::getPlayingOffset(ma_sound& sound) { diff --git a/src/SFML/Audio/MiniaudioUtils.hpp b/src/SFML/Audio/MiniaudioUtils.hpp index cc61a3c73..a9d86ce63 100644 --- a/src/SFML/Audio/MiniaudioUtils.hpp +++ b/src/SFML/Audio/MiniaudioUtils.hpp @@ -44,9 +44,10 @@ class Time; namespace priv::MiniaudioUtils { -[[nodiscard]] ma_channel soundChannelToMiniaudioChannel(SoundChannel soundChannel); -[[nodiscard]] Time getPlayingOffset(ma_sound& sound); -[[nodiscard]] ma_uint64 getFrameIndex(ma_sound& sound, Time timeOffset); +[[nodiscard]] ma_channel soundChannelToMiniaudioChannel(SoundChannel soundChannel); +[[nodiscard]] SoundChannel miniaudioChannelToSoundChannel(ma_channel soundChannel); +[[nodiscard]] Time getPlayingOffset(ma_sound& sound); +[[nodiscard]] ma_uint64 getFrameIndex(ma_sound& sound, Time timeOffset); void reinitializeSound(ma_sound& sound, const std::function& initializeFn); void initializeSound(const ma_data_source_vtable& vtable, diff --git a/src/SFML/Audio/SoundFileReaderWav.cpp b/src/SFML/Audio/SoundFileReaderWav.cpp index 6769f59d8..469109837 100644 --- a/src/SFML/Audio/SoundFileReaderWav.cpp +++ b/src/SFML/Audio/SoundFileReaderWav.cpp @@ -25,82 +25,64 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// +#include #include #include #include -#include +#include #include +#include #include #include -#include namespace { -// The following functions read integers as little endian and -// return them in the host byte order - -bool decode(sf::InputStream& stream, std::uint8_t& value) +ma_result onRead(ma_decoder* decoder, void* buffer, size_t bytesToRead, size_t* bytesRead) { - return static_cast(stream.read(&value, sizeof(value))) == sizeof(value); + auto* stream = static_cast(decoder->pUserData); + const auto count = stream->read(buffer, static_cast(bytesToRead)); + + if (count < 0) + return MA_ERROR; + + *bytesRead = static_cast(count); + return MA_SUCCESS; } -bool decode(sf::InputStream& stream, std::int16_t& value) +ma_result onSeek(ma_decoder* decoder, ma_int64 byteOffset, ma_seek_origin origin) { - std::byte bytes[sizeof(value)]; - if (static_cast(stream.read(bytes, static_cast(sizeof(bytes)))) != sizeof(bytes)) - return false; + auto* stream = static_cast(decoder->pUserData); - value = sf::toInteger(bytes[0], bytes[1]); + switch (origin) + { + case ma_seek_origin_start: + { + if (stream->seek(byteOffset) < 0) + return MA_ERROR; - return true; + return MA_SUCCESS; + } + case ma_seek_origin_current: + { + const auto currentPosition = stream->tell(); + + if (currentPosition < 0) + return MA_ERROR; + + if (stream->seek(stream->tell() + byteOffset) < 0) + return MA_ERROR; + + return MA_SUCCESS; + } + // According to miniaudio comments, ma_seek_origin_end is not used by decoders + default: + return MA_ERROR; + } } - -bool decode(sf::InputStream& stream, std::uint16_t& value) -{ - std::byte bytes[sizeof(value)]; - if (static_cast(stream.read(bytes, static_cast(sizeof(bytes)))) != sizeof(bytes)) - return false; - - value = sf::toInteger(bytes[0], bytes[1]); - - return true; -} - -bool decode24bit(sf::InputStream& stream, std::uint32_t& value) -{ - std::byte bytes[3]; - if (static_cast(stream.read(bytes, static_cast(sizeof(bytes)))) != sizeof(bytes)) - return false; - - value = sf::toInteger(bytes[0], bytes[1], bytes[2]); - - return true; -} - -bool decode(sf::InputStream& stream, std::uint32_t& value) -{ - std::byte bytes[sizeof(value)]; - if (static_cast(stream.read(bytes, static_cast(sizeof(bytes)))) != sizeof(bytes)) - return false; - - value = sf::toInteger(bytes[0], bytes[1], bytes[2], bytes[3]); - - return true; -} - -const std::uint64_t mainChunkSize = 12; - -const std::uint16_t waveFormatPcm = 1; - -const std::uint16_t waveFormatExtensible = 65534; - -const char* waveSubformatPcm = - "\x01\x00\x00\x00\x00\x00\x10\x00" - "\x80\x00\x00\xAA\x00\x38\x9B\x71"; } // namespace namespace sf::priv @@ -108,331 +90,114 @@ namespace sf::priv //////////////////////////////////////////////////////////// bool SoundFileReaderWav::check(InputStream& stream) { - char header[mainChunkSize]; - if (stream.read(header, sizeof(header)) < static_cast(sizeof(header))) - return false; + auto config = ma_decoder_config_init_default(); + config.encodingFormat = ma_encoding_format_wav; + config.format = ma_format_s16; + ma_decoder decoder{}; - return (header[0] == 'R') && (header[1] == 'I') && (header[2] == 'F') && (header[3] == 'F') && (header[8] == 'W') && - (header[9] == 'A') && (header[10] == 'V') && (header[11] == 'E'); + if (ma_decoder_init(&onRead, &onSeek, &stream, &config, &decoder) == MA_SUCCESS) + { + ma_decoder_uninit(&decoder); + return true; + } + + return false; +} + + +//////////////////////////////////////////////////////////// +SoundFileReaderWav::~SoundFileReaderWav() +{ + if (m_decoder) + { + if (const ma_result result = ma_decoder_uninit(&*m_decoder); result != MA_SUCCESS) + err() << "Failed to uninitialize wav decoder: " << ma_result_description(result) << std::endl; + } } //////////////////////////////////////////////////////////// std::optional SoundFileReaderWav::open(InputStream& stream) { - m_stream = &stream; + if (m_decoder) + { + if (const ma_result result = ma_decoder_uninit(&*m_decoder); result != MA_SUCCESS) + { + err() << "Failed to uninitialize wav decoder: " << ma_result_description(result) << std::endl; + return std::nullopt; + } + } + else + { + m_decoder.emplace(); + } - auto info = parseHeader(); - if (!info) - err() << "Failed to open WAV sound file (invalid or unsupported file)" << std::endl; + auto config = ma_decoder_config_init_default(); + config.encodingFormat = ma_encoding_format_wav; + config.format = ma_format_s16; - return info; + if (const ma_result result = ma_decoder_init(&onRead, &onSeek, &stream, &config, &*m_decoder); result != MA_SUCCESS) + { + err() << "Failed to initialize wav decoder: " << ma_result_description(result) << std::endl; + m_decoder = std::nullopt; + return std::nullopt; + } + + ma_uint64 frameCount{}; + if (const ma_result result = ma_decoder_get_available_frames(&*m_decoder, &frameCount); result != MA_SUCCESS) + { + err() << "Failed to get available frames from wav decoder: " << ma_result_description(result) << std::endl; + return std::nullopt; + } + + auto format = ma_format_unknown; + ma_uint32 sampleRate{}; + std::array channelMap{}; + if (const ma_result result = ma_decoder_get_data_format(&*m_decoder, + &format, + &m_channelCount, + &sampleRate, + channelMap.data(), + channelMap.size()); + result != MA_SUCCESS) + { + err() << "Failed to get data format from wav decoder: " << ma_result_description(result) << std::endl; + return std::nullopt; + } + + std::vector soundChannels; + soundChannels.reserve(m_channelCount); + + for (auto i = 0u; i < m_channelCount; ++i) + soundChannels.emplace_back(priv::MiniaudioUtils::miniaudioChannelToSoundChannel(channelMap[i])); + + return Info{frameCount * m_channelCount, m_channelCount, sampleRate, std::move(soundChannels)}; } //////////////////////////////////////////////////////////// void SoundFileReaderWav::seek(std::uint64_t sampleOffset) { - assert(m_stream && "Input stream cannot be null. Call SoundFileReaderWav::open() to initialize it."); + assert(m_decoder && "wav decoder not initialized. Call SoundFileReaderWav::open() to initialize it."); - if (m_stream->seek(static_cast(m_dataStart + sampleOffset * m_bytesPerSample) == -1)) - err() << "Failed to seek WAV sound stream" << std::endl; + if (const ma_result result = ma_decoder_seek_to_pcm_frame(&*m_decoder, sampleOffset / m_channelCount); + result != MA_SUCCESS) + err() << "Failed to seek wav sound stream: " << ma_result_description(result) << std::endl; } //////////////////////////////////////////////////////////// std::uint64_t SoundFileReaderWav::read(std::int16_t* samples, std::uint64_t maxCount) { - assert(m_stream && "Input stream cannot be null. Call SoundFileReaderWav::open() to initialize it."); + assert(m_decoder && "wav decoder not initialized. Call SoundFileReaderWav::open() to initialize it."); - std::uint64_t count = 0; - const auto startPos = static_cast(m_stream->tell()); + ma_uint64 framesRead{}; - // Tracking of m_dataEnd is important to prevent sf::Music from reading - // data until EOF, as WAV files may have metadata at the end. - while ((count < maxCount) && (startPos + count * m_bytesPerSample < m_dataEnd)) - { - switch (m_bytesPerSample) - { - case 1: - { - std::uint8_t sample = 0; - if (decode(*m_stream, sample)) - *samples++ = static_cast((static_cast(sample) - 128) << 8); - else - return count; - break; - } + if (const ma_result result = ma_decoder_read_pcm_frames(&*m_decoder, samples, maxCount / m_channelCount, &framesRead); + result != MA_SUCCESS) + err() << "Failed to read from wav sound stream: " << ma_result_description(result) << std::endl; - case 2: - { - std::int16_t sample = 0; - if (decode(*m_stream, sample)) - *samples++ = sample; - else - return count; - break; - } - - case 3: - { - std::uint32_t sample = 0; - if (decode24bit(*m_stream, sample)) - *samples++ = static_cast(sample >> 8); - else - return count; - break; - } - - case 4: - { - std::uint32_t sample = 0; - if (decode(*m_stream, sample)) - *samples++ = static_cast(sample >> 16); - else - return count; - break; - } - - default: - { - assert(false && "Invalid bytes per sample. Must be 1, 2, 3, or 4."); - return 0; - } - } - - ++count; - } - - return count; -} - - -//////////////////////////////////////////////////////////// -std::optional SoundFileReaderWav::parseHeader() -{ - assert(m_stream && "Input stream cannot be null. Call SoundFileReaderWav::open() to initialize it."); - - // If we are here, it means that the first part of the header - // (the format) has already been checked - char mainChunk[mainChunkSize]; - if (static_cast(m_stream->read(mainChunk, static_cast(sizeof(mainChunk)))) != - sizeof(mainChunk)) - return std::nullopt; - - // Parse all the sub-chunks - Info info; - bool dataChunkFound = false; - while (!dataChunkFound) - { - // Parse the sub-chunk id and size - char subChunkId[4]; - if (static_cast(m_stream->read(subChunkId, static_cast(sizeof(subChunkId)))) != - sizeof(subChunkId)) - return std::nullopt; - std::uint32_t subChunkSize = 0; - if (!decode(*m_stream, subChunkSize)) - return std::nullopt; - const std::int64_t subChunkStart = m_stream->tell(); - if (subChunkStart == -1) - return std::nullopt; - - // Check which chunk it is - if ((subChunkId[0] == 'f') && (subChunkId[1] == 'm') && (subChunkId[2] == 't') && (subChunkId[3] == ' ')) - { - // "fmt" chunk - - // Audio format - std::uint16_t format = 0; - if (!decode(*m_stream, format)) - return std::nullopt; - if ((format != waveFormatPcm) && (format != waveFormatExtensible)) - return std::nullopt; - - // Channel count - std::uint16_t channelCount = 0; - if (!decode(*m_stream, channelCount)) - return std::nullopt; - info.channelCount = channelCount; - - // Sample rate - std::uint32_t sampleRate = 0; - if (!decode(*m_stream, sampleRate)) - return std::nullopt; - info.sampleRate = sampleRate; - - // Byte rate - std::uint32_t byteRate = 0; - if (!decode(*m_stream, byteRate)) - return std::nullopt; - - // Block align - std::uint16_t blockAlign = 0; - if (!decode(*m_stream, blockAlign)) - return std::nullopt; - - // Bits per sample - std::uint16_t bitsPerSample = 0; - if (!decode(*m_stream, bitsPerSample)) - return std::nullopt; - if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && bitsPerSample != 32) - { - err() << "Unsupported sample size: " << bitsPerSample - << " bit (Supported sample sizes are 8/16/24/32 bit)" << std::endl; - return std::nullopt; - } - m_bytesPerSample = bitsPerSample / 8; - - if (format == waveFormatExtensible) - { - // Extension size - std::uint16_t extensionSize = 0; - if (!decode(*m_stream, extensionSize)) - return std::nullopt; - - // Valid bits per sample - std::uint16_t validBitsPerSample = 0; - if (!decode(*m_stream, validBitsPerSample)) - return std::nullopt; - - // Channel mask - std::uint32_t channelMask = 0; - if (!decode(*m_stream, channelMask)) - return std::nullopt; - - // NOLINTBEGIN(readability-identifier-naming) - // For WAVE channel mapping refer to: https://learn.microsoft.com/en-us/previous-versions/windows/hardware/design/dn653308(v=vs.85)#default-channel-ordering - static constexpr auto SPEAKER_FRONT_LEFT = 0x1u; - static constexpr auto SPEAKER_FRONT_RIGHT = 0x2u; - static constexpr auto SPEAKER_FRONT_CENTER = 0x4u; - static constexpr auto SPEAKER_LOW_FREQUENCY = 0x8u; - static constexpr auto SPEAKER_BACK_LEFT = 0x10u; - static constexpr auto SPEAKER_BACK_RIGHT = 0x20u; - static constexpr auto SPEAKER_FRONT_LEFT_OF_CENTER = 0x40u; - static constexpr auto SPEAKER_FRONT_RIGHT_OF_CENTER = 0x80u; - static constexpr auto SPEAKER_BACK_CENTER = 0x100u; - static constexpr auto SPEAKER_SIDE_LEFT = 0x200u; - static constexpr auto SPEAKER_SIDE_RIGHT = 0x400u; - static constexpr auto SPEAKER_TOP_CENTER = 0x800u; - static constexpr auto SPEAKER_TOP_FRONT_LEFT = 0x1000u; - static constexpr auto SPEAKER_TOP_FRONT_CENTER = 0x2000u; - static constexpr auto SPEAKER_TOP_FRONT_RIGHT = 0x4000u; - static constexpr auto SPEAKER_TOP_BACK_LEFT = 0x8000u; - static constexpr auto SPEAKER_TOP_BACK_CENTER = 0x10000u; - static constexpr auto SPEAKER_TOP_BACK_RIGHT = 0x20000u; - // NOLINTEND(readability-identifier-naming) - - info.channelMap.clear(); - - const auto checkChannel = [channelMask, &info](auto bit, auto soundChannel) - { - if ((channelMask & bit) != 0) - info.channelMap.push_back(soundChannel); - }; - - checkChannel(SPEAKER_FRONT_LEFT, SoundChannel::FrontLeft); - checkChannel(SPEAKER_FRONT_RIGHT, SoundChannel::FrontRight); - checkChannel(SPEAKER_FRONT_CENTER, SoundChannel::FrontCenter); - checkChannel(SPEAKER_LOW_FREQUENCY, SoundChannel::LowFrequencyEffects); - checkChannel(SPEAKER_BACK_LEFT, SoundChannel::BackLeft); - checkChannel(SPEAKER_BACK_RIGHT, SoundChannel::BackRight); - checkChannel(SPEAKER_FRONT_LEFT_OF_CENTER, SoundChannel::FrontLeftOfCenter); - checkChannel(SPEAKER_FRONT_RIGHT_OF_CENTER, SoundChannel::FrontRightOfCenter); - checkChannel(SPEAKER_BACK_CENTER, SoundChannel::BackCenter); - checkChannel(SPEAKER_SIDE_LEFT, SoundChannel::SideLeft); - checkChannel(SPEAKER_SIDE_RIGHT, SoundChannel::SideRight); - checkChannel(SPEAKER_TOP_CENTER, SoundChannel::TopCenter); - checkChannel(SPEAKER_TOP_FRONT_LEFT, SoundChannel::TopFrontLeft); - checkChannel(SPEAKER_TOP_FRONT_CENTER, SoundChannel::TopFrontCenter); - checkChannel(SPEAKER_TOP_FRONT_RIGHT, SoundChannel::TopFrontRight); - checkChannel(SPEAKER_TOP_BACK_LEFT, SoundChannel::TopBackLeft); - checkChannel(SPEAKER_TOP_BACK_CENTER, SoundChannel::TopBackCenter); - checkChannel(SPEAKER_TOP_BACK_RIGHT, SoundChannel::TopBackRight); - - assert(info.channelCount == info.channelMap.size()); - - if (info.channelCount != info.channelMap.size()) - { - err() << "WAV sound file channel count does not match number of set bits in channel mask" << std::endl; - return std::nullopt; - } - - // Subformat - char subformat[16]; - if (static_cast(m_stream->read(subformat, static_cast(sizeof(subformat)))) != - sizeof(subformat)) - return std::nullopt; - - if (std::memcmp(subformat, waveSubformatPcm, sizeof(subformat)) != 0) - { - err() << "Unsupported format: extensible format with non-PCM subformat" << std::endl; - return std::nullopt; - } - - if (validBitsPerSample != bitsPerSample) - { - err() << "Unsupported format: sample size (" << validBitsPerSample - << " bits) and " - "sample container size (" - << bitsPerSample << " bits) differ" << std::endl; - return std::nullopt; - } - } - else - { - // If we don't have a waveFormatExtensible header, fill the channel map based on a best guess for mono/stereo - info.channelMap.clear(); - - if (info.channelCount == 0) - { - err() << "WAV sound file channel count 0" << std::endl; - return std::nullopt; - } - else if (info.channelCount == 1) - { - info.channelMap.push_back(SoundChannel::Mono); - } - else if (info.channelCount == 2) - { - info.channelMap.push_back(SoundChannel::FrontLeft); - info.channelMap.push_back(SoundChannel::FrontRight); - } - else - { - info.channelMap.push_back(SoundChannel::FrontLeft); - info.channelMap.push_back(SoundChannel::FrontRight); - - for (auto i = 2u; i < info.channelCount; ++i) - info.channelMap.push_back(SoundChannel::Unspecified); - } - } - - // Skip potential extra information - if (m_stream->seek(subChunkStart + subChunkSize) == -1) - return std::nullopt; - } - else if ((subChunkId[0] == 'd') && (subChunkId[1] == 'a') && (subChunkId[2] == 't') && (subChunkId[3] == 'a')) - { - // "data" chunk - - // Compute the total number of samples - info.sampleCount = subChunkSize / m_bytesPerSample; - - // Store the start and end position of samples in the file - m_dataStart = static_cast(subChunkStart); - m_dataEnd = m_dataStart + info.sampleCount * m_bytesPerSample; - - dataChunkFound = true; - } - else - { - // unknown chunk, skip it - if (m_stream->seek(m_stream->tell() + subChunkSize) == -1) - return std::nullopt; - } - } - - return info; + return framesRead * m_channelCount; } } // namespace sf::priv diff --git a/src/SFML/Audio/SoundFileReaderWav.hpp b/src/SFML/Audio/SoundFileReaderWav.hpp index fc47c061a..d666897bc 100644 --- a/src/SFML/Audio/SoundFileReaderWav.hpp +++ b/src/SFML/Audio/SoundFileReaderWav.hpp @@ -29,6 +29,8 @@ //////////////////////////////////////////////////////////// #include +#include + #include #include @@ -58,6 +60,12 @@ public: //////////////////////////////////////////////////////////// [[nodiscard]] static bool check(InputStream& stream); + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + ~SoundFileReaderWav() override; + //////////////////////////////////////////////////////////// /// \brief Open a sound file for reading /// @@ -95,23 +103,11 @@ public: [[nodiscard]] std::uint64_t read(std::int16_t* samples, std::uint64_t maxCount) override; private: - //////////////////////////////////////////////////////////// - /// \brief Read the header of the open file - /// - /// \param info Attributes of the sound file - /// - /// \return True on success, false on error - /// - //////////////////////////////////////////////////////////// - std::optional parseHeader(); - //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - InputStream* m_stream{}; //!< Source stream to read from - unsigned int m_bytesPerSample{}; //!< Size of a sample, in bytes - std::uint64_t m_dataStart{}; //!< Starting position of the audio data in the open file - std::uint64_t m_dataEnd{}; //!< Position one byte past the end of the audio data in the open file + std::optional m_decoder; //!< wav decoder + ma_uint32 m_channelCount{}; //!< Number of channels }; } // namespace sf::priv