pile-audio refactor
Some checks failed
CI / Typos (push) Successful in 17s
CI / Clippy (push) Has been cancelled
CI / Build and test (push) Has been cancelled

This commit is contained in:
2026-02-21 19:14:52 -08:00
parent 5aab61bd1b
commit 4b41467cc2
35 changed files with 1968 additions and 3366 deletions

View File

@@ -0,0 +1,79 @@
use std::{
fmt::Debug,
io::{Cursor, Read},
};
use super::{FlacMetablockDecode, FlacMetablockEncode, FlacMetablockHeader, FlacMetablockType};
use crate::{FlacDecodeError, FlacEncodeError};
/// An application block in a flac file
pub struct FlacApplicationBlock {
/// Registered application ID
pub application_id: u32,
/// The application data
pub data: Vec<u8>,
}
impl Debug for FlacApplicationBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FlacApplicationBlock")
.field("application_id", &self.application_id)
.field("data_len", &self.data.len())
.finish()
}
}
impl FlacMetablockDecode for FlacApplicationBlock {
fn decode(data: &[u8]) -> Result<Self, FlacDecodeError> {
let mut d = Cursor::new(data);
let mut block = [0u8; 4];
#[expect(clippy::map_err_ignore)]
d.read_exact(&mut block)
.map_err(|_| FlacDecodeError::MalformedBlock)?;
let application_id = u32::from_be_bytes(block);
let data = {
let mut data = Vec::with_capacity(data.len());
d.read_to_end(&mut data)?;
data
};
Ok(Self {
application_id,
data,
})
}
}
impl FlacMetablockEncode for FlacApplicationBlock {
#[expect(clippy::expect_used)]
fn get_len(&self) -> u32 {
(self.data.len() + 4)
.try_into()
.expect("application block size does not fit into u32")
}
fn encode(
&self,
is_last: bool,
with_header: bool,
target: &mut impl std::io::Write,
) -> Result<(), FlacEncodeError> {
if with_header {
let header = FlacMetablockHeader {
block_type: FlacMetablockType::Application,
length: self.get_len(),
is_last,
};
header.encode(target)?;
}
target.write_all(&self.application_id.to_be_bytes())?;
target.write_all(&self.data)?;
return Ok(());
}
}

View File

@@ -0,0 +1,42 @@
use crate::{FlacDecodeError, FlacEncodeError};
use std::fmt::Debug;
/// An audio frame in a flac file
pub struct FlacAudioFrame {
/// The audio frame
pub data: Vec<u8>,
}
impl Debug for FlacAudioFrame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FlacAudioFrame")
.field("data_len", &self.data.len())
.finish()
}
}
impl FlacAudioFrame {
/// Decode the given data as a flac audio frame.
/// This should start with a sync sequence.
pub fn decode(data: &[u8]) -> Result<Self, FlacDecodeError> {
if data.len() <= 2 {
return Err(FlacDecodeError::MalformedBlock);
}
if !(data[0] == 0b1111_1111 && data[1] & 0b1111_1100 == 0b1111_1000) {
return Err(FlacDecodeError::BadSyncBytes);
}
Ok(Self {
data: Vec::from(data),
})
}
}
impl FlacAudioFrame {
/// Encode this audio frame.
pub fn encode(&self, target: &mut impl std::io::Write) -> Result<(), FlacEncodeError> {
target.write_all(&self.data)?;
return Ok(());
}
}

View File

@@ -0,0 +1,50 @@
use std::fmt::Debug;
use super::{FlacMetablockDecode, FlacMetablockEncode, FlacMetablockHeader, FlacMetablockType};
use crate::{FlacDecodeError, FlacEncodeError, VorbisComment};
/// A vorbis comment metablock in a flac file
pub struct FlacCommentBlock {
/// The vorbis comment stored inside this block
pub comment: VorbisComment,
}
impl Debug for FlacCommentBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FlacCommentBlock")
.field("comment", &self.comment)
.finish()
}
}
impl FlacMetablockDecode for FlacCommentBlock {
fn decode(data: &[u8]) -> Result<Self, FlacDecodeError> {
let comment = VorbisComment::decode(data)?;
Ok(Self { comment })
}
}
impl FlacMetablockEncode for FlacCommentBlock {
fn get_len(&self) -> u32 {
self.comment.get_len()
}
fn encode(
&self,
is_last: bool,
with_header: bool,
target: &mut impl std::io::Write,
) -> Result<(), FlacEncodeError> {
if with_header {
let header = FlacMetablockHeader {
block_type: FlacMetablockType::VorbisComment,
length: self.get_len(),
is_last,
};
header.encode(target)?;
}
self.comment.encode(target)?;
return Ok(());
}
}

