Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions src/alaw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ pub fn decode_alaw(encoded: u8) -> i16 {
}

// encoding algorithm is based on "A-Law and mu-Law Companding Implementations Using the TMS320C54x,
// Application Note: SPRA163A", page 16: https://www.ti.com/lit/an/spra163a/spra163a.pdf
// Application Note: SPRA163A", pages 18 and 23: https://www.ti.com/lit/an/spra163a/spra163a.pdf
// the segment (chord) is the position of the highest set bit, so counting leading zeros gives it
// directly, without the linear to A-law table on page 16. the application note does the same with
// the EXP instruction, which "allows the extraction of the most significant bits without requiring
// a look-up table" (page 18), and determines the A-law chord that way on page 23
// see also https://en.wikipedia.org/wiki/G.711#A-law

/// Encodes a linear 16-bit signed integer sample value to a 8-bit encoded G.711 A-law value.
Expand All @@ -60,24 +64,20 @@ pub fn encode_alaw(linear: i16) -> u8 {
};
#[allow(clippy::cast_sign_loss)] // sign loss is expected and handled after the cast to u16
let linear = (linear >> 3) as u16;
let inputval = if sign == 0x80 {
let inputval = u32::from(if sign == 0x80 {
// make a positive value using 1s' complement (a tip from wikipedia)
linear ^ 0xffff
} else {
linear
};
let compressed_code_word: u16 = match inputval {
#[allow(clippy::identity_op)]
0b000000000000..=0b000000011111 => 0b000_0000 | (inputval & 0b000000011110) >> 1,
0b000000100000..=0b000000111111 => 0b001_0000 | (inputval & 0b000000011110) >> 1,
0b000001000000..=0b000001111111 => 0b010_0000 | (inputval & 0b000000111100) >> 2,
0b000010000000..=0b000011111111 => 0b011_0000 | (inputval & 0b000001111000) >> 3,
0b000100000000..=0b000111111111 => 0b100_0000 | (inputval & 0b000011110000) >> 4,
0b001000000000..=0b001111111111 => 0b101_0000 | (inputval & 0b000111100000) >> 5,
0b010000000000..=0b011111111111 => 0b110_0000 | (inputval & 0b001111000000) >> 6,
0b100000000000..=0b111111111111 => 0b111_0000 | (inputval & 0b011110000000) >> 7,
4096.. => 0b111_1111
};
});
// the number of bits inputval has beyond the first segment, which ends at 0b000000011111.
// the or never moves the highest set bit, but it makes inputval nonzero and at least 5 bits
// long, so that the bit count needs no zero check and the subtraction cannot underflow.
// inputval never needs more than 12 bits, so the segment is always 0..=7
let segment = (u32::BITS - (inputval | 0b11111).leading_zeros()) - 5;
// segments 0 and 1 have the same step size, so they use the same shift
let shift = if segment < 2 { 1 } else { segment };
let compressed_code_word: u32 = (segment << 4) | ((inputval >> shift) & 0b1111);
#[allow(clippy::cast_possible_truncation)] // compressed_code_word is always less than 255
let result = (sign | compressed_code_word as u8) ^ 0xd5;
result
Expand Down
28 changes: 14 additions & 14 deletions src/ulaw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ pub fn decode_ulaw(encoded: u8) -> i16 {
}

// encoding algorithm is based on "A-Law and mu-Law Companding Implementations Using the TMS320C54x,
// Application Note: SPRA163A", page 13: https://www.ti.com/lit/an/spra163a/spra163a.pdf
// Application Note: SPRA163A", pages 18 and 20: https://www.ti.com/lit/an/spra163a/spra163a.pdf
// the segment (chord) is the position of the highest set bit, so counting leading zeros gives it
// directly, without the binary encoding table on page 13. the application note does the same with
// the EXP instruction, which "allows the extraction of the most significant bits without requiring
// a look-up table" (page 18), giving mchd = (0x19 - T|EXP) << 4 on page 20
// see also https://en.wikipedia.org/wiki/G.711#μ-law

/// Encodes a linear 16-bit signed integer sample value to a 8-bit encoded G.711 μ-law value.
Expand All @@ -60,24 +64,20 @@ pub fn encode_ulaw(linear: i16) -> u8 {
};
#[allow(clippy::cast_sign_loss)] // sign loss is expected and handled after the cast to u16
let linear = (linear >> 2) as u16;
let absval = if sign == 0x80 {
let absval = u32::from(if sign == 0x80 {
// make a positive value using 1s' complement (a tip from wikipedia)
linear ^ 0xffff
} else {
linear
};
});
let inputval = absval + 33;
let compressed_code_word = match inputval {
#[allow(clippy::identity_op)]
0b0000000000000..=0b0000000111111 => 0b000_0000 | (inputval & 0b0000000011110) >> 1,
0b0000001000000..=0b0000001111111 => 0b001_0000 | (inputval & 0b0000000111100) >> 2,
0b0000010000000..=0b0000011111111 => 0b010_0000 | (inputval & 0b0000001111000) >> 3,
0b0000100000000..=0b0000111111111 => 0b011_0000 | (inputval & 0b0000011110000) >> 4,
0b0001000000000..=0b0001111111111 => 0b100_0000 | (inputval & 0b0000111100000) >> 5,
0b0010000000000..=0b0011111111111 => 0b101_0000 | (inputval & 0b0001111000000) >> 6,
0b0100000000000..=0b0111111111111 => 0b110_0000 | (inputval & 0b0011110000000) >> 7,
0b1000000000000..=0b1111111111111 => 0b111_0000 | (inputval & 0b0111100000000) >> 8,
8192.. => 0b111_1111
// the number of bits inputval has beyond the first segment, which ends at 0b0000000111111
let segment = (u32::BITS - inputval.leading_zeros()).saturating_sub(6);
let compressed_code_word: u32 = if segment >= 8 {
// inputval is past the last segment
0b111_1111
} else {
(segment << 4) | ((inputval >> (segment + 1)) & 0b1111)
};
#[allow(clippy::cast_possible_truncation)] // compressed_code_word is always less than 255
let result = (sign | compressed_code_word as u8) ^ 0xff;
Expand Down
Loading