Skip to main content

chilen_ipc/
playback.rs

1use std::{path::PathBuf, time::Duration};
2
3use serde::{Deserialize, Serialize};
4
5use crate::library::Track;
6
7/// Playback state of the player.
8#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub enum PlaybackState {
10    /// The player is playing.
11    Playing,
12    /// The player is paused.
13    Paused,
14    /// The player is stopped.
15    ///
16    /// Playing will play the current track from the beginning.
17    #[default]
18    Stopped,
19}
20
21impl std::fmt::Display for PlaybackState {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        match self {
24            Self::Playing => write!(f, "Playing"),
25            Self::Paused => write!(f, "Paused"),
26            Self::Stopped => write!(f, "Stopped"),
27        }
28    }
29}
30
31/// Specifies how the player loops playback.
32#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
33pub enum LoopState {
34    /// The playback will stop when there are no more tracks to play.
35    #[default]
36    Off,
37    /// The current track will start again from the beginning once it has finished playing.
38    Track,
39    /// The playback will loop through the entire queue.
40    Playlist,
41}
42
43/// Shuffle state of the player.
44#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
45pub enum ShuffleState {
46    /// Play tracks in their original order.
47    ///
48    /// For example tracks from a playlist will be played in the order they appear in the playlist.
49    #[default]
50    Off,
51    /// Shuffle the tracks in the queue.
52    On,
53}
54
55impl From<ShuffleState> for bool {
56    fn from(s: ShuffleState) -> bool {
57        match s {
58            ShuffleState::Off => false,
59            ShuffleState::On => true,
60        }
61    }
62}
63
64impl From<bool> for ShuffleState {
65    fn from(value: bool) -> Self {
66        if value { Self::On } else { Self::Off }
67    }
68}
69
70impl std::fmt::Display for ShuffleState {
71    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72        match self {
73            Self::Off => write!(f, "Off"),
74            Self::On => write!(f, "On"),
75        }
76    }
77}
78
79impl std::fmt::Display for LoopState {
80    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81        match self {
82            Self::Off => write!(f, "Off"),
83            Self::Track => write!(f, "Track"),
84            Self::Playlist => write!(f, "Playlist"),
85        }
86    }
87}
88
89/// Signed duration type used for seeking.
90///
91/// This is just a bare bones type that should only be used for
92/// [seeking player position](PlaybackCommand::Seek), and not as a [`Duration`] replacement.
93#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
94pub enum SignedDuration {
95    Positive(Duration),
96    Negative(Duration),
97}
98
99impl SignedDuration {
100    pub fn from_secs<T: Into<i64> + Copy>(secs: T) -> SignedDuration {
101        if secs.into() < 0 {
102            SignedDuration::Negative(Duration::from_secs(secs.into().abs().try_into().unwrap()))
103        } else {
104            SignedDuration::Positive(Duration::from_secs(secs.into().abs().try_into().unwrap()))
105        }
106    }
107}
108
109/// Player volume.
110///
111/// Values passed to this struct will be clamped between `0.0` (no sound at all) and `1.0` (regular
112/// volume).
113#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
114pub struct PlayerVolume {
115    volume: f64,
116}
117
118impl Default for PlayerVolume {
119    fn default() -> Self {
120        Self { volume: 1.0 }
121    }
122}
123
124impl std::fmt::Display for PlayerVolume {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126        write!(f, "Player volume: {}", self.volume)
127    }
128}
129
130impl PlayerVolume {
131    /// Create a new [`PlayerVolume`] struct with the specified volume.
132    ///
133    /// The passed `volume` parameter will be clamped between `0.0` and `1.0`.
134    pub fn new(volume: f64) -> Self {
135        Self {
136            volume: volume.clamp(0.0, 1.0),
137        }
138    }
139
140    /// Set the volume.
141    ///
142    /// The passed `volume` parameter will be clamped between 0.0 and 1.0.
143    pub fn set(&mut self, volume: f64) {
144        self.volume = volume.clamp(0.0, 1.0);
145    }
146
147    /// Get the wrapped value.
148    pub fn get(&self) -> f64 {
149        self.volume
150    }
151}
152
153/// The speed at which tracks are played.
154///
155/// Rate of `1.0` will play tracks at their original speed, `2.0` will play them twice as fast, and
156/// `0.5` will slow them to half speed.
157#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
158pub struct PlaybackRate {
159    rate: f64,
160    min: f64,
161    max: f64,
162}
163
164impl Default for PlaybackRate {
165    fn default() -> Self {
166        Self {
167            rate: 1.0,
168            min: 0.1,
169            max: 10.0,
170        }
171    }
172}
173
174impl<T: Into<f64>> From<T> for PlaybackRate {
175    fn from(value: T) -> Self {
176        let mut def = Self::default();
177        def.set_value(value.into());
178        def
179    }
180}
181
182impl std::fmt::Display for PlaybackRate {
183    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184        write!(
185            f,
186            "Current playback rate: {}, min: {}, max: {}",
187            self.rate, self.min, self.max
188        )
189    }
190}
191
192impl PlaybackRate {
193    /// Create a new [PlaybackRate] struct.
194    pub fn new(rate: f64, min: f64, max: f64) -> PlaybackRate {
195        Self { rate, min, max }
196    }
197
198    fn clamp(&mut self) {
199        self.rate = self.rate.clamp(self.min, self.max)
200    }
201
202    /// Checks if the given rate value is in the allowed range.
203    pub fn is_in_range(&self, rate: f64) -> bool {
204        rate >= self.min && rate <= self.max
205    }
206
207    /// Set the rate multiplier.
208    ///
209    /// This value will be clamped with the min and max values.
210    pub fn set_value(&mut self, rate: f64) {
211        self.rate = rate.clamp(self.min, self.max)
212    }
213
214    /// Get the rate multiplier.
215    pub fn get_value(&self) -> f64 {
216        self.rate
217    }
218
219    /// Get the rate multiplier clamped to `f32`.
220    pub fn get_value_f32(&self) -> f32 {
221        if self.rate.is_nan() {
222            f32::NAN
223        } else if self.rate > f32::MAX as f64 {
224            f32::MAX
225        } else if self.rate < f32::MIN as f64 {
226            f32::MIN
227        } else {
228            self.rate as f32
229        }
230    }
231
232    /// Set the minimum acceptable value for the playback rate.
233    pub fn set_min(&mut self, min: f64) {
234        self.min = min.clamp(0.0, self.max);
235        self.clamp();
236    }
237
238    /// Get the minimum acceptable value for the playback rate.
239    pub fn get_min(&self) -> f64 {
240        self.min
241    }
242
243    /// Set the maximum acceptable value for the playback rate.
244    pub fn set_max(&mut self, max: f64) {
245        self.max = max.clamp(self.min, f64::MAX);
246        self.clamp();
247    }
248
249    /// Get the maximum acceptable value for the playback rate.
250    pub fn get_max(&self) -> f64 {
251        self.max
252    }
253}
254
255/// Subcommand of [`Command`](crate::Command) for managing audio playback in the daemon.
256///
257/// The expected response may be different depending on the command sent. If it isn't specified in
258/// the variant documentation, assume [`Response::Ok`](crate::Response::Ok) is the expected
259/// response.
260#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
261pub enum PlaybackCommand {
262    /// Play the current track or play a track at a specific index in the queue.
263    Play(Option<usize>),
264    /// Pause the player.
265    Pause,
266    /// Stop the player.
267    Stop,
268    /// Toggle between play/pause.
269    TogglePlaying,
270    /// Get the [`PlaybackState`] of the player.
271    ///
272    /// The daemon will respond with [`PlaybackResponse::PlaybackState`] if successful.
273    GetPlaybackState,
274    /// Set a new queue for the player.
275    ///
276    /// If any of the provided tracks are not registered by chilen (added after last library
277    /// reload), the daemon will return [`Error::UnknownTrack`](crate::Error::UnknownTrack).
278    SetQueue(Vec<PathBuf>),
279    /// Append tracks to the queue.
280    ///
281    /// If any of the provided tracks are not registered by chilen (added after last library
282    /// reload), the daemon will return [`Error::UnknownTrack`](crate::Error::UnknownTrack).
283    AppendToQueue(Vec<PathBuf>),
284    /// Load a playlist and append its tracks to the queue.
285    ///
286    /// If there's no [playlist](crate::library::Playlist) with the provided name present in the
287    /// [music library](crate::library::MusicLibrary),
288    /// [`Error::UnknownPlaylist`](crate::Error::UnknownPlaylist) will be returned, and no changes
289    /// to the queue will be made.
290    AppendPlaylist(String),
291    /// Load a playlist and put its tracks in the queue.
292    ///
293    /// If there's no [playlist](crate::library::Playlist) with the provided name present in the
294    /// [music library](crate::library::MusicLibrary),
295    /// [`Error::UnknownPlaylist`](crate::Error::UnknownPlaylist) will be returned, and no changes
296    /// to the queue will be made.
297    SetPlaylist(String),
298    /// Get the current [track](Track).
299    ///
300    /// The daemon will respond to this with [`PlaybackResponse::Track`] if successful.
301    GetCurrentTrack,
302    /// Skip to the next track.
303    Next,
304    /// Skip to the previous track.
305    Previous,
306    /// Set the [loop state](LoopState) of the player.
307    SetLoopState(LoopState),
308    /// Get the [loop state](LoopState) of the player.
309    ///
310    /// The daemon will respond to this with [`PlaybackResponse::LoopState`] if successful.
311    GetLoopState,
312    /// Set the [playback rate](PlaybackRate) of the player.
313    ///
314    /// This command will fail if the daemon is configured to not allow playback rate
315    /// modification or if the specified rate value was out of the acceptable range.
316    ///
317    /// If the daemon is configured not to allow playback rate modification,
318    /// [`Error::FixedRate`](crate::Error::FixedRate) will be returned.
319    ///
320    /// If the provided rate value is out of the allowed range,
321    /// [`Error::RateOutOfRange`](crate::Error::RateOutOfRange) will be returned.
322    SetRate(f64),
323    /// Get the [playback rate](PlaybackRate) of the player.
324    ///
325    /// The daemon will respond to this with [`PlaybackResponse::PlaybackRate`] if
326    /// successful.
327    GetRate,
328    /// Set the [shuffle state](ShuffleState) of the player.
329    ///
330    /// The daemon will always respond to this command with
331    /// [`Error::ShuffleNotSupported`](crate::Error::ShuffleNotSupported) if it was built without
332    /// shuffle support.
333    SetShuffleState(ShuffleState),
334    /// Get the [shuffle state](ShuffleState) of the player.
335    ///
336    /// If the daemon was built without shuffle support, it will always respond to this command with
337    /// [`ShuffleState::Off`]. Otherwise, it will return [`PlaybackResponse::ShuffleState`].
338    GetShuffleState,
339    /// Set the position of the player.
340    SetPlayerPosition(Duration),
341    /// Change the player position by a time delta.
342    Seek(SignedDuration),
343    /// Get the position of the player.
344    ///
345    /// The daemon will respond to this with [`PlaybackResponse::PlayerPosition`] if
346    /// successful.
347    GetPlayerPosition,
348    /// Set the volume of the player.
349    SetPlayerVolume(PlayerVolume),
350    /// Get the volume of the player.
351    ///
352    /// The daemon will respond to this with [`PlaybackResponse::PlayerVolume`] if
353    /// successful.
354    GetPlayerVolume,
355    /// Set a track, a directory with tracks or an M3U8 playlist as the current queue.
356    ///
357    /// Tracks outside of the music library will be ignored.
358    OpenURI(String),
359}
360
361/// Response originating from the playback module of the daemon used in
362/// ed as an enum value for th[`Response`](crate::Response).
363#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
364pub enum PlaybackResponse {
365    PlaybackState(PlaybackState),
366    LoopState(LoopState),
367    PlaybackRate(PlaybackRate),
368    ShuffleState(ShuffleState),
369    Track(Option<Box<Track>>),
370    PlayerVolume(PlayerVolume),
371    PlayerPosition(Duration),
372}
373
374impl std::fmt::Display for PlaybackResponse {
375    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376        match self {
377            Self::PlaybackState(state) => write!(f, "{state}"),
378            Self::LoopState(state) => write!(f, "{state}"),
379            Self::PlaybackRate(rate) => write!(f, "{rate}"),
380            Self::ShuffleState(state) => write!(f, "{state}"),
381            Self::Track(track) => match track {
382                Some(track) => write!(f, "{track}"),
383                None => write!(f, "None"),
384            },
385            Self::PlayerVolume(volume) => write!(f, "{volume}"),
386            Self::PlayerPosition(position) => write!(f, "{position:?}"),
387        }
388    }
389}
390
391/// Event originating from the playback module of the daemon.
392#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
393pub enum PlaybackEvent {
394    /// Sent when the [playback state](PlaybackState) changes, for instance when the player is
395    /// paused.
396    PlaybackStateChanged(PlaybackState),
397    /// Sent when the [loop state](LoopState) of the player changes.
398    LoopStateChanged(LoopState),
399    /// Sent when the [shuffle state](ShuffleState) of the player changes.
400    ShuffleStateChanged(ShuffleState),
401    /// Sent when the track queue changes.
402    QueueChanged(Vec<Track>),
403    /// Sent when the current track changes.
404    PositionChanged(usize),
405    /// Sent when the position of the player changes.
406    PlayerPositionChanged(Duration),
407    /// Sent when the [volume](PlayerVolume) of the player changes.
408    PlayerVolumeChanged(PlayerVolume),
409    /// Sent when the [playback rate](PlaybackRate) of the player changes.
410    RateChanged(PlaybackRate),
411}