View File

@@ -0,0 +1,53 @@
use std::fmt::Debug;
use super::{FlacMetablockDecode, FlacMetablockEncode, FlacMetablockHeader, FlacMetablockType};
use crate::{FlacDecodeError, FlacEncodeError};
/// A cuesheet meta in a flac file
pub struct FlacCuesheetBlock {
/// The seek table
pub data: Vec<u8>,
}
impl Debug for FlacCuesheetBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FlacAudioFrame")
.field("data_len", &self.data.len())
.finish()
}
}
impl FlacMetablockDecode for FlacCuesheetBlock {
fn decode(data: &[u8]) -> Result<Self, FlacDecodeError> {
Ok(Self { data: data.into() })
}
}
impl FlacMetablockEncode for FlacCuesheetBlock {
#[expect(clippy::expect_used)]
fn get_len(&self) -> u32 {
self.data
.len()
.try_into()
.expect("cuesheet size does not fit into u32")
}
fn encode(
&self,
is_last: bool,
with_header: bool,
target: &mut impl std::io::Write,
) -> Result<(), FlacEncodeError> {
if with_header {
let header = FlacMetablockHeader {
block_type: FlacMetablockType::Cuesheet,
length: self.get_len(),
is_last,
};
header.encode(target)?;
}
target.write_all(&self.data)?;
return Ok(());
}
}

View File

@@ -0,0 +1,87 @@
//! FLAC metablock headers. See spec.
use std::fmt::Debug;
use crate::{FlacDecodeError, FlacEncodeError};
/// A type of flac metadata block
#[expect(missing_docs)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum FlacMetablockType {
Streaminfo,
Padding,
Application,
Seektable,
VorbisComment,
Cuesheet,
Picture,
}
impl FlacMetablockType {
/// Read and parse a metablock header from the given reader.
/// Returns (block_type, block_data_length, is_last)
pub(crate) fn from_id(id: u8) -> Result<Self, FlacDecodeError> {
return Ok(match id & 0b01111111 {
0 => FlacMetablockType::Streaminfo,
1 => FlacMetablockType::Padding,
2 => FlacMetablockType::Application,
3 => FlacMetablockType::Seektable,
4 => FlacMetablockType::VorbisComment,
5 => FlacMetablockType::Cuesheet,
6 => FlacMetablockType::Picture,
x => return Err(FlacDecodeError::BadMetablockType(x)),
});
}
}
/// The header of a flac metadata block
#[derive(Debug, Clone)]
pub struct FlacMetablockHeader {
/// The type of block this is
pub block_type: FlacMetablockType,
/// The length of this block, in bytes
/// (not including this header)
pub length: u32,
/// If true, this is the last metadata block
pub is_last: bool,
}
impl FlacMetablockHeader {
/// Try to decode the given bytes as a flac metablock header
pub fn decode(header: &[u8]) -> Result<Self, FlacDecodeError> {
if header.len() != 4 {
return Err(FlacDecodeError::MalformedBlock);
}
return Ok(Self {
block_type: FlacMetablockType::from_id(header[0])?,
length: u32::from_be_bytes([0, header[1], header[2], header[3]]),
is_last: header[0] & 0b10000000 == 0b10000000,
});
}
}
impl FlacMetablockHeader {
/// Try to encode this header
pub fn encode(&self, target: &mut impl std::io::Write) -> Result<(), FlacEncodeError> {
let mut block_type = match self.block_type {
FlacMetablockType::Streaminfo => 0,
FlacMetablockType::Padding => 1,
FlacMetablockType::Application => 2,
FlacMetablockType::Seektable => 3,
FlacMetablockType::VorbisComment => 4,
FlacMetablockType::Cuesheet => 5,
FlacMetablockType::Picture => 6,
};
if self.is_last {
block_type |= 0b1000_0000;
};
let x = self.length.to_be_bytes();
target.write_all(&[block_type, x[1], x[2], x[3]])?;
return Ok(());
}
}

