use parking_lot::{Mutex, RwLock};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::thread::{self, JoinHandle};
use crate::playback::ToneSettings;
use crate::song_player::SharedSongPlayer;
const DEFAULT_BUFFER_SIZE: usize = 32768;
const BUFFER_BACKOFF_MICROS: u64 = 500;
const SAMPLES_PER_BATCH: usize = 882;
const MIN_BUFFER_FILL: f32 = 0.5;
pub struct RingBuffer {
buffer: Mutex<Vec<f32>>,
write_pos: AtomicUsize,
read_pos: AtomicUsize,
capacity: usize,
mask: usize,
}
impl RingBuffer {
pub fn new(requested_capacity: usize) -> Self {
let capacity = requested_capacity.max(1024).next_power_of_two();
let mask = capacity - 1;
Self {
buffer: Mutex::new(vec![0.0; capacity]),
write_pos: AtomicUsize::new(0),
read_pos: AtomicUsize::new(0),
capacity,
mask,
}
}
pub fn available_read(&self) -> usize {
let write = self.write_pos.load(Ordering::Acquire);
let read = self.read_pos.load(Ordering::Acquire);
write.wrapping_sub(read)
}
pub fn available_write(&self) -> usize {
self.capacity - self.available_read() - 1
}
pub fn write(&self, samples: &[f32]) -> usize {
let mut buf = self.buffer.lock();
let write_pos = self.write_pos.load(Ordering::Acquire);
let read_pos = self.read_pos.load(Ordering::Acquire);
let available = self.capacity - write_pos.wrapping_sub(read_pos) - 1;
let to_write = samples.len().min(available);
if to_write == 0 {
return 0;
}
let write_idx = write_pos & self.mask;
if write_idx + to_write <= self.capacity {
buf[write_idx..write_idx + to_write].copy_from_slice(&samples[..to_write]);
} else {
let first_part = self.capacity - write_idx;
buf[write_idx..].copy_from_slice(&samples[..first_part]);
buf[..to_write - first_part].copy_from_slice(&samples[first_part..to_write]);
}
drop(buf);
self.write_pos
.store(write_pos.wrapping_add(to_write), Ordering::Release);
to_write
}
pub fn read(&self, dest: &mut [f32]) -> usize {
let buf = self.buffer.lock();
let write_pos = self.write_pos.load(Ordering::Acquire);
let read_pos = self.read_pos.load(Ordering::Acquire);
let available = write_pos.wrapping_sub(read_pos);
let to_read = dest.len().min(available);
if to_read == 0 {
return 0;
}
let read_idx = read_pos & self.mask;
if read_idx + to_read <= self.capacity {
dest[..to_read].copy_from_slice(&buf[read_idx..read_idx + to_read]);
} else {
let first_part = self.capacity - read_idx;
dest[..first_part].copy_from_slice(&buf[read_idx..]);
dest[first_part..to_read].copy_from_slice(&buf[..to_read - first_part]);
}
drop(buf);
self.read_pos
.store(read_pos.wrapping_add(to_read), Ordering::Release);
to_read
}
pub fn flush(&self) {
let write_pos = self.write_pos.load(Ordering::Acquire);
self.read_pos.store(write_pos, Ordering::Release);
}
pub fn fill_percentage(&self) -> f32 {
self.available_read() as f32 / self.capacity as f32
}
}
pub struct AudioStreamState {
pub buffer: RingBuffer,
pub running: AtomicBool,
pub ready: AtomicBool,
pub stereo_gain: RwLock<(f32, f32)>,
pub tone_settings: RwLock<ToneSettings>,
pub seek_counter: AtomicUsize,
}
impl AudioStreamState {
pub fn new() -> Self {
Self {
buffer: RingBuffer::new(DEFAULT_BUFFER_SIZE),
running: AtomicBool::new(true),
ready: AtomicBool::new(false),
stereo_gain: RwLock::new((1.0, 1.0)),
tone_settings: RwLock::new(ToneSettings::default()),
seek_counter: AtomicUsize::new(0),
}
}
pub fn notify_seek(&self) {
self.seek_counter.fetch_add(1, Ordering::Release);
}
pub fn set_stereo_gain(&self, left: f32, right: f32) {
*self.stereo_gain.write() = (left, right);
}
pub fn set_tone_settings(&self, settings: ToneSettings) {
*self.tone_settings.write() = settings;
}
pub fn is_ready(&self) -> bool {
self.ready.load(Ordering::Acquire)
}
}
impl Default for AudioStreamState {
fn default() -> Self {
Self::new()
}
}
pub struct AudioStream {
pub state: Arc<AudioStreamState>,
producer_handle: Option<JoinHandle<()>>,
}
impl AudioStream {
pub fn start(player: SharedSongPlayer) -> Self {
let state = Arc::new(AudioStreamState::new());
let state_clone = Arc::clone(&state);
let producer_handle = thread::spawn(move || {
run_producer_loop(player, state_clone);
});
let start = std::time::Instant::now();
let timeout = std::time::Duration::from_millis(500);
while !state.is_ready() && start.elapsed() < timeout {
thread::sleep(std::time::Duration::from_millis(5));
}
Self {
state,
producer_handle: Some(producer_handle),
}
}
pub fn shared_state(&self) -> Arc<AudioStreamState> {
Arc::clone(&self.state)
}
}
impl Drop for AudioStream {
fn drop(&mut self) {
self.state.running.store(false, Ordering::Release);
if let Some(handle) = self.producer_handle.take() {
let _ = handle.join();
}
}
}
struct ToneFilter {
prev0: f32,
prev1: f32,
envelope: f32,
}
impl ToneFilter {
fn new() -> Self {
Self {
prev0: 0.0,
prev1: 0.0,
envelope: 0.0,
}
}
fn process(&mut self, sample: f32, settings: &ToneSettings) -> f32 {
let mut s = sample;
if settings.accent > 0.0 {
let target = s.abs();
self.envelope += 0.001 * (target - self.envelope);
let boost = 1.0 + self.envelope * settings.accent;
s *= boost;
}
if settings.saturation > 0.0 {
let drive = 1.0 + settings.saturation * 0.5;
s = (s * drive).tanh() / drive;
}
if settings.color_filter {
let filtered = (self.prev0 * 0.25) + (self.prev1 * 0.5) + (s * 0.25);
self.prev0 = self.prev1;
self.prev1 = s;
s = filtered;
} else {
self.prev0 = s;
self.prev1 = s;
}
s.clamp(-1.0, 1.0)
}
}
fn run_producer_loop(player: SharedSongPlayer, state: Arc<AudioStreamState>) {
let mut mono_buffer = vec![0.0f32; SAMPLES_PER_BATCH];
let mut stereo_buffer = vec![0.0f32; SAMPLES_PER_BATCH * 2];
let mut filter = ToneFilter::new();
let mut marked_ready = false;
{
let mut player_guard = player.write();
player_guard.play();
}
while state.running.load(Ordering::Acquire) {
{
let mut player_guard = player.write();
player_guard.generate_samples_into(&mut mono_buffer);
}
let (left_gain, right_gain) = *state.stereo_gain.read();
let tone_settings = *state.tone_settings.read();
for (i, &mono_sample) in mono_buffer.iter().enumerate() {
let processed = filter.process(mono_sample, &tone_settings);
let width = tone_settings.widen.clamp(-0.5, 0.5);
stereo_buffer[i * 2] = processed * (left_gain + width);
stereo_buffer[i * 2 + 1] = processed * (right_gain - width);
}
let mut written = 0;
while written < stereo_buffer.len() && state.running.load(Ordering::Relaxed) {
let n = state.buffer.write(&stereo_buffer[written..]);
written += n;
if n == 0 {
thread::sleep(std::time::Duration::from_micros(BUFFER_BACKOFF_MICROS));
}
}
if !marked_ready && state.buffer.fill_percentage() >= MIN_BUFFER_FILL {
state.ready.store(true, Ordering::Release);
marked_ready = true;
}
}
}
pub struct StreamingDecoder {
state: Arc<AudioStreamState>,
sample_rate: u32,
total_samples: usize,
current_sample: usize,
local_buffer: Vec<f32>,
local_pos: usize,
last_seek_counter: usize,
}
impl StreamingDecoder {
pub fn new(state: Arc<AudioStreamState>, sample_rate: u32, total_samples: usize) -> Self {
let last_seek_counter = state.seek_counter.load(Ordering::Acquire);
Self {
state,
sample_rate,
total_samples,
current_sample: 0,
local_buffer: Vec::new(),
local_pos: 0,
last_seek_counter,
}
}
fn check_seek(&mut self) {
let current = self.state.seek_counter.load(Ordering::Acquire);
if current != self.last_seek_counter {
self.last_seek_counter = current;
self.local_buffer.clear();
self.local_pos = 0;
}
}
fn refill_local_buffer(&mut self) {
const LOCAL_BATCH: usize = 1024;
if self.local_buffer.len() != LOCAL_BATCH {
self.local_buffer.resize(LOCAL_BATCH, 0.0);
}
let read = self.state.buffer.read(&mut self.local_buffer);
for sample in self.local_buffer[read..].iter_mut() {
*sample = 0.0;
}
self.local_pos = 0;
}
}
impl Iterator for StreamingDecoder {
type Item = f32;
fn next(&mut self) -> Option<Self::Item> {
if self.total_samples > 0 && self.current_sample >= self.total_samples * 2 {
return None;
}
self.check_seek();
if self.local_pos >= self.local_buffer.len() {
self.refill_local_buffer();
}
let sample = self
.local_buffer
.get(self.local_pos)
.copied()
.unwrap_or(0.0);
self.local_pos += 1;
self.current_sample += 1;
Some(sample)
}
}
impl bevy::audio::Source for StreamingDecoder {
fn current_frame_len(&self) -> Option<usize> {
if self.total_samples == 0 {
None
} else {
Some(
self.total_samples
.saturating_mul(2)
.saturating_sub(self.current_sample),
)
}
}
fn channels(&self) -> u16 {
2
}
fn sample_rate(&self) -> u32 {
self.sample_rate
}
fn total_duration(&self) -> Option<std::time::Duration> {
if self.total_samples == 0 {
None
} else {
Some(std::time::Duration::from_secs_f32(
self.total_samples as f32 / self.sample_rate as f32,
))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ring_buffer_basic() {
let rb = RingBuffer::new(1024);
assert_eq!(rb.available_read(), 0);
assert!(rb.available_write() > 0);
let samples = [1.0, 2.0, 3.0, 4.0];
let written = rb.write(&samples);
assert_eq!(written, 4);
assert_eq!(rb.available_read(), 4);
let mut dest = [0.0; 4];
let read = rb.read(&mut dest);
assert_eq!(read, 4);
assert_eq!(dest, samples);
}
#[test]
fn test_ring_buffer_wraparound() {
let rb = RingBuffer::new(16);
let data1 = [1.0; 10];
rb.write(&data1);
let mut buf = [0.0; 6];
rb.read(&mut buf);
let data2 = [2.0; 8];
let written = rb.write(&data2);
assert!(written > 0);
}
#[test]
fn test_audio_stream_state() {
let state = AudioStreamState::new();
state.set_stereo_gain(0.5, 0.8);
let (left, right) = *state.stereo_gain.read();
assert!((left - 0.5).abs() < 0.001);
assert!((right - 0.8).abs() < 0.001);
}
}