Skip to content

Commit

Permalink
wav: Handle incorrect channel mask. (#180)
Browse files Browse the repository at this point in the history
A channel mask of 0 is a valid, but ambiguous case. Follow
Microsoft's recommendations for handling this case and
allowing playback.
  • Loading branch information
jesnor committed Feb 2, 2023
1 parent 412f44d commit ad4ba8f
Showing 1 changed file with 37 additions and 6 deletions.
43 changes: 37 additions & 6 deletions symphonia-format-wav/src/chunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,42 @@ impl ParseChunkTag for NullChunks {
}
}

fn fix_channel_mask(mut channel_mask: u32, n_channels: u16) -> u32 {
let channel_diff = n_channels as i32 - channel_mask.count_ones() as i32;

if channel_diff != 0 {
info!("Channel mask not set correctly, channel positions may be incorrect!");
}

// Check that the number of ones in the channel mask match the number of channels.
if channel_diff > 0 {
// Too few ones in mask so add extra ones above the most significant one
let shift = 32 - (!channel_mask).leading_ones();
channel_mask |= ((1 << channel_diff) - 1) << shift;
}
else {
// Too many ones in mask so remove the most significant extra ones
while channel_mask.count_ones() != n_channels as u32 {
let highest_one = 31 - (!channel_mask).leading_ones();
channel_mask &= !(1 << highest_one);
}
}

channel_mask
}

#[test]
fn test_fix_channel_mask() {
// Too few
assert_eq!(fix_channel_mask(0, 9), 0b111111111);
assert_eq!(fix_channel_mask(0b101000, 5), 0b111101000);

// Too many
assert_eq!(fix_channel_mask(0b1111111, 0), 0);
assert_eq!(fix_channel_mask(0b101110111010, 5), 0b10111010);
assert_eq!(fix_channel_mask(0xFFFFFFFF, 8), 0b11111111);
}

/// `ChunksReader` reads chunks from a `ByteStream`. It is generic across a type, usually an enum,
/// implementing the `ParseChunkTag` trait. When a new chunk is encountered in the stream,
/// `parse_tag` on T is called to return an object capable of parsing/reading that chunk or `None`.
Expand Down Expand Up @@ -394,12 +430,7 @@ impl WaveFormatChunk {
);
}

let channel_mask = reader.read_u32()?;

// The number of ones in the channel mask should match the number of channels.
if channel_mask.count_ones() != u32::from(n_channels) {
return decode_error("wav: channel mask mismatch with number of channels for fmt_ext");
}
let channel_mask = fix_channel_mask(reader.read_u32()?, n_channels);

// Try to map channels.
let channels = match Channels::from_bits(channel_mask) {
Expand Down

0 comments on commit ad4ba8f

Please sign in to comment.