Skip to main content

proteus_lib/playback/player/
settings.rs

1//! Runtime tuning and debug accessors for `Player`.
2//!
3//! These methods expose buffering/fade/jitter controls used by the runtime
4//! worker thread, plus lightweight debug snapshots for diagnostics.
5
6use std::sync::atomic::Ordering;
7
8use crate::playback::engine::InlineTrackMixUpdate;
9
10use super::{Player, PlayerState};
11
12impl Player {
13    /// Configure the minimum buffered audio (ms) before playback starts.
14    ///
15    /// # Arguments
16    ///
17    /// * `start_buffer_ms` - Startup prebuffer target in milliseconds.
18    pub fn set_start_buffer_ms(&self, start_buffer_ms: f32) {
19        let mut settings = self.buffer_settings.lock().unwrap();
20        settings.start_buffer_ms = start_buffer_ms.max(0.0);
21    }
22
23    /// Configure heuristic end-of-track threshold for containers (ms).
24    ///
25    /// # Arguments
26    ///
27    /// * `track_eos_ms` - End-of-track threshold in milliseconds.
28    pub fn set_track_eos_ms(&self, track_eos_ms: f32) {
29        let mut settings = self.buffer_settings.lock().unwrap();
30        settings.track_eos_ms = track_eos_ms.max(0.0);
31    }
32
33    /// Configure minimum sink chunks queued before playback starts/resumes.
34    pub fn set_start_sink_chunks(&self, chunks: usize) {
35        let mut settings = self.buffer_settings.lock().unwrap();
36        settings.start_sink_chunks = chunks;
37    }
38
39    /// Configure the maximum sink chunks queued before producer backpressure.
40    ///
41    /// Set to `0` to disable this guard.
42    pub fn set_max_sink_chunks(&self, chunks: usize) {
43        let mut settings = self.buffer_settings.lock().unwrap();
44        settings.max_sink_chunks = chunks;
45    }
46
47    /// Configure the startup silence pre-roll (ms).
48    pub fn set_startup_silence_ms(&self, ms: f32) {
49        let mut settings = self.buffer_settings.lock().unwrap();
50        settings.startup_silence_ms = ms.max(0.0);
51    }
52
53    /// Configure the startup fade-in length (ms).
54    pub fn set_startup_fade_ms(&self, ms: f32) {
55        let mut settings = self.buffer_settings.lock().unwrap();
56        settings.startup_fade_ms = ms.max(0.0);
57    }
58
59    /// Configure seek fade-out length (ms) before restarting playback.
60    pub fn set_seek_fade_out_ms(&self, ms: f32) {
61        let mut settings = self.buffer_settings.lock().unwrap();
62        settings.seek_fade_out_ms = ms.max(0.0);
63    }
64
65    /// Configure seek fade-in length (ms) after restarting playback.
66    pub fn set_seek_fade_in_ms(&self, ms: f32) {
67        let mut settings = self.buffer_settings.lock().unwrap();
68        settings.seek_fade_in_ms = ms.max(0.0);
69    }
70
71    /// Configure the append jitter logging threshold (ms). 0 disables logging.
72    pub fn set_append_jitter_log_ms(&self, ms: f32) {
73        let mut settings = self.buffer_settings.lock().unwrap();
74        settings.append_jitter_log_ms = ms.max(0.0);
75    }
76
77    /// Configure inline effects transition duration (ms) for `set_effects_inline`.
78    pub fn set_inline_effects_transition_ms(&self, ms: f32) {
79        let mut settings = self.buffer_settings.lock().unwrap();
80        settings.inline_effects_transition_ms = ms.max(0.0);
81    }
82
83    /// Enable or disable per-effect boundary discontinuity logging.
84    pub fn set_effect_boundary_log(&self, enabled: bool) {
85        let mut settings = self.buffer_settings.lock().unwrap();
86        settings.effect_boundary_log = enabled;
87    }
88
89    /// Update per-slot track level/pan without restarting playback.
90    ///
91    /// This mutates the underlying track model and queues an inline update for
92    /// the active mix thread. Returns `false` if `slot_index` is out of range.
93    pub fn set_track_mix_inline(&self, slot_index: usize, level: f32, pan: f32) -> bool {
94        let linked_slots = {
95            let mut prot = self.prot.lock().unwrap();
96            if !prot.set_slot_mix_settings(slot_index, level, pan) {
97                return false;
98            }
99            prot.linked_slot_indices(slot_index)
100        };
101        let Some(linked_slots) = linked_slots else {
102            return false;
103        };
104
105        let mut pending = self.inline_track_mix_updates.lock().unwrap();
106        for slot_index in linked_slots {
107            pending.push(InlineTrackMixUpdate {
108                slot_index,
109                level,
110                pan,
111            });
112        }
113        true
114    }
115
116    /// Debug helper returning thread alive, state, and audio heard flags.
117    pub fn debug_playback_state(&self) -> (bool, PlayerState, bool) {
118        (
119            self.playback_thread_exists.load(Ordering::SeqCst),
120            *self.state.lock().unwrap(),
121            self.audio_heard.load(Ordering::Relaxed),
122        )
123    }
124
125    /// Debug helper indicating whether buffering has completed.
126    pub fn debug_buffering_done(&self) -> bool {
127        self.buffering_done.load(Ordering::Relaxed)
128    }
129
130    /// Debug helper returning internal timing markers in milliseconds.
131    pub fn debug_timing_ms(&self) -> (u64, u64) {
132        (
133            self.last_chunk_ms.load(Ordering::Relaxed),
134            self.last_time_update_ms.load(Ordering::Relaxed),
135        )
136    }
137
138    /// Debug helper returning sink paused/empty flags and queued length.
139    pub fn debug_sink_state(&self) -> (bool, bool, usize) {
140        let sink = self.sink.lock().unwrap();
141        let paused = sink.is_paused();
142        let empty = sink.empty();
143        let len = sink.len();
144        (paused, empty, len)
145    }
146}