Files
AFFiNE-Mirror/packages/frontend/native/media_capture/src/audio_callback.rs
T
DarkSky 9b56a05159 feat(native): async recorder (#14700)
#### PR Dependency Tree


* **PR #14700** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Durable, resumable import queue with explicit import lifecycle and
updated popup/tray status behavior.
* Async native recording APIs and ability to abort recordings; audio
quality metrics (degraded, overflow count).
  * Added "Importing..." translation.

* **Bug Fixes**
* More reliable single-claim import processing, retries and cleanup to
avoid duplicate imports.
  * Improved stop/abort teardown stability and safer shutdown behavior.

* **Tests**
* New/updated tests covering coordinator, import queue, native async
flows and teardown scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-23 22:38:52 +08:00

66 lines
1.6 KiB
Rust

use std::sync::{
Arc,
atomic::{AtomicU64, Ordering},
};
use crossbeam_channel::{Sender, TrySendError};
use napi::{
bindgen_prelude::Float32Array,
threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode},
};
/// Internal callback abstraction so audio taps can target JS or native
/// pipelines.
#[derive(Clone)]
pub enum AudioCallback {
Js(Arc<ThreadsafeFunction<Float32Array, ()>>),
Channel {
sender: Sender<Vec<f32>>,
overflow_count: Arc<AtomicU64>,
},
}
impl AudioCallback {
pub fn call(&self, samples: Vec<f32>) {
match self {
Self::Js(func) => {
// Non-blocking call into JS; errors are ignored to avoid blocking the
// audio thread.
let _ = func.call(Ok(samples.into()), ThreadsafeFunctionCallMode::NonBlocking);
}
Self::Channel { sender, overflow_count } => match sender.try_send(samples) {
Ok(()) => {}
Err(TrySendError::Full(_)) => {
overflow_count.fetch_add(1, Ordering::Relaxed);
}
Err(TrySendError::Disconnected(_)) => {}
},
}
}
}
#[cfg(test)]
mod tests {
use std::sync::atomic::Ordering;
use crossbeam_channel::bounded;
use super::AudioCallback;
#[test]
fn channel_overflow_only_increments_the_counter() {
let (sender, _rx) = bounded(1);
let overflow_count = std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0));
let callback = AudioCallback::Channel {
sender: sender.clone(),
overflow_count: overflow_count.clone(),
};
sender.send(vec![0.0]).unwrap();
callback.call(vec![1.0]);
callback.call(vec![2.0]);
assert_eq!(overflow_count.load(Ordering::Relaxed), 2);
}
}