Hide the inner workings of the UnsyncFloatBuf more
This commit is contained in:
parent
e7507b2dd0
commit
c73199185b
2 changed files with 131 additions and 0 deletions
|
@ -312,6 +312,7 @@ pub mod matrix_repr;
|
||||||
pub mod monitor;
|
pub mod monitor;
|
||||||
pub mod nodes;
|
pub mod nodes;
|
||||||
pub mod sample_lib;
|
pub mod sample_lib;
|
||||||
|
pub mod unsync_float_buf;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
pub use cell_dir::CellDir;
|
pub use cell_dir::CellDir;
|
||||||
|
@ -323,6 +324,7 @@ pub use matrix_repr::load_patch_from_file;
|
||||||
pub use matrix_repr::save_patch_to_file;
|
pub use matrix_repr::save_patch_to_file;
|
||||||
pub use nodes::{new_node_engine, NodeConfigurator, NodeExecutor};
|
pub use nodes::{new_node_engine, NodeConfigurator, NodeExecutor};
|
||||||
pub use sample_lib::{SampleLibrary, SampleLoadError};
|
pub use sample_lib::{SampleLibrary, SampleLoadError};
|
||||||
|
pub use unsync_float_buf::UnsyncFloatBuf;
|
||||||
|
|
||||||
pub struct Context<'a, 'b, 'c, 'd> {
|
pub struct Context<'a, 'b, 'c, 'd> {
|
||||||
pub nframes: usize,
|
pub nframes: usize,
|
||||||
|
|
129
src/unsync_float_buf.rs
Normal file
129
src/unsync_float_buf.rs
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
// Copyright (c) 2022 Weird Constructor <weirdconstructor@gmail.com>
|
||||||
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
|
use crate::util::AtomicFloat;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// A float buffer that can be written to and read from in an unsynchronized manner.
|
||||||
|
///
|
||||||
|
/// One use case is writing samples to this buffer in the audio thread while
|
||||||
|
/// a GUI thread reads from this buffer. Mostly useful for an oscilloscope.
|
||||||
|
///
|
||||||
|
///```
|
||||||
|
/// use hexodsp::UnsyncFloatBuf;
|
||||||
|
///
|
||||||
|
/// let handle1 = UnsyncFloatBuf::new_with_len(10);
|
||||||
|
/// let handle2 = handle1.clone();
|
||||||
|
///
|
||||||
|
/// std::thread::spawn(move || {
|
||||||
|
/// handle1.write(9, 2032.0);
|
||||||
|
/// }).join().unwrap();
|
||||||
|
///
|
||||||
|
/// std::thread::spawn(move || {
|
||||||
|
/// assert_eq!(handle2.read(9), 2032.0);
|
||||||
|
/// assert_eq!(handle2.read(20), 0.0); // out of range!
|
||||||
|
/// }).join().unwrap();
|
||||||
|
///```
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct UnsyncFloatBuf(Arc<UnsyncFloatBufImpl>);
|
||||||
|
|
||||||
|
impl UnsyncFloatBuf {
|
||||||
|
/// Creates a new unsynchronized float buffer with the given length.
|
||||||
|
pub fn new_with_len(len: usize) -> Self {
|
||||||
|
Self(UnsyncFloatBufImpl::new_shared(len))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write float to the given index.
|
||||||
|
///
|
||||||
|
/// If index is out of range, nothing will be written.
|
||||||
|
pub fn write(&self, idx: usize, v: f32) {
|
||||||
|
self.0.write(idx, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads a float from the given index.
|
||||||
|
///
|
||||||
|
/// If index is out of range, 0.0 will be returned.
|
||||||
|
pub fn read(&self, idx: usize) -> f32 {
|
||||||
|
self.0.read(idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct UnsyncFloatBufImpl {
|
||||||
|
data_store: Vec<AtomicFloat>,
|
||||||
|
len: usize,
|
||||||
|
ptr: *mut AtomicFloat,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Sync for UnsyncFloatBuf {}
|
||||||
|
unsafe impl Send for UnsyncFloatBuf {}
|
||||||
|
|
||||||
|
impl UnsyncFloatBufImpl {
|
||||||
|
fn new_shared(len: usize) -> Arc<Self> {
|
||||||
|
let mut rc = Arc::new(Self { data_store: Vec::new(), len, ptr: std::ptr::null_mut() });
|
||||||
|
|
||||||
|
let mut unsync_buf = Arc::get_mut(&mut rc).expect("No other reference to this Arc");
|
||||||
|
unsync_buf.data_store.resize_with(len, || AtomicFloat::new(0.0));
|
||||||
|
// Taking the pointer to the Vec data buffer is fine,
|
||||||
|
// because it will not be moved when inside the Arc.
|
||||||
|
unsync_buf.ptr = unsync_buf.data_store.as_mut_ptr();
|
||||||
|
|
||||||
|
rc
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&self, idx: usize, v: f32) {
|
||||||
|
if idx < self.len {
|
||||||
|
unsafe {
|
||||||
|
(*self.ptr.add(idx)).set(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&self, idx: usize) -> f32 {
|
||||||
|
if idx < self.len {
|
||||||
|
unsafe { (*self.ptr.add(idx)).get() }
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_unsync_float_buf_working() {
|
||||||
|
let handle1 = UnsyncFloatBuf::new_with_len(512);
|
||||||
|
for i in 0..512 {
|
||||||
|
handle1.write(i, i as f32);
|
||||||
|
}
|
||||||
|
let handle2 = handle1.clone();
|
||||||
|
for i in 0..512 {
|
||||||
|
assert_eq!(handle2.read(i), i as f32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_unsync_float_buf_thread() {
|
||||||
|
let handle1 = UnsyncFloatBuf::new_with_len(512);
|
||||||
|
let handle2 = handle1.clone();
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
for i in 0..512 {
|
||||||
|
handle1.write(i, i as f32);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.join()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
for i in 0..512 {
|
||||||
|
assert_eq!(handle2.read(i), i as f32);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.join()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue