Created
October 17, 2021 03:21
-
-
Save Ralith/a27ae71f88b4052fddddb0bb937f30e1 to your computer and use it in GitHub Desktop.
Decoding opus files
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fn decode_opus(data: &[u8]) -> Result<Arc<oddio::Frames<f32>>> { | |
let _span = tracy_client::span!("opus decode"); | |
let mut ogg = ogg::PacketReader::new(std::io::Cursor::new(data)); | |
let mut decoder = opus::Decoder::new(SAMPLE_RATE, opus::Channels::Mono).unwrap(); | |
let mut buffer = Vec::new(); | |
let mut serial = None; | |
let mut pre_skip = 0; | |
let mut scale = 1.0; | |
let mut seen_comment = false; | |
while let Some(packet) = ogg.read_packet().context("reading ogg packet")? { | |
if serial.is_none() && packet.first_in_stream() { | |
// Is this an Opus stream? | |
if !packet.data.starts_with(ID_MAGIC) || packet.data[8] & 0xF0 != 0 { | |
continue; | |
} | |
// Is it sensible? | |
let channels = packet.data[9]; | |
if channels != 1 { | |
bail!("unsupported channel count: {}", channels); | |
} | |
serial = Some(packet.stream_serial()); | |
pre_skip = usize::from(u16::from_le_bytes(packet.data[10..12].try_into().unwrap())); | |
let output_gain = i16::from_le_bytes(packet.data[16..18].try_into().unwrap()); | |
scale = 10.0f32.powf(f32::from(output_gain) / (20.0 * 256.0)); | |
let channel_mapping_family = packet.data[18]; | |
if channel_mapping_family != 0 { | |
bail!( | |
"unsupported channel mapping family {}", | |
channel_mapping_family | |
); | |
} | |
continue; | |
} | |
// Is this part of the Opus stream? | |
if Some(packet.stream_serial()) != serial { | |
continue; | |
} | |
// Have we passed the comment header yet? | |
if !seen_comment { | |
seen_comment = true; | |
if packet.data.starts_with(COMMENT_MAGIC) { | |
continue; | |
} else { | |
error!("missing comment header"); | |
} | |
} | |
// Decode samples | |
let n = decoder | |
.get_nb_samples(&packet.data) | |
.context("decoding opus packet")?; | |
let start = buffer.len(); | |
buffer.resize(start + n, 0.0); | |
decoder | |
.decode_float(&packet.data, &mut buffer[start..], false) | |
.context("decoding opus")?; | |
if pre_skip > 0 { | |
let skip = pre_skip.min(buffer.len()); | |
buffer.drain(0..skip); | |
pre_skip -= skip; | |
} | |
} | |
for sample in &mut buffer { | |
*sample *= scale; | |
} | |
trace!( | |
"loaded {:.02} seconds of audio", | |
buffer.len() as f32 / SAMPLE_RATE as f32 | |
); | |
Ok(oddio::Frames::from_slice(SAMPLE_RATE, &buffer)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment