lonelyradio/monoclient/src/main.rs
Ivan Bushchik 6daba088b8
0.1.7: switch logs err/out, bump symphonia, buffer wisely
Signed-off-by: Ivan Bushchik <ivabus@ivabus.dev>
2024-02-28 17:50:24 +03:00

116 lines
3 KiB
Rust

use byteorder::ByteOrder;
use clap::Parser;
use rodio::buffer::SamplesBuffer;
use rodio::{OutputStream, Sink};
use std::io::Read;
use std::net::TcpStream;
use std::time::Instant;
// How many samples to cache before playing in samples (both channels) SHOULD BE EVEN
const BUFFER_SIZE: usize = 2400;
// How many buffers to cache
const CACHE_SIZE: usize = 100;
enum Channel {
Right,
Left,
Stereo,
}
#[derive(Parser)]
struct Args {
/// Remote address
address: String,
#[arg(short, long, default_value = "s")]
/// L, R or S for Left, Right or Stereo
channel: String,
#[arg(short)]
/// Play only on specified channel, with it if channel = Right => L=0 and R=R, without L=R and R=R. No effect on Stereo
single: bool,
/// More verbose
#[arg(short)]
verbose: bool,
}
fn main() {
let start = Instant::now();
let args = Args::parse();
let mut stream = TcpStream::connect(args.address).unwrap();
if args.verbose {
eprintln!(
"Connected to {} from {}",
stream.peer_addr().unwrap(),
stream.local_addr().unwrap()
)
}
let channel = match args.channel.to_ascii_lowercase().as_str() {
"l" => Channel::Left,
"r" => Channel::Right,
"s" => Channel::Stereo,
_ => panic!("Wrong channel specified"),
};
let (_stream, stream_handle) = OutputStream::try_default().unwrap();
let sink = Sink::try_new(&stream_handle).unwrap();
let mut buffer = [0u8; 4];
let mut samples = [0f32; BUFFER_SIZE];
let mut index = 0usize;
let mut first = true;
while stream.read_exact(&mut buffer).is_ok() {
let sample_l = byteorder::LittleEndian::read_i16(&buffer[..2]) as f32 / 32768.0;
let sample_r = byteorder::LittleEndian::read_i16(&buffer[2..]) as f32 / 32768.0;
// Left channel
samples[index] = match channel {
Channel::Left | Channel::Stereo => sample_l,
Channel::Right => {
if args.single {
0f32
} else {
sample_r
}
}
};
index += 1;
// Right channel
samples[index] = match channel {
Channel::Right | Channel::Stereo => sample_r,
Channel::Left => {
if args.single {
0f32
} else {
sample_l
}
}
};
index += 1;
if index == BUFFER_SIZE {
let mut first_wait_iteration = true;
// Sink's thread is detached from main thread, so we need to synchronize with it
// Why we should synchronize with it?
// Let's say, that if we don't synchronize with it, we would have
// a lot (no upper limit, actualy) of buffered sound, waiting for playing in sink
while sink.len() >= CACHE_SIZE {
if args.verbose && first_wait_iteration {
eprint!(".");
first_wait_iteration = false;
}
// Sleeping exactly one buffer
std::thread::sleep(std::time::Duration::from_secs_f32(
(if sink.len() >= 2 {
sink.len() - 2
} else {
1
} as f32) * BUFFER_SIZE as f32
/ 44100.0 / 2.0,
))
}
if first && args.verbose {
eprintln!("Started playing in {} ms", (Instant::now() - start).as_millis());
first = false;
}
sink.append(SamplesBuffer::new(2, 44100, samples.as_slice()));
index = 0;
}
}
}