Merge pull request #104 from kslr/main
fix: 修正Amlogic A113X PDM 24→16 位对齐导致的约 -48 dB 衰减
This commit is contained in:
@@ -16,6 +16,8 @@ enum State {
|
||||
Recording,
|
||||
}
|
||||
|
||||
const A113_CAPTURE_BITS_PER_SAMPLE: u16 = 32;
|
||||
|
||||
pub struct AudioRecorder {
|
||||
state: Arc<Mutex<State>>,
|
||||
arecord_thread: Arc<Mutex<Option<Child>>>,
|
||||
@@ -69,9 +71,97 @@ impl AudioRecorder {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let config = config.unwrap_or_else(|| (*AUDIO_CONFIG).clone());
|
||||
let requested_config = config.unwrap_or_else(|| (*AUDIO_CONFIG).clone());
|
||||
let capture_config = capture_config_for_recording(&requested_config);
|
||||
let mut arecord_thread = spawn_arecord(&capture_config)?;
|
||||
|
||||
let mut arecord_thread = Command::new("arecord")
|
||||
let mut stdout = arecord_thread.stdout.take().unwrap();
|
||||
let read_thread = tokio::spawn(async move {
|
||||
let bytes_per_sample = (capture_config.bits_per_sample.max(8) / 8) as usize;
|
||||
let bytes_per_frame = bytes_per_sample * capture_config.channels.max(1) as usize;
|
||||
let target_frames = capture_config.buffer_size.max(1) as usize;
|
||||
let read_frames = capture_config.period_size.max(1) as usize;
|
||||
let target_size = target_frames * bytes_per_frame;
|
||||
let read_size = read_frames * bytes_per_frame;
|
||||
|
||||
let mut accumulated_data = Vec::new();
|
||||
let mut buffer = vec![0u8; read_size];
|
||||
|
||||
loop {
|
||||
match stdout.read(&mut buffer).await {
|
||||
Ok(size) if size > 0 => {
|
||||
accumulated_data.extend_from_slice(&buffer[..size]);
|
||||
while accumulated_data.len() >= target_size {
|
||||
let data_to_send =
|
||||
accumulated_data.drain(..target_size).collect::<Vec<u8>>();
|
||||
let data_to_send =
|
||||
transform_stream_chunk(data_to_send, &requested_config, &capture_config);
|
||||
if !data_to_send.is_empty() {
|
||||
let _ = on_stream(data_to_send).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
let _ = AudioRecorder::instance().stop_recording().await;
|
||||
});
|
||||
|
||||
self.arecord_thread.lock().await.replace(arecord_thread);
|
||||
self.read_thread.lock().await.replace(read_thread);
|
||||
|
||||
*state = State::Recording;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn capture_config_for_recording(requested: &AudioConfig) -> AudioConfig {
|
||||
let mut capture = requested.clone();
|
||||
if requested.bits_per_sample == 16 {
|
||||
capture.bits_per_sample = A113_CAPTURE_BITS_PER_SAMPLE;
|
||||
}
|
||||
capture
|
||||
}
|
||||
|
||||
fn transform_stream_chunk(
|
||||
chunk: Vec<u8>,
|
||||
requested: &AudioConfig,
|
||||
capture: &AudioConfig,
|
||||
) -> Vec<u8> {
|
||||
if requested.bits_per_sample != 16 || capture.bits_per_sample != A113_CAPTURE_BITS_PER_SAMPLE {
|
||||
return chunk;
|
||||
}
|
||||
convert_a113_s32_to_s16(&chunk)
|
||||
}
|
||||
|
||||
fn convert_a113_s32_to_s16(chunk: &[u8]) -> Vec<u8> {
|
||||
if chunk.len() % 4 != 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let frame_count = chunk.len() / 4;
|
||||
let mut out = vec![0u8; frame_count * 2];
|
||||
|
||||
for frame in 0..frame_count {
|
||||
let base = frame * 4;
|
||||
let sample = i32::from_le_bytes([
|
||||
chunk[base],
|
||||
chunk[base + 1],
|
||||
chunk[base + 2],
|
||||
chunk[base + 3],
|
||||
]);
|
||||
// A113 PDM data lives in lower 24 bits of S32_LE: shift by 8 (not 16).
|
||||
let mapped = (sample >> 8).clamp(i16::MIN as i32, i16::MAX as i32) as i16;
|
||||
let out_base = frame * 2;
|
||||
out[out_base..out_base + 2].copy_from_slice(&mapped.to_le_bytes());
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
fn spawn_arecord(config: &AudioConfig) -> Result<Child, AppError> {
|
||||
let child = Command::new("arecord")
|
||||
.args([
|
||||
"--quiet",
|
||||
"-t",
|
||||
@@ -91,36 +181,5 @@ impl AudioRecorder {
|
||||
])
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
let mut stdout = arecord_thread.stdout.take().unwrap();
|
||||
let read_thread = tokio::spawn(async move {
|
||||
let size = (config.bits_per_sample / 8) as usize;
|
||||
let target_size = config.buffer_size as usize * size;
|
||||
|
||||
let mut accumulated_data = Vec::new();
|
||||
let mut buffer = vec![0u8; config.period_size as usize * size];
|
||||
|
||||
loop {
|
||||
match stdout.read(&mut buffer).await {
|
||||
Ok(size) if size > 0 => {
|
||||
accumulated_data.extend_from_slice(&buffer[..size]);
|
||||
if accumulated_data.len() >= target_size {
|
||||
let data_to_send =
|
||||
accumulated_data.drain(..target_size).collect::<Vec<u8>>();
|
||||
let _ = on_stream(data_to_send).await;
|
||||
}
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
let _ = AudioRecorder::instance().stop_recording().await;
|
||||
});
|
||||
|
||||
self.arecord_thread.lock().await.replace(arecord_thread);
|
||||
self.read_thread.lock().await.replace(read_thread);
|
||||
|
||||
*state = State::Recording;
|
||||
Ok(())
|
||||
}
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user