diff --git a/Userland/Libraries/LibAudio/FlacWriter.cpp b/Userland/Libraries/LibAudio/FlacWriter.cpp index 1a5229057d..257c96362b 100644 --- a/Userland/Libraries/LibAudio/FlacWriter.cpp +++ b/Userland/Libraries/LibAudio/FlacWriter.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include namespace Audio { @@ -107,11 +109,23 @@ ErrorOr FlacWriter::set_bits_per_sample(u16 bits_per_sample) return {}; } +ErrorOr FlacWriter::set_metadata(Metadata const& metadata) +{ + AllocatingMemoryStream vorbis_stream; + TRY(write_vorbis_comment(metadata, vorbis_stream)); + + auto vorbis_data = TRY(vorbis_stream.read_until_eof()); + FlacRawMetadataBlock vorbis_block { + .is_last_block = false, + .type = FlacMetadataBlockType::VORBIS_COMMENT, + .length = static_cast(vorbis_data.size()), + .data = move(vorbis_data), + }; + return add_metadata_block(move(vorbis_block), 0); +} + ErrorOr FlacWriter::write_header() { - TRY(m_stream->write_until_depleted(flac_magic.bytes())); - m_streaminfo_start_index = TRY(m_stream->tell()); - ByteBuffer data; // STREAMINFO is always exactly 34 bytes long. TRY(data.try_resize(34)); @@ -140,13 +154,44 @@ ErrorOr FlacWriter::write_header() .is_last_block = true, .type = FlacMetadataBlockType::STREAMINFO, .length = static_cast(data.size()), - .data = data, + .data = move(data), }; + TRY(add_metadata_block(move(streaminfo_block), 0)); - TRY(m_stream->write_value(streaminfo_block)); + TRY(m_stream->write_until_depleted(flac_magic.bytes())); + m_streaminfo_start_index = TRY(m_stream->tell()); + + for (size_t i = 0; i < m_cached_metadata_blocks.size(); ++i) { + auto& block = m_cached_metadata_blocks[i]; + // Correct is_last_block flag here to avoid index shenanigans in add_metadata_block. + auto const is_last_block = i == m_cached_metadata_blocks.size() - 1; + block.is_last_block = is_last_block; + + TRY(write_metadata_block(block)); + } + + m_cached_metadata_blocks.clear(); return {}; } +ErrorOr FlacWriter::add_metadata_block(FlacRawMetadataBlock block, Optional insertion_index) +{ + if (m_state != WriteState::HeaderUnwritten) + return Error::from_string_view("Metadata blocks can only be added before the header is finalized"sv); + + if (insertion_index.has_value()) + TRY(m_cached_metadata_blocks.try_insert(insertion_index.value(), move(block))); + else + TRY(m_cached_metadata_blocks.try_append(move(block))); + + return {}; +} + +ErrorOr FlacWriter::write_metadata_block(FlacRawMetadataBlock const& block) +{ + return m_stream->write_value(block); +} + ErrorOr FlacRawMetadataBlock::write_to_stream(Stream& stream) const { BigEndianOutputBitStream bit_stream { MaybeOwned { stream } }; diff --git a/Userland/Libraries/LibAudio/FlacWriter.h b/Userland/Libraries/LibAudio/FlacWriter.h index 5939826735..fbb05492a6 100644 --- a/Userland/Libraries/LibAudio/FlacWriter.h +++ b/Userland/Libraries/LibAudio/FlacWriter.h @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -80,6 +82,9 @@ public: ErrorOr set_num_channels(u8 num_channels); ErrorOr set_sample_rate(u32 sample_rate); ErrorOr set_bits_per_sample(u16 bits_per_sample); + + virtual ErrorOr set_metadata(Metadata const& metadata) override; + ErrorOr finalize_header_format(); private: @@ -98,6 +103,9 @@ private: // In this case, an empty Optional is returned. ErrorOr> encode_fixed_lpc(FlacFixedLPC order, ReadonlySpan subframe, size_t current_min_cost, u8 bits_per_sample); + ErrorOr add_metadata_block(FlacRawMetadataBlock block, Optional insertion_index = {}); + ErrorOr write_metadata_block(FlacRawMetadataBlock const& block); + NonnullOwnPtr m_stream; WriteState m_state { WriteState::HeaderUnwritten }; @@ -114,6 +122,9 @@ private: size_t m_sample_count { 0 }; // Remember where the STREAMINFO block was written in the stream. size_t m_streaminfo_start_index; + + // Raw metadata blocks that will be written out before header finalization. + Vector m_cached_metadata_blocks; }; }