View File

@@ -0,0 +1,52 @@
//! Read and write implementations for all flac block types
mod header;
pub use header::{FlacMetablockHeader, FlacMetablockType};
mod audiodata;
pub use audiodata::FlacAudioFrame;
mod streaminfo;
pub use streaminfo::FlacStreaminfoBlock;
mod picture;
pub use picture::FlacPictureBlock;
mod padding;
pub use padding::FlacPaddingBlock;
mod application;
pub use application::FlacApplicationBlock;
mod seektable;
pub use seektable::FlacSeektableBlock;
mod cuesheet;
pub use cuesheet::FlacCuesheetBlock;
mod comment;
pub use comment::FlacCommentBlock;
/// A decode implementation for a
/// flac metadata block
pub trait FlacMetablockDecode: Sized {
/// Try to decode this block from bytes.
/// `data` should NOT include the metablock header.
fn decode(data: &[u8]) -> Result<Self, crate::FlacDecodeError>;
}
/// A encode implementation for a
/// flac metadata block
pub trait FlacMetablockEncode: Sized {
/// Get the number of bytes that `encode()` will write.
/// This does NOT include header length.
fn get_len(&self) -> u32;
/// Try to encode this block as bytes.
fn encode(
&self,
is_last: bool,
with_header: bool,
target: &mut impl std::io::Write,
) -> Result<(), crate::FlacEncodeError>;
}

View File

@@ -0,0 +1,53 @@
use std::{fmt::Debug, io::Read};
use super::{FlacMetablockDecode, FlacMetablockEncode, FlacMetablockHeader, FlacMetablockType};
use crate::{FlacDecodeError, FlacEncodeError};
/// A padding block in a FLAC file.
#[derive(Debug)]
pub struct FlacPaddingBlock {
/// The length of this padding, in bytes.
pub size: u32,
}
impl FlacMetablockDecode for FlacPaddingBlock {
#[expect(clippy::expect_used)]
fn decode(data: &[u8]) -> Result<Self, FlacDecodeError> {
if data.iter().any(|x| *x != 0u8) {
return Err(FlacDecodeError::MalformedBlock);
}
Ok(Self {
size: data
.len()
.try_into()
.expect("padding size does not fit into u32"),
})
}
}
impl FlacMetablockEncode for FlacPaddingBlock {
fn get_len(&self) -> u32 {
self.size
}
fn encode(
&self,
is_last: bool,
with_header: bool,
target: &mut impl std::io::Write,
) -> Result<(), FlacEncodeError> {
if with_header {
let header = FlacMetablockHeader {
block_type: FlacMetablockType::Padding,
length: self.get_len(),
is_last,
};
header.encode(target)?;
}
std::io::copy(&mut std::io::repeat(0u8).take(self.size.into()), target)?;
return Ok(());
}
}

View File

@@ -0,0 +1,213 @@
use mime::Mime;
use std::{
fmt::Debug,
io::{Cursor, Read},
};
use super::{FlacMetablockDecode, FlacMetablockEncode, FlacMetablockHeader, FlacMetablockType};
use crate::{FlacDecodeError, FlacEncodeError, PictureType};
/// A picture metablock in a flac file
pub struct FlacPictureBlock {
/// The type of this picture
pub picture_type: PictureType,
/// The format of this picture
pub mime: Mime,
/// The description of this picture
pub description: String,
/// The width of this picture, in px
pub width: u32,
/// The height of this picture, in px
pub height: u32,
/// The bit depth of this picture
pub bit_depth: u32,
/// The color count of this picture (if indexed)
pub color_count: u32,
/// The image data
pub img_data: Vec<u8>,
}
impl Debug for FlacPictureBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FlacPicture")
.field("type", &self.picture_type)
.field("mime", &self.mime)
.field("img_data.len()", &self.img_data.len())
.finish()
}
}
impl FlacMetablockDecode for FlacPictureBlock {
fn decode(data: &[u8]) -> Result<Self, FlacDecodeError> {
let mut d = Cursor::new(data);
// This is re-used whenever we need to read four bytes
let mut block = [0u8; 4];
#[expect(clippy::map_err_ignore)]
d.read_exact(&mut block)
.map_err(|_| FlacDecodeError::MalformedBlock)?;
let picture_type = u32::from_be_bytes(block);
let picture_type = PictureType::from_idx(picture_type)
.ok_or(FlacDecodeError::InvalidPictureType(picture_type))?;
// Image format
let mime = {
d.read_exact(&mut block)
.map_err(|_err| FlacDecodeError::MalformedBlock)?;
#[expect(clippy::expect_used)]
let mime_length = u32::from_be_bytes(block)
.try_into()
.expect("mime length does not fit into usize");
let mut mime = vec![0u8; mime_length];
d.read_exact(&mut mime)
.map_err(|_err| FlacDecodeError::MalformedBlock)?;
String::from_utf8(mime)
.ok()
.and_then(|x| x.parse().ok())
.unwrap_or(mime::APPLICATION_OCTET_STREAM)
};
// Image description
let description = {
d.read_exact(&mut block)
.map_err(|_err| FlacDecodeError::MalformedBlock)?;
#[expect(clippy::expect_used)]
let desc_length = u32::from_be_bytes(block)
.try_into()
.expect("description length does not fit into usize");
let mut desc = vec![0u8; desc_length];
d.read_exact(&mut desc)
.map_err(|_err| FlacDecodeError::MalformedBlock)?;
String::from_utf8(desc)?
};
// Image width
#[expect(clippy::map_err_ignore)]
d.read_exact(&mut block)
.map_err(|_| FlacDecodeError::MalformedBlock)?;
let width = u32::from_be_bytes(block);
// Image height
#[expect(clippy::map_err_ignore)]
d.read_exact(&mut block)
.map_err(|_| FlacDecodeError::MalformedBlock)?;
let height = u32::from_be_bytes(block);
// Image bit depth
#[expect(clippy::map_err_ignore)]
d.read_exact(&mut block)
.map_err(|_| FlacDecodeError::MalformedBlock)?;
let depth = u32::from_be_bytes(block);
// Color count for indexed images
#[expect(clippy::map_err_ignore)]
d.read_exact(&mut block)
.map_err(|_| FlacDecodeError::MalformedBlock)?;
let color_count = u32::from_be_bytes(block);
// Image data length
let img_data = {
d.read_exact(&mut block)
.map_err(|_err| FlacDecodeError::MalformedBlock)?;
#[expect(clippy::expect_used)]
let data_length = u32::from_be_bytes(block)
.try_into()
.expect("image data length does not fit into usize");
let mut img_data = vec![0u8; data_length];
d.read_exact(&mut img_data)
.map_err(|_err| FlacDecodeError::MalformedBlock)?;
img_data
};
Ok(Self {
picture_type,
mime,
description,
width,
height,
bit_depth: depth,
color_count,
img_data,
})
}
}
impl FlacMetablockEncode for FlacPictureBlock {
#[expect(clippy::expect_used)]
fn get_len(&self) -> u32 {
(4 + (4 + self.mime.to_string().len())
+ (4 + self.description.len())
+ 4 + 4 + 4
+ 4 + (4 + self.img_data.len()))
.try_into()
.expect("picture block size does not fit into u32")
}
fn encode(
&self,
is_last: bool,
with_header: bool,
target: &mut impl std::io::Write,
) -> Result<(), FlacEncodeError> {
if with_header {
let header = FlacMetablockHeader {
block_type: FlacMetablockType::Picture,
length: self.get_len(),
is_last,
};
header.encode(target)?;
}
target.write_all(&self.picture_type.to_idx().to_be_bytes())?;
#[expect(clippy::expect_used)]
{
let mime = self.mime.to_string();
target.write_all(
&u32::try_from(mime.len())
.expect("mime length does not fit into u32")
.to_be_bytes(),
)?;
target.write_all(self.mime.to_string().as_bytes())?;
drop(mime);
target.write_all(
&u32::try_from(self.description.len())
.expect("description length does not fit into u32")
.to_be_bytes(),
)?;
target.write_all(self.description.as_bytes())?;
target.write_all(&self.width.to_be_bytes())?;
target.write_all(&self.height.to_be_bytes())?;
target.write_all(&self.bit_depth.to_be_bytes())?;
target.write_all(&self.color_count.to_be_bytes())?;
target.write_all(
&u32::try_from(self.img_data.len())
.expect("image data length does not fit into u32")
.to_be_bytes(),
)?;
}
target.write_all(&self.img_data)?;
return Ok(());
}
}

