use super::{
in_out::{IOHandle, Signal},
serde::{SerDePlaylist, SerDeTrack},
utilities::{clear, fmt_path},
ChannelError, Error, VectorError,
};
use crossbeam_channel::TryRecvError;
use fastrand::Rng;
use std::{
cell::Cell,
fs::File,
io::{Cursor, Read, Seek},
path::PathBuf,
time::{Duration, Instant},
};
const STEP: f32 = 0.025;
pub struct Playlist {
track_map: Cell<Vec<usize>>,
shuffle: bool,
length: usize,
tracks: Vec<Track>,
repeats: Cell<isize>,
}
pub struct Track {
file_path: PathBuf,
repeats: Cell<isize>,
}
pub struct Playhandle {
current_track_index: Cell<usize>,
current_playlist_index: Cell<usize>,
has_reached_current_playlist_end: Cell<bool>,
has_reached_entire_end: Cell<bool>,
playlists: Vec<Playlist>,
volume: Cell<f32>,
paused: Cell<bool>,
io_handle: IOHandle,
}
#[cfg_attr(any(debug_assertions, feature = "debug"), derive(Debug))]
#[derive(Default)]
pub enum ControlFlow {
Break,
Skip,
SkipSkip,
#[default]
Default,
}
impl Playlist {
#[inline(always)]
pub fn tracks_count(&self) -> usize {
self.tracks
.len()
}
#[inline(always)]
pub fn tracks_is_empty(&self) -> bool {
self.tracks_count() == 0
}
#[inline(always)]
pub fn shuffle_can(&self) -> bool {
self.shuffle
}
pub fn play_through(
&self,
handle: &Playhandle,
should_shuffle: bool,
) -> Result<ControlFlow, Error> {
while handle
.track_index_check()
.is_none()
{
match unsafe {
self.nth_unchecked(handle.track_index_get_unchecked())
.play_through(handle)
} {
Ok(ControlFlow::Break) => return Ok(ControlFlow::Break),
Ok(ControlFlow::SkipSkip) => return Ok(ControlFlow::Skip),
Ok(ControlFlow::Skip) => continue,
Ok(ControlFlow::Default) => {},
Err(Error::Vector(VectorError::OutOfBounds)) => {
handle.track_index_reset();
break;
},
Err(other) => Err(other)?,
}
}
if self.repeats_can() {
self.repeats_update();
if should_shuffle {
self.shuffle()
}
handle.track_index_reset();
return self.play_through(handle, should_shuffle);
}
let _ = handle.playlist_index_try_set(|old| old + 1);
Ok(().into())
}
pub fn shuffle(&self) {
let mut map = self
.track_map
.take();
let mut generator = Rng::new();
generator.shuffle(&mut map);
for index in 0..self.length {
map.swap(index, generator.usize(0..=index));
map.swap(index, generator.usize(index..self.length));
}
self.track_map
.set(map)
}
#[inline(always)]
pub fn index_get(&self, index: usize) -> Option<usize> {
let map = self
.track_map
.take();
let mapped_index = map
.get(index)
.copied();
self.track_map
.set(map);
mapped_index
}
#[inline(always)]
pub unsafe fn index_get_unchecked(&self, index: usize) -> usize {
let map = self
.track_map
.take();
let mapped_index = unsafe { *map.get_unchecked(index) };
self.track_map
.set(map);
mapped_index
}
#[inline]
pub fn nth(&self, index: usize) -> Option<&Track> {
self.index_get(index)
.map(|index| unsafe {
self.tracks
.get_unchecked(index)
})
}
#[inline]
pub fn nth_mut(&mut self, index: usize) -> Option<&mut Track> {
self.index_get(index)
.map(|index| unsafe {
self.tracks
.get_unchecked_mut(index)
})
}
#[inline]
pub unsafe fn nth_unchecked(&self, index: usize) -> &Track {
self.tracks
.get_unchecked(self.index_get_unchecked(index))
}
#[inline]
pub unsafe fn nth_unchecked_mut(&mut self, index: usize) -> &mut Track {
let mapped_index = self.index_get_unchecked(index);
self.tracks
.get_unchecked_mut(mapped_index)
}
#[inline]
pub fn repeats_can(&self) -> bool {
self.repeats
.get() != 0
}
#[inline]
pub fn repeats_update(&self) {
let old = self
.repeats
.get();
self.repeats
.set(old - 1);
}
}
impl TryFrom<SerDePlaylist> for Playlist {
type Error = Error;
fn try_from(SerDePlaylist { song, time, vary }: SerDePlaylist) -> Result<Self, Error> {
let f = |tuple: Vec<(usize, Track)>| {
let (track_map, tracks): (Vec<usize>, Vec<Track>) = tuple
.into_iter()
.unzip();
if track_map.is_empty() {
Err(VectorError::Empty)?
}
Ok(Self {
shuffle: vary.unwrap_or(true),
track_map: Cell::new(track_map),
length: tracks.len(),
tracks,
repeats: Cell::new(time.unwrap_or_default()),
})
};
match song
.into_iter()
.enumerate()
.map(|(index, track)| Ok((index, track.try_into()?)))
.collect::<Result<Vec<(usize, Track)>, Error>>()
.map(f)
{
Err(error) | Ok(Err(error)) => Err(error)?,
Ok(Ok(playlist)) => Ok(playlist),
}
}
}
impl Track {
pub fn play_through(&self, data: &Playhandle) -> Result<ControlFlow, Error> {
let mut stream = Vec::with_capacity(127);
File::open(&self.file_path)?.read_to_end(&mut stream)?;
data.stream_play(Cursor::new(unsafe { &*(stream.as_slice() as *const [u8]) }))?;
let controls = data
.io_handle_get()
.controls_get();
let mut whole_elapsed_time = Duration::ZERO;
let decrement: fn(usize) -> usize = |old| old - (old > 0) as usize;
let increment: fn(usize) -> usize = |old| old + 1;
data.playback_play();
while !data.playback_has_ended() {
let moment = Instant::now();
match controls.signal_receive() {
Err(TryRecvError::Empty) => {},
Ok(Signal::Exit) => {
data.playback_clear();
clear()?;
return Ok(ControlFlow::Break);
},
Ok(signal) if signal.is_skip() => {
data.playback_clear();
clear()?;
let setter = if signal.is_next_skip() {
increment
} else {
decrement
};
(if signal.is_track_skip() {
|data: &Playhandle, setter| {
data.track_index_try_set(setter)
}
} else {
|data: &Playhandle, setter| {
data.playlist_index_try_set(setter)
}
})(data, setter)?;
return Ok(ControlFlow::Skip);
},
Ok(Signal::Play) => data.playback_toggle(),
Ok(Signal::PlaylistReset) => {
data.playlist_index_reset();
return Ok(().into());
},
Ok(Signal::TrackReset) => {
data.track_index_reset();
return Ok(().into());
},
Ok(signal) if signal.is_volume() => {
match signal {
Signal::VolumeIncrease => data.volume_increment(),
Signal::VolumeDecrease => data.volume_decrement(),
Signal::Mute => data.volume_mute(),
Signal::VolumeReset => data.volume_reset(),
_ => unreachable!(),
}
data.volume_update()
},
Ok(_) => unreachable!(),
Err(TryRecvError::Disconnected) => Err(ChannelError::Disconnect)?,
}
if !data.playback_is_paused() {
whole_elapsed_time += moment.elapsed()
}
}
if self.repeats_can() {
self.repeats_update();
return self.play_through(data);
}
data.track_index_try_set(increment)?;
Ok(().into())
}
#[inline(always)]
pub fn repeats_can(&self) -> bool {
self.repeats
.get() != 0
}
#[inline]
pub fn repeats_update(&self) {
let old = self
.repeats
.get();
self.repeats
.set(old - 1);
}
}
impl TryFrom<SerDeTrack> for Track {
type Error = Error;
fn try_from(SerDeTrack { file, time }: SerDeTrack) -> Result<Self, Error> {
let file_path = fmt_path(file)?;
Ok(Self {
file_path,
repeats: Cell::new(time.unwrap_or_default()),
})
}
}
impl Playhandle {
#[inline(always)]
pub fn playlists_count(&self) -> usize {
self.playlists
.len()
}
#[inline(always)]
pub fn tracks_count(&self) -> usize {
unsafe {
self.playlists
.get_unchecked(self.track_index_get_unchecked())
}
.tracks_count()
}
#[inline(always)]
pub fn all_tracks_count(&self) -> usize {
self.playlists
.iter()
.map(|playlist| playlist.tracks_count())
.sum()
}
#[inline(always)]
pub fn entries_is_empty(&self) -> bool {
self.playlists
.is_empty()
}
#[inline(always)]
pub fn playlist_has_ended(&self) -> bool {
self.has_reached_current_playlist_end
.take()
}
#[inline(always)]
pub fn playlists_have_ended(&self) -> bool {
self.has_reached_entire_end
.take()
}
pub fn all_playlists_play(&mut self, should_shuffle: bool) -> Result<ControlFlow, Error> {
while self
.playlist_index_check()
.is_none()
{
let index = unsafe { self.playlist_index_get_unchecked() };
let playlist = unsafe {
self.playlists
.get_unchecked(index)
};
let shufflable = should_shuffle && playlist.shuffle_can();
if shufflable {
playlist.shuffle()
}
match playlist.play_through(self, shufflable)? {
ControlFlow::Break => return Ok(ControlFlow::Break),
ControlFlow::Skip => {}, ControlFlow::SkipSkip => unimplemented!(), ControlFlow::Default => clear()?,
}
if self.playlist_has_ended() || self.playlists_have_ended() {
return Ok(().into());
}
}
Ok(().into())
}
#[inline(always)]
pub fn stream_play(
&self,
source: impl Read + Seek + Send + Sync + 'static,
) -> Result<(), Error> {
self.io_handle
.stream_play(source)
}
#[inline]
pub fn playlist_index_check(&self) -> Option<VectorError> {
(self.current_playlist_index
.get() >= self.playlists_count())
.then_some(VectorError::OutOfBounds)
}
#[inline]
pub fn track_index_check(&self) -> Option<VectorError> {
let playlist_index = match self.playlist_index_get() {
Ok(index) => index,
Err(error) => return Some(error),
};
let maximum = unsafe {
self.playlists
.get_unchecked(playlist_index)
.tracks_count()
};
(self.current_track_index
.get() >= maximum)
.then_some(VectorError::OutOfBounds)
}
#[inline]
pub fn playlist_index_get(&self) -> Result<usize, VectorError> {
self.playlist_index_check()
.map_or_else(|| Ok(unsafe { self.playlist_index_get_unchecked() }), Err)
}
#[inline]
pub fn track_index_get(&self) -> Result<usize, VectorError> {
self.playlist_index_check()
.map_or_else(|| Ok(unsafe { self.track_index_get_unchecked() }), Err)
}
#[inline(always)]
pub unsafe fn playlist_index_get_unchecked(&self) -> usize {
self.current_playlist_index
.get()
}
#[inline(always)]
pub unsafe fn track_index_get_unchecked(&self) -> usize {
self.current_track_index
.get()
}
pub fn playlist_index_try_set(
&self,
setter: impl FnOnce(usize) -> usize,
) -> Result<(), VectorError> {
self.track_index_reset();
let old_index = unsafe { self.playlist_index_get_unchecked() };
let new_index = setter(old_index);
if new_index >= self.playlists_count() {
self.has_reached_entire_end
.set(true);
Err(VectorError::OutOfBounds)?
}
self.current_playlist_index
.set(new_index);
Ok(())
}
pub fn track_index_try_set(
&self,
setter: impl FnOnce(usize) -> usize,
) -> Result<(), VectorError> {
let old_index = unsafe { self.track_index_get_unchecked() };
let new_index = setter(old_index);
let playlist_index = match self.playlist_index_get() {
Ok(index) => index,
Err(error) => Err(error)?,
};
let maximum = unsafe {
self.playlists
.get_unchecked(playlist_index)
.tracks_count()
};
if new_index >= maximum {
self.has_reached_current_playlist_end
.set(true);
Err(VectorError::OutOfBounds)?
}
unsafe { self.track_index_set_unchecked(move |_| new_index) }
Ok(())
}
#[inline(always)]
pub fn playlist_index_reset(&self) {
unsafe {
self.track_index_reset();
self.playlist_index_set_unchecked(|_| 0)
}
}
#[inline(always)]
pub fn track_index_reset(&self) {
unsafe { self.track_index_set_unchecked(|_| 0) }
}
#[inline]
pub unsafe fn playlist_index_set_unchecked(&self, setter: impl FnOnce(usize) -> usize) {
let new = setter(self.playlist_index_get_unchecked());
self.current_playlist_index
.set(new)
}
#[inline]
pub unsafe fn track_index_set_unchecked(&self, setter: impl FnOnce(usize) -> usize) {
let new = setter(self.track_index_get_unchecked());
self.playback_clear();
self.current_track_index
.set(new)
}
#[inline(always)]
pub fn io_handle_get(&self) -> &IOHandle {
&self.io_handle
}
#[inline(always)]
pub fn io_handle_take(self) -> IOHandle {
self.io_handle
}
#[inline]
pub fn volume_get(&self) -> f32 {
self.volume_get_raw()
.clamp(0.0, 2.0)
}
#[inline]
pub fn volume_get_raw(&self) -> f32 {
self.volume
.get()
}
#[inline]
pub fn volume_set(&self, map: impl FnOnce(f32) -> f32) {
self.volume
.set(map(self.volume_get()))
}
#[inline(always)]
pub fn volume_reset(&self) {
self.volume_set_raw(|_| 1.0)
}
#[inline(always)]
pub fn volume_set_raw(&self, map: impl FnOnce(f32) -> f32) {
self.volume
.set(map(self.volume_get_raw()))
}
#[inline(always)]
pub fn volume_mute(&self) {
self.volume_set_raw(|old| old + 2.0 * -old)
}
#[inline(always)]
pub fn volume_increment(&self) {
self.volume_set_raw(|old| old + STEP)
}
#[inline(always)]
pub fn volume_decrement(&self) {
self.volume_set_raw(|old| old - STEP)
}
pub fn volume_update(&self) {
self.io_handle_get()
.playback_get()
.set_volume(self.volume_get());
}
#[inline]
pub fn playback_has_ended(&self) -> bool {
self.io_handle_get()
.playback_get()
.empty()
}
#[inline(always)]
pub fn playback_play(&self) {
self.io_handle_get()
.playback_get()
.play();
self.paused
.set(false)
}
#[inline(always)]
pub fn playback_pause(&self) {
self.io_handle_get()
.playback_get()
.pause();
self.paused
.set(true)
}
#[inline(always)]
pub fn playback_toggle(&self) {
if self.playback_is_paused() {
self.playback_play()
} else {
self.playback_pause()
}
}
#[inline]
pub fn playback_clear(&self) {
self.io_handle_get()
.playback_get()
.clear()
}
#[inline(always)]
pub fn playback_is_paused(&self) -> bool {
self.paused
.get()
}
pub fn raw_parts_from(io_handle: IOHandle, streams_vector: Vec<Playlist>) -> Self {
Self {
current_track_index: Cell::new(0),
current_playlist_index: Cell::new(0),
has_reached_current_playlist_end: Cell::new(false),
has_reached_entire_end: Cell::new(false),
playlists: streams_vector,
volume: Cell::new(1.0),
paused: Cell::new(
io_handle
.playback_get()
.is_paused(),
),
io_handle,
}
}
pub fn playlists_swap(&mut self, new: Vec<Playlist>) {
self.playback_clear();
self.playlists = new;
self.playlist_index_reset();
}
}
impl TryFrom<Vec<Playlist>> for Playhandle {
type Error = Error;
#[inline(always)]
fn try_from(streams_vector: Vec<Playlist>) -> Result<Self, Error> {
IOHandle::try_new().map(|io_handle| Self::raw_parts_from(io_handle, streams_vector))
}
}
impl From<()> for ControlFlow {
fn from(_: ()) -> Self {
Self::Default
}
}