Browse Source

LibAudio: Write FLAC metadata

This includes a generalization of the metadata block infrastructure, so
adding and writing blocks is handled in a generalized fashion.
kleines Filmröllchen 1 năm trước cách đây
mục cha
commit
bf3fa19314

+ 50 - 5
Userland/Libraries/LibAudio/FlacWriter.cpp

@@ -11,6 +11,8 @@
 #include <AK/IntegralMath.h>
 #include <AK/MemoryStream.h>
 #include <AK/Statistics.h>
+#include <LibAudio/Metadata.h>
+#include <LibAudio/VorbisComment.h>
 #include <LibCrypto/Checksum/ChecksummingStream.h>
 
 namespace Audio {
@@ -107,11 +109,23 @@ ErrorOr<void> FlacWriter::set_bits_per_sample(u16 bits_per_sample)
     return {};
 }
 
-ErrorOr<void> FlacWriter::write_header()
+ErrorOr<void> FlacWriter::set_metadata(Metadata const& metadata)
 {
-    TRY(m_stream->write_until_depleted(flac_magic.bytes()));
-    m_streaminfo_start_index = TRY(m_stream->tell());
+    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<u32>(vorbis_data.size()),
+        .data = move(vorbis_data),
+    };
+    return add_metadata_block(move(vorbis_block), 0);
+}
 
+ErrorOr<void> FlacWriter::write_header()
+{
     ByteBuffer data;
     // STREAMINFO is always exactly 34 bytes long.
     TRY(data.try_resize(34));
@@ -140,13 +154,44 @@ ErrorOr<void> FlacWriter::write_header()
         .is_last_block = true,
         .type = FlacMetadataBlockType::STREAMINFO,
         .length = static_cast<u32>(data.size()),
-        .data = data,
+        .data = move(data),
     };
+    TRY(add_metadata_block(move(streaminfo_block), 0));
+
+    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<void> FlacWriter::add_metadata_block(FlacRawMetadataBlock block, Optional<size_t> 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)));
 
-    TRY(m_stream->write_value(streaminfo_block));
     return {};
 }
 
+ErrorOr<void> FlacWriter::write_metadata_block(FlacRawMetadataBlock const& block)
+{
+    return m_stream->write_value(block);
+}
+
 ErrorOr<void> FlacRawMetadataBlock::write_to_stream(Stream& stream) const
 {
     BigEndianOutputBitStream bit_stream { MaybeOwned<Stream> { stream } };

+ 11 - 0
Userland/Libraries/LibAudio/FlacWriter.h

@@ -13,6 +13,8 @@
 #include <AK/StringView.h>
 #include <LibAudio/Encoder.h>
 #include <LibAudio/FlacTypes.h>
+#include <LibAudio/Forward.h>
+#include <LibAudio/GenericTypes.h>
 #include <LibAudio/Sample.h>
 #include <LibAudio/SampleFormats.h>
 #include <LibCore/Forward.h>
@@ -80,6 +82,9 @@ public:
     ErrorOr<void> set_num_channels(u8 num_channels);
     ErrorOr<void> set_sample_rate(u32 sample_rate);
     ErrorOr<void> set_bits_per_sample(u16 bits_per_sample);
+
+    virtual ErrorOr<void> set_metadata(Metadata const& metadata) override;
+
     ErrorOr<void> finalize_header_format();
 
 private:
@@ -98,6 +103,9 @@ private:
     // In this case, an empty Optional is returned.
     ErrorOr<Optional<FlacLPCEncodedSubframe>> encode_fixed_lpc(FlacFixedLPC order, ReadonlySpan<i64> subframe, size_t current_min_cost, u8 bits_per_sample);
 
+    ErrorOr<void> add_metadata_block(FlacRawMetadataBlock block, Optional<size_t> insertion_index = {});
+    ErrorOr<void> write_metadata_block(FlacRawMetadataBlock const& block);
+
     NonnullOwnPtr<SeekableStream> 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<FlacRawMetadataBlock> m_cached_metadata_blocks;
 };
 
 }