Skip to content

Commit 3bc5ba2

Browse files
committed
[*] refactor
1 parent 0301372 commit 3bc5ba2

File tree

13 files changed

+220
-68
lines changed

13 files changed

+220
-68
lines changed

lib/denoise/src/lib.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ pub struct RealTimeDenoise<'a, T: SampleType = f32> {
314314
buffer: Vec<Vec<f32>>,
315315
states: Vec<Box<DenoiseState<'a>>>,
316316
states_output_frames: Vec<Vec<f32>>,
317+
first_frame: bool,
317318
_marker: PhantomData<T>,
318319
}
319320

@@ -342,6 +343,7 @@ impl<'a, T: SampleType> RealTimeDenoise<'a, T> {
342343
states,
343344
states_output_frames,
344345
original_sample_rate: spec.sample_rate,
346+
first_frame: true,
345347
_marker: PhantomData,
346348
})
347349
}
@@ -400,11 +402,17 @@ impl<'a, T: SampleType> RealTimeDenoise<'a, T> {
400402
.process_frame(&mut self.states_output_frames[channel], input_slice);
401403
}
402404

403-
for sample_idx in 0..FRAME_SIZE {
404-
for channel in 0..channels {
405-
let sample = self.states_output_frames[channel][sample_idx];
406-
output.push(sample);
405+
// Skip output for first frame to avoid popping artifacts from
406+
// uninitialized synthesis_mem in nnnoiseless overlap-add
407+
if !self.first_frame {
408+
for sample_idx in 0..FRAME_SIZE {
409+
for channel in 0..channels {
410+
let sample = self.states_output_frames[channel][sample_idx];
411+
output.push(sample);
412+
}
407413
}
414+
} else {
415+
self.first_frame = false;
408416
}
409417
}
410418

@@ -413,6 +421,11 @@ impl<'a, T: SampleType> RealTimeDenoise<'a, T> {
413421
self.buffer[channel].drain(0..samples_to_remove);
414422
}
415423

424+
// If no output (first frame case), return None
425+
if output.is_empty() {
426+
return Ok(None);
427+
}
428+
416429
// Step 4: Resample back to original rate if needed
417430
let final_output = if self.original_sample_rate != DENOISE_SAMPLE_RATE {
418431
resample_from_denoise_rate(&output, self.original_sample_rate, self.spec.channels)?
@@ -443,6 +456,10 @@ impl<'a, T: SampleType> RealTimeDenoise<'a, T> {
443456
self.buffer[0].len()
444457
}
445458

459+
pub fn is_first_frame(&self) -> bool {
460+
self.first_frame
461+
}
462+
446463
pub fn flush(&mut self) -> Option<Vec<T>> {
447464
let channels = self.spec.channels as usize;
448465
let remaining_samples = self.buffer[0].len();

lib/record-audio/src/lib.rs

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ pub struct AudioRecorder {
121121
channels: u16,
122122
sample_rate: u32,
123123
gain_db: f32,
124+
mono: bool,
124125
}
125126

126127
impl Default for AudioRecorder {
@@ -140,6 +141,7 @@ impl AudioRecorder {
140141
channels: 2,
141142
sample_rate: 48000,
142143
gain_db: 0.0,
144+
mono: false,
143145
}
144146
}
145147

@@ -153,6 +155,11 @@ impl AudioRecorder {
153155
self
154156
}
155157

158+
pub fn with_mono(mut self, mono: bool) -> Self {
159+
self.mono = mono;
160+
self
161+
}
162+
156163
pub fn get_input_devices(&self) -> Result<Vec<AudioDeviceInfo>> {
157164
let devices = self
158165
.host
@@ -214,6 +221,7 @@ impl AudioRecorder {
214221
let samples = self.recorded_samples.clone();
215222
let level_sender = self.level_sender.clone();
216223
let channels = self.channels;
224+
let mono = self.mono;
217225

218226
let linear_gain = if self.gain_db == 0.0 {
219227
1.0
@@ -229,26 +237,45 @@ impl AudioRecorder {
229237
.build_input_stream(
230238
&config,
231239
move |data: &[f32], _: &cpal::InputCallbackInfo| {
232-
let gain_samples: Vec<f32> = data
233-
.iter()
234-
.map(|s| (s * linear_gain).clamp(-1.0, 1.0))
235-
.collect();
240+
let processed_samples: Vec<f32> = if mono && channels > 1 {
241+
let num_channels = channels as usize;
242+
let num_frames = data.len() / num_channels;
243+
let mut mono_samples = Vec::with_capacity(num_frames);
244+
245+
for frame_idx in 0..num_frames {
246+
let start = frame_idx * num_channels;
247+
let end = (start + num_channels).min(data.len());
248+
let frame_samples = &data[start..end];
249+
let avg = frame_samples.iter().sum::<f32>() / num_channels as f32;
250+
mono_samples.push((avg * linear_gain).clamp(-1.0, 1.0));
251+
}
252+
mono_samples
253+
} else {
254+
data.iter()
255+
.map(|s| (s * linear_gain).clamp(-1.0, 1.0))
256+
.collect()
257+
};
236258

237259
if let Ok(mut s) = samples.lock() {
238-
s.extend_from_slice(&gain_samples);
260+
s.extend_from_slice(&processed_samples);
239261
}
240262

241263
if let Some(sender) = &level_sender
242264
&& let Ok(mut buf) = level_buffer_clone.lock()
243265
{
244-
buf.extend_from_slice(&gain_samples);
266+
buf.extend_from_slice(&processed_samples);
245267

246268
// Calculate and send level every ~100ms worth of samples
247-
let threshold = (config.sample_rate as usize / 10) * channels as usize;
269+
let threshold = if mono && channels > 1 {
270+
config.sample_rate as usize / 10
271+
} else {
272+
(config.sample_rate as usize / 10) * channels as usize
273+
};
248274
if buf.len() < threshold {
249275
return;
250276
}
251277

278+
// For level display, always show left/right even if recording mono
252279
let levels: (f32, f32) = if channels > 1 {
253280
let parts = split_audio_channels(buf.clone(), channels as usize);
254281

@@ -301,9 +328,15 @@ impl AudioRecorder {
301328
Vec::new()
302329
};
303330

331+
let output_channels = if self.mono && self.channels > 1 {
332+
1
333+
} else {
334+
self.channels
335+
};
336+
304337
Ok(RecordedAudio {
305338
samples,
306-
channels: self.channels,
339+
channels: output_channels,
307340
sample_rate: self.sample_rate,
308341
})
309342
}

lib/video-editor/src/filters/audio/denoise.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ use crate::{
44
};
55
use denoise::{DENOISE_MODEL, RealTimeDenoise};
66
use hound::{SampleFormat, WavSpec};
7-
use std::cell::RefCell;
7+
use std::{cell::RefCell, sync::Arc};
88

99
thread_local! {
10-
static DENOISE_STATE: RefCell<Option<RealTimeDenoise<'static, f32>>> = RefCell::new(None);
10+
static DENOISE_STATE: RefCell<(Option<RealTimeDenoise<'static, f32>>, Option<usize>)>
11+
= RefCell::new((None, None));
1112
}
1213

1314
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
@@ -37,6 +38,7 @@ impl AudioFilter for DenoiseFilter {
3738

3839
let channels = data.config.channels;
3940
let sample_rate = data.config.sample_rate;
41+
let segment_id = Arc::as_ptr(&data.from_segment) as usize;
4042

4143
let spec = WavSpec {
4244
channels,
@@ -47,23 +49,31 @@ impl AudioFilter for DenoiseFilter {
4749

4850
DENOISE_STATE.with(|state_cell| {
4951
let mut state_guard = state_cell.borrow_mut();
52+
let (rt, last_segment_id) = &mut *state_guard;
5053

51-
let needs_reinit = state_guard
52-
.as_ref()
53-
.map(|rt| rt.spec().channels != channels || rt.spec().sample_rate != sample_rate)
54-
.unwrap_or(true);
54+
// Check if we need to reinitialize:
55+
// - No existing state
56+
// - Segment changed (boundary crossing - causes popping artifacts)
57+
// - Audio config changed
58+
let needs_reinit = rt.is_none()
59+
|| *last_segment_id != Some(segment_id)
60+
|| rt.as_ref().unwrap().spec().channels != channels
61+
|| rt.as_ref().unwrap().spec().sample_rate != sample_rate;
5562

5663
if needs_reinit {
57-
*state_guard = Some(RealTimeDenoise::new(&DENOISE_MODEL, spec).map_err(|e| {
64+
// Creating a new RealTimeDenoise resets first_frame to true,
65+
// which will skip the first frame output to avoid popping
66+
*rt = Some(RealTimeDenoise::new(&DENOISE_MODEL, spec).map_err(|e| {
5867
crate::Error::InvalidConfig(format!("Failed to create denoise state: {}", e))
5968
})?);
69+
*last_segment_id = Some(segment_id);
6070
}
6171

62-
let rt = state_guard.as_mut().unwrap();
72+
let rt = rt.as_mut().unwrap();
6373

6474
match rt.process(&data.samples) {
6575
Ok(Some(processed)) => data.samples = processed,
66-
Ok(None) => {}
76+
Ok(None) => {} // First frame skipped or insufficient samples
6777
Err(e) => {
6878
return Err(crate::Error::InvalidConfig(format!(
6979
"Denoise processing error: {}",
@@ -76,4 +86,3 @@ impl AudioFilter for DenoiseFilter {
7686
})
7787
}
7888
}
79-

lib/video-editor/src/filters/video/fade_in.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,8 @@ impl VideoFilter for FadeInFilter {
4141
let ratio = progress_ratio_from_offset(frame_time_offset, self.duration);
4242

4343
buffer.par_pixels_mut().for_each(|pixel| {
44-
let [r, g, b, a] = pixel.0;
45-
pixel.0 = [
46-
(r as f32 * ratio).clamp(0.0, 255.0) as u8,
47-
(g as f32 * ratio).clamp(0.0, 255.0) as u8,
48-
(b as f32 * ratio).clamp(0.0, 255.0) as u8,
49-
a,
50-
];
44+
// 淡入:alpha 从 0 渐变到原始值
45+
pixel.0[3] = ((pixel.0[3] as f32) * ratio).clamp(0.0, 255.0) as u8;
5146
});
5247
}
5348
}

lib/video-editor/src/filters/video/fade_out.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,8 @@ impl VideoFilter for FadeOutFilter {
5454
let ratio = progress_ratio_from_offset(time_until_end, self.duration);
5555

5656
buffer.par_pixels_mut().for_each(|pixel| {
57-
let [r, g, b, a] = pixel.0;
58-
pixel.0 = [
59-
(r as f32 * ratio).clamp(0.0, 255.0) as u8,
60-
(g as f32 * ratio).clamp(0.0, 255.0) as u8,
61-
(b as f32 * ratio).clamp(0.0, 255.0) as u8,
62-
a,
63-
];
57+
// 淡出:alpha 从原始值渐变到 0
58+
pixel.0[3] = ((pixel.0[3] as f32) * ratio).clamp(0.0, 255.0) as u8;
6459
});
6560
}
6661
}

lib/video-editor/src/filters/video/fly_in.rs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
filters::traits::{EasingFunction, VideoData, VideoFilter},
55
tracks::video_frame_cache::VideoImage,
66
};
7-
use image::{Rgba, RgbaImage};
7+
use image::{Rgba, RgbaImage, imageops};
88
use rayon::prelude::*;
99
use std::time::Duration;
1010

@@ -87,7 +87,22 @@ impl FlyInFilter {
8787
}
8888
}
8989

90-
fn apply_move(&self, buffer: &mut RgbaImage, frame_time_offset: Duration) -> Result<()> {
90+
fn apply_move(
91+
&self,
92+
buffer: &mut RgbaImage,
93+
canvas_width: u32,
94+
canvas_height: u32,
95+
frame_time_offset: Duration,
96+
) -> Result<()> {
97+
// Ensure buffer is canvas-sized (decoded frames may be smaller than canvas)
98+
if buffer.width() != canvas_width || buffer.height() != canvas_height {
99+
let mut canvas = RgbaImage::new(canvas_width, canvas_height);
100+
let x = (canvas_width.saturating_sub(buffer.width())) / 2;
101+
let y = (canvas_height.saturating_sub(buffer.height())) / 2;
102+
imageops::overlay(&mut canvas, buffer, x as i64, y as i64);
103+
*buffer = canvas;
104+
}
105+
91106
// Calculate ratio - after duration, stay at final position (ratio = 1.0)
92107
let ratio = if frame_time_offset > self.duration {
93108
1.0
@@ -96,7 +111,9 @@ impl FlyInFilter {
96111
};
97112
let eased_ratio = self.easing.apply(ratio);
98113

99-
let (width, height) = (buffer.width(), buffer.height());
114+
// Use canvas dimensions for animation calculations
115+
let width = canvas_width;
116+
let height = canvas_height;
100117

101118
// Calculate fly-in start position (image center just outside canvas edge)
102119
let start_pos = Self::calculate_entry_direction(self.move_to_position);
@@ -135,11 +152,18 @@ impl FlyInFilter {
135152
impl VideoFilter for FlyInFilter {
136153
crate::impl_default_video_filter!(FlyInFilter);
137154

155+
fn take_effect_in_layer_frame(&self) -> bool {
156+
false // Operate on image_for_composite stream, consistent with transform
157+
}
158+
138159
fn apply(&self, data: &mut VideoData) -> Result<()> {
160+
let canvas_width = data.config.output_width;
161+
let canvas_height = data.config.output_height;
139162
let frame_time_offset = data.relative_timeline_offset;
163+
140164
for frame in data.frames.iter_mut() {
141165
if let VideoImage::Image { buffer, .. } = frame {
142-
self.apply_move(buffer, frame_time_offset)?;
166+
self.apply_move(buffer, canvas_width, canvas_height, frame_time_offset)?;
143167
}
144168
}
145169

0 commit comments

Comments
 (0)