Skip to content

Commit

Permalink
Always write opus data at 48khz.
Browse files Browse the repository at this point in the history
  • Loading branch information
LaurentMazare committed Aug 22, 2024
1 parent 9fd395c commit 0b2fb8d
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 32 deletions.
13 changes: 10 additions & 3 deletions py_src/sphn/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ def read_opus(filename):
"""
Reads the whole content of an ogg/opus encoded file.
This returns a two dimensional array as well as the sample rate.
This returns a two dimensional array as well as the sample rate. Currently all opus audio is
encoded at 48kHz so this value is always returned.
"""
pass

Expand All @@ -36,13 +37,19 @@ def read_opus_bytes(bytes):
"""
Reads bytes corresponding to an ogg/opus encoded file.
This returns a two dimensional array as well as the sample rate.
This returns a two dimensional array as well as the sample rate. Currently all opus audio is
encoded at 48kHz so this value is always returned.
"""
pass

@staticmethod
def write_opus(filename, data, sample_rate):
""" """
"""
Writes an opus file containing the input pcm data.
Opus content is always encoded at 48kHz so the pcm data is resampled if sample_rate is
different from 48000.
"""
pass

@staticmethod
Expand Down
13 changes: 7 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,25 +188,26 @@ fn write_wav(
Ok(())
}

/// Writes an opus file containing the input pcm data.
///
/// Opus content is always encoded at 48kHz so the pcm data is resampled if sample_rate is
/// different from 48000.
#[pyfunction]
#[pyo3(signature = (filename, data, sample_rate, resample_to=None))]
#[pyo3(signature = (filename, data, sample_rate))]
fn write_opus(
filename: std::path::PathBuf,
data: numpy::PyReadonlyArray1<f32>,
sample_rate: u32,
resample_to: Option<u32>,
) -> PyResult<()> {
let w = std::fs::File::create(&filename).w_f(&filename)?;
let mut w = std::io::BufWriter::new(w);
let data = data.as_array();
match data.as_slice() {
None => {
let data = data.to_vec();
opus::write_ogg_mono(&mut w, data.as_ref(), sample_rate, resample_to).w_f(&filename)?
}
Some(data) => {
opus::write_ogg_mono(&mut w, data, sample_rate, resample_to).w_f(&filename)?
opus::write_ogg_mono(&mut w, data.as_ref(), sample_rate).w_f(&filename)?
}
Some(data) => opus::write_ogg_mono(&mut w, data, sample_rate).w_f(&filename)?,
}
Ok(())
}
Expand Down
35 changes: 13 additions & 22 deletions src/opus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,32 +129,29 @@ fn write_opus_tags<W: std::io::Write>(w: &mut W) -> std::io::Result<()> {
Ok(())
}

fn write_ogg_<W: std::io::Write>(
// Opus audio is always encoded at 48kHz, this function assumes that it is the case. The
// input_sample_rate is only indicative of the sample rate of the original source (which appears in
// the opus header).
fn write_ogg_48khz<W: std::io::Write>(
w: &mut W,
pcm: &[f32],
sample_rate: u32,
input_sample_rate: u32,
stereo: bool,
) -> Result<()> {
match sample_rate {
8000 | 12000 | 16000 | 24000 | 48000 => {}
sample_rate => {
anyhow::bail!("unsupported sample rate for opus {sample_rate}, try resample_to=48000")
}
}
let mut pw = ogg::PacketWriter::new(w);
let channels = if stereo { 2 } else { 1 };

// Write the opus headers and tags
let mut head = Vec::new();
write_opus_header(&mut head, channels as u8, sample_rate)?;
write_opus_header(&mut head, channels as u8, input_sample_rate)?;
pw.write_packet(head, 42, ogg::PacketWriteEndInfo::EndPage, 0)?;
let mut tags = Vec::new();
write_opus_tags(&mut tags)?;
pw.write_packet(tags, 42, ogg::PacketWriteEndInfo::EndPage, 0)?;

// Write the actual pcm data
let mut encoder =
opus::Encoder::new(sample_rate, opus::Channels::Mono, opus::Application::Voip)?;
opus::Encoder::new(OPUS_SAMPLE_RATE, opus::Channels::Mono, opus::Application::Voip)?;
let mut out_encoded = vec![0u8; 50_000];

let mut total_data = 0;
Expand All @@ -174,17 +171,11 @@ fn write_ogg_<W: std::io::Write>(
Ok(())
}

pub fn write_ogg_mono<W: std::io::Write>(
w: &mut W,
pcm: &[f32],
sample_rate: u32,
resample_to: Option<u32>,
) -> Result<()> {
match resample_to {
None => write_ogg_(w, pcm, sample_rate, false),
Some(resample_to) => {
let pcm = crate::audio::resample(pcm, sample_rate as usize, resample_to as usize)?;
write_ogg_(w, &pcm, resample_to, false)
}
pub fn write_ogg_mono<W: std::io::Write>(w: &mut W, pcm: &[f32], sample_rate: u32) -> Result<()> {
if sample_rate == OPUS_SAMPLE_RATE {
write_ogg_48khz(w, pcm, sample_rate, false)
} else {
let pcm = crate::audio::resample(pcm, sample_rate as usize, OPUS_SAMPLE_RATE as usize)?;
write_ogg_48khz(w, &pcm, sample_rate, false)
}
}
2 changes: 1 addition & 1 deletion test/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
print(data.shape, sr)

sphn.write_wav("bria.wav", data[0], sr)
sphn.write_opus("bria.opus", data[0], sr, resample_to=48000)
sphn.write_opus("bria.opus", data[0], sr)

0 comments on commit 0b2fb8d

Please sign in to comment.