View File

@@ -0,0 +1,53 @@
use std::fmt::Debug;
use super::{FlacMetablockDecode, FlacMetablockEncode, FlacMetablockHeader, FlacMetablockType};
use crate::{FlacDecodeError, FlacEncodeError};
/// A seektable block in a flac file
pub struct FlacSeektableBlock {
/// The seek table
pub data: Vec<u8>,
}
impl Debug for FlacSeektableBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FlacSeektableBlock")
.field("data_len", &self.data.len())
.finish()
}
}
impl FlacMetablockDecode for FlacSeektableBlock {
fn decode(data: &[u8]) -> Result<Self, FlacDecodeError> {
Ok(Self { data: data.into() })
}
}
impl FlacMetablockEncode for FlacSeektableBlock {
#[expect(clippy::expect_used)]
fn get_len(&self) -> u32 {
self.data
.len()
.try_into()
.expect("seektable size does not fit into u32")
}
fn encode(
&self,
is_last: bool,
with_header: bool,
target: &mut impl std::io::Write,
) -> Result<(), FlacEncodeError> {
if with_header {
let header = FlacMetablockHeader {
block_type: FlacMetablockType::Seektable,
length: self.get_len(),
is_last,
};
header.encode(target)?;
}
target.write_all(&self.data)?;
return Ok(());
}
}

View File

@@ -0,0 +1,217 @@
use std::io::{Cursor, Read};
use super::{FlacMetablockDecode, FlacMetablockEncode, FlacMetablockHeader, FlacMetablockType};
use crate::{FlacDecodeError, FlacEncodeError};
/// A streaminfo block in a flac file
#[derive(Debug)]
pub struct FlacStreaminfoBlock {
/// The minimum block size (in samples) used in the stream.
pub min_block_size: u32,
/// The maximum block size (in samples) used in the stream.
/// (Minimum blocksize == maximum blocksize) implies a fixed-blocksize stream.
pub max_block_size: u32,
/// The minimum frame size (in bytes) used in the stream.
/// May be 0 to imply the value is not known.
pub min_frame_size: u32,
/// The minimum frame size (in bytes) used in the stream.
/// May be 0 to imply the value is not known.
pub max_frame_size: u32,
/// Sample rate in Hz. Though 20 bits are available,
/// the maximum sample rate is limited by the structure of frame headers to 655350Hz.
/// Also, a value of 0 is invalid.
pub sample_rate: u32,
/// (number of channels)-1. FLAC supports from 1 to 8 channels
pub channels: u8,
/// (bits per sample)-1. FLAC supports from 4 to 32 bits per sample.
pub bits_per_sample: u8,
/// Total samples in stream. 'Samples' means inter-channel sample, i.e. one second of 44.1Khz audio will have 44100 samples regardless of the number of channels. A value of zero here means the number of total samples is unknown.
pub total_samples: u128,
/// MD5 signature of the unencoded audio data. This allows the decoder to determine if an error exists in the audio data even when the error does not result in an invalid bitstream.
pub md5_signature: [u8; 16],
}
impl FlacMetablockDecode for FlacStreaminfoBlock {
fn decode(data: &[u8]) -> Result<Self, FlacDecodeError> {
let mut d = Cursor::new(data);
let min_block_size = {
let mut block = [0u8; 4];
#[expect(clippy::map_err_ignore)]
d.read_exact(&mut block[2..])
.map_err(|_| FlacDecodeError::MalformedBlock)?;
u32::from_be_bytes(block)
};
let max_block_size = {
let mut block = [0u8; 4];
#[expect(clippy::map_err_ignore)]
d.read_exact(&mut block[2..])
.map_err(|_| FlacDecodeError::MalformedBlock)?;
u32::from_be_bytes(block)
};
let min_frame_size = {
let mut block = [0u8; 4];
#[expect(clippy::map_err_ignore)]
d.read_exact(&mut block[1..])
.map_err(|_| FlacDecodeError::MalformedBlock)?;
u32::from_be_bytes(block)
};
let max_frame_size = {
let mut block = [0u8; 4];
#[expect(clippy::map_err_ignore)]
d.read_exact(&mut block[1..])
.map_err(|_| FlacDecodeError::MalformedBlock)?;
u32::from_be_bytes(block)
};
let (sample_rate, channels, bits_per_sample, total_samples) = {
let mut block = [0u8; 8];
#[expect(clippy::map_err_ignore)]
d.read_exact(&mut block)
.map_err(|_| FlacDecodeError::MalformedBlock)?;
(
// 20 bits: sample rate in hz
u32::from_be_bytes([0, block[0], block[1], block[2]]) >> 4,
// 3 bits: number of channels - 1.
// FLAC supports 1 - 8 channels.
((u8::from_le_bytes([block[2]]) & 0b0000_1110) >> 1) + 1,
// 5 bits: bits per sample - 1.
// FLAC supports 4 - 32 bps.
((u8::from_le_bytes([block[2]]) & 0b0000_0001) << 4)
+ ((u8::from_le_bytes([block[3]]) & 0b1111_0000) >> 4)
+ 1,
// 36 bits: total "cross-channel" samples in the stream.
// (one second of 44.1Khz audio will have 44100 samples regardless of the number of channels)
// Zero means we don't know.
u128::from_be_bytes([
0,
0,
0,
0,
//
0,
0,
0,
0,
//
0,
0,
0,
block[3] & 0b0000_1111,
//
block[4],
block[5],
block[6],
block[7],
]),
)
};
let md5_signature = {
let mut block = [0u8; 16];
#[expect(clippy::map_err_ignore)]
d.read_exact(&mut block)
.map_err(|_| FlacDecodeError::MalformedBlock)?;
block
};
Ok(Self {
min_block_size,
max_block_size,
min_frame_size,
max_frame_size,
sample_rate,
channels,
bits_per_sample,
total_samples,
md5_signature,
})
}
}
impl FlacMetablockEncode for FlacStreaminfoBlock {
fn get_len(&self) -> u32 {
34
}
fn encode(
&self,
is_last: bool,
with_header: bool,
target: &mut impl std::io::Write,
) -> Result<(), FlacEncodeError> {
if with_header {
let header = FlacMetablockHeader {
block_type: FlacMetablockType::Streaminfo,
length: self.get_len(),
is_last,
};
header.encode(target)?;
}
target.write_all(&self.min_block_size.to_be_bytes()[2..])?;
target.write_all(&self.max_block_size.to_be_bytes()[2..])?;
target.write_all(&self.min_frame_size.to_be_bytes()[1..])?;
target.write_all(&self.max_frame_size.to_be_bytes()[1..])?;
// Layout of the next 8 bytes:
// [8]: full bytes
// [4 ]: first 4 bits are from this
// [ 3]: next 3 bits are from this
//
// [8][8][4 ]: Sample rate
// [ ][ ][ 3 ]: channels
// [ ][ ][ 1][4 ]: bits per sample
// [ ][ ][ ][ 4][8 x 4]: total samples
let mut out = [0u8; 8];
let sample_rate = &self.sample_rate.to_be_bytes()[1..4];
out[0] = (sample_rate[0] << 4) & 0b1111_0000;
out[0] |= (sample_rate[1] >> 4) & 0b0000_1111;
out[1] = (sample_rate[1] << 4) & 0b1111_0000;
out[1] |= (sample_rate[2] >> 4) & 0b000_1111;
out[2] = (sample_rate[2] << 4) & 0b1111_0000;
let channels = self.channels - 1;
out[2] |= (channels << 1) & 0b0000_1110;
let bits_per_sample = self.bits_per_sample - 1;
out[2] |= (bits_per_sample >> 4) & 0b0000_0001;
out[3] |= (bits_per_sample << 4) & 0b1111_0000;
let total_samples = self.total_samples.to_be_bytes();
out[3] |= total_samples[10] & 0b0000_1111;
out[4] = total_samples[12];
out[5] = total_samples[13];
out[6] = total_samples[14];
out[7] = total_samples[15];
target.write_all(&out)?;
target.write_all(&self.md5_signature)?;
return Ok(());
}
}