blob: b8020a3af052e76c2b9ab044b0e8ef92de55de08 [file] [log] [blame] [edit]
// Copyright (C) 2025, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::ffi::{CCodecConfig, CCodecType};
use crate::streamer::FifoFrame;
#[cfg(feature = "lc3")]
use crate::lc3::{Lc3CConfig, Lc3Encoder};
#[cfg(feature = "opus")]
use crate::opus::{OpusCConfig, OpusEncoder};
pub(crate) use crate::streamer::AudioConfig;
#[derive(Clone, Copy)]
pub enum CodecConfig {
#[cfg(feature = "lc3")]
Lc3(Lc3CConfig),
#[cfg(feature = "opus")]
Opus(OpusCConfig),
}
impl CodecConfig {
pub fn from(ty: &CCodecType, config: &CCodecConfig) -> Self {
match ty {
#[cfg(feature = "lc3")]
CCodecType::Lc3 => {
// SAFETY: The C Code gives an LC3 configuration type according
// to the selected LC3 codec type.
unsafe { CodecConfig::Lc3(config.lc3) }
}
#[cfg(feature = "opus")]
CCodecType::Opus => {
// SAFETY: The C Code gives an Opus configuration type according
// to the selected Opus codec type.
unsafe { CodecConfig::Opus(config.opus) }
}
#[cfg(not(feature = "lc3"))]
CCodecType::Lc3 => panic!("LC3 feature not enabled"),
#[cfg(not(feature = "opus"))]
CCodecType::Opus => panic!("Opus feature not enabled"),
}
}
}
pub fn validate_encoder(config: &AudioConfig, channels: usize) -> Result<(), String> {
match config.codec {
#[cfg(feature = "lc3")]
CodecConfig::Lc3(..) => Lc3Encoder::validate(config, channels),
#[cfg(feature = "opus")]
CodecConfig::Opus(..) => OpusEncoder::validate(config, channels),
}
}
pub fn new_encoder(config: &AudioConfig, channels: usize, max_enc_bytes: usize) -> Box<dyn Encode> {
match config.codec {
#[cfg(feature = "lc3")]
CodecConfig::Lc3(..) => Box::new(Lc3Encoder::new(config, channels, max_enc_bytes)),
#[cfg(feature = "opus")]
CodecConfig::Opus(..) => Box::new(OpusEncoder::new(config, channels, max_enc_bytes)),
}
}
pub trait Encode {
fn encode(&self, pcm: &PcmFrame) -> Vec<u8>;
}
pub struct PcmFrame<'a> {
channels: usize,
bitdepth: usize,
stride: usize,
data: &'a [u8],
}
impl<'a> PcmFrame<'a> {
pub fn from_fifo(fifo: &'a FifoFrame<'a>) -> Self {
Self { data: fifo.data(), bitdepth: fifo.bitdepth, channels: fifo.channels, stride: 1 }
}
pub fn channel(self, idx: usize) -> Self {
assert!(idx < self.channels);
let offset = idx * (self.bitdepth / 8);
Self { data: &self.data[offset..], channels: 1, stride: self.channels, ..self }
}
pub fn to_vec_f32(&self) -> Vec<f32> {
match self.bitdepth {
16 => Self::to_vec_f32_impl::<16usize>(self.data, self.stride),
24 => Self::to_vec_f32_impl::<24usize>(self.data, self.stride),
32 => Self::to_vec_f32_impl::<32usize>(self.data, self.stride),
_ => unimplemented!(),
}
}
fn to_vec_f32_impl<const BITDEPTH: usize>(data: &[u8], stride: usize) -> Vec<f32> {
data.chunks(BITDEPTH / 8)
.step_by(stride)
.map(|bytes| {
let it = bytes.iter().enumerate();
let v = it.fold(0i32, |v, (i, b)| v | ((*b as i32) << (i * 8 + (32 - BITDEPTH))));
v as f32 * (-31f32).exp2()
})
.collect()
}
}