use std::ffi::CString;
use std::sync::OnceLock;
use super::bindings::*;
struct EmbeddedWav {
data: &'static [u8],
}
pub(super) struct Pcm {
pub(super) samples: Vec<u8>,
pub(super) srate: u32,
pub(super) channels: u8,
}
pub(super) fn parse_wav(data: &[u8]) -> Option<Pcm> {
if data.len() < 44 {
return None;
}
if &data[0..4] != b"RIFF" || &data[8..12] != b"WAVE" {
return None;
}
let mut pos = 12;
let mut audio_format: u16 = 1;
let mut srate: u32 = 8000;
let mut channels: u16 = 1;
let mut pcm: Option<Vec<u8>> = None;
while pos + 8 <= data.len() {
let chunk_id = &data[pos..pos + 4];
let chunk_size =
u32::from_le_bytes([data[pos + 4], data[pos + 5], data[pos + 6], data[pos + 7]])
as usize;
let chunk_start = pos + 8;
let chunk_end = chunk_start + chunk_size;
if chunk_end > data.len() {
break;
}
match chunk_id {
b"fmt " if chunk_size >= 16 => {
audio_format = u16::from_le_bytes([data[chunk_start], data[chunk_start + 1]]);
channels = u16::from_le_bytes([data[chunk_start + 2], data[chunk_start + 3]]);
srate = u32::from_le_bytes([
data[chunk_start + 4],
data[chunk_start + 5],
data[chunk_start + 6],
data[chunk_start + 7],
]);
}
b"data" => {
let raw = &data[chunk_start..chunk_end];
let samples = match audio_format {
1 => raw.to_vec(), 7 => mu2pcm(raw), _ => raw.to_vec(), };
pcm = Some(samples);
}
_ => {}
}
pos = chunk_end + (chunk_size % 2);
}
Some(Pcm {
samples: pcm?,
srate,
channels: channels as u8,
})
}
fn mu2pcm(data: &[u8]) -> Vec<u8> {
static U2L: [i16; 256] = [
-32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, -23932, -22908, -21884,
-20860, -19836, -18812, -17788, -16764, -15996, -15484, -14972, -14460, -13948, -13436,
-12924, -12412, -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, -7932, -7676,
-7420, -7164, -6908, -6652, -6396, -6140, -5884, -5628, -5372, -5116, -4860, -4604, -4348,
-4092, -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, -2876, -2748, -2620, -2492,
-2364, -2236, -2108, -1980, -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, -1372,
-1308, -1244, -1180, -1116, -1052, -988, -924, -876, -844, -812, -780, -748, -716, -684,
-652, -620, -588, -556, -524, -492, -460, -428, -396, -372, -356, -340, -324, -308, -292,
-276, -260, -244, -228, -212, -196, -180, -164, -148, -132, -120, -112, -104, -96, -88,
-80, -72, -64, -56, -48, -40, -32, -24, -16, -8, -2, 32124, 31100, 30076, 29052, 28028,
27004, 25980, 24956, 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, 15996, 15484,
14972, 14460, 13948, 13436, 12924, 12412, 11900, 11388, 10876, 10364, 9852, 9340, 8828,
8316, 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, 5884, 5628, 5372, 5116, 4860, 4604,
4348, 4092, 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, 2876, 2748, 2620, 2492, 2364,
2236, 2108, 1980, 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, 1372, 1308, 1244, 1180,
1116, 1052, 988, 924, 876, 844, 812, 780, 748, 716, 684, 652, 620, 588, 556, 524, 492, 460,
428, 396, 372, 356, 340, 324, 308, 292, 276, 260, 244, 228, 212, 196, 180, 164, 148, 132,
120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 2,
];
let mut out = Vec::with_capacity(data.len() * 2);
for &b in data {
let s = U2L[b as usize];
out.push((s & 0xff) as u8);
out.push((s >> 8) as u8);
}
out
}
fn alert_playp() -> *mut *mut Play {
static CELL: OnceLock<usize> = OnceLock::new();
*CELL.get_or_init(|| Box::into_raw(Box::new(std::ptr::null_mut::<Play>())) as usize)
as *mut *mut Play
}
pub fn play_alert(filename: &str) {
stop_alert();
let wav = match filename {
"ring.wav" => EmbeddedWav {
data: include_bytes!("../../vendor/baresip/share/ring.wav"),
},
"ringback.wav" => EmbeddedWav {
data: include_bytes!("../../vendor/baresip/share/ringback.wav"),
},
_ => return,
};
let pcm = match parse_wav(wav.data) {
Some(p) => p,
None => {
crate::rlog!(Warn, "play_alert: failed to parse WAV '{filename}'");
return;
}
};
let (play_mod, play_dev) = get_alert_device();
let samples = pcm.samples;
let srate = pcm.srate;
let ch = pcm.channels;
unsafe {
let player = baresip_player();
if player.is_null() {
crate::rlog!(Warn, "play_alert: baresip_player() returned null");
return;
}
let mb = mbuf_alloc(samples.len());
if mb.is_null() {
crate::rlog!(Warn, "play_alert: mbuf_alloc() failed");
return;
}
let rc = mbuf_write_mem(mb, samples.as_ptr(), samples.len());
if rc != 0 {
crate::rlog!(Warn, "play_alert: mbuf_write_mem() failed (rc={rc})");
mem_deref(mb as *mut std::os::raw::c_void);
return;
}
let playp = alert_playp();
let rc = play_tone(playp, player, mb, srate, ch, -1, play_mod, play_dev);
mem_deref(mb as *mut std::os::raw::c_void);
if rc != 0 {
crate::rlog!(Warn, "play_alert: play_tone('{filename}') failed (rc={rc})");
}
}
}
pub fn stop_alert() {
unsafe {
let playp = alert_playp();
if !(*playp).is_null() {
mem_deref(*playp as *mut std::os::raw::c_void);
}
}
}
fn get_alert_device() -> (*const std::os::raw::c_char, *const std::os::raw::c_char) {
use std::ffi::CStr;
use std::os::raw::c_char;
static CACHED: OnceLock<Option<(CString, CString)>> = OnceLock::new();
let cached = CACHED.get_or_init(|| {
let conf = unsafe { conf_cur() };
if conf.is_null() {
return None;
}
let mut buf = [0 as c_char; 256];
let rc =
unsafe { conf_get_str(conf, c"audio_alert".as_ptr(), buf.as_mut_ptr(), buf.len()) };
if rc != 0 {
return None;
}
let s = unsafe { CStr::from_ptr(buf.as_ptr()) }
.to_str()
.unwrap_or("");
let mut parts = s.splitn(2, ',');
let m = parts.next().unwrap_or("aubridge");
let d = parts.next().unwrap_or("default");
Some((
CString::new(m).unwrap_or_default(),
CString::new(d).unwrap_or_default(),
))
});
match cached {
Some((m, d)) => (m.as_ptr(), d.as_ptr()),
None => (std::ptr::null(), std::ptr::null()),
}
}