1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use ffmpeg_next::sys;
use std::{ffi::c_void, ptr, sync::Arc};
use miniaudio_aurex as miniaudio;
use crate::{
engine::AudioFifo,
singletons::{add_played, get_decoder_eof},
};
use std::sync::Mutex;
use crossbeam_channel::Sender;
use crate::enums::EngineSignal;
#[unsafe(no_mangle)]
pub extern "C" fn data_callback(
p_device: *mut miniaudio::ma_device,
p_output: *mut c_void,
_p_input: *const c_void,
frame_count: miniaudio::ma_uint32,
) {
unsafe {
// Defensive checks
if p_device.is_null() || p_output.is_null() {
return;
}
// bytes and sizing
let bytes_per_sample = sys::av_get_bytes_per_sample(sys::AVSampleFormat::AV_SAMPLE_FMT_S32) as usize;
let channels = (*p_device).playback.channels as usize;
let bytes_per_frame = bytes_per_sample.saturating_mul(channels);
let total_bytes = (frame_count as usize).saturating_mul(bytes_per_frame);
// NOTE: pUserData is a pointer to the inner Mutex<(Arc<Mutex<AudioFifo>>, Sender<EngineSignal>)>
// This pointer was set using Arc::as_ptr(...) in engine.rs so we must not call Arc::from_raw here.
let user_data_ptr = (*p_device).pUserData as *const Mutex<(Arc<Mutex<AudioFifo>>, Sender<EngineSignal>)>;
if user_data_ptr.is_null() {
// must not crash in callback; zero-fill and bail
ptr::write_bytes(p_output as *mut u8, 0, total_bytes);
return;
}
// SAFETY: the owning Arc is kept alive by AudioEngine. Use the raw pointer as a reference.
let user_data_ref: &Mutex<(Arc<Mutex<AudioFifo>>, Sender<EngineSignal>)> = &*user_data_ptr;
match user_data_ref.try_lock() {
Ok(guard) => {
// clone inner handles quickly then drop guard to minimize lock hold time
let buffer_arc = guard.0.clone();
let _signal_tx = guard.1.clone();
drop(guard);
match buffer_arc.try_lock() {
Ok(buffer_guard) => {
let fifo = buffer_guard.0;
let available = sys::av_audio_fifo_size(fifo);
let mut data_ptrs = [p_output as *mut c_void];
let frames_to_read = available.min(frame_count as i32);
let got = if frames_to_read > 0 {
sys::av_audio_fifo_read(
fifo,
data_ptrs.as_mut_ptr(),
frames_to_read,
)
} else {
0
};
// Increment global played samples tracker
if got > 0 {
add_played(got as u64);
}
if get_decoder_eof() && (got == 0) {
//Media Ended
_ = _signal_tx.send(EngineSignal::MediaEnd);
}
// Zero-fill any remaining frames
if got < frame_count as i32 {
let written_bytes = (got as usize).saturating_mul(bytes_per_frame);
let remaining_bytes = total_bytes.saturating_sub(written_bytes);
ptr::write_bytes(
(p_output as *mut u8).add(written_bytes),
0,
remaining_bytes,
);
}
}
Err(_) => {
// Buffer lock contention - zero-fill entire buffer to avoid glitches
ptr::write_bytes(p_output as *mut u8, 0, total_bytes);
}
}
}
Err(_) => {
// Lock contention on outer user_data - zero-fill entire buffer to avoid glitches
ptr::write_bytes(p_output as *mut u8, 0, total_bytes);
}
}
}
}