use candle::{Result, Tensor};
pub fn normalize_loudness(
wav: &Tensor,
sample_rate: u32,
loudness_compressor: bool,
) -> Result<Tensor> {
let energy = wav.sqr()?.mean_all()?.sqrt()?.to_vec0::<f32>()?;
if energy < 2e-3 {
return Ok(wav.clone());
}
let wav_array = wav.to_vec1::<f32>()?;
let mut meter = crate::bs1770::ChannelLoudnessMeter::new(sample_rate);
meter.push(wav_array.into_iter());
let power = meter.as_100ms_windows();
let loudness = match crate::bs1770::gated_mean(power) {
None => return Ok(wav.clone()),
Some(gp) => gp.loudness_lkfs() as f64,
};
let delta_loudness = -14. - loudness;
let gain = 10f64.powf(delta_loudness / 20.);
let wav = (wav * gain)?;
if loudness_compressor {
wav.tanh()
} else {
Ok(wav)
}
}
#[cfg(feature = "symphonia")]
pub fn pcm_decode<P: AsRef<std::path::Path>>(path: P) -> Result<(Vec<f32>, u32)> {
use symphonia::core::audio::conv::FromSample;
use symphonia::core::audio::sample::Sample;
use symphonia::core::audio::{Audio, AudioBuffer, GenericAudioBufferRef};
use symphonia::core::codecs::audio::AudioDecoderOptions;
use symphonia::core::formats::probe::Hint;
use symphonia::core::formats::TrackType;
fn conv<T>(samples: &mut Vec<f32>, data: &AudioBuffer<T>)
where
T: Sample,
f32: FromSample<T>,
{
if let Some(plane) = data.plane(0) {
samples.extend(plane.iter().map(|v| f32::from_sample(*v)))
}
}
let src = std::fs::File::open(path).map_err(candle::Error::wrap)?;
let mss = symphonia::core::io::MediaSourceStream::new(Box::new(src), Default::default());
let hint = Hint::new();
let meta_opts: symphonia::core::meta::MetadataOptions = Default::default();
let fmt_opts: symphonia::core::formats::FormatOptions = Default::default();
let mut format = symphonia::default::get_probe()
.probe(&hint, mss, fmt_opts, meta_opts)
.map_err(candle::Error::wrap)?;
let (track_id, sample_rate, mut decoder) = {
let track = format
.first_track_known_codec(TrackType::Audio)
.ok_or_else(|| candle::Error::Msg("no supported audio tracks".to_string()))?;
let codec_params = track
.codec_params
.as_ref()
.and_then(|params| params.audio())
.ok_or_else(|| candle::Error::Msg("no supported audio tracks".to_string()))?;
let dec_opts: AudioDecoderOptions = Default::default();
let decoder = symphonia::default::get_codecs()
.make_audio_decoder(codec_params, &dec_opts)
.map_err(|_| candle::Error::Msg("unsupported codec".to_string()))?;
(track.id, codec_params.sample_rate.unwrap_or(0), decoder)
};
let mut pcm_data = Vec::new();
while let Ok(Some(packet)) = format.next_packet() {
while !format.metadata().is_latest() {
format.metadata().pop();
}
if packet.track_id != track_id {
continue;
}
match decoder.decode(&packet).map_err(candle::Error::wrap)? {
GenericAudioBufferRef::F32(buf) => {
if let Some(plane) = buf.plane(0) {
pcm_data.extend(plane)
}
}
GenericAudioBufferRef::U8(data) => conv(&mut pcm_data, data),
GenericAudioBufferRef::U16(data) => conv(&mut pcm_data, data),
GenericAudioBufferRef::U24(data) => conv(&mut pcm_data, data),
GenericAudioBufferRef::U32(data) => conv(&mut pcm_data, data),
GenericAudioBufferRef::S8(data) => conv(&mut pcm_data, data),
GenericAudioBufferRef::S16(data) => conv(&mut pcm_data, data),
GenericAudioBufferRef::S24(data) => conv(&mut pcm_data, data),
GenericAudioBufferRef::S32(data) => conv(&mut pcm_data, data),
GenericAudioBufferRef::F64(data) => conv(&mut pcm_data, data),
}
}
Ok((pcm_data, sample_rate))
}
#[cfg(feature = "rubato")]
pub fn resample(pcm_in: &[f32], sr_in: u32, sr_out: u32) -> Result<Vec<f32>> {
use rubato::audioadapter_buffers::direct::SequentialSlice;
use rubato::Resampler;
let mut resampler = rubato::Fft::<f32>::new(
sr_in as usize,
sr_out as usize,
1024,
1,
rubato::FixedSync::Both,
)
.map_err(candle::Error::wrap)?;
let buffer_in = SequentialSlice::new(pcm_in, 1, pcm_in.len()).map_err(candle::Error::wrap)?;
let pcm_out = resampler
.process_all(&buffer_in, pcm_in.len(), None)
.map_err(candle::Error::wrap)?;
Ok(pcm_out.take_data())
}