use std::sync::mpsc;
use std::thread;
use serde::{Deserialize, Serialize};
const MAX_ACCUMULATOR_SAMPLES: usize = 125_000_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum RecordingMode {
Normal,
Overdub,
Replace,
}
pub struct RecordManager {
sender: Option<mpsc::SyncSender<Vec<f32>>>,
handle: Option<thread::JoinHandle<Vec<f32>>>,
dropped_samples: u64,
}
impl RecordManager {
pub fn new(channel_bound: usize) -> Self {
let (sender, receiver) = mpsc::sync_channel::<Vec<f32>>(channel_bound.max(1));
let handle = thread::spawn(move || {
let mut accumulated = Vec::new();
while let Ok(chunk) = receiver.recv() {
if accumulated.len() + chunk.len() <= MAX_ACCUMULATOR_SAMPLES {
accumulated.extend_from_slice(&chunk);
}
}
accumulated
});
Self {
sender: Some(sender),
handle: Some(handle),
dropped_samples: 0,
}
}
pub fn push_samples(&mut self, data: &[f32]) {
if let Some(sender) = &self.sender {
match sender.try_send(data.to_vec()) {
Ok(()) => {}
Err(mpsc::TrySendError::Full(_)) => {
self.dropped_samples += data.len() as u64;
}
Err(mpsc::TrySendError::Disconnected(_)) => {
self.dropped_samples += data.len() as u64;
}
}
}
}
pub fn dropped_samples(&self) -> u64 {
self.dropped_samples
}
pub fn finish(mut self) -> Vec<f32> {
self.sender.take();
self.handle
.take()
.and_then(|h| h.join().ok())
.unwrap_or_default()
}
}
impl Drop for RecordManager {
fn drop(&mut self) {
self.sender.take();
if let Some(handle) = self.handle.take() {
let _ = handle.join();
}
}
}
pub struct LoopRecordManager {
sender: Option<mpsc::SyncSender<RecordChunk>>,
handle: Option<thread::JoinHandle<Vec<Vec<f32>>>>,
mode: RecordingMode,
dropped_samples: u64,
}
enum RecordChunk {
Samples(Vec<f32>),
LoopMarker,
}
impl LoopRecordManager {
pub fn new(channel_bound: usize, mode: RecordingMode) -> Self {
let (sender, receiver) = mpsc::sync_channel::<RecordChunk>(channel_bound.max(1));
let handle = thread::spawn(move || {
let mut takes: Vec<Vec<f32>> = vec![Vec::new()];
while let Ok(chunk) = receiver.recv() {
match chunk {
RecordChunk::Samples(data) => {
if let Some(current) = takes.last_mut()
&& current.len() + data.len() <= MAX_ACCUMULATOR_SAMPLES
{
current.extend_from_slice(&data);
}
}
RecordChunk::LoopMarker => {
takes.push(Vec::new());
}
}
}
takes
});
Self {
sender: Some(sender),
handle: Some(handle),
mode,
dropped_samples: 0,
}
}
pub fn mode(&self) -> RecordingMode {
self.mode
}
pub fn push_samples(&mut self, data: &[f32]) {
if let Some(sender) = &self.sender {
match sender.try_send(RecordChunk::Samples(data.to_vec())) {
Ok(()) => {}
Err(mpsc::TrySendError::Full(_)) => {
self.dropped_samples += data.len() as u64;
}
Err(mpsc::TrySendError::Disconnected(_)) => {
self.dropped_samples += data.len() as u64;
}
}
}
}
pub fn push_loop_marker(&mut self) {
if let Some(sender) = &self.sender {
let _ = sender.try_send(RecordChunk::LoopMarker);
}
}
pub fn dropped_samples(&self) -> u64 {
self.dropped_samples
}
pub fn finish(mut self) -> Vec<Vec<f32>> {
self.sender.take();
self.handle
.take()
.and_then(|h| h.join().ok())
.unwrap_or_default()
}
}
impl Drop for LoopRecordManager {
fn drop(&mut self) {
self.sender.take();
if let Some(handle) = self.handle.take() {
let _ = handle.join();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn record_and_finish() {
let mut rec = RecordManager::new(64);
rec.push_samples(&[0.1, 0.2, 0.3, 0.4]);
rec.push_samples(&[0.5, 0.6]);
let result = rec.finish();
assert_eq!(result.len(), 6);
assert!((result[0] - 0.1).abs() < f32::EPSILON);
assert!((result[5] - 0.6).abs() < f32::EPSILON);
}
#[test]
fn record_empty() {
let rec = RecordManager::new(16);
let result = rec.finish();
assert!(result.is_empty());
}
#[test]
fn loop_record_single_take() {
let mut rec = LoopRecordManager::new(64, RecordingMode::Normal);
rec.push_samples(&[1.0, 2.0, 3.0]);
let takes = rec.finish();
assert_eq!(takes.len(), 1);
assert_eq!(takes[0].len(), 3);
}
#[test]
fn loop_record_multiple_takes() {
let mut rec = LoopRecordManager::new(64, RecordingMode::Normal);
rec.push_samples(&[1.0, 2.0]);
rec.push_loop_marker();
rec.push_samples(&[3.0, 4.0, 5.0]);
rec.push_loop_marker();
rec.push_samples(&[6.0]);
let takes = rec.finish();
assert_eq!(takes.len(), 3);
assert_eq!(takes[0].len(), 2);
assert_eq!(takes[1].len(), 3);
assert_eq!(takes[2].len(), 1);
}
#[test]
fn loop_record_mode() {
let rec = LoopRecordManager::new(16, RecordingMode::Overdub);
assert_eq!(rec.mode(), RecordingMode::Overdub);
}
#[test]
fn dropped_samples_initially_zero() {
let rec = RecordManager::new(16);
assert_eq!(rec.dropped_samples(), 0);
}
#[test]
fn record_drop_without_finish() {
let mut rec = RecordManager::new(16);
rec.push_samples(&[1.0, 2.0, 3.0]);
drop(rec); }
#[test]
fn loop_record_drop_without_finish() {
let mut rec = LoopRecordManager::new(16, RecordingMode::Normal);
rec.push_samples(&[1.0, 2.0]);
rec.push_loop_marker();
rec.push_samples(&[3.0]);
drop(rec);
}
#[test]
fn loop_record_empty_takes() {
let mut rec = LoopRecordManager::new(64, RecordingMode::Normal);
rec.push_loop_marker(); rec.push_loop_marker(); rec.push_samples(&[1.0]);
let takes = rec.finish();
assert_eq!(takes.len(), 3);
assert!(takes[0].is_empty());
assert!(takes[1].is_empty());
assert_eq!(takes[2].len(), 1);
}
#[test]
fn loop_record_replace_mode() {
let rec = LoopRecordManager::new(16, RecordingMode::Replace);
assert_eq!(rec.mode(), RecordingMode::Replace);
}
#[test]
fn loop_record_dropped_samples() {
let rec = LoopRecordManager::new(16, RecordingMode::Normal);
assert_eq!(rec.dropped_samples(), 0);
}
#[test]
fn large_recording() {
let mut rec = RecordManager::new(128);
for _ in 0..100 {
rec.push_samples(&[0.5; 1024]);
}
let result = rec.finish();
assert_eq!(result.len(), 100 * 1024);
}
}