pub enum ResidualPartition<const RICE_MAX: u32, I> {
Standard {
rice: BitCount<RICE_MAX>,
residuals: Vec<I>,
},
Escaped {
escape_size: SignedBitCount<0b11111>,
residuals: Vec<I>,
},
Constant {
partition_len: usize,
},
}Expand description
An individual residual block partition
Each partition consists of a Rice parameter followed by an optional escape code and signed residual values. The number of bits to read for the Rice parameters depends on if we’re using coding method 0 or 1. If the Rice parameter equals the maximum, it means the partition is escaped in some way (this is an uncommon case).
| Bits | Meaning |
|---|---|
| 4 or 5 | Rice parameter |
| (5) | escape code if parameter is 1111 or 11111 |
The total number of residuals in the partition is:
block size ÷ partition count - predictor order
for the first partition, and:
block size ÷ partition count
for subsequent partitions.
If the partition is escaped, we read an additional 5 bit value to determine the size of each signed residual in the partition. If the escape code is 0, all the residuals in the partition are 0 (this is an even more uncommon case).
§Example
use flac_codec::stream::ResidualPartition;
use bitstream_io::{BitReader, BitRead, BigEndian, BitCount};
let data: &[u8] = &[
0b0001_01_0_0, // Rice code + residuals
0b01_0_001_0_0,
0b01_0_001_0_0,
0b01_0_001_0_0,
0b01_0_001_0_0,
0b01_0_001_0_0,
0b01_0_001_0_0,
0b01_0_001_0_0,
0b01_0_001_0_0,
0b01_0_001_0_0,
];
let mut r = BitReader::endian(data, BigEndian);
assert_eq!(
r.parse_using::<ResidualPartition<0b1111, i32>>(19).unwrap(),
ResidualPartition::Standard {
rice: BitCount::new::<0b0001>(),
residuals: vec![
1, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2
],
},
);Each individual residual is a unary value with a stop bit of 1 for the most-significant bits, followed by “Rice” number of bits as the least significant bits, combined into a single unsigned value.
Unary-encoding is simply counting the number of 0 bits before the next 1 bit:
| Bits | Value |
|---|---|
1 | 0 |
01 | 1 |
001 | 2 |
0001 | 3 |
00001 | 4 |
| ⋮ |
Unlike regular twos-complement signed values, individual residuals are stored with the sign in the least significant bit position. They can be transformed from unsigned to signed like:
fn unsigned_to_signed(unsigned: u32) -> i32 {
if (unsigned & 1) == 1 {
// negative residual
-((unsigned >> 1) as i32) - 1
} else {
// positive residual
(unsigned >> 1) as i32
}
}In our example, above, the Rice parameter happens to be 1
and all the sign bits happen to be 0, so the value of each
signed residual is simply its preceding unary value
(which are all 01 or 001, meaning 1 and 2).
As one can see, the smaller the value each residual has, the smaller it can be when written to disk. And the key to making residual values small is to choose predictor coefficients which best match the input signal. The more accurate the prediction, the less difference there is between the predicted values and the actual values - which means smaller residuals - which means better compression.
Variants§
Standard
A standard residual partition
Fields
Escaped
An escaped residual partition
Fields
escape_size: SignedBitCount<0b11111>The size of each residual in bits
Constant
A partition in which all residuals are 0
Trait Implementations§
Source§impl<const RICE_MAX: u32, I: SignedInteger> FromBitStreamUsing for ResidualPartition<RICE_MAX, I>
impl<const RICE_MAX: u32, I: SignedInteger> FromBitStreamUsing for ResidualPartition<RICE_MAX, I>
Source§impl<const RICE_MAX: u32, I: PartialEq> PartialEq for ResidualPartition<RICE_MAX, I>
impl<const RICE_MAX: u32, I: PartialEq> PartialEq for ResidualPartition<RICE_MAX, I>
Source§fn eq(&self, other: &ResidualPartition<RICE_MAX, I>) -> bool
fn eq(&self, other: &ResidualPartition<RICE_MAX, I>) -> bool
self and other values to be equal, and is used by ==.