Skip to content

Commit

Permalink
Rework i16 output to eliminate unsafe double &mut
Browse files Browse the repository at this point in the history
  • Loading branch information
PolyMeilex committed Dec 28, 2024
1 parent 03a684b commit b9f8115
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 72 deletions.
2 changes: 1 addition & 1 deletion oxisynth/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub struct Synth {
pub settings: Settings,

#[cfg(feature = "i16-out")]
dither_index: i32,
dither_index: usize,
}

impl Default for Synth {
Expand Down
64 changes: 28 additions & 36 deletions oxisynth/src/core/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ impl Synth {
self.ticks = self.ticks.wrapping_add(64);
}

#[inline]
pub fn read_next(&mut self) -> (f32, f32) {
if self.cur == 64 {
self.one_block(false);
Expand All @@ -79,6 +80,7 @@ impl Synth {
out
}

#[inline]
pub fn write<F: FnMut(usize, f32, f32)>(&mut self, len: usize, incr: usize, mut cb: F) {
for i in 0..len {
let next = self.read_next();
Expand All @@ -87,6 +89,7 @@ impl Synth {
}
}

#[inline]
pub fn write_f32(
&mut self,
len: usize,
Expand All @@ -105,6 +108,7 @@ impl Synth {
}
}

#[inline]
pub fn write_f64(
&mut self,
len: usize,
Expand All @@ -124,71 +128,59 @@ impl Synth {
}

#[cfg(feature = "i16-out")]
pub fn write_s16(
#[inline]
pub fn write_i16(
&mut self,
len: usize,
left_out: &mut [i16],
loff: usize,
lincr: usize,
right_out: &mut [i16],
roff: usize,
rincr: usize,
mut cb: impl FnMut((usize, i16), (usize, i16)),
) {
let mut di: i32 = self.dither_index;
let mut di = self.dither_index;

let mut cur = self.cur;
let mut i: usize = 0;
let mut j = loff;
let mut k = roff;

while i < len {
/* fill up the buffers as needed */
// fill up the buffers as needed
if cur == 64 {
self.one_block(false);
cur = 0;
}
/*
* Converts stereo floating point sample data to signed 16 bit data with
* dithering.
*/

let mut left_sample = f32::round(
self.left_buf[0][cur as usize] * 32766.0f32
+ RAND_TABLE[0 as i32 as usize][di as usize],
);
let mut right_sample = f32::round(
self.right_buf[0][cur as usize] * 32766.0f32
+ RAND_TABLE[1 as i32 as usize][di as usize],
);

// Converts stereo floating point sample data to signed 16 bit data with
// dithering.

let mut left_sample = f32::round(self.left_buf[0][cur] * 32766.0 + RAND_TABLE[0][di]);
let mut right_sample = f32::round(self.right_buf[0][cur] * 32766.0 + RAND_TABLE[1][di]);

di += 1;
if di >= 48000 as i32 {
di = 0 as i32
if di >= 48000 {
di = 0;
}

/* digital clipping */
if left_sample > 32767.0f32 {
left_sample = 32767.0f32
}
if left_sample < -32768.0f32 {
left_sample = -32768.0f32
}
if right_sample > 32767.0f32 {
right_sample = 32767.0f32
}
if right_sample < -32768.0f32 {
right_sample = -32768.0f32
}
// digital clipping
left_sample = left_sample.clamp(-32768.0, 32767.0);
right_sample = right_sample.clamp(-32768.0, 32767.0);

left_out[j as usize] = left_sample as i16;
right_out[k as usize] = right_sample as i16;
cb(
(j, left_sample as i16),
(k, right_sample as i16),
//
);

i += 1;
cur += 1;
j += lincr;
k += rincr
}

self.cur = cur;
/* keep dither buffer continuous */
// keep dither buffer continuous
self.dither_index = di;
}
}
Expand Down
53 changes: 33 additions & 20 deletions oxisynth/src/synth/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,35 @@ pub trait IsSamples {
#[cfg(feature = "i16-out")]
impl IsSamples for &mut [i16] {
/// Write samples interleaved
#[inline(always)]
fn write_samples(self, synth: &mut Synth) {
crate::unsafe_stuff::write_samples_interleaved(self, synth)
let len = self.len() / 2;

// interleaved
synth.write_i16(len, 0, 2, 1, 2, |left, right| {
self[left.0] = left.1;
self[right.0] = right.1;
});
}
}

#[cfg(feature = "i16-out")]
impl IsSamples for (&mut [i16], &mut [i16]) {
/// Write samples non-interleaved
#[inline(always)]
fn write_samples(self, synth: &mut Synth) {
let len = self.0.len().min(self.1.len());
synth.write_i16(len, self.0, 0, 1, self.1, 0, 1)

synth.write_i16(len, 0, 1, 0, 1, |left, right| {
self.0[left.0] = left.1;
self.1[right.0] = right.1;
});
}
}

impl IsSamples for &mut [f32] {
/// Write samples interleaved
#[inline(always)]
fn write_samples(self, synth: &mut Synth) {
let len = self.len() / 2;
synth.write_cb(len, 2, |id, l, r| {
Expand All @@ -35,6 +48,7 @@ impl IsSamples for &mut [f32] {

impl IsSamples for (&mut [f32], &mut [f32]) {
/// Write samples non-interleaved
#[inline(always)]
fn write_samples(self, synth: &mut Synth) {
let len = self.0.len().min(self.1.len());
synth.write_cb(len, 1, |id, l, r| {
Expand All @@ -46,6 +60,7 @@ impl IsSamples for (&mut [f32], &mut [f32]) {

impl IsSamples for &mut [f64] {
/// Write samples interleaved
#[inline(always)]
fn write_samples(self, synth: &mut Synth) {
let len = self.len() / 2;
synth.write_cb(len, 2, |id, l, r| {
Expand All @@ -70,33 +85,31 @@ impl Synth {
self.core.read_next()
}

#[inline(always)]
pub fn write_cb<F: FnMut(usize, f32, f32)>(&mut self, len: usize, incr: usize, cb: F) {
self.core.write(len, incr, cb)
}

/**
Write samples as 16-bit signed integers
# Safety
The `len` must corresponds to the lenghtes of buffers.
*/
/// Write samples as 16-bit signed integers
///
/// ```ignore
/// synth.write_i16(len, 0, 1, 0, 1, |left, right| {
/// left_buf[left.0] = left.1;
/// right_buf[right.0] = right.1;
/// });
/// ```
#[cfg(feature = "i16-out")]
#[allow(clippy::too_many_arguments)]
#[inline]
#[inline(always)]
pub fn write_i16(
&mut self,
len: usize,
left_out: &mut [i16],
loff: u32,
lincr: u32,
right_out: &mut [i16],
roff: u32,
rincr: u32,
loff: usize,
lincr: usize,
roff: usize,
rincr: usize,
cb: impl FnMut((usize, i16), (usize, i16)),
) {
self.core.write_s16(
len as _, left_out, loff as _, lincr as _, right_out, roff as _, rincr as _,
)
self.core.write_i16(len, loff, lincr, roff, rincr, cb);
}

/**
Expand Down
15 changes: 0 additions & 15 deletions oxisynth/src/unsafe_stuff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,3 @@ pub fn slice_f32_to_u8(slice: &[f32]) -> &[u8] {
let len = std::mem::size_of_val(slice);
unsafe { std::slice::from_raw_parts(slice.as_ptr() as *const u8, len) }
}

/// Write samples interleaved
#[cfg(feature = "i16-out")]
pub fn write_samples_interleaved(slice: &mut [i16], synth: &mut crate::Synth) {
let len = slice.len() / 2;
unsafe {
// TODO: Use raw pointers for this
//
// This is most likely UB, even tho those pointer will never be acccesed at the same time,
// the shear fact that we are creating double &mut to the same ptr might cause the compiler
// to miss-optimise something
let ptr = slice as *mut [i16];
synth.write_i16(len, &mut *ptr, 0, 2, &mut *ptr, 1, 2)
}
}

0 comments on commit b9f8115

Please sign in to comment.