use anyhow::Error;
use env_logger::Env;
use log::trace;
use std::{
convert::TryInto,
io::{BufWriter, Read, Write, stdin, stdout},
iter::repeat_n,
sync::Arc,
};
use tonari_actor::{Actor, Context, Recipient, System};
type Sample = [i16; 2];
const SAMPLE_BYTES: usize = std::mem::size_of::<Sample>();
const CHUNK_SAMPLES: usize = 44100 / 60;
type Chunk = Arc<[Sample; CHUNK_SAMPLES]>;
const DELAY_CHUNKS: usize = 60;
struct DryChunk(Chunk);
struct WetChunk(Chunk);
impl From<DryChunk> for Chunk {
fn from(orig: DryChunk) -> Self {
orig.0
}
}
impl From<WetChunk> for Chunk {
fn from(orig: WetChunk) -> Self {
orig.0
}
}
struct ReadNext;
fn silence_chunk() -> Chunk {
Arc::new([[0i16; 2]; CHUNK_SAMPLES])
}
struct Input {
next: Recipient<DryChunk>,
}
impl Actor for Input {
type Context = Context<Self::Message>;
type Error = Error;
type Message = ReadNext;
fn handle(
&mut self,
context: &mut Self::Context,
_message: ReadNext,
) -> Result<(), Self::Error> {
let mut bytes = [0u8; CHUNK_SAMPLES * SAMPLE_BYTES];
stdin().read_exact(&mut bytes)?;
let chunk_slice: Arc<[Sample]> = bytes
.chunks(SAMPLE_BYTES)
.map(|b| [i16::from_ne_bytes([b[0], b[1]]), i16::from_ne_bytes([b[2], b[3]])])
.collect();
let chunk: Chunk = chunk_slice.try_into().expect("sample count is correct");
trace!("[Input] decoded chunk: {:?}...", &chunk[..5]);
self.next.send(DryChunk(chunk))?;
context.myself.send(ReadNext).map_err(Error::from)
}
}
struct Output;
impl Actor for Output {
type Context = Context<Self::Message>;
type Error = Error;
type Message = Chunk;
const DEFAULT_CAPACITY_NORMAL: usize = 60;
fn handle(&mut self, _context: &mut Self::Context, message: Chunk) -> Result<(), Self::Error> {
let mut buffered_stdout = BufWriter::new(stdout());
for sample in message.iter() {
for bytes in sample.iter().map(|s| s.to_ne_bytes()) {
buffered_stdout.write_all(&bytes)?;
}
}
buffered_stdout.flush().map_err(Error::from)
}
}
enum MixerInput {
Dry(DryChunk),
Wet(WetChunk),
}
impl From<DryChunk> for MixerInput {
fn from(orig: DryChunk) -> Self {
Self::Dry(orig)
}
}
impl From<WetChunk> for MixerInput {
fn from(orig: WetChunk) -> Self {
Self::Wet(orig)
}
}
struct Mixer {
out_1: Recipient<Chunk>,
out_2: Recipient<Chunk>,
dry_buffer: Option<DryChunk>,
wet_buffer: Option<WetChunk>,
}
impl Mixer {
fn new(out_1: Recipient<Chunk>, out_2: Recipient<Chunk>) -> Self {
Self {
out_1,
out_2,
dry_buffer: Some(DryChunk(silence_chunk())),
wet_buffer: Some(WetChunk(silence_chunk())),
}
}
}
impl Actor for Mixer {
type Context = Context<Self::Message>;
type Error = Error;
type Message = MixerInput;
fn handle(&mut self, _context: &mut Self::Context, message: MixerInput) -> Result<(), Error> {
match message {
MixerInput::Dry(chunk) => self.dry_buffer = Some(chunk),
MixerInput::Wet(chunk) => self.wet_buffer = Some(chunk),
}
if let (Some(dry), Some(wet)) = (&self.dry_buffer, &self.wet_buffer) {
let mixed_slice: Arc<[Sample]> = dry
.0
.iter()
.zip(wet.0.iter())
.map(|(a, b)| [a[0].saturating_add(b[0]), a[1].saturating_add(b[1])])
.collect();
let mixed: Chunk = mixed_slice.try_into().expect("sample count is correct");
self.out_1.send(Arc::clone(&mixed))?;
self.out_2.send(mixed)?;
self.dry_buffer = None;
self.wet_buffer = None;
}
Ok(())
}
}
struct Delay {
next: Recipient<WetChunk>,
buffer: Vec<Chunk>,
index: usize,
}
impl Delay {
fn new(next: Recipient<WetChunk>) -> Self {
let buffer: Vec<Chunk> = repeat_n(silence_chunk(), DELAY_CHUNKS).collect();
Self { next, buffer, index: 0 }
}
}
impl Actor for Delay {
type Context = Context<Self::Message>;
type Error = Error;
type Message = Chunk;
fn handle(&mut self, _context: &mut Self::Context, message: Chunk) -> Result<(), Error> {
self.buffer[self.index] = message;
self.index = (self.index + 1) % self.buffer.len();
self.next.send(WetChunk(Arc::clone(&self.buffer[self.index]))).map_err(Error::from)
}
}
struct Damper {
next: Recipient<WetChunk>,
}
impl Actor for Damper {
type Context = Context<Self::Message>;
type Error = Error;
type Message = Chunk;
fn handle(&mut self, _context: &mut Self::Context, message: Chunk) -> Result<(), Error> {
let chunk_slice: Arc<[Sample]> = message.iter().map(|s| [s[0] / 2, s[1] / 2]).collect();
let chunk: Chunk = chunk_slice.try_into().expect("sample count is correct");
self.next.send(WetChunk(chunk)).map_err(Error::from)
}
}
fn main() -> Result<(), Error> {
env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init();
let mut system = System::new("Echo System");
let output_addr = system.spawn(Output)?;
let mixer_addr = Mixer::addr();
let delay_addr = system.spawn(Delay::new(mixer_addr.recipient()))?;
let damper_addr = system.spawn(Damper { next: delay_addr.recipient() })?;
system
.prepare_fn(move || Mixer::new(output_addr.recipient(), damper_addr.recipient()))
.with_addr(mixer_addr.clone())
.spawn()?;
let input_addr = system.spawn(Input { next: mixer_addr.recipient() })?;
input_addr.send(ReadNext)?;
system.run().map_err(Error::from)
}