mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
LibRIFF: Rework to match LibGfx needs
There's now two namespaces, RIFF (little-endian) and IFF (big-endian) which (for the most part) contain the same kinds of structures for handling similar data in both formats. (They also share almost all of their implementation) The main types are ChunkHeader and (Owned)Chunk. While Chunk has no ownership over the data it accesses (and can only be constructed from a byte view), OwnedChunk has ownership over this data and is aimed at reading from streams. OwnedList, implementing the standard RIFF LIST type, is currently only implemented for RIFF due to its only user being WAV, but it may be generalized in the future for use by IFF. Co-authored-by: Timothy Flynn <trflynn89@pm.me>
This commit is contained in:
parent
d125d16287
commit
64598473cc
Notes:
sideshowbarker
2024-07-17 07:08:37 +09:00
Author: https://github.com/kleinesfilmroellchen Commit: https://github.com/SerenityOS/serenity/commit/64598473cc Pull-request: https://github.com/SerenityOS/serenity/pull/22554 Reviewed-by: https://github.com/ADKaster ✅
11 changed files with 366 additions and 145 deletions
|
@ -185,16 +185,12 @@ MaybeLoaderError WavLoaderPlugin::parse_header()
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
auto riff = TRY(m_stream->read_value<RIFF::ChunkID>());
|
||||
CHECK(riff == RIFF::riff_magic, LoaderError::Category::Format, "RIFF header magic invalid");
|
||||
auto file_header = TRY(m_stream->read_value<RIFF::FileHeader>());
|
||||
CHECK(file_header.magic() == RIFF::riff_magic, LoaderError::Category::Format, "RIFF header magic invalid");
|
||||
CHECK(file_header.subformat == Wav::wave_subformat_id, LoaderError::Category::Format, "WAVE subformat id invalid");
|
||||
|
||||
TRY(m_stream->read_value<LittleEndian<u32>>()); // File size header
|
||||
|
||||
auto wave = TRY(m_stream->read_value<RIFF::ChunkID>());
|
||||
CHECK(wave == Wav::wave_subformat_id, LoaderError::Category::Format, "WAVE subformat id invalid");
|
||||
|
||||
auto format_chunk = TRY(m_stream->read_value<RIFF::Chunk>());
|
||||
CHECK(format_chunk.id.as_ascii_string() == Wav::format_chunk_id, LoaderError::Category::Format, "FMT chunk id invalid");
|
||||
auto format_chunk = TRY(m_stream->read_value<RIFF::OwnedChunk>());
|
||||
CHECK(format_chunk.id().as_ascii_string() == Wav::format_chunk_id, LoaderError::Category::Format, "FMT chunk id invalid");
|
||||
|
||||
auto format_stream = format_chunk.data_stream();
|
||||
u16 audio_format = TRY(format_stream.read_value<LittleEndian<u16>>());
|
||||
|
@ -212,7 +208,7 @@ MaybeLoaderError WavLoaderPlugin::parse_header()
|
|||
u16 bits_per_sample = TRY(format_stream.read_value<LittleEndian<u16>>());
|
||||
|
||||
if (audio_format == to_underlying(Wav::WaveFormat::Extensible)) {
|
||||
CHECK(format_chunk.size == 40, LoaderError::Category::Format, "Extensible fmt size is not 40 bytes");
|
||||
CHECK(format_chunk.size() == 40, LoaderError::Category::Format, "Extensible fmt size is not 40 bytes");
|
||||
|
||||
// Discard everything until the GUID.
|
||||
// We've already read 16 bytes from the stream. The GUID starts in another 8 bytes.
|
||||
|
@ -260,9 +256,9 @@ MaybeLoaderError WavLoaderPlugin::parse_header()
|
|||
found_data = true;
|
||||
} else {
|
||||
TRY(m_stream->seek(-RIFF::chunk_id_size, SeekMode::FromCurrentPosition));
|
||||
auto chunk = TRY(m_stream->read_value<RIFF::Chunk>());
|
||||
if (chunk.id == RIFF::list_chunk_id) {
|
||||
auto maybe_list = chunk.data_stream().read_value<RIFF::List>();
|
||||
auto chunk = TRY(m_stream->read_value<RIFF::OwnedChunk>());
|
||||
if (chunk.id() == RIFF::list_chunk_id) {
|
||||
auto maybe_list = chunk.data_stream().read_value<RIFF::OwnedList>();
|
||||
if (maybe_list.is_error()) {
|
||||
dbgln("WAV Warning: LIST chunk invalid, error: {}", maybe_list.release_error());
|
||||
continue;
|
||||
|
@ -278,7 +274,7 @@ MaybeLoaderError WavLoaderPlugin::parse_header()
|
|||
dbgln("Unhandled WAV list of type {} with {} subchunks", list.type.as_ascii_string(), list.chunks.size());
|
||||
}
|
||||
} else {
|
||||
dbgln_if(AWAVLOADER_DEBUG, "Unhandled WAV chunk of type {}, size {} bytes", chunk.id.as_ascii_string(), chunk.size);
|
||||
dbgln_if(AWAVLOADER_DEBUG, "Unhandled WAV chunk of type {}, size {} bytes", chunk.id().as_ascii_string(), chunk.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -299,12 +295,12 @@ MaybeLoaderError WavLoaderPlugin::parse_header()
|
|||
|
||||
// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/riffmci.pdf page 23 (LIST type)
|
||||
// We only recognize the relevant official metadata types; types added in later errata of RIFF are not relevant for audio.
|
||||
MaybeLoaderError WavLoaderPlugin::load_wav_info_block(Vector<RIFF::Chunk> info_chunks)
|
||||
MaybeLoaderError WavLoaderPlugin::load_wav_info_block(Vector<RIFF::OwnedChunk> info_chunks)
|
||||
{
|
||||
for (auto const& chunk : info_chunks) {
|
||||
auto metadata_name = chunk.id.as_ascii_string();
|
||||
auto metadata_name = chunk.id().as_ascii_string();
|
||||
// Chunk contents are zero-terminated strings "ZSTR", so we just drop the null terminator.
|
||||
StringView metadata_text { chunk.data.span().trim(chunk.data.size() - 1) };
|
||||
StringView metadata_text { chunk.data().trim(chunk.size() - 1) };
|
||||
// Note that we assume chunks to be unique, since that seems to almost always be the case.
|
||||
// Worst case we just drop some metadata.
|
||||
if (metadata_name == "IART"sv) {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <AK/Span.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <LibAudio/Loader.h>
|
||||
#include <LibRIFF/Types.h>
|
||||
#include <LibRIFF/RIFF.h>
|
||||
|
||||
namespace Audio {
|
||||
|
||||
|
@ -46,7 +46,7 @@ public:
|
|||
|
||||
private:
|
||||
MaybeLoaderError parse_header();
|
||||
MaybeLoaderError load_wav_info_block(Vector<RIFF::Chunk> info_chunks);
|
||||
MaybeLoaderError load_wav_info_block(Vector<RIFF::OwnedChunk> info_chunks);
|
||||
|
||||
LoaderSamples samples_from_pcm_data(ReadonlyBytes data, size_t samples_to_read) const;
|
||||
template<typename SampleReader>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
set(SOURCES
|
||||
Types.cpp
|
||||
Decoding.cpp
|
||||
Details.cpp
|
||||
)
|
||||
|
||||
serenity_lib(LibRIFF riff)
|
||||
|
|
63
Userland/Libraries/LibRIFF/ChunkID.h
Normal file
63
Userland/Libraries/LibRIFF/ChunkID.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2023, kleines Filmröllchen <filmroellchen@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Array.h>
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace RIFF {
|
||||
|
||||
static constexpr size_t const chunk_id_size = 4;
|
||||
|
||||
// Also referred to as "FourCC" (four character code) in the context of some formats.
|
||||
struct ChunkID {
|
||||
constexpr ChunkID(char const name[4])
|
||||
{
|
||||
id_data[0] = static_cast<u8>(name[0]);
|
||||
id_data[1] = static_cast<u8>(name[1]);
|
||||
id_data[2] = static_cast<u8>(name[2]);
|
||||
id_data[3] = static_cast<u8>(name[3]);
|
||||
}
|
||||
constexpr ChunkID(Array<u8, chunk_id_size> data)
|
||||
: id_data(data)
|
||||
{
|
||||
}
|
||||
constexpr ChunkID(ChunkID const&) = default;
|
||||
constexpr ChunkID(ChunkID&&) = default;
|
||||
constexpr ChunkID& operator=(ChunkID const&) = default;
|
||||
static constexpr ChunkID from_big_endian_number(u32 number) { return bit_cast<Array<u8, chunk_id_size>>(AK::convert_between_host_and_big_endian(number)); }
|
||||
|
||||
static ErrorOr<ChunkID> read_from_stream(Stream& stream);
|
||||
|
||||
StringView as_ascii_string() const;
|
||||
constexpr u32 as_big_endian_number() const
|
||||
{
|
||||
return AK::convert_between_host_and_big_endian((id_data[0] << 24) | (id_data[1] << 16) | (id_data[2] << 8) | id_data[3]);
|
||||
}
|
||||
|
||||
bool operator==(ChunkID const&) const = default;
|
||||
bool operator==(StringView) const;
|
||||
|
||||
Array<u8, chunk_id_size> id_data;
|
||||
};
|
||||
static_assert(AssertSize<ChunkID, chunk_id_size>());
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
struct AK::Formatter<RIFF::ChunkID> : StandardFormatter {
|
||||
ErrorOr<void> format(FormatBuilder& builder, RIFF::ChunkID const& chunk_id)
|
||||
{
|
||||
TRY(builder.put_padding('\'', 1));
|
||||
TRY(builder.put_literal(chunk_id.as_ascii_string()));
|
||||
TRY(builder.put_padding('\'', 1));
|
||||
return {};
|
||||
}
|
||||
};
|
28
Userland/Libraries/LibRIFF/Decoding.cpp
Normal file
28
Userland/Libraries/LibRIFF/Decoding.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2023, kleines Filmröllchen <filmroellchen@serenityos.org>
|
||||
* Copyright (c) 2023, Nicolas Ramz <nicolas.ramz@gmail.com>
|
||||
* Copyright (c) 2023, Nico Weber <thakis@chromium.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Stream.h>
|
||||
#include <LibRIFF/ChunkID.h>
|
||||
#include <LibRIFF/RIFF.h>
|
||||
|
||||
ErrorOr<RIFF::ChunkID> RIFF::ChunkID::read_from_stream(Stream& stream)
|
||||
{
|
||||
Array<u8, chunk_id_size> id;
|
||||
TRY(stream.read_until_filled(id.span()));
|
||||
return ChunkID { id };
|
||||
}
|
||||
|
||||
ErrorOr<RIFF::OwnedList> RIFF::OwnedList::read_from_stream(Stream& stream)
|
||||
{
|
||||
auto type = TRY(stream.read_value<ChunkID>());
|
||||
Vector<RIFF::OwnedChunk> chunks;
|
||||
while (!stream.is_eof())
|
||||
TRY(chunks.try_append(TRY(stream.read_value<RIFF::OwnedChunk>())));
|
||||
|
||||
return RIFF::OwnedList { .type = type, .chunks = move(chunks) };
|
||||
}
|
125
Userland/Libraries/LibRIFF/Details.cpp
Normal file
125
Userland/Libraries/LibRIFF/Details.cpp
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (c) 2023, kleines Filmröllchen <filmroellchen@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Details.h"
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibRIFF/IFF.h>
|
||||
#include <LibRIFF/RIFF.h>
|
||||
|
||||
namespace RIFF {
|
||||
|
||||
StringView ChunkID::as_ascii_string() const
|
||||
{
|
||||
return StringView { id_data.span() };
|
||||
}
|
||||
|
||||
bool ChunkID::operator==(StringView other_string) const
|
||||
{
|
||||
return as_ascii_string() == other_string;
|
||||
}
|
||||
|
||||
namespace Detail {
|
||||
|
||||
template<typename WordType>
|
||||
auto ChunkHeader<WordType>::read_from_stream(Stream& stream) -> ErrorOr<ChunkHeader>
|
||||
{
|
||||
auto id = TRY(stream.read_value<RIFF::ChunkID>());
|
||||
u32 size = TRY(stream.read_value<WordType>());
|
||||
return ChunkHeader { id, size };
|
||||
}
|
||||
|
||||
template<typename HeaderType>
|
||||
auto FileHeader<HeaderType>::read_from_stream(Stream& stream) -> ErrorOr<FileHeader>
|
||||
{
|
||||
auto header = TRY(stream.read_value<HeaderType>());
|
||||
auto subformat = TRY(stream.read_value<RIFF::ChunkID>());
|
||||
return FileHeader { header, subformat };
|
||||
}
|
||||
|
||||
template<typename HeaderType>
|
||||
Chunk<HeaderType>::Chunk(HeaderType header, ReadonlyBytes data)
|
||||
: m_header(header)
|
||||
, m_data(data)
|
||||
{
|
||||
VERIFY(data.size() == header.size);
|
||||
}
|
||||
|
||||
template<typename HeaderType>
|
||||
FixedMemoryStream Chunk<HeaderType>::data_stream() const
|
||||
{
|
||||
return FixedMemoryStream { m_data };
|
||||
}
|
||||
|
||||
template<typename HeaderType>
|
||||
auto Chunk<HeaderType>::decode(ReadonlyBytes data) -> ErrorOr<Chunk>
|
||||
{
|
||||
auto data_stream = FixedMemoryStream { data };
|
||||
auto header = TRY(HeaderType::read_from_stream(data_stream));
|
||||
|
||||
if (data.size() < sizeof(HeaderType) + header.size)
|
||||
return Error::from_string_literal("Not enough data for IFF/RIFF chunk");
|
||||
|
||||
return Chunk { header, data.slice(sizeof(HeaderType), header.size) };
|
||||
}
|
||||
|
||||
template<typename HeaderType>
|
||||
auto Chunk<HeaderType>::decode_and_advance(ReadonlyBytes& data) -> ErrorOr<Chunk>
|
||||
{
|
||||
auto chunk = TRY(decode(data));
|
||||
data = data.slice(sizeof(HeaderType) + chunk.size());
|
||||
// add padding if needed
|
||||
if (chunk.size() % 2 != 0) {
|
||||
if (data.is_empty())
|
||||
return Error::from_string_literal("Missing data for padding byte");
|
||||
if (*data.data() != 0)
|
||||
return Error::from_string_literal("Padding byte is not 0");
|
||||
data = data.slice(1);
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
template<typename HeaderType>
|
||||
OwnedChunk<HeaderType>::OwnedChunk(HeaderType header, Buffer backing_data)
|
||||
: Chunk<HeaderType>(header, backing_data.span())
|
||||
, m_backing_data(move(backing_data))
|
||||
{
|
||||
}
|
||||
|
||||
template<typename HeaderType>
|
||||
auto OwnedChunk<HeaderType>::read_from_stream(Stream& stream) -> ErrorOr<OwnedChunk>
|
||||
{
|
||||
auto header = TRY(stream.read_value<HeaderType>());
|
||||
|
||||
auto data = TRY(Buffer::create_uninitialized(header.size));
|
||||
TRY(stream.read_until_filled(data.span()));
|
||||
|
||||
// RIFF chunks may have trailing padding to align to x86 "words" (i.e. 2 bytes).
|
||||
if (is<SeekableStream>(stream)) {
|
||||
if (!stream.is_eof()) {
|
||||
auto stream_position = TRY(static_cast<SeekableStream&>(stream).tell());
|
||||
if (stream_position % 2 != 0)
|
||||
TRY(static_cast<SeekableStream&>(stream).seek(1, SeekMode::FromCurrentPosition));
|
||||
}
|
||||
} else {
|
||||
dbgln("RIFF Warning: Cannot align stream to 2-byte boundary, next chunk may be bogus!");
|
||||
}
|
||||
|
||||
return OwnedChunk { header, data };
|
||||
}
|
||||
|
||||
template class Chunk<IFF::ChunkHeader>;
|
||||
template class Chunk<RIFF::ChunkHeader>;
|
||||
template class OwnedChunk<IFF::ChunkHeader>;
|
||||
template class OwnedChunk<RIFF::ChunkHeader>;
|
||||
template struct ChunkHeader<IFF::WordType>;
|
||||
template struct ChunkHeader<RIFF::WordType>;
|
||||
template struct FileHeader<IFF::ChunkHeader>;
|
||||
template struct FileHeader<RIFF::ChunkHeader>;
|
||||
|
||||
}
|
||||
|
||||
}
|
74
Userland/Libraries/LibRIFF/Details.h
Normal file
74
Userland/Libraries/LibRIFF/Details.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) 2023, kleines Filmröllchen <filmroellchen@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <LibRIFF/ChunkID.h>
|
||||
|
||||
// Despite the name, this header contains details for both RIFF and IFF
|
||||
namespace RIFF::Detail {
|
||||
|
||||
// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/riffmci.pdf page 11 (Chunks)
|
||||
template<typename WordType>
|
||||
struct ChunkHeader {
|
||||
static ErrorOr<ChunkHeader> read_from_stream(Stream& stream);
|
||||
|
||||
RIFF::ChunkID id;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
// Standard RIFF/IFF file formats use a global chunk with a chunk ID (magic bytes) such as "RIFF" or "FORM".
|
||||
// A chunk ID right at the start of the global chunk specifies the subformat specific to the file type.
|
||||
// Example for RIFF from WebP: https://developers.google.com/speed/webp/docs/riff_container#webp_file_header
|
||||
template<typename HeaderType>
|
||||
struct FileHeader {
|
||||
HeaderType global_header;
|
||||
RIFF::ChunkID subformat;
|
||||
|
||||
static ErrorOr<FileHeader> read_from_stream(Stream& stream);
|
||||
|
||||
constexpr ChunkID magic() const { return global_header.id; }
|
||||
constexpr u32 file_size() const { return global_header.size; }
|
||||
};
|
||||
|
||||
// An RIFF or IFF chunk.
|
||||
template<typename HeaderType>
|
||||
class Chunk {
|
||||
public:
|
||||
Chunk(HeaderType header, ReadonlyBytes data);
|
||||
|
||||
// Note that the resulting chunk will refer to the provided data.
|
||||
static ErrorOr<Chunk> decode(ReadonlyBytes data);
|
||||
static ErrorOr<Chunk> decode_and_advance(ReadonlyBytes& data);
|
||||
|
||||
RIFF::ChunkID id() const { return m_header.id; }
|
||||
u32 size() const { return m_header.size; }
|
||||
ReadonlyBytes data() const { return m_data; }
|
||||
FixedMemoryStream data_stream() const;
|
||||
|
||||
u8 operator[](size_t index) const { return data()[index]; }
|
||||
|
||||
private:
|
||||
HeaderType m_header;
|
||||
ReadonlyBytes m_data;
|
||||
};
|
||||
|
||||
// Owns the chunk data and can therefore be parsed from a stream.
|
||||
template<typename HeaderType>
|
||||
class OwnedChunk : public Chunk<HeaderType> {
|
||||
public:
|
||||
using Buffer = AK::Detail::ByteBuffer<0>;
|
||||
OwnedChunk(HeaderType, Buffer);
|
||||
|
||||
static ErrorOr<OwnedChunk> read_from_stream(Stream& stream);
|
||||
|
||||
private:
|
||||
Buffer m_backing_data;
|
||||
};
|
||||
|
||||
}
|
24
Userland/Libraries/LibRIFF/IFF.h
Normal file
24
Userland/Libraries/LibRIFF/IFF.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2023, the SerenityOS developers.
|
||||
* Copyright (c) 2023, Nico Weber <thakis@chromium.org>
|
||||
* Copyright (c) 2023, kleines Filmröllchen <filmroellchen@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <LibRIFF/ChunkID.h>
|
||||
#include <LibRIFF/Details.h>
|
||||
|
||||
// IFF chunks (as often used by Amiga, EA and more modern formats) use big-endian fields.
|
||||
namespace IFF {
|
||||
|
||||
using WordType = BigEndian<u32>;
|
||||
using ChunkHeader = RIFF::Detail::ChunkHeader<WordType>;
|
||||
using FileHeader = RIFF::Detail::FileHeader<ChunkHeader>;
|
||||
using Chunk = RIFF::Detail::Chunk<ChunkHeader>;
|
||||
using OwnedChunk = RIFF::Detail::OwnedChunk<ChunkHeader>;
|
||||
|
||||
}
|
35
Userland/Libraries/LibRIFF/RIFF.h
Normal file
35
Userland/Libraries/LibRIFF/RIFF.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2023, the SerenityOS developers.
|
||||
* Copyright (c) 2023, kleines Filmröllchen <filmroellchen@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibRIFF/ChunkID.h>
|
||||
#include <LibRIFF/Details.h>
|
||||
|
||||
// RIFF chunks (as often used by Microsoft's older formats) use little-endian fields.
|
||||
namespace RIFF {
|
||||
|
||||
static constexpr StringView const riff_magic = "RIFF"sv;
|
||||
static constexpr StringView const list_chunk_id = "LIST"sv;
|
||||
|
||||
using WordType = LittleEndian<u32>;
|
||||
using ChunkHeader = RIFF::Detail::ChunkHeader<WordType>;
|
||||
using FileHeader = RIFF::Detail::FileHeader<ChunkHeader>;
|
||||
using Chunk = RIFF::Detail::Chunk<ChunkHeader>;
|
||||
using OwnedChunk = RIFF::Detail::OwnedChunk<ChunkHeader>;
|
||||
|
||||
// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/riffmci.pdf page 23 (LIST type)
|
||||
struct OwnedList {
|
||||
static ErrorOr<OwnedList> read_from_stream(Stream& stream);
|
||||
|
||||
ChunkID type;
|
||||
Vector<OwnedChunk> chunks;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, kleines Filmröllchen <filmroellchen@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Types.h"
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/Stream.h>
|
||||
#include <AK/Try.h>
|
||||
#include <AK/TypeCasts.h>
|
||||
|
||||
namespace RIFF {
|
||||
|
||||
ErrorOr<ChunkID> ChunkID::read_from_stream(Stream& stream)
|
||||
{
|
||||
Array<u8, chunk_id_size> id;
|
||||
TRY(stream.read_until_filled(id.span()));
|
||||
return ChunkID { id };
|
||||
}
|
||||
|
||||
ErrorOr<Chunk> Chunk::read_from_stream(Stream& stream)
|
||||
{
|
||||
auto id = TRY(stream.read_value<ChunkID>());
|
||||
|
||||
u32 size = TRY(stream.read_value<LittleEndian<u32>>());
|
||||
auto data = TRY(FixedArray<u8>::create(size));
|
||||
TRY(stream.read_until_filled(data.span()));
|
||||
|
||||
// RIFF chunks may have trailing padding to align to x86 "words" (i.e. 2 bytes).
|
||||
if (is<SeekableStream>(stream)) {
|
||||
if (!stream.is_eof()) {
|
||||
auto stream_position = TRY(static_cast<SeekableStream&>(stream).tell());
|
||||
if (stream_position % 2 != 0)
|
||||
TRY(static_cast<SeekableStream&>(stream).seek(1, SeekMode::FromCurrentPosition));
|
||||
}
|
||||
} else {
|
||||
dbgln("RIFF Warning: Cannot align stream to 2-byte boundary, next chunk may be bogus!");
|
||||
}
|
||||
|
||||
return Chunk {
|
||||
id,
|
||||
size,
|
||||
move(data),
|
||||
};
|
||||
}
|
||||
|
||||
ErrorOr<List> List::read_from_stream(Stream& stream)
|
||||
{
|
||||
auto type = TRY(stream.read_value<ChunkID>());
|
||||
Vector<Chunk> chunks;
|
||||
while (!stream.is_eof())
|
||||
TRY(chunks.try_append(TRY(stream.read_value<Chunk>())));
|
||||
|
||||
return List {
|
||||
.type = type,
|
||||
.chunks = move(chunks),
|
||||
};
|
||||
}
|
||||
|
||||
StringView ChunkID::as_ascii_string() const
|
||||
{
|
||||
return StringView { id_data.span() };
|
||||
}
|
||||
|
||||
bool ChunkID::operator==(StringView const& other_string) const
|
||||
{
|
||||
return as_ascii_string() == other_string;
|
||||
}
|
||||
|
||||
FixedMemoryStream Chunk::data_stream()
|
||||
{
|
||||
return FixedMemoryStream { data.span() };
|
||||
}
|
||||
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2023, the SerenityOS developers.
|
||||
* Copyright (c) 2023, kleines Filmröllchen <filmroellchen@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/FixedArray.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace RIFF {
|
||||
|
||||
static constexpr StringView const riff_magic = "RIFF"sv;
|
||||
static constexpr StringView const list_chunk_id = "LIST"sv;
|
||||
|
||||
static constexpr size_t const chunk_id_size = 4;
|
||||
|
||||
struct ChunkID {
|
||||
static ErrorOr<ChunkID> read_from_stream(Stream& stream);
|
||||
StringView as_ascii_string() const;
|
||||
bool operator==(ChunkID const&) const = default;
|
||||
bool operator==(StringView const&) const;
|
||||
|
||||
Array<u8, chunk_id_size> id_data;
|
||||
};
|
||||
|
||||
// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/riffmci.pdf page 11 (Chunks)
|
||||
struct Chunk {
|
||||
static ErrorOr<Chunk> read_from_stream(Stream& stream);
|
||||
FixedMemoryStream data_stream();
|
||||
|
||||
ChunkID id;
|
||||
u32 size;
|
||||
FixedArray<u8> data;
|
||||
};
|
||||
|
||||
// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/riffmci.pdf page 23 (LIST type)
|
||||
struct List {
|
||||
static ErrorOr<List> read_from_stream(Stream& stream);
|
||||
|
||||
ChunkID type;
|
||||
Vector<Chunk> chunks;
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in a new issue