2021-12-26 20:18:42 +00:00
use std ::{
cmp ::max ,
2021-12-27 23:04:09 +00:00
collections ::HashMap ,
2021-12-28 22:46:37 +00:00
fmt ,
2021-12-26 20:18:42 +00:00
future ::Future ,
io ::{ self , Read , Seek , SeekFrom } ,
mem ,
pin ::Pin ,
process ::exit ,
2021-12-27 23:04:09 +00:00
sync ::Arc ,
2021-12-26 20:18:42 +00:00
task ::{ Context , Poll } ,
thread ,
time ::{ Duration , Instant } ,
} ;
2021-02-22 08:55:40 +00:00
use byteorder ::{ LittleEndian , ReadBytesExt } ;
2021-12-26 20:18:42 +00:00
use futures_util ::{ future , stream ::futures_unordered ::FuturesUnordered , StreamExt , TryFutureExt } ;
2021-12-27 23:04:09 +00:00
use parking_lot ::Mutex ;
2022-01-02 23:13:28 +00:00
use symphonia ::core ::io ::MediaSource ;
2021-02-22 08:55:40 +00:00
use tokio ::sync ::{ mpsc , oneshot } ;
2021-12-26 20:18:42 +00:00
use crate ::{
audio ::{
AudioDecrypt , AudioFile , StreamLoaderController , READ_AHEAD_BEFORE_PLAYBACK ,
READ_AHEAD_BEFORE_PLAYBACK_ROUNDTRIPS , READ_AHEAD_DURING_PLAYBACK ,
READ_AHEAD_DURING_PLAYBACK_ROUNDTRIPS ,
} ,
audio_backend ::Sink ,
config ::{ Bitrate , NormalisationMethod , NormalisationType , PlayerConfig } ,
convert ::Converter ,
core ::{ util ::SeqGenerator , Error , Session , SpotifyId } ,
2022-01-06 21:46:50 +00:00
decoder ::{
2022-01-07 10:13:23 +00:00
AudioDecoder , AudioPacket , AudioPacketPosition , PassthroughDecoder , SymphoniaDecoder ,
2022-01-06 21:46:50 +00:00
} ,
2022-01-03 21:20:29 +00:00
metadata ::audio ::{ AudioFileFormat , AudioFiles , AudioItem } ,
2021-12-26 20:18:42 +00:00
mixer ::AudioFilter ,
2019-11-11 07:22:41 +00:00
} ;
2016-03-13 15:15:15 +00:00
2022-01-03 23:17:30 +00:00
use crate ::SAMPLES_PER_SECOND ;
2021-03-12 22:05:38 +00:00
2020-01-31 21:41:11 +00:00
const PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS : u32 = 30000 ;
2021-05-30 18:09:39 +00:00
pub const DB_VOLTAGE_RATIO : f64 = 20.0 ;
2020-01-31 21:41:11 +00:00
2022-01-03 21:20:29 +00:00
// Spotify inserts a custom Ogg packet at the start with custom metadata values, that you would
// otherwise expect in Vorbis comments. This packet isn't well-formed and players may balk at it.
const SPOTIFY_OGG_HEADER_END : u64 = 0xa7 ;
2021-12-26 20:18:42 +00:00
pub type PlayerResult = Result < ( ) , Error > ;
2021-12-18 22:44:13 +00:00
2015-09-01 11:20:37 +00:00
pub struct Player {
2021-01-21 21:22:32 +00:00
commands : Option < mpsc ::UnboundedSender < PlayerCommand > > ,
2021-02-20 21:14:15 +00:00
thread_handle : Option < thread ::JoinHandle < ( ) > > ,
2020-01-31 21:41:11 +00:00
play_request_id_generator : SeqGenerator < u64 > ,
2015-07-02 19:42:49 +00:00
}
2015-06-23 14:38:29 +00:00
2020-03-10 12:26:01 +00:00
#[ derive(PartialEq, Debug, Clone, Copy) ]
pub enum SinkStatus {
Running ,
Closed ,
TemporarilyClosed ,
}
pub type SinkEventCallback = Box < dyn Fn ( SinkStatus ) + Send > ;
2015-09-01 11:20:37 +00:00
struct PlayerInternal {
session : Session ,
2017-08-03 18:31:15 +00:00
config : PlayerConfig ,
2021-01-21 21:22:32 +00:00
commands : mpsc ::UnboundedReceiver < PlayerCommand > ,
2021-12-27 23:04:09 +00:00
load_handles : Arc < Mutex < HashMap < thread ::ThreadId , thread ::JoinHandle < ( ) > > > > ,
2017-01-29 14:11:20 +00:00
state : PlayerState ,
2020-01-31 21:41:11 +00:00
preload : PlayerPreload ,
2021-04-01 16:41:01 +00:00
sink : Box < dyn Sink > ,
2020-03-10 12:26:01 +00:00
sink_status : SinkStatus ,
sink_event_callback : Option < SinkEventCallback > ,
2019-10-08 09:31:18 +00:00
audio_filter : Option < Box < dyn AudioFilter + Send > > ,
2021-01-21 21:22:32 +00:00
event_senders : Vec < mpsc ::UnboundedSender < PlayerEvent > > ,
Implement dithering (#694)
Dithering lowers digital-to-analog conversion ("requantization") error, linearizing output, lowering distortion and replacing it with a constant, fixed noise level, which is more pleasant to the ear than the distortion.
Guidance:
- On S24, S24_3 and S24, the default is to use triangular dithering. Depending on personal preference you may use Gaussian dithering instead; it's not as good objectively, but it may be preferred subjectively if you are looking for a more "analog" sound akin to tape hiss.
- Advanced users who know that they have a DAC without noise shaping have a third option: high-passed dithering, which is like triangular dithering except that it moves dithering noise up in frequency where it is less audible. Note: 99% of DACs are of delta-sigma design with noise shaping, so unless you have a multibit / R2R DAC, or otherwise know what you are doing, this is not for you.
- Don't dither or shape noise on S32 or F32. On F32 it's not supported anyway (there are no integer conversions and so no rounding errors) and on S32 the noise level is so far down that it is simply inaudible even after volume normalisation and control.
New command line option:
--dither DITHER Specify the dither algorithm to use - [none, gpdf,
tpdf, tpdf_hp]. Defaults to 'tpdf' for formats S16
S24, S24_3 and 'none' for other formats.
Notes:
This PR also features some opportunistic improvements. Worthy of mention are:
- matching reference Vorbis sample conversion techniques for lower noise
- a cleanup of the convert API
2021-05-26 19:19:17 +00:00
converter : Converter ,
2021-02-24 20:39:42 +00:00
limiter_active : bool ,
limiter_attack_counter : u32 ,
limiter_release_counter : u32 ,
2021-05-30 18:09:39 +00:00
limiter_peak_sample : f64 ,
limiter_factor : f64 ,
limiter_strength : f64 ,
2021-09-20 17:22:02 +00:00
auto_normalise_as_album : bool ,
2015-07-02 19:42:49 +00:00
}
2015-07-09 20:08:14 +00:00
enum PlayerCommand {
2020-01-31 21:41:11 +00:00
Load {
track_id : SpotifyId ,
play_request_id : u64 ,
play : bool ,
position_ms : u32 ,
} ,
Preload {
track_id : SpotifyId ,
} ,
2015-07-09 20:08:14 +00:00
Play ,
Pause ,
Stop ,
2016-01-02 15:19:39 +00:00
Seek ( u32 ) ,
2021-01-21 21:22:32 +00:00
AddEventSender ( mpsc ::UnboundedSender < PlayerEvent > ) ,
2020-03-10 12:26:01 +00:00
SetSinkEventCallback ( Option < SinkEventCallback > ) ,
2020-01-31 21:41:11 +00:00
EmitVolumeSetEvent ( u16 ) ,
2021-09-20 17:22:02 +00:00
SetAutoNormaliseAsAlbum ( bool ) ,
2021-12-30 22:50:28 +00:00
SkipExplicitContent ( ) ,
2015-07-02 19:42:49 +00:00
}
2018-02-20 20:57:42 +00:00
#[ derive(Debug, Clone) ]
pub enum PlayerEvent {
2020-03-12 12:01:45 +00:00
// Fired when the player is stopped (e.g. by issuing a "stop" command to the player).
2020-02-03 07:58:44 +00:00
Stopped {
2020-01-31 21:41:11 +00:00
play_request_id : u64 ,
track_id : SpotifyId ,
} ,
2020-03-12 12:01:45 +00:00
// The player started working on playback of a track while it was in a stopped state.
// This is always immediately followed up by a "Loading" or "Playing" event.
2020-02-03 07:58:44 +00:00
Started {
2020-01-31 21:41:11 +00:00
play_request_id : u64 ,
track_id : SpotifyId ,
position_ms : u32 ,
2018-02-20 20:57:42 +00:00
} ,
2020-03-12 12:01:45 +00:00
// Same as started but in the case that the player already had a track loaded.
// The player was either playing the loaded track or it was paused.
2018-02-20 20:57:42 +00:00
Changed {
old_track_id : SpotifyId ,
new_track_id : SpotifyId ,
} ,
2020-03-12 12:01:45 +00:00
// The player is delayed by loading a track.
Loading {
2020-01-31 21:41:11 +00:00
play_request_id : u64 ,
track_id : SpotifyId ,
2020-02-03 07:58:44 +00:00
position_ms : u32 ,
2020-01-31 21:41:11 +00:00
} ,
2020-12-10 21:17:41 +00:00
// The player is preloading a track.
Preloading {
track_id : SpotifyId ,
} ,
2020-03-12 12:01:45 +00:00
// The player is playing a track.
// This event is issued at the start of playback of whenever the position must be communicated
// because it is out of sync. This includes:
// start of a track
// un-pausing
// after a seek
// after a buffer-underrun
2020-02-03 07:58:44 +00:00
Playing {
2020-01-31 21:41:11 +00:00
play_request_id : u64 ,
track_id : SpotifyId ,
2020-02-03 07:58:44 +00:00
position_ms : u32 ,
duration_ms : u32 ,
2020-01-31 21:41:11 +00:00
} ,
2020-03-12 12:01:45 +00:00
// The player entered a paused state.
2020-01-31 21:41:11 +00:00
Paused {
play_request_id : u64 ,
track_id : SpotifyId ,
position_ms : u32 ,
duration_ms : u32 ,
} ,
2020-03-12 12:01:45 +00:00
// The player thinks it's a good idea to issue a preload command for the next track now.
// This event is intended for use within spirc.
2020-02-03 07:58:44 +00:00
TimeToPreloadNextTrack {
play_request_id : u64 ,
track_id : SpotifyId ,
2020-05-13 09:49:26 +00:00
} ,
2020-03-12 12:01:45 +00:00
// The player reached the end of a track.
// This event is intended for use within spirc. Spirc will respond by issuing another command
// which will trigger another event (e.g. Changed or Stopped)
2020-02-03 07:58:44 +00:00
EndOfTrack {
2020-05-13 09:49:26 +00:00
play_request_id : u64 ,
track_id : SpotifyId ,
2020-02-03 07:58:44 +00:00
} ,
2020-05-13 10:19:33 +00:00
// The player was unable to load the requested track.
Unavailable {
2020-01-31 21:41:11 +00:00
play_request_id : u64 ,
2018-02-20 20:57:42 +00:00
track_id : SpotifyId ,
2018-02-26 01:50:41 +00:00
} ,
2020-03-12 12:01:45 +00:00
// The mixer volume was set to a new level.
2020-01-31 21:41:11 +00:00
VolumeSet {
volume : u16 ,
} ,
}
impl PlayerEvent {
pub fn get_play_request_id ( & self ) -> Option < u64 > {
use PlayerEvent ::* ;
match self {
Loading {
play_request_id , ..
}
2020-05-13 09:49:26 +00:00
| Unavailable {
play_request_id , ..
}
2020-01-31 21:41:11 +00:00
| Started {
play_request_id , ..
}
| Playing {
play_request_id , ..
}
| TimeToPreloadNextTrack {
play_request_id , ..
}
| EndOfTrack {
play_request_id , ..
}
| Paused {
play_request_id , ..
}
| Stopped {
play_request_id , ..
} = > Some ( * play_request_id ) ,
2020-12-10 21:17:41 +00:00
Changed { .. } | Preloading { .. } | VolumeSet { .. } = > None ,
2020-01-31 21:41:11 +00:00
}
}
2018-02-20 20:57:42 +00:00
}
2021-01-21 21:22:32 +00:00
pub type PlayerEventChannel = mpsc ::UnboundedReceiver < PlayerEvent > ;
2018-02-24 19:16:28 +00:00
2021-05-30 18:09:39 +00:00
pub fn db_to_ratio ( db : f64 ) -> f64 {
f64 ::powf ( 10.0 , db / DB_VOLTAGE_RATIO )
2021-05-24 13:53:32 +00:00
}
2021-05-30 18:09:39 +00:00
pub fn ratio_to_db ( ratio : f64 ) -> f64 {
2021-05-24 13:53:32 +00:00
ratio . log10 ( ) * DB_VOLTAGE_RATIO
}
2018-02-23 19:08:20 +00:00
#[ derive(Clone, Copy, Debug) ]
2021-02-24 20:39:42 +00:00
pub struct NormalisationData {
2022-01-02 23:13:28 +00:00
// Spotify provides these as `f32`, but audio metadata can contain up to `f64`.
// Also, this negates the need for casting during sample processing.
pub track_gain_db : f64 ,
pub track_peak : f64 ,
pub album_gain_db : f64 ,
pub album_peak : f64 ,
2018-02-23 19:08:20 +00:00
}
2021-12-18 22:44:13 +00:00
impl Default for NormalisationData {
fn default ( ) -> Self {
Self {
track_gain_db : 0.0 ,
track_peak : 1.0 ,
album_gain_db : 0.0 ,
album_peak : 1.0 ,
}
}
}
2018-02-23 19:08:20 +00:00
impl NormalisationData {
2022-01-02 23:13:28 +00:00
fn parse_from_ogg < T : Read + Seek > ( mut file : T ) -> io ::Result < NormalisationData > {
2018-02-24 15:30:24 +00:00
const SPOTIFY_NORMALIZATION_HEADER_START_OFFSET : u64 = 144 ;
2021-12-28 22:46:37 +00:00
let newpos = file . seek ( SeekFrom ::Start ( SPOTIFY_NORMALIZATION_HEADER_START_OFFSET ) ) ? ;
if newpos ! = SPOTIFY_NORMALIZATION_HEADER_START_OFFSET {
error! (
" NormalisationData::parse_from_file seeking to {} but position is now {} " ,
SPOTIFY_NORMALIZATION_HEADER_START_OFFSET , newpos
) ;
error! ( " Falling back to default (non-track and non-album) normalisation data. " ) ;
return Ok ( NormalisationData ::default ( ) ) ;
}
2018-02-23 19:08:20 +00:00
2022-01-02 23:13:28 +00:00
let track_gain_db = file . read_f32 ::< LittleEndian > ( ) ? as f64 ;
let track_peak = file . read_f32 ::< LittleEndian > ( ) ? as f64 ;
let album_gain_db = file . read_f32 ::< LittleEndian > ( ) ? as f64 ;
let album_peak = file . read_f32 ::< LittleEndian > ( ) ? as f64 ;
2018-02-23 19:08:20 +00:00
2018-02-24 15:30:24 +00:00
let r = NormalisationData {
2021-03-10 21:39:01 +00:00
track_gain_db ,
track_peak ,
album_gain_db ,
album_peak ,
2018-02-24 15:30:24 +00:00
} ;
Ok ( r )
2018-02-23 19:08:20 +00:00
}
2021-05-30 18:09:39 +00:00
fn get_factor ( config : & PlayerConfig , data : NormalisationData ) -> f64 {
2021-05-17 19:27:34 +00:00
if ! config . normalisation {
return 1.0 ;
}
2021-09-20 17:22:02 +00:00
let [ gain_db , gain_peak ] = if config . normalisation_type = = NormalisationType ::Album {
[ data . album_gain_db , data . album_peak ]
} else {
[ data . track_gain_db , data . track_peak ]
2021-01-21 19:16:05 +00:00
} ;
2018-02-23 19:08:20 +00:00
2022-01-02 23:13:28 +00:00
let normalisation_power = gain_db + config . normalisation_pregain ;
2021-05-24 13:53:32 +00:00
let mut normalisation_factor = db_to_ratio ( normalisation_power ) ;
2021-02-24 20:39:42 +00:00
2022-01-02 23:13:28 +00:00
if normalisation_factor * gain_peak > config . normalisation_threshold {
let limited_normalisation_factor = config . normalisation_threshold / gain_peak ;
2021-05-24 13:53:32 +00:00
let limited_normalisation_power = ratio_to_db ( limited_normalisation_factor ) ;
2021-02-24 20:39:42 +00:00
if config . normalisation_method = = NormalisationMethod ::Basic {
2021-04-16 18:49:21 +00:00
warn! ( " Limiting gain to {:.2} dB for the duration of this track to stay under normalisation threshold. " , limited_normalisation_power ) ;
2021-02-24 20:39:42 +00:00
normalisation_factor = limited_normalisation_factor ;
} else {
warn! (
" This track will at its peak be subject to {:.2} dB of dynamic limiting. " ,
normalisation_power - limited_normalisation_power
) ;
}
warn! ( " Please lower pregain to avoid. " ) ;
2018-02-23 19:08:20 +00:00
}
2018-02-24 15:30:24 +00:00
debug! ( " Normalisation Data: {:?} " , data ) ;
2021-09-20 17:22:02 +00:00
debug! (
" Calculated Normalisation Factor for {:?}: {:.2}% " ,
config . normalisation_type ,
normalisation_factor * 100.0
) ;
2018-02-24 15:30:24 +00:00
2022-01-02 23:13:28 +00:00
normalisation_factor
2018-02-23 19:08:20 +00:00
}
}
2015-09-01 11:20:37 +00:00
impl Player {
2018-02-26 01:50:41 +00:00
pub fn new < F > (
config : PlayerConfig ,
session : Session ,
2019-10-08 09:31:18 +00:00
audio_filter : Option < Box < dyn AudioFilter + Send > > ,
2018-02-26 01:50:41 +00:00
sink_builder : F ,
) -> ( Player , PlayerEventChannel )
where
2021-04-01 16:41:01 +00:00
F : FnOnce ( ) -> Box < dyn Sink > + Send + 'static ,
2017-08-03 18:31:15 +00:00
{
2021-02-22 08:55:40 +00:00
let ( cmd_tx , cmd_rx ) = mpsc ::unbounded_channel ( ) ;
let ( event_sender , event_receiver ) = mpsc ::unbounded_channel ( ) ;
2015-07-02 19:42:49 +00:00
2021-05-26 20:03:52 +00:00
if config . normalisation {
debug! ( " Normalisation Type: {:?} " , config . normalisation_type ) ;
2021-05-26 21:14:24 +00:00
debug! (
" Normalisation Pregain: {:.1} dB " ,
config . normalisation_pregain
) ;
2021-05-26 20:03:52 +00:00
debug! (
" Normalisation Threshold: {:.1} dBFS " ,
ratio_to_db ( config . normalisation_threshold )
) ;
debug! ( " Normalisation Method: {:?} " , config . normalisation_method ) ;
if config . normalisation_method = = NormalisationMethod ::Dynamic {
2021-05-31 20:32:39 +00:00
debug! ( " Normalisation Attack: {:?} " , config . normalisation_attack ) ;
debug! ( " Normalisation Release: {:?} " , config . normalisation_release ) ;
2021-05-26 20:03:52 +00:00
debug! ( " Normalisation Knee: {:?} " , config . normalisation_knee ) ;
}
}
2021-02-20 21:14:15 +00:00
let handle = thread ::spawn ( move | | {
debug! ( " new Player[{}] " , session . session_id ( ) ) ;
Implement dithering (#694)
Dithering lowers digital-to-analog conversion ("requantization") error, linearizing output, lowering distortion and replacing it with a constant, fixed noise level, which is more pleasant to the ear than the distortion.
Guidance:
- On S24, S24_3 and S24, the default is to use triangular dithering. Depending on personal preference you may use Gaussian dithering instead; it's not as good objectively, but it may be preferred subjectively if you are looking for a more "analog" sound akin to tape hiss.
- Advanced users who know that they have a DAC without noise shaping have a third option: high-passed dithering, which is like triangular dithering except that it moves dithering noise up in frequency where it is less audible. Note: 99% of DACs are of delta-sigma design with noise shaping, so unless you have a multibit / R2R DAC, or otherwise know what you are doing, this is not for you.
- Don't dither or shape noise on S32 or F32. On F32 it's not supported anyway (there are no integer conversions and so no rounding errors) and on S32 the noise level is so far down that it is simply inaudible even after volume normalisation and control.
New command line option:
--dither DITHER Specify the dither algorithm to use - [none, gpdf,
tpdf, tpdf_hp]. Defaults to 'tpdf' for formats S16
S24, S24_3 and 'none' for other formats.
Notes:
This PR also features some opportunistic improvements. Worthy of mention are:
- matching reference Vorbis sample conversion techniques for lower noise
- a cleanup of the convert API
2021-05-26 19:19:17 +00:00
let converter = Converter ::new ( config . ditherer ) ;
2021-02-20 21:14:15 +00:00
let internal = PlayerInternal {
2021-03-01 02:37:22 +00:00
session ,
config ,
2021-02-20 21:14:15 +00:00
commands : cmd_rx ,
2021-12-27 23:04:09 +00:00
load_handles : Arc ::new ( Mutex ::new ( HashMap ::new ( ) ) ) ,
2021-02-20 21:14:15 +00:00
state : PlayerState ::Stopped ,
preload : PlayerPreload ::None ,
sink : sink_builder ( ) ,
sink_status : SinkStatus ::Closed ,
sink_event_callback : None ,
2021-03-01 02:37:22 +00:00
audio_filter ,
2021-02-20 21:14:15 +00:00
event_senders : [ event_sender ] . to_vec ( ) ,
Implement dithering (#694)
Dithering lowers digital-to-analog conversion ("requantization") error, linearizing output, lowering distortion and replacing it with a constant, fixed noise level, which is more pleasant to the ear than the distortion.
Guidance:
- On S24, S24_3 and S24, the default is to use triangular dithering. Depending on personal preference you may use Gaussian dithering instead; it's not as good objectively, but it may be preferred subjectively if you are looking for a more "analog" sound akin to tape hiss.
- Advanced users who know that they have a DAC without noise shaping have a third option: high-passed dithering, which is like triangular dithering except that it moves dithering noise up in frequency where it is less audible. Note: 99% of DACs are of delta-sigma design with noise shaping, so unless you have a multibit / R2R DAC, or otherwise know what you are doing, this is not for you.
- Don't dither or shape noise on S32 or F32. On F32 it's not supported anyway (there are no integer conversions and so no rounding errors) and on S32 the noise level is so far down that it is simply inaudible even after volume normalisation and control.
New command line option:
--dither DITHER Specify the dither algorithm to use - [none, gpdf,
tpdf, tpdf_hp]. Defaults to 'tpdf' for formats S16
S24, S24_3 and 'none' for other formats.
Notes:
This PR also features some opportunistic improvements. Worthy of mention are:
- matching reference Vorbis sample conversion techniques for lower noise
- a cleanup of the convert API
2021-05-26 19:19:17 +00:00
converter ,
2021-02-24 20:39:42 +00:00
limiter_active : false ,
limiter_attack_counter : 0 ,
limiter_release_counter : 0 ,
limiter_peak_sample : 0.0 ,
limiter_factor : 1.0 ,
limiter_strength : 0.0 ,
2021-09-20 17:22:02 +00:00
auto_normalise_as_album : false ,
2021-02-20 21:14:15 +00:00
} ;
2017-01-29 14:11:20 +00:00
2021-02-20 21:14:15 +00:00
// While PlayerInternal is written as a future, it still contains blocking code.
2021-02-22 08:55:40 +00:00
// It must be run by using block_on() in a dedicated thread.
2021-11-26 22:21:27 +00:00
let runtime = tokio ::runtime ::Runtime ::new ( ) . expect ( " Failed to create Tokio runtime " ) ;
runtime . block_on ( internal ) ;
2020-01-31 21:41:11 +00:00
debug! ( " PlayerInternal thread finished. " ) ;
2017-01-29 14:11:20 +00:00
} ) ;
2015-09-01 11:20:37 +00:00
2018-02-26 01:50:41 +00:00
(
Player {
commands : Some ( cmd_tx ) ,
2021-02-20 21:14:15 +00:00
thread_handle : Some ( handle ) ,
2020-01-31 21:41:11 +00:00
play_request_id_generator : SeqGenerator ::new ( 0 ) ,
2018-02-26 01:50:41 +00:00
} ,
event_receiver ,
)
2015-07-02 19:42:49 +00:00
}
2015-07-09 20:08:14 +00:00
fn command ( & self , cmd : PlayerCommand ) {
2021-09-20 17:29:12 +00:00
if let Some ( commands ) = self . commands . as_ref ( ) {
if let Err ( e ) = commands . send ( cmd ) {
error! ( " Player Commands Error: {} " , e ) ;
}
}
2015-07-02 19:42:49 +00:00
}
2016-01-20 14:11:49 +00:00
2020-01-31 21:41:11 +00:00
pub fn load ( & mut self , track_id : SpotifyId , start_playing : bool , position_ms : u32 ) -> u64 {
let play_request_id = self . play_request_id_generator . get ( ) ;
self . command ( PlayerCommand ::Load {
track_id ,
play_request_id ,
play : start_playing ,
position_ms ,
} ) ;
play_request_id
}
2017-01-29 14:11:20 +00:00
2020-02-02 00:07:05 +00:00
pub fn preload ( & self , track_id : SpotifyId ) {
self . command ( PlayerCommand ::Preload { track_id } ) ;
2016-01-20 14:11:49 +00:00
}
pub fn play ( & self ) {
self . command ( PlayerCommand ::Play )
}
pub fn pause ( & self ) {
self . command ( PlayerCommand ::Pause )
}
pub fn stop ( & self ) {
self . command ( PlayerCommand ::Stop )
}
pub fn seek ( & self , position_ms : u32 ) {
self . command ( PlayerCommand ::Seek ( position_ms ) ) ;
}
2020-01-31 21:41:11 +00:00
pub fn get_player_event_channel ( & self ) -> PlayerEventChannel {
2021-02-22 08:55:40 +00:00
let ( event_sender , event_receiver ) = mpsc ::unbounded_channel ( ) ;
2020-01-31 21:41:11 +00:00
self . command ( PlayerCommand ::AddEventSender ( event_sender ) ) ;
event_receiver
}
2021-02-22 08:55:40 +00:00
pub async fn await_end_of_track ( & self ) {
2021-02-12 17:19:04 +00:00
let mut channel = self . get_player_event_channel ( ) ;
2021-02-22 08:55:40 +00:00
while let Some ( event ) = channel . recv ( ) . await {
2021-02-13 09:29:00 +00:00
if matches! (
event ,
PlayerEvent ::EndOfTrack { .. } | PlayerEvent ::Stopped { .. }
) {
2021-02-12 17:19:04 +00:00
return ;
}
}
2020-02-02 00:07:05 +00:00
}
2020-03-10 12:26:01 +00:00
pub fn set_sink_event_callback ( & self , callback : Option < SinkEventCallback > ) {
self . command ( PlayerCommand ::SetSinkEventCallback ( callback ) ) ;
}
2020-01-31 21:41:11 +00:00
pub fn emit_volume_set_event ( & self , volume : u16 ) {
self . command ( PlayerCommand ::EmitVolumeSetEvent ( volume ) ) ;
}
2021-09-20 17:22:02 +00:00
pub fn set_auto_normalise_as_album ( & self , setting : bool ) {
self . command ( PlayerCommand ::SetAutoNormaliseAsAlbum ( setting ) ) ;
}
2021-12-30 22:50:28 +00:00
pub fn skip_explicit_content ( & self ) {
self . command ( PlayerCommand ::SkipExplicitContent ( ) ) ;
}
2015-07-02 19:42:49 +00:00
}
2015-06-23 14:38:29 +00:00
2017-11-27 23:35:04 +00:00
impl Drop for Player {
fn drop ( & mut self ) {
debug! ( " Shutting down player thread ... " ) ;
self . commands = None ;
2021-02-20 21:14:15 +00:00
if let Some ( handle ) = self . thread_handle . take ( ) {
match handle . join ( ) {
Ok ( _ ) = > ( ) ,
2021-09-20 17:29:12 +00:00
Err ( e ) = > error! ( " Player thread Error: {:?} " , e ) ,
2021-02-20 21:14:15 +00:00
}
2017-11-27 23:35:04 +00:00
}
}
}
2020-01-31 21:41:11 +00:00
struct PlayerLoadedTrackData {
decoder : Decoder ,
2021-09-20 17:22:02 +00:00
normalisation_data : NormalisationData ,
2020-01-31 21:41:11 +00:00
stream_loader_controller : StreamLoaderController ,
bytes_per_second : usize ,
duration_ms : u32 ,
2022-01-03 23:17:30 +00:00
stream_position_ms : u32 ,
2021-12-30 22:50:28 +00:00
is_explicit : bool ,
2020-01-31 21:41:11 +00:00
}
enum PlayerPreload {
None ,
Loading {
track_id : SpotifyId ,
2021-01-22 21:51:41 +00:00
loader : Pin < Box < dyn Future < Output = Result < PlayerLoadedTrackData , ( ) > > + Send > > ,
2020-01-31 21:41:11 +00:00
} ,
Ready {
track_id : SpotifyId ,
2021-01-22 21:51:41 +00:00
loaded_track : Box < PlayerLoadedTrackData > ,
2020-01-31 21:41:11 +00:00
} ,
}
2021-01-07 06:42:38 +00:00
type Decoder = Box < dyn AudioDecoder + Send > ;
2020-01-31 21:41:11 +00:00
2017-01-29 14:11:20 +00:00
enum PlayerState {
Stopped ,
2020-01-31 21:41:11 +00:00
Loading {
track_id : SpotifyId ,
play_request_id : u64 ,
start_playback : bool ,
2021-01-22 21:51:41 +00:00
loader : Pin < Box < dyn Future < Output = Result < PlayerLoadedTrackData , ( ) > > + Send > > ,
2020-01-31 21:41:11 +00:00
} ,
2017-01-29 14:11:20 +00:00
Paused {
2018-02-15 23:16:38 +00:00
track_id : SpotifyId ,
2020-01-31 21:41:11 +00:00
play_request_id : u64 ,
2017-01-29 14:11:20 +00:00
decoder : Decoder ,
2021-09-20 17:22:02 +00:00
normalisation_data : NormalisationData ,
2021-05-30 18:09:39 +00:00
normalisation_factor : f64 ,
2019-11-01 19:46:28 +00:00
stream_loader_controller : StreamLoaderController ,
2019-11-07 13:02:53 +00:00
bytes_per_second : usize ,
2020-01-31 21:41:11 +00:00
duration_ms : u32 ,
2022-01-03 23:17:30 +00:00
stream_position_ms : u32 ,
2020-01-31 21:41:11 +00:00
suggested_to_preload_next_track : bool ,
2021-12-30 22:50:28 +00:00
is_explicit : bool ,
2017-01-29 14:11:20 +00:00
} ,
Playing {
2018-02-15 23:16:38 +00:00
track_id : SpotifyId ,
2020-01-31 21:41:11 +00:00
play_request_id : u64 ,
2017-01-29 14:11:20 +00:00
decoder : Decoder ,
2021-09-20 17:22:02 +00:00
normalisation_data : NormalisationData ,
2021-05-30 18:09:39 +00:00
normalisation_factor : f64 ,
2019-11-01 19:46:28 +00:00
stream_loader_controller : StreamLoaderController ,
2019-11-07 13:02:53 +00:00
bytes_per_second : usize ,
2020-01-31 21:41:11 +00:00
duration_ms : u32 ,
2022-01-03 23:17:30 +00:00
stream_position_ms : u32 ,
2020-01-31 21:41:11 +00:00
reported_nominal_start_time : Option < Instant > ,
suggested_to_preload_next_track : bool ,
2021-12-30 22:50:28 +00:00
is_explicit : bool ,
2017-01-29 14:11:20 +00:00
} ,
2018-02-26 01:50:41 +00:00
EndOfTrack {
track_id : SpotifyId ,
2020-01-31 21:41:11 +00:00
play_request_id : u64 ,
2020-03-12 12:01:45 +00:00
loaded_track : PlayerLoadedTrackData ,
2018-02-26 01:50:41 +00:00
} ,
2017-01-29 14:11:20 +00:00
Invalid ,
2016-02-05 20:54:47 +00:00
}
2017-01-29 14:11:20 +00:00
impl PlayerState {
fn is_playing ( & self ) -> bool {
use self ::PlayerState ::* ;
match * self {
2020-01-31 21:41:11 +00:00
Stopped | EndOfTrack { .. } | Paused { .. } | Loading { .. } = > false ,
2017-01-29 14:11:20 +00:00
Playing { .. } = > true ,
2021-09-20 17:29:12 +00:00
Invalid = > {
2021-12-28 22:46:37 +00:00
error! ( " PlayerState::is_playing in invalid state " ) ;
2021-09-20 17:29:12 +00:00
exit ( 1 ) ;
}
2017-01-29 14:11:20 +00:00
}
2016-05-04 08:11:03 +00:00
}
2017-01-05 13:25:14 +00:00
2020-05-09 11:59:28 +00:00
#[ allow(dead_code) ]
2020-01-31 21:41:11 +00:00
fn is_stopped ( & self ) -> bool {
2021-02-20 21:14:15 +00:00
use self ::PlayerState ::* ;
2021-03-01 02:37:22 +00:00
matches! ( self , Stopped )
2020-01-31 21:41:11 +00:00
}
2020-05-09 11:59:28 +00:00
fn is_loading ( & self ) -> bool {
2021-02-20 21:14:15 +00:00
use self ::PlayerState ::* ;
2021-03-01 02:37:22 +00:00
matches! ( self , Loading { .. } )
2020-05-09 11:59:28 +00:00
}
2017-01-29 14:11:20 +00:00
fn decoder ( & mut self ) -> Option < & mut Decoder > {
2021-02-20 21:14:15 +00:00
use self ::PlayerState ::* ;
2017-01-29 14:11:20 +00:00
match * self {
2020-01-31 21:41:11 +00:00
Stopped | EndOfTrack { .. } | Loading { .. } = > None ,
2019-10-08 09:31:18 +00:00
Paused {
ref mut decoder , ..
}
| Playing {
ref mut decoder , ..
} = > Some ( decoder ) ,
2021-09-20 17:29:12 +00:00
Invalid = > {
2021-12-28 22:46:37 +00:00
error! ( " PlayerState::decoder in invalid state " ) ;
2021-09-20 17:29:12 +00:00
exit ( 1 ) ;
}
2016-05-04 08:11:03 +00:00
}
2017-01-29 14:11:20 +00:00
}
2016-05-04 08:11:03 +00:00
2018-02-20 20:58:02 +00:00
fn playing_to_end_of_track ( & mut self ) {
use self ::PlayerState ::* ;
2021-12-28 22:46:37 +00:00
let new_state = mem ::replace ( self , Invalid ) ;
match new_state {
2018-02-26 01:50:41 +00:00
Playing {
track_id ,
2020-01-31 21:41:11 +00:00
play_request_id ,
2020-02-02 14:37:17 +00:00
decoder ,
duration_ms ,
bytes_per_second ,
2021-09-20 17:22:02 +00:00
normalisation_data ,
2020-02-02 14:37:17 +00:00
stream_loader_controller ,
2022-01-03 23:17:30 +00:00
stream_position_ms ,
2021-12-30 22:50:28 +00:00
is_explicit ,
2018-02-26 01:50:41 +00:00
..
} = > {
2020-01-31 21:41:11 +00:00
* self = EndOfTrack {
track_id ,
play_request_id ,
2020-03-12 12:01:45 +00:00
loaded_track : PlayerLoadedTrackData {
2020-02-02 14:37:17 +00:00
decoder ,
2021-09-20 17:22:02 +00:00
normalisation_data ,
2020-02-02 14:37:17 +00:00
stream_loader_controller ,
2021-05-09 10:59:34 +00:00
bytes_per_second ,
duration_ms ,
2022-01-03 23:17:30 +00:00
stream_position_ms ,
2021-12-30 22:50:28 +00:00
is_explicit ,
2020-03-12 12:01:45 +00:00
} ,
2020-01-31 21:41:11 +00:00
} ;
2018-02-26 01:50:41 +00:00
}
2021-09-20 17:29:12 +00:00
_ = > {
2021-12-28 22:46:37 +00:00
error! (
" Called playing_to_end_of_track in non-playing state: {:?} " ,
new_state
) ;
2021-09-20 17:29:12 +00:00
exit ( 1 ) ;
}
2016-05-04 08:11:03 +00:00
}
2017-01-29 14:11:20 +00:00
}
2016-05-04 08:11:03 +00:00
2017-01-29 14:11:20 +00:00
fn paused_to_playing ( & mut self ) {
use self ::PlayerState ::* ;
2021-12-28 22:46:37 +00:00
let new_state = mem ::replace ( self , Invalid ) ;
match new_state {
2018-02-26 01:50:41 +00:00
Paused {
track_id ,
2020-01-31 21:41:11 +00:00
play_request_id ,
2018-02-26 01:50:41 +00:00
decoder ,
2021-09-20 17:22:02 +00:00
normalisation_data ,
2018-02-26 01:50:41 +00:00
normalisation_factor ,
2019-11-01 19:46:28 +00:00
stream_loader_controller ,
2020-01-31 21:41:11 +00:00
duration_ms ,
2019-11-11 07:22:41 +00:00
bytes_per_second ,
2022-01-03 23:17:30 +00:00
stream_position_ms ,
2020-01-31 21:41:11 +00:00
suggested_to_preload_next_track ,
2021-12-30 22:50:28 +00:00
is_explicit ,
2018-02-26 01:50:41 +00:00
} = > {
2017-01-29 14:11:20 +00:00
* self = Playing {
2020-01-31 21:41:11 +00:00
track_id ,
play_request_id ,
decoder ,
2021-09-20 17:22:02 +00:00
normalisation_data ,
2020-01-31 21:41:11 +00:00
normalisation_factor ,
stream_loader_controller ,
duration_ms ,
bytes_per_second ,
2022-01-03 23:17:30 +00:00
stream_position_ms ,
2020-01-31 21:41:11 +00:00
reported_nominal_start_time : None ,
suggested_to_preload_next_track ,
2021-12-30 22:50:28 +00:00
is_explicit ,
2017-01-29 14:11:20 +00:00
} ;
}
2021-09-20 17:29:12 +00:00
_ = > {
2021-12-28 22:46:37 +00:00
error! (
" PlayerState::paused_to_playing in invalid state: {:?} " ,
new_state
) ;
2021-09-20 17:29:12 +00:00
exit ( 1 ) ;
}
2017-01-29 14:11:20 +00:00
}
}
2016-09-08 18:49:17 +00:00
2017-01-29 14:11:20 +00:00
fn playing_to_paused ( & mut self ) {
use self ::PlayerState ::* ;
2021-12-28 22:46:37 +00:00
let new_state = mem ::replace ( self , Invalid ) ;
match new_state {
2018-02-26 01:50:41 +00:00
Playing {
track_id ,
2020-01-31 21:41:11 +00:00
play_request_id ,
2018-02-26 01:50:41 +00:00
decoder ,
2021-09-20 17:22:02 +00:00
normalisation_data ,
2018-02-26 01:50:41 +00:00
normalisation_factor ,
2019-11-01 19:46:28 +00:00
stream_loader_controller ,
2020-01-31 21:41:11 +00:00
duration_ms ,
2019-11-07 13:02:53 +00:00
bytes_per_second ,
2022-01-03 23:17:30 +00:00
stream_position_ms ,
2020-01-31 21:41:11 +00:00
reported_nominal_start_time : _ ,
suggested_to_preload_next_track ,
2021-12-30 22:50:28 +00:00
is_explicit ,
2018-02-26 01:50:41 +00:00
} = > {
2017-01-29 14:11:20 +00:00
* self = Paused {
2020-01-31 21:41:11 +00:00
track_id ,
play_request_id ,
decoder ,
2021-09-20 17:22:02 +00:00
normalisation_data ,
2020-01-31 21:41:11 +00:00
normalisation_factor ,
stream_loader_controller ,
duration_ms ,
bytes_per_second ,
2022-01-03 23:17:30 +00:00
stream_position_ms ,
2020-01-31 21:41:11 +00:00
suggested_to_preload_next_track ,
2021-12-30 22:50:28 +00:00
is_explicit ,
2017-01-29 14:11:20 +00:00
} ;
}
2021-09-20 17:29:12 +00:00
_ = > {
2021-12-28 22:46:37 +00:00
error! (
" PlayerState::playing_to_paused in invalid state: {:?} " ,
new_state
) ;
2021-09-20 17:29:12 +00:00
exit ( 1 ) ;
}
2017-01-29 14:11:20 +00:00
}
}
2016-09-08 18:49:17 +00:00
}
2020-01-31 21:41:11 +00:00
struct PlayerTrackLoader {
session : Session ,
config : PlayerConfig ,
}
impl PlayerTrackLoader {
2021-02-22 08:58:08 +00:00
async fn find_available_alternative ( & self , audio : AudioItem ) -> Option < AudioItem > {
2021-12-27 08:35:11 +00:00
if let Err ( e ) = audio . availability {
error! ( " Track is unavailable: {} " , e ) ;
None
} else if ! audio . files . is_empty ( ) {
2021-02-22 08:58:08 +00:00
Some ( audio )
2021-01-21 21:22:32 +00:00
} else if let Some ( alternatives ) = & audio . alternatives {
2021-02-22 08:58:08 +00:00
let alternatives : FuturesUnordered < _ > = alternatives
2021-01-21 21:22:32 +00:00
. iter ( )
2021-12-07 22:22:24 +00:00
. map ( | alt_id | AudioItem ::get_file ( & self . session , * alt_id ) )
2021-02-22 08:58:08 +00:00
. collect ( ) ;
2021-01-21 21:22:32 +00:00
alternatives
2021-02-22 08:58:08 +00:00
. filter_map ( | x | future ::ready ( x . ok ( ) ) )
2021-12-07 22:22:24 +00:00
. filter ( | x | future ::ready ( x . availability . is_ok ( ) ) )
2021-02-22 08:58:08 +00:00
. next ( )
. await
2020-01-31 21:41:11 +00:00
} else {
2021-12-27 08:35:11 +00:00
error! ( " Track should be available, but no alternatives found. " ) ;
2021-01-21 21:22:32 +00:00
None
2020-01-31 21:41:11 +00:00
}
}
2021-12-07 22:22:24 +00:00
fn stream_data_rate ( & self , format : AudioFileFormat ) -> usize {
2021-12-18 22:44:13 +00:00
let kbps = match format {
AudioFileFormat ::OGG_VORBIS_96 = > 12 ,
AudioFileFormat ::OGG_VORBIS_160 = > 20 ,
AudioFileFormat ::OGG_VORBIS_320 = > 40 ,
AudioFileFormat ::MP3_256 = > 32 ,
AudioFileFormat ::MP3_320 = > 40 ,
AudioFileFormat ::MP3_160 = > 20 ,
AudioFileFormat ::MP3_96 = > 12 ,
AudioFileFormat ::MP3_160_ENC = > 20 ,
AudioFileFormat ::AAC_24 = > 3 ,
AudioFileFormat ::AAC_48 = > 6 ,
AudioFileFormat ::FLAC_FLAC = > 112 , // assume 900 kbit/s on average
} ;
kbps * 1024
2020-01-31 21:41:11 +00:00
}
2021-01-21 21:22:32 +00:00
async fn load_track (
& self ,
spotify_id : SpotifyId ,
position_ms : u32 ,
) -> Option < PlayerLoadedTrackData > {
2021-12-07 22:22:24 +00:00
let audio = match AudioItem ::get_file ( & self . session , spotify_id ) . await {
2020-01-31 21:41:11 +00:00
Ok ( audio ) = > audio ,
2021-09-20 17:29:12 +00:00
Err ( e ) = > {
error! ( " Unable to load audio item: {:?} " , e ) ;
2020-01-31 21:41:11 +00:00
return None ;
}
} ;
2021-12-07 22:22:24 +00:00
info! (
" Loading <{}> with Spotify URI <{}> " ,
audio . name , audio . spotify_uri
) ;
2020-01-31 21:41:11 +00:00
2021-12-30 22:50:28 +00:00
let is_explicit = audio . is_explicit ;
if is_explicit {
if let Some ( value ) = self . session . get_user_attribute ( " filter-explicit-content " ) {
if & value = = " 1 " {
warn! ( " Track is marked as explicit, which client setting forbids. " ) ;
return None ;
}
}
}
2021-02-22 08:58:08 +00:00
let audio = match self . find_available_alternative ( audio ) . await {
2020-01-31 21:41:11 +00:00
Some ( audio ) = > audio ,
None = > {
2021-12-26 20:18:42 +00:00
error! ( " <{}> is not available " , spotify_id . to_uri ( ) ) ;
2020-01-31 21:41:11 +00:00
return None ;
}
} ;
2021-12-11 20:32:34 +00:00
if audio . duration < 0 {
error! (
" Track duration for <{}> cannot be {} " ,
spotify_id . to_uri ( ) ,
audio . duration
) ;
return None ;
}
2020-01-31 21:41:11 +00:00
let duration_ms = audio . duration as u32 ;
2022-01-02 23:13:28 +00:00
// (Most) podcasts seem to support only 96 kbps Ogg Vorbis, so fall back to it
2020-01-31 21:41:11 +00:00
let formats = match self . config . bitrate {
Bitrate ::Bitrate96 = > [
2021-12-07 22:22:24 +00:00
AudioFileFormat ::OGG_VORBIS_96 ,
2022-01-02 23:13:28 +00:00
AudioFileFormat ::MP3_96 ,
2021-12-07 22:22:24 +00:00
AudioFileFormat ::OGG_VORBIS_160 ,
2022-01-02 23:13:28 +00:00
AudioFileFormat ::MP3_160 ,
AudioFileFormat ::MP3_256 ,
2021-12-07 22:22:24 +00:00
AudioFileFormat ::OGG_VORBIS_320 ,
2022-01-02 23:13:28 +00:00
AudioFileFormat ::MP3_320 ,
2020-01-31 21:41:11 +00:00
] ,
Bitrate ::Bitrate160 = > [
2021-12-07 22:22:24 +00:00
AudioFileFormat ::OGG_VORBIS_160 ,
2022-01-02 23:13:28 +00:00
AudioFileFormat ::MP3_160 ,
2021-12-07 22:22:24 +00:00
AudioFileFormat ::OGG_VORBIS_96 ,
2022-01-02 23:13:28 +00:00
AudioFileFormat ::MP3_96 ,
AudioFileFormat ::MP3_256 ,
2021-12-07 22:22:24 +00:00
AudioFileFormat ::OGG_VORBIS_320 ,
2022-01-02 23:13:28 +00:00
AudioFileFormat ::MP3_320 ,
2020-01-31 21:41:11 +00:00
] ,
Bitrate ::Bitrate320 = > [
2021-12-07 22:22:24 +00:00
AudioFileFormat ::OGG_VORBIS_320 ,
2022-01-02 23:13:28 +00:00
AudioFileFormat ::MP3_320 ,
AudioFileFormat ::MP3_256 ,
2021-12-07 22:22:24 +00:00
AudioFileFormat ::OGG_VORBIS_160 ,
2022-01-02 23:13:28 +00:00
AudioFileFormat ::MP3_160 ,
2021-12-07 22:22:24 +00:00
AudioFileFormat ::OGG_VORBIS_96 ,
2022-01-02 23:13:28 +00:00
AudioFileFormat ::MP3_96 ,
2020-01-31 21:41:11 +00:00
] ,
} ;
2021-02-09 08:15:55 +00:00
let entry = formats . iter ( ) . find_map ( | format | {
if let Some ( & file_id ) = audio . files . get ( format ) {
Some ( ( * format , file_id ) )
} else {
2021-02-02 01:19:15 +00:00
None
2021-02-09 08:15:55 +00:00
}
} ) ;
let ( format , file_id ) = match entry {
Some ( t ) = > t ,
2020-01-31 21:41:11 +00:00
None = > {
2021-12-26 20:18:42 +00:00
error! ( " <{}> is not available in any supported format " , audio . name ) ;
2020-01-31 21:41:11 +00:00
return None ;
}
} ;
2021-02-09 08:15:55 +00:00
let bytes_per_second = self . stream_data_rate ( format ) ;
2020-01-31 21:41:11 +00:00
2021-02-10 20:51:33 +00:00
// This is only a loop to be able to reload the file if an error occured
// while opening a cached file.
loop {
2022-01-04 21:57:00 +00:00
let encrypted_file = AudioFile ::open ( & self . session , file_id , bytes_per_second ) ;
2020-01-31 21:41:11 +00:00
2021-02-10 20:51:33 +00:00
let encrypted_file = match encrypted_file . await {
Ok ( encrypted_file ) = > encrypted_file ,
2021-09-20 17:29:12 +00:00
Err ( e ) = > {
error! ( " Unable to load encrypted file: {:?} " , e ) ;
2021-02-10 20:51:33 +00:00
return None ;
}
} ;
2021-12-18 22:44:13 +00:00
2021-02-10 20:51:33 +00:00
let is_cached = encrypted_file . is_cached ( ) ;
2020-01-31 21:41:11 +00:00
2021-12-18 22:44:13 +00:00
let stream_loader_controller = encrypted_file . get_stream_loader_controller ( ) . ok ( ) ? ;
2020-01-31 21:41:11 +00:00
2022-01-02 23:13:28 +00:00
// Not all audio files are encrypted. If we can't get a key, try loading the track
// without decryption. If the file was encrypted after all, the decoder will fail
// parsing and bail out, so we should be safe from outputting ear-piercing noise.
2021-02-12 17:19:04 +00:00
let key = match self . session . audio_key ( ) . request ( spotify_id , file_id ) . await {
2022-01-02 23:13:28 +00:00
Ok ( key ) = > Some ( key ) ,
2021-09-20 17:29:12 +00:00
Err ( e ) = > {
2022-01-02 23:13:28 +00:00
warn! ( " Unable to load key, continuing without decryption: {} " , e ) ;
None
2021-02-10 20:51:33 +00:00
}
} ;
2022-01-03 21:20:29 +00:00
let mut decrypted_file = AudioDecrypt ::new ( key , encrypted_file ) ;
let is_ogg_vorbis = AudioFiles ::is_ogg_vorbis ( format ) ;
let ( offset , mut normalisation_data ) = if is_ogg_vorbis {
// Spotify stores normalisation data in a custom Ogg packet instead of Vorbis comments.
let normalisation_data =
NormalisationData ::parse_from_ogg ( & mut decrypted_file ) . ok ( ) ;
( SPOTIFY_OGG_HEADER_END , normalisation_data )
} else {
( 0 , None )
} ;
2022-01-06 20:55:08 +00:00
let audio_file = match Subfile ::new (
2022-01-03 21:20:29 +00:00
decrypted_file ,
offset ,
stream_loader_controller . len ( ) as u64 ,
2022-01-06 20:55:08 +00:00
) {
Ok ( audio_file ) = > audio_file ,
Err ( e ) = > {
error! ( " PlayerTrackLoader::load_track error opening subfile: {} " , e ) ;
return None ;
}
} ;
2020-01-31 21:41:11 +00:00
2021-02-23 13:45:01 +00:00
let result = if self . config . passthrough {
2022-01-02 23:13:28 +00:00
PassthroughDecoder ::new ( audio_file , format ) . map ( | x | Box ::new ( x ) as Decoder )
2021-02-23 13:45:01 +00:00
} else {
2022-01-02 23:13:28 +00:00
SymphoniaDecoder ::new ( audio_file , format ) . map ( | mut decoder | {
// For formats other that Vorbis, we'll try getting normalisation data from
// ReplayGain metadata fields, if present.
if normalisation_data . is_none ( ) {
normalisation_data = decoder . normalisation_data ( ) ;
}
Box ::new ( decoder ) as Decoder
} )
2021-02-23 13:45:01 +00:00
} ;
2021-01-07 06:42:38 +00:00
2022-01-02 23:13:28 +00:00
let normalisation_data = normalisation_data . unwrap_or_else ( | | {
warn! ( " Unable to get normalisation data, continuing with defaults. " ) ;
NormalisationData ::default ( )
} ) ;
2021-02-23 13:45:01 +00:00
let mut decoder = match result {
2021-02-10 20:51:33 +00:00
Ok ( decoder ) = > decoder ,
Err ( e ) if is_cached = > {
warn! (
" Unable to read cached audio file: {}. Trying to download it. " ,
e
) ;
2020-01-31 21:41:11 +00:00
2021-09-20 17:29:12 +00:00
match self . session . cache ( ) {
Some ( cache ) = > {
if cache . remove_file ( file_id ) . is_err ( ) {
error! ( " Error removing file from cache " ) ;
return None ;
}
}
None = > {
error! ( " If the audio file is cached, a cache should exist " ) ;
return None ;
}
2021-02-10 20:51:33 +00:00
}
2020-01-31 21:41:11 +00:00
2021-02-10 20:51:33 +00:00
// Just try it again
continue ;
}
Err ( e ) = > {
error! ( " Unable to read audio file: {} " , e ) ;
2021-02-02 01:18:58 +00:00
return None ;
}
2021-02-10 20:51:33 +00:00
} ;
2020-01-31 21:41:11 +00:00
2021-12-30 20:52:15 +00:00
// Ensure the starting position. Even when we want to play from the beginning,
// the cursor may have been moved by parsing normalisation data. This may not
// matter for playback (but won't hurt either), but may be useful for the
// passthrough decoder.
2022-01-03 23:17:30 +00:00
let stream_position_ms = match decoder . seek ( position_ms ) {
2022-01-06 20:55:08 +00:00
Ok ( new_position_ms ) = > new_position_ms ,
2021-12-30 20:52:15 +00:00
Err ( e ) = > {
2022-01-06 20:55:08 +00:00
error! (
" PlayerTrackLoader::load_track error seeking to starting position {}: {} " ,
2022-01-03 23:17:30 +00:00
position_ms , e
2021-12-30 20:52:15 +00:00
) ;
2022-01-06 20:55:08 +00:00
return None ;
2021-02-10 20:51:33 +00:00
}
2021-12-28 22:46:37 +00:00
} ;
2022-01-06 20:55:08 +00:00
// Ensure streaming mode now that we are ready to play from the requested position.
2021-12-29 21:18:38 +00:00
stream_loader_controller . set_stream_mode ( ) ;
2021-02-10 20:51:33 +00:00
info! ( " <{}> ({} ms) loaded " , audio . name , audio . duration ) ;
2020-01-31 21:41:11 +00:00
2021-02-10 20:51:33 +00:00
return Some ( PlayerLoadedTrackData {
decoder ,
2021-09-20 17:22:02 +00:00
normalisation_data ,
2021-02-10 20:51:33 +00:00
stream_loader_controller ,
bytes_per_second ,
duration_ms ,
2022-01-03 23:17:30 +00:00
stream_position_ms ,
2021-12-30 22:50:28 +00:00
is_explicit ,
2021-02-10 20:51:33 +00:00
} ) ;
2020-01-31 21:41:11 +00:00
}
}
}
impl Future for PlayerInternal {
2021-01-21 21:22:32 +00:00
type Output = ( ) ;
2020-01-31 21:41:11 +00:00
2021-01-21 21:22:32 +00:00
fn poll ( mut self : Pin < & mut Self > , cx : & mut Context < '_ > ) -> Poll < ( ) > {
2020-02-03 07:58:44 +00:00
// While this is written as a future, it still contains blocking code.
// It must be run on its own thread.
2021-02-23 13:45:01 +00:00
let passthrough = self . config . passthrough ;
2020-02-03 07:58:44 +00:00
2020-01-31 21:41:11 +00:00
loop {
let mut all_futures_completed_or_not_ready = true ;
// process commands that were sent to us
2021-02-22 08:55:40 +00:00
let cmd = match self . commands . poll_recv ( cx ) {
2021-01-21 21:22:32 +00:00
Poll ::Ready ( None ) = > return Poll ::Ready ( ( ) ) , // client has disconnected - shut down.
Poll ::Ready ( Some ( cmd ) ) = > {
2020-01-31 21:41:11 +00:00
all_futures_completed_or_not_ready = false ;
Some ( cmd )
2017-01-31 08:21:30 +00:00
}
2021-01-21 21:22:32 +00:00
_ = > None ,
2015-12-28 17:45:13 +00:00
} ;
2017-01-29 14:11:20 +00:00
if let Some ( cmd ) = cmd {
2021-12-18 22:44:13 +00:00
if let Err ( e ) = self . handle_command ( cmd ) {
error! ( " Error handling command: {} " , e ) ;
}
2017-01-29 14:11:20 +00:00
}
2016-05-04 08:11:03 +00:00
2020-01-31 21:41:11 +00:00
// Handle loading of a new track to play
if let PlayerState ::Loading {
ref mut loader ,
track_id ,
start_playback ,
play_request_id ,
} = self . state
{
2021-01-21 21:22:32 +00:00
match loader . as_mut ( ) . poll ( cx ) {
Poll ::Ready ( Ok ( loaded_track ) ) = > {
2020-01-31 21:41:11 +00:00
self . start_playback (
track_id ,
play_request_id ,
loaded_track ,
start_playback ,
) ;
if let PlayerState ::Loading { .. } = self . state {
2021-09-20 17:29:12 +00:00
error! ( " The state wasn't changed by start_playback() " ) ;
exit ( 1 ) ;
2020-01-31 21:41:11 +00:00
}
}
2021-12-12 19:01:05 +00:00
Poll ::Ready ( Err ( e ) ) = > {
2021-12-26 20:18:42 +00:00
error! (
2021-12-12 19:01:05 +00:00
" Skipping to next track, unable to load track <{:?}>: {:?} " ,
track_id , e
) ;
2021-12-11 20:32:34 +00:00
debug_assert! ( self . state . is_loading ( ) ) ;
2022-01-02 23:13:28 +00:00
self . send_event ( PlayerEvent ::Unavailable {
2020-05-09 11:59:28 +00:00
track_id ,
play_request_id ,
} )
2020-01-31 21:41:11 +00:00
}
2021-01-21 21:22:32 +00:00
Poll ::Pending = > ( ) ,
2020-01-31 21:41:11 +00:00
}
}
// handle pending preload requests.
if let PlayerPreload ::Loading {
ref mut loader ,
track_id ,
} = self . preload
{
2021-01-21 21:22:32 +00:00
match loader . as_mut ( ) . poll ( cx ) {
Poll ::Ready ( Ok ( loaded_track ) ) = > {
2020-12-10 21:17:41 +00:00
self . send_event ( PlayerEvent ::Preloading { track_id } ) ;
2020-01-31 21:41:11 +00:00
self . preload = PlayerPreload ::Ready {
track_id ,
2021-01-22 21:51:41 +00:00
loaded_track : Box ::new ( loaded_track ) ,
2020-01-31 21:41:11 +00:00
} ;
}
2021-01-21 21:22:32 +00:00
Poll ::Ready ( Err ( _ ) ) = > {
2020-05-13 09:49:26 +00:00
debug! ( " Unable to preload {:?} " , track_id ) ;
self . preload = PlayerPreload ::None ;
// Let Spirc know that the track was unavailable.
2020-05-10 12:31:43 +00:00
if let PlayerState ::Playing {
play_request_id , ..
}
| PlayerState ::Paused {
play_request_id , ..
} = self . state
{
2020-05-13 09:49:26 +00:00
self . send_event ( PlayerEvent ::Unavailable {
2020-05-10 12:31:43 +00:00
track_id ,
play_request_id ,
} ) ;
}
2020-01-31 21:41:11 +00:00
}
2021-01-21 21:22:32 +00:00
Poll ::Pending = > ( ) ,
2020-01-31 21:41:11 +00:00
}
}
2020-02-02 23:11:27 +00:00
if self . state . is_playing ( ) {
self . ensure_sink_running ( ) ;
2018-02-23 19:08:20 +00:00
2020-02-03 07:58:44 +00:00
if let PlayerState ::Playing {
2020-01-31 21:41:11 +00:00
track_id ,
play_request_id ,
2018-02-26 01:50:41 +00:00
ref mut decoder ,
normalisation_factor ,
2022-01-03 23:17:30 +00:00
ref mut stream_position_ms ,
2020-01-31 21:41:11 +00:00
ref mut reported_nominal_start_time ,
duration_ms ,
2018-02-26 01:50:41 +00:00
..
} = self . state
{
2021-09-20 17:29:12 +00:00
match decoder . next_packet ( ) {
2022-01-03 23:17:30 +00:00
Ok ( result ) = > {
2022-01-06 21:46:50 +00:00
if let Some ( ( ref packet_position , ref packet ) ) = result {
let new_stream_position_ms = packet_position . position_ms ;
2022-01-09 22:04:14 +00:00
let expected_position_ms = std ::mem ::replace (
& mut * stream_position_ms ,
new_stream_position_ms ,
) ;
2022-01-06 21:11:53 +00:00
2022-01-03 23:17:30 +00:00
if ! passthrough {
2021-09-20 17:29:12 +00:00
match packet . samples ( ) {
2022-01-03 23:17:30 +00:00
Ok ( _ ) = > {
2022-01-09 21:46:44 +00:00
let new_stream_position = Duration ::from_millis (
new_stream_position_ms as u64 ,
) ;
2022-01-09 22:04:14 +00:00
let now = Instant ::now ( ) ;
// Only notify if we're skipped some packets *or* we are behind.
// If we're ahead it's probably due to a buffer of the backend
// and we're actually in time.
let notify_about_position =
match * reported_nominal_start_time {
2021-09-20 17:29:12 +00:00
None = > true ,
Some ( reported_nominal_start_time ) = > {
2022-01-09 21:46:44 +00:00
let mut notify = false ;
2022-01-09 22:04:14 +00:00
if packet_position . skipped {
if let Some ( ahead ) = new_stream_position
. checked_sub ( Duration ::from_millis (
expected_position_ms as u64 ,
) )
{
notify | =
ahead > = Duration ::from_secs ( 1 )
}
}
if let Some ( lag ) = now
2022-01-09 21:46:44 +00:00
. checked_duration_since (
reported_nominal_start_time ,
)
{
if let Some ( lag ) =
lag . checked_sub ( new_stream_position )
{
2022-01-09 22:04:14 +00:00
notify | =
lag > = Duration ::from_secs ( 1 )
2022-01-09 21:46:44 +00:00
}
}
2022-01-09 22:04:14 +00:00
2022-01-09 21:46:44 +00:00
notify
2021-09-20 17:29:12 +00:00
}
} ;
2022-01-09 22:04:14 +00:00
2021-09-20 17:29:12 +00:00
if notify_about_position {
2022-01-09 21:46:44 +00:00
* reported_nominal_start_time =
2022-01-09 22:04:14 +00:00
now . checked_sub ( new_stream_position ) ;
2021-09-20 17:29:12 +00:00
self . send_event ( PlayerEvent ::Playing {
track_id ,
play_request_id ,
2022-01-03 23:17:30 +00:00
position_ms : new_stream_position_ms as u32 ,
2021-09-20 17:29:12 +00:00
duration_ms ,
} ) ;
}
}
Err ( e ) = > {
2021-12-26 20:18:42 +00:00
error! ( " Skipping to next track, unable to decode samples for track <{:?}>: {:?} " , track_id , e ) ;
2021-12-11 20:32:34 +00:00
self . send_event ( PlayerEvent ::EndOfTrack {
track_id ,
play_request_id ,
} )
2021-09-20 17:29:12 +00:00
}
}
2020-01-31 21:41:11 +00:00
}
}
2021-09-20 17:29:12 +00:00
2022-01-03 23:17:30 +00:00
self . handle_packet ( result , normalisation_factor ) ;
2021-09-20 17:29:12 +00:00
}
Err ( e ) = > {
2021-12-26 20:18:42 +00:00
error! ( " Skipping to next track, unable to get next packet for track <{:?}>: {:?} " , track_id , e ) ;
2021-12-11 20:32:34 +00:00
self . send_event ( PlayerEvent ::EndOfTrack {
track_id ,
play_request_id ,
} )
2020-01-31 21:41:11 +00:00
}
}
2017-11-28 23:18:12 +00:00
} else {
2021-09-20 17:29:12 +00:00
error! ( " PlayerInternal poll: Invalid PlayerState " ) ;
exit ( 1 ) ;
2017-11-28 23:18:12 +00:00
} ;
2017-01-29 14:11:20 +00:00
}
2018-04-21 15:46:29 +00:00
2020-01-31 21:41:11 +00:00
if let PlayerState ::Playing {
track_id ,
play_request_id ,
duration_ms ,
2022-01-03 23:17:30 +00:00
stream_position_ms ,
2020-01-31 21:41:11 +00:00
ref mut stream_loader_controller ,
ref mut suggested_to_preload_next_track ,
..
}
| PlayerState ::Paused {
track_id ,
play_request_id ,
duration_ms ,
2022-01-03 23:17:30 +00:00
stream_position_ms ,
2020-01-31 21:41:11 +00:00
ref mut stream_loader_controller ,
ref mut suggested_to_preload_next_track ,
..
} = self . state
{
if ( ! * suggested_to_preload_next_track )
2022-01-03 23:17:30 +00:00
& & ( ( duration_ms as i64 - stream_position_ms as i64 )
2020-01-31 21:41:11 +00:00
< PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS as i64 )
2021-12-26 20:18:42 +00:00
& & stream_loader_controller . range_to_end_available ( )
2020-01-31 21:41:11 +00:00
{
* suggested_to_preload_next_track = true ;
self . send_event ( PlayerEvent ::TimeToPreloadNextTrack {
track_id ,
play_request_id ,
} ) ;
}
}
2018-04-21 15:46:29 +00:00
if self . session . is_invalid ( ) {
2021-01-21 21:22:32 +00:00
return Poll ::Ready ( ( ) ) ;
2020-01-31 21:41:11 +00:00
}
2020-02-03 07:58:44 +00:00
if ( ! self . state . is_playing ( ) ) & & all_futures_completed_or_not_ready {
2021-01-21 21:22:32 +00:00
return Poll ::Pending ;
2018-04-21 15:46:29 +00:00
}
2017-01-29 14:11:20 +00:00
}
}
2020-01-31 21:41:11 +00:00
}
2016-01-02 15:19:39 +00:00
2020-01-31 21:41:11 +00:00
impl PlayerInternal {
2020-02-02 23:11:27 +00:00
fn ensure_sink_running ( & mut self ) {
2020-03-10 12:26:01 +00:00
if self . sink_status ! = SinkStatus ::Running {
2020-02-02 23:11:27 +00:00
trace! ( " == Starting sink == " ) ;
2020-03-10 12:26:01 +00:00
if let Some ( callback ) = & mut self . sink_event_callback {
callback ( SinkStatus ::Running ) ;
}
2020-02-02 23:11:27 +00:00
match self . sink . start ( ) {
2020-03-10 12:26:01 +00:00
Ok ( ( ) ) = > self . sink_status = SinkStatus ::Running ,
2021-09-20 17:29:12 +00:00
Err ( e ) = > {
error! ( " {} " , e ) ;
2021-06-18 18:25:09 +00:00
exit ( 1 ) ;
}
2020-02-02 23:11:27 +00:00
}
2017-11-28 23:18:12 +00:00
}
}
2020-03-10 12:26:01 +00:00
fn ensure_sink_stopped ( & mut self , temporarily : bool ) {
match self . sink_status {
SinkStatus ::Running = > {
trace! ( " == Stopping sink == " ) ;
2021-06-18 18:25:09 +00:00
match self . sink . stop ( ) {
Ok ( ( ) ) = > {
self . sink_status = if temporarily {
SinkStatus ::TemporarilyClosed
} else {
SinkStatus ::Closed
} ;
if let Some ( callback ) = & mut self . sink_event_callback {
callback ( self . sink_status ) ;
}
}
2021-09-20 17:29:12 +00:00
Err ( e ) = > {
error! ( " {} " , e ) ;
2021-06-18 18:25:09 +00:00
exit ( 1 ) ;
}
2020-03-10 12:26:01 +00:00
}
}
SinkStatus ::TemporarilyClosed = > {
if ! temporarily {
self . sink_status = SinkStatus ::Closed ;
if let Some ( callback ) = & mut self . sink_event_callback {
callback ( SinkStatus ::Closed ) ;
}
}
}
SinkStatus ::Closed = > ( ) ,
2017-11-28 23:18:12 +00:00
}
}
2020-01-31 21:41:11 +00:00
fn handle_player_stop ( & mut self ) {
match self . state {
PlayerState ::Playing {
track_id ,
play_request_id ,
..
}
| PlayerState ::Paused {
track_id ,
play_request_id ,
..
}
| PlayerState ::EndOfTrack {
track_id ,
play_request_id ,
2020-02-02 14:37:17 +00:00
..
2020-01-31 21:41:11 +00:00
}
| PlayerState ::Loading {
track_id ,
play_request_id ,
..
} = > {
2020-03-10 12:26:01 +00:00
self . ensure_sink_stopped ( false ) ;
2020-01-31 21:41:11 +00:00
self . send_event ( PlayerEvent ::Stopped {
track_id ,
play_request_id ,
} ) ;
self . state = PlayerState ::Stopped ;
}
PlayerState ::Stopped = > ( ) ,
2021-09-20 17:29:12 +00:00
PlayerState ::Invalid = > {
2021-12-28 22:46:37 +00:00
error! ( " PlayerInternal::handle_player_stop in invalid state " ) ;
2021-09-20 17:29:12 +00:00
exit ( 1 ) ;
}
2020-01-31 21:41:11 +00:00
}
}
2020-02-02 14:37:17 +00:00
fn handle_play ( & mut self ) {
if let PlayerState ::Paused {
track_id ,
play_request_id ,
2022-01-03 23:17:30 +00:00
stream_position_ms ,
2020-02-03 07:58:44 +00:00
duration_ms ,
2020-02-02 14:37:17 +00:00
..
} = self . state
{
self . state . paused_to_playing ( ) ;
2020-02-03 07:58:44 +00:00
self . send_event ( PlayerEvent ::Playing {
2020-02-02 14:37:17 +00:00
track_id ,
play_request_id ,
2022-01-03 23:17:30 +00:00
position_ms : stream_position_ms ,
2020-02-03 07:58:44 +00:00
duration_ms ,
2020-02-02 14:37:17 +00:00
} ) ;
2020-02-02 23:11:27 +00:00
self . ensure_sink_running ( ) ;
2020-02-02 14:37:17 +00:00
} else {
2021-12-28 22:46:37 +00:00
error! ( " Player::play called from invalid state: {:?} " , self . state ) ;
2020-02-02 14:37:17 +00:00
}
}
fn handle_pause ( & mut self ) {
if let PlayerState ::Playing {
track_id ,
play_request_id ,
2022-01-03 23:17:30 +00:00
stream_position_ms ,
2020-02-02 14:37:17 +00:00
duration_ms ,
..
} = self . state
{
self . state . playing_to_paused ( ) ;
2020-03-10 12:26:01 +00:00
self . ensure_sink_stopped ( false ) ;
2020-02-02 14:37:17 +00:00
self . send_event ( PlayerEvent ::Paused {
track_id ,
play_request_id ,
2022-01-03 23:17:30 +00:00
position_ms : stream_position_ms ,
2020-02-02 14:37:17 +00:00
duration_ms ,
} ) ;
} else {
2021-12-28 22:46:37 +00:00
error! ( " Player::pause called from invalid state: {:?} " , self . state ) ;
2020-02-02 14:37:17 +00:00
}
}
2022-01-06 21:46:50 +00:00
fn handle_packet (
& mut self ,
packet : Option < ( AudioPacketPosition , AudioPacket ) > ,
normalisation_factor : f64 ,
) {
2017-01-29 14:11:20 +00:00
match packet {
2022-01-03 23:17:30 +00:00
Some ( ( _ , mut packet ) ) = > {
2021-01-07 06:42:38 +00:00
if ! packet . is_empty ( ) {
if let AudioPacket ::Samples ( ref mut data ) = packet {
2021-03-10 21:32:24 +00:00
if self . config . normalisation
2021-05-30 18:09:39 +00:00
& & ! ( f64 ::abs ( normalisation_factor - 1.0 ) < = f64 ::EPSILON
2021-04-16 13:54:38 +00:00
& & self . config . normalisation_method = = NormalisationMethod ::Basic )
2021-03-10 21:32:24 +00:00
{
2021-02-24 20:39:42 +00:00
for sample in data . iter_mut ( ) {
let mut actual_normalisation_factor = normalisation_factor ;
if self . config . normalisation_method = = NormalisationMethod ::Dynamic
{
if self . limiter_active {
2021-03-14 13:28:16 +00:00
// "S"-shaped curve with a configurable knee during attack and release:
2021-02-24 20:39:42 +00:00
// - > 1.0 yields soft knees at start and end, steeper in between
// - 1.0 yields a linear function from 0-100%
// - between 0.0 and 1.0 yields hard knees at start and end, flatter in between
// - 0.0 yields a step response to 50%, causing distortion
// - Rates < 0.0 invert the limiter and are invalid
let mut shaped_limiter_strength = self . limiter_strength ;
if shaped_limiter_strength > 0.0
& & shaped_limiter_strength < 1.0
{
shaped_limiter_strength = 1.0
/ ( 1.0
2021-05-30 18:09:39 +00:00
+ f64 ::powf (
2021-02-24 20:39:42 +00:00
shaped_limiter_strength
/ ( 1.0 - shaped_limiter_strength ) ,
2021-05-30 18:09:39 +00:00
- self . config . normalisation_knee ,
2021-02-24 20:39:42 +00:00
) ) ;
}
actual_normalisation_factor =
( 1.0 - shaped_limiter_strength ) * normalisation_factor
+ shaped_limiter_strength * self . limiter_factor ;
} ;
2021-05-31 20:32:39 +00:00
// Cast the fields here for better readability
let normalisation_attack =
self . config . normalisation_attack . as_secs_f64 ( ) ;
let normalisation_release =
self . config . normalisation_release . as_secs_f64 ( ) ;
let limiter_release_counter =
self . limiter_release_counter as f64 ;
let limiter_attack_counter = self . limiter_attack_counter as f64 ;
let samples_per_second = SAMPLES_PER_SECOND as f64 ;
2021-02-24 20:39:42 +00:00
// Always check for peaks, even when the limiter is already active.
// There may be even higher peaks than we initially targeted.
// Check against the normalisation factor that would be applied normally.
2021-05-30 18:09:39 +00:00
let abs_sample = f64 ::abs ( * sample * normalisation_factor ) ;
2021-02-24 20:39:42 +00:00
if abs_sample > self . config . normalisation_threshold {
self . limiter_active = true ;
if self . limiter_release_counter > 0 {
// A peak was encountered while releasing the limiter;
// synchronize with the current release limiter strength.
2021-05-31 20:32:39 +00:00
self . limiter_attack_counter = ( ( ( samples_per_second
* normalisation_release )
- limiter_release_counter )
/ ( normalisation_release / normalisation_attack ) )
2021-02-24 20:39:42 +00:00
as u32 ;
self . limiter_release_counter = 0 ;
}
self . limiter_attack_counter =
self . limiter_attack_counter . saturating_add ( 1 ) ;
2021-05-31 20:32:39 +00:00
self . limiter_strength = limiter_attack_counter
/ ( samples_per_second * normalisation_attack ) ;
2021-02-24 20:39:42 +00:00
if abs_sample > self . limiter_peak_sample {
self . limiter_peak_sample = abs_sample ;
self . limiter_factor =
self . config . normalisation_threshold
/ self . limiter_peak_sample ;
}
} else if self . limiter_active {
if self . limiter_attack_counter > 0 {
// Release may start within the attack period, before
// the limiter reached full strength. For that reason
// start the release by synchronizing with the current
// attack limiter strength.
2021-05-31 20:32:39 +00:00
self . limiter_release_counter = ( ( ( samples_per_second
* normalisation_attack )
- limiter_attack_counter )
* ( normalisation_release / normalisation_attack ) )
2021-02-24 20:39:42 +00:00
as u32 ;
self . limiter_attack_counter = 0 ;
}
self . limiter_release_counter =
self . limiter_release_counter . saturating_add ( 1 ) ;
if self . limiter_release_counter
2021-05-31 20:32:39 +00:00
> ( samples_per_second * normalisation_release ) as u32
2021-02-24 20:39:42 +00:00
{
self . reset_limiter ( ) ;
} else {
2021-05-31 20:32:39 +00:00
self . limiter_strength = ( ( samples_per_second
* normalisation_release )
- limiter_release_counter )
/ ( samples_per_second * normalisation_release ) ;
2021-02-24 20:39:42 +00:00
}
}
}
2021-05-30 18:09:39 +00:00
* sample * = actual_normalisation_factor ;
2021-01-07 06:42:38 +00:00
}
2018-03-20 13:01:15 +00:00
}
2021-09-01 18:54:47 +00:00
if let Some ( ref editor ) = self . audio_filter {
editor . modify_stream ( data )
}
2018-02-23 19:08:20 +00:00
}
2021-12-29 15:26:24 +00:00
if let Err ( e ) = self . sink . write ( packet , & mut self . converter ) {
2021-09-20 17:29:12 +00:00
error! ( " {} " , e ) ;
2021-06-18 18:25:09 +00:00
exit ( 1 ) ;
2018-03-20 13:01:15 +00:00
}
2017-11-28 23:18:12 +00:00
}
2017-01-29 14:11:20 +00:00
}
None = > {
2018-02-20 20:58:02 +00:00
self . state . playing_to_end_of_track ( ) ;
2020-01-31 21:41:11 +00:00
if let PlayerState ::EndOfTrack {
track_id ,
play_request_id ,
2020-02-02 14:37:17 +00:00
..
2020-01-31 21:41:11 +00:00
} = self . state
{
self . send_event ( PlayerEvent ::EndOfTrack {
track_id ,
play_request_id ,
} )
} else {
2021-09-20 17:29:12 +00:00
error! ( " PlayerInternal handle_packet: Invalid PlayerState " ) ;
exit ( 1 ) ;
2020-01-31 21:41:11 +00:00
}
}
}
}
2021-02-24 20:39:42 +00:00
fn reset_limiter ( & mut self ) {
self . limiter_active = false ;
self . limiter_release_counter = 0 ;
self . limiter_attack_counter = 0 ;
self . limiter_peak_sample = 0.0 ;
self . limiter_factor = 1.0 ;
self . limiter_strength = 0.0 ;
}
2020-01-31 21:41:11 +00:00
fn start_playback (
& mut self ,
track_id : SpotifyId ,
play_request_id : u64 ,
loaded_track : PlayerLoadedTrackData ,
start_playback : bool ,
) {
2022-01-03 23:17:30 +00:00
let position_ms = loaded_track . stream_position_ms ;
2020-01-31 21:41:11 +00:00
2021-09-20 17:22:02 +00:00
let mut config = self . config . clone ( ) ;
if config . normalisation_type = = NormalisationType ::Auto {
if self . auto_normalise_as_album {
config . normalisation_type = NormalisationType ::Album ;
} else {
config . normalisation_type = NormalisationType ::Track ;
}
} ;
let normalisation_factor =
NormalisationData ::get_factor ( & config , loaded_track . normalisation_data ) ;
2020-01-31 21:41:11 +00:00
if start_playback {
2020-02-02 23:11:27 +00:00
self . ensure_sink_running ( ) ;
2020-01-31 21:41:11 +00:00
self . send_event ( PlayerEvent ::Playing {
track_id ,
play_request_id ,
position_ms ,
duration_ms : loaded_track . duration_ms ,
} ) ;
self . state = PlayerState ::Playing {
2021-03-10 21:39:01 +00:00
track_id ,
play_request_id ,
2020-01-31 21:41:11 +00:00
decoder : loaded_track . decoder ,
2021-09-20 17:22:02 +00:00
normalisation_data : loaded_track . normalisation_data ,
normalisation_factor ,
2020-01-31 21:41:11 +00:00
stream_loader_controller : loaded_track . stream_loader_controller ,
duration_ms : loaded_track . duration_ms ,
bytes_per_second : loaded_track . bytes_per_second ,
2022-01-03 23:17:30 +00:00
stream_position_ms : loaded_track . stream_position_ms ,
2022-01-09 21:46:44 +00:00
reported_nominal_start_time : Instant ::now ( )
. checked_sub ( Duration ::from_millis ( position_ms as u64 ) ) ,
2020-01-31 21:41:11 +00:00
suggested_to_preload_next_track : false ,
2021-12-30 22:50:28 +00:00
is_explicit : loaded_track . is_explicit ,
2020-01-31 21:41:11 +00:00
} ;
} else {
2020-03-10 12:26:01 +00:00
self . ensure_sink_stopped ( false ) ;
2020-02-03 07:58:44 +00:00
2020-01-31 21:41:11 +00:00
self . state = PlayerState ::Paused {
2021-03-10 21:39:01 +00:00
track_id ,
play_request_id ,
2020-01-31 21:41:11 +00:00
decoder : loaded_track . decoder ,
2021-09-20 17:22:02 +00:00
normalisation_data : loaded_track . normalisation_data ,
normalisation_factor ,
2020-01-31 21:41:11 +00:00
stream_loader_controller : loaded_track . stream_loader_controller ,
duration_ms : loaded_track . duration_ms ,
bytes_per_second : loaded_track . bytes_per_second ,
2022-01-03 23:17:30 +00:00
stream_position_ms : loaded_track . stream_position_ms ,
2020-01-31 21:41:11 +00:00
suggested_to_preload_next_track : false ,
2021-12-30 22:50:28 +00:00
is_explicit : loaded_track . is_explicit ,
2020-01-31 21:41:11 +00:00
} ;
self . send_event ( PlayerEvent ::Paused {
track_id ,
play_request_id ,
position_ms ,
duration_ms : loaded_track . duration_ms ,
} ) ;
2017-01-29 14:11:20 +00:00
}
}
2015-07-02 19:42:49 +00:00
2020-02-03 07:58:44 +00:00
fn handle_command_load (
& mut self ,
track_id : SpotifyId ,
play_request_id : u64 ,
play : bool ,
position_ms : u32 ,
2022-01-06 20:55:08 +00:00
) -> PlayerResult {
2020-03-10 12:00:57 +00:00
if ! self . config . gapless {
2020-03-10 12:53:58 +00:00
self . ensure_sink_stopped ( play ) ;
2020-03-10 12:00:57 +00:00
}
2020-02-03 07:58:44 +00:00
// emit the correct player event
match self . state {
PlayerState ::Playing {
track_id : old_track_id ,
..
}
| PlayerState ::Paused {
track_id : old_track_id ,
..
}
| PlayerState ::EndOfTrack {
track_id : old_track_id ,
..
}
| PlayerState ::Loading {
track_id : old_track_id ,
..
} = > self . send_event ( PlayerEvent ::Changed {
2021-03-10 21:39:01 +00:00
old_track_id ,
2020-02-03 07:58:44 +00:00
new_track_id : track_id ,
} ) ,
PlayerState ::Stopped = > self . send_event ( PlayerEvent ::Started {
2020-01-31 21:41:11 +00:00
track_id ,
play_request_id ,
position_ms ,
2020-02-03 07:58:44 +00:00
} ) ,
2021-09-20 17:29:12 +00:00
PlayerState ::Invalid { .. } = > {
2022-01-06 20:55:08 +00:00
return Err ( Error ::internal ( format! (
2021-12-28 22:46:37 +00:00
" Player::handle_command_load called from invalid state: {:?} " ,
self . state
2022-01-06 20:55:08 +00:00
) ) ) ;
2021-09-20 17:29:12 +00:00
}
2020-02-03 07:58:44 +00:00
}
2018-02-15 23:16:38 +00:00
2020-02-03 07:58:44 +00:00
// Now we check at different positions whether we already have a pre-loaded version
// of this track somewhere. If so, use it and return.
2020-02-02 14:37:17 +00:00
2020-02-03 07:58:44 +00:00
// Check if there's a matching loaded track in the EndOfTrack player state.
// This is the case if we're repeating the same track again.
if let PlayerState ::EndOfTrack {
track_id : previous_track_id ,
..
} = self . state
{
if previous_track_id = = track_id {
2020-03-12 12:01:45 +00:00
let mut loaded_track = match mem ::replace ( & mut self . state , PlayerState ::Invalid ) {
PlayerState ::EndOfTrack { loaded_track , .. } = > loaded_track ,
2021-09-20 17:29:12 +00:00
_ = > {
2022-01-06 20:55:08 +00:00
return Err ( Error ::internal ( format! ( " PlayerInternal::handle_command_load repeating the same track: invalid state: {:?} " , self . state ) ) ) ;
2021-09-20 17:29:12 +00:00
}
2020-03-12 12:01:45 +00:00
} ;
2022-01-03 23:17:30 +00:00
if position_ms ! = loaded_track . stream_position_ms {
2021-12-28 22:46:37 +00:00
// This may be blocking.
2022-01-06 20:55:08 +00:00
loaded_track . stream_position_ms = loaded_track . decoder . seek ( position_ms ) ? ;
2020-03-12 12:01:45 +00:00
}
self . preload = PlayerPreload ::None ;
2020-03-20 06:31:18 +00:00
self . start_playback ( track_id , play_request_id , loaded_track , play ) ;
2020-03-12 12:01:45 +00:00
if let PlayerState ::Invalid = self . state {
2022-01-06 20:55:08 +00:00
return Err ( Error ::internal ( format! ( " PlayerInternal::handle_command_load repeating the same track: start_playback() did not transition to valid player state: {:?} " , self . state ) ) ) ;
2020-02-03 07:58:44 +00:00
}
2022-01-06 20:55:08 +00:00
return Ok ( ( ) ) ;
2020-02-03 07:58:44 +00:00
}
}
// Check if we are already playing the track. If so, just do a seek and update our info.
if let PlayerState ::Playing {
track_id : current_track_id ,
2022-01-03 23:17:30 +00:00
ref mut stream_position_ms ,
2020-02-03 07:58:44 +00:00
ref mut decoder ,
..
}
| PlayerState ::Paused {
track_id : current_track_id ,
2022-01-03 23:17:30 +00:00
ref mut stream_position_ms ,
2020-02-03 07:58:44 +00:00
ref mut decoder ,
..
} = self . state
{
if current_track_id = = track_id {
// we can use the current decoder. Ensure it's at the correct position.
2022-01-03 23:17:30 +00:00
if position_ms ! = * stream_position_ms {
2021-12-28 22:46:37 +00:00
// This may be blocking.
2022-01-06 20:55:08 +00:00
* stream_position_ms = decoder . seek ( position_ms ) ? ;
2020-01-31 21:41:11 +00:00
}
2017-01-29 14:11:20 +00:00
2020-02-03 07:58:44 +00:00
// Move the info from the current state into a PlayerLoadedTrackData so we can use
// the usual code path to start playback.
let old_state = mem ::replace ( & mut self . state , PlayerState ::Invalid ) ;
2020-02-02 14:37:17 +00:00
if let PlayerState ::Playing {
2022-01-03 23:17:30 +00:00
stream_position_ms ,
2020-02-03 07:58:44 +00:00
decoder ,
stream_loader_controller ,
bytes_per_second ,
duration_ms ,
2021-09-20 17:22:02 +00:00
normalisation_data ,
2021-12-30 22:50:28 +00:00
is_explicit ,
2020-02-02 14:37:17 +00:00
..
}
| PlayerState ::Paused {
2022-01-03 23:17:30 +00:00
stream_position_ms ,
2020-02-03 07:58:44 +00:00
decoder ,
stream_loader_controller ,
bytes_per_second ,
duration_ms ,
2021-09-20 17:22:02 +00:00
normalisation_data ,
2021-12-30 22:50:28 +00:00
is_explicit ,
2020-02-02 14:37:17 +00:00
..
2020-02-03 07:58:44 +00:00
} = old_state
2020-02-02 14:37:17 +00:00
{
2020-02-03 07:58:44 +00:00
let loaded_track = PlayerLoadedTrackData {
decoder ,
2021-09-20 17:22:02 +00:00
normalisation_data ,
2020-02-03 07:58:44 +00:00
stream_loader_controller ,
bytes_per_second ,
duration_ms ,
2022-01-03 23:17:30 +00:00
stream_position_ms ,
2021-12-30 22:50:28 +00:00
is_explicit ,
2020-02-03 07:58:44 +00:00
} ;
2020-02-03 03:31:15 +00:00
2020-02-07 12:52:20 +00:00
self . preload = PlayerPreload ::None ;
2020-02-03 07:58:44 +00:00
self . start_playback ( track_id , play_request_id , loaded_track , play ) ;
2020-02-03 03:31:15 +00:00
2020-02-03 07:58:44 +00:00
if let PlayerState ::Invalid = self . state {
2022-01-06 20:55:08 +00:00
return Err ( Error ::internal ( format! ( " PlayerInternal::handle_command_load already playing this track: start_playback() did not transition to valid player state: {:?} " , self . state ) ) ) ;
2020-02-03 07:58:44 +00:00
}
2020-02-03 03:31:15 +00:00
2022-01-06 20:55:08 +00:00
return Ok ( ( ) ) ;
2020-02-03 07:58:44 +00:00
} else {
2022-01-06 20:55:08 +00:00
return Err ( Error ::internal ( format! ( " PlayerInternal::handle_command_load already playing this track: invalid state: {:?} " , self . state ) ) ) ;
2020-02-03 07:58:44 +00:00
}
}
}
2020-02-03 03:31:15 +00:00
2020-02-03 07:58:44 +00:00
// Check if the requested track has been preloaded already. If so use the preloaded data.
if let PlayerPreload ::Ready {
track_id : loaded_track_id ,
..
} = self . preload
{
if track_id = = loaded_track_id {
let preload = std ::mem ::replace ( & mut self . preload , PlayerPreload ::None ) ;
if let PlayerPreload ::Ready {
track_id ,
mut loaded_track ,
} = preload
{
2022-01-03 23:17:30 +00:00
if position_ms ! = loaded_track . stream_position_ms {
2021-12-28 22:46:37 +00:00
// This may be blocking
2022-01-06 20:55:08 +00:00
loaded_track . stream_position_ms = loaded_track . decoder . seek ( position_ms ) ? ;
2020-02-02 14:37:17 +00:00
}
2021-01-22 21:51:41 +00:00
self . start_playback ( track_id , play_request_id , * loaded_track , play ) ;
2022-01-06 20:55:08 +00:00
return Ok ( ( ) ) ;
2020-02-03 07:58:44 +00:00
} else {
2022-01-06 20:55:08 +00:00
return Err ( Error ::internal ( format! ( " PlayerInternal::handle_command_loading preloaded track: invalid state: {:?} " , self . state ) ) ) ;
2020-02-02 14:37:17 +00:00
}
2020-02-03 07:58:44 +00:00
}
}
2020-02-02 14:37:17 +00:00
2020-02-03 07:58:44 +00:00
// We need to load the track - either from scratch or by completing a preload.
// In any case we go into a Loading state to load the track.
2020-03-10 12:26:01 +00:00
self . ensure_sink_stopped ( play ) ;
2020-02-03 07:58:44 +00:00
self . send_event ( PlayerEvent ::Loading {
track_id ,
play_request_id ,
position_ms ,
} ) ;
// Try to extract a pending loader from the preloading mechanism
let loader = if let PlayerPreload ::Loading {
track_id : loaded_track_id ,
..
} = self . preload
{
if ( track_id = = loaded_track_id ) & & ( position_ms = = 0 ) {
let mut preload = PlayerPreload ::None ;
std ::mem ::swap ( & mut preload , & mut self . preload ) ;
if let PlayerPreload ::Loading { loader , .. } = preload {
Some ( loader )
} else {
None
2020-02-02 14:37:17 +00:00
}
2020-02-03 07:58:44 +00:00
} else {
None
}
} else {
None
} ;
2020-02-02 14:37:17 +00:00
2020-02-03 07:58:44 +00:00
self . preload = PlayerPreload ::None ;
2020-02-02 23:11:27 +00:00
2020-02-03 07:58:44 +00:00
// If we don't have a loader yet, create one from scratch.
2021-01-21 21:22:32 +00:00
let loader = loader . unwrap_or_else ( | | Box ::pin ( self . load_track ( track_id , position_ms ) ) ) ;
2020-01-31 21:41:11 +00:00
2020-02-03 07:58:44 +00:00
// Set ourselves to a loading state.
self . state = PlayerState ::Loading {
track_id ,
play_request_id ,
start_playback : play ,
loader ,
} ;
2022-01-06 20:55:08 +00:00
Ok ( ( ) )
2020-02-03 07:58:44 +00:00
}
2020-01-31 21:41:11 +00:00
2020-02-03 07:58:44 +00:00
fn handle_command_preload ( & mut self , track_id : SpotifyId ) {
debug! ( " Preloading track " ) ;
let mut preload_track = true ;
// check whether the track is already loaded somewhere or being loaded.
if let PlayerPreload ::Loading {
track_id : currently_loading ,
..
}
| PlayerPreload ::Ready {
track_id : currently_loading ,
..
} = self . preload
{
if currently_loading = = track_id {
// we're already preloading the requested track.
preload_track = false ;
} else {
// we're preloading something else - cancel it.
self . preload = PlayerPreload ::None ;
}
}
2016-01-02 15:48:44 +00:00
2020-02-03 07:58:44 +00:00
if let PlayerState ::Playing {
track_id : current_track_id ,
..
}
| PlayerState ::Paused {
track_id : current_track_id ,
..
}
| PlayerState ::EndOfTrack {
track_id : current_track_id ,
..
} = self . state
{
if current_track_id = = track_id {
// we already have the requested track loaded.
preload_track = false ;
2020-01-31 21:41:11 +00:00
}
2020-02-03 07:58:44 +00:00
}
2020-01-31 21:41:11 +00:00
2020-03-12 12:01:45 +00:00
// schedule the preload of the current track if desired.
2020-02-03 07:58:44 +00:00
if preload_track {
let loader = self . load_track ( track_id , 0 ) ;
2021-01-21 21:22:32 +00:00
self . preload = PlayerPreload ::Loading {
track_id ,
loader : Box ::pin ( loader ) ,
}
2020-02-03 07:58:44 +00:00
}
}
2020-02-02 14:37:17 +00:00
2021-12-18 22:44:13 +00:00
fn handle_command_seek ( & mut self , position_ms : u32 ) -> PlayerResult {
2020-02-03 07:58:44 +00:00
if let Some ( decoder ) = self . state . decoder ( ) {
2022-01-03 23:17:30 +00:00
match decoder . seek ( position_ms ) {
2022-01-06 20:55:08 +00:00
Ok ( new_position_ms ) = > {
2020-02-03 07:58:44 +00:00
if let PlayerState ::Playing {
2022-01-03 23:17:30 +00:00
ref mut stream_position_ms ,
2020-02-03 07:58:44 +00:00
..
2017-01-29 14:11:20 +00:00
}
2020-02-03 07:58:44 +00:00
| PlayerState ::Paused {
2022-01-03 23:17:30 +00:00
ref mut stream_position_ms ,
2020-02-03 07:58:44 +00:00
..
} = self . state
{
2022-01-06 20:55:08 +00:00
* stream_position_ms = new_position_ms ;
2020-02-02 14:37:17 +00:00
}
}
2021-12-28 22:46:37 +00:00
Err ( e ) = > error! ( " PlayerInternal::handle_command_seek error: {} " , e ) ,
2017-01-29 14:11:20 +00:00
}
2020-02-03 07:58:44 +00:00
} else {
2021-12-28 22:46:37 +00:00
error! ( " Player::seek called from invalid state: {:?} " , self . state ) ;
2020-02-03 07:58:44 +00:00
}
2016-04-24 13:48:15 +00:00
2020-02-03 07:58:44 +00:00
// ensure we have a bit of a buffer of downloaded data
2021-12-18 22:44:13 +00:00
self . preload_data_before_playback ( ) ? ;
2020-01-31 21:41:11 +00:00
2020-02-03 07:58:44 +00:00
if let PlayerState ::Playing {
track_id ,
play_request_id ,
ref mut reported_nominal_start_time ,
duration_ms ,
..
} = self . state
{
* reported_nominal_start_time =
2022-01-09 21:46:44 +00:00
Instant ::now ( ) . checked_sub ( Duration ::from_millis ( position_ms as u64 ) ) ;
2020-02-03 07:58:44 +00:00
self . send_event ( PlayerEvent ::Playing {
track_id ,
play_request_id ,
position_ms ,
duration_ms ,
} ) ;
}
if let PlayerState ::Paused {
track_id ,
play_request_id ,
duration_ms ,
..
} = self . state
{
self . send_event ( PlayerEvent ::Paused {
track_id ,
play_request_id ,
position_ms ,
duration_ms ,
} ) ;
}
2021-12-18 22:44:13 +00:00
Ok ( ( ) )
2020-02-03 07:58:44 +00:00
}
2020-01-31 21:41:11 +00:00
2021-12-18 22:44:13 +00:00
fn handle_command ( & mut self , cmd : PlayerCommand ) -> PlayerResult {
2020-02-03 07:58:44 +00:00
debug! ( " command={:?} " , cmd ) ;
2021-12-18 22:44:13 +00:00
let result = match cmd {
2020-02-03 07:58:44 +00:00
PlayerCommand ::Load {
track_id ,
play_request_id ,
play ,
position_ms ,
2022-01-06 20:55:08 +00:00
} = > self . handle_command_load ( track_id , play_request_id , play , position_ms ) ? ,
2015-07-02 19:42:49 +00:00
2020-02-03 07:58:44 +00:00
PlayerCommand ::Preload { track_id } = > self . handle_command_preload ( track_id ) ,
2016-04-24 13:48:15 +00:00
2021-12-18 22:44:13 +00:00
PlayerCommand ::Seek ( position_ms ) = > self . handle_command_seek ( position_ms ) ? ,
2020-02-03 07:58:44 +00:00
PlayerCommand ::Play = > self . handle_play ( ) ,
PlayerCommand ::Pause = > self . handle_pause ( ) ,
2016-04-24 13:48:15 +00:00
2020-01-31 21:41:11 +00:00
PlayerCommand ::Stop = > self . handle_player_stop ( ) ,
2015-06-23 14:38:29 +00:00
2020-01-31 21:41:11 +00:00
PlayerCommand ::AddEventSender ( sender ) = > self . event_senders . push ( sender ) ,
2016-01-20 13:55:36 +00:00
2020-03-10 12:26:01 +00:00
PlayerCommand ::SetSinkEventCallback ( callback ) = > self . sink_event_callback = callback ,
2020-01-31 21:41:11 +00:00
PlayerCommand ::EmitVolumeSetEvent ( volume ) = > {
self . send_event ( PlayerEvent ::VolumeSet { volume } )
2019-10-07 22:27:26 +00:00
}
2021-09-20 17:22:02 +00:00
PlayerCommand ::SetAutoNormaliseAsAlbum ( setting ) = > {
self . auto_normalise_as_album = setting
}
2021-12-30 22:50:28 +00:00
PlayerCommand ::SkipExplicitContent ( ) = > {
if let PlayerState ::Playing {
track_id ,
play_request_id ,
is_explicit ,
..
}
| PlayerState ::Paused {
track_id ,
play_request_id ,
is_explicit ,
..
} = self . state
{
if is_explicit {
warn! ( " Currently loaded track is explicit, which client setting forbids -- skipping to next track. " ) ;
self . send_event ( PlayerEvent ::EndOfTrack {
track_id ,
play_request_id ,
} )
}
}
}
2021-12-18 22:44:13 +00:00
} ;
Ok ( result )
2016-01-15 00:12:08 +00:00
}
2015-07-09 20:08:14 +00:00
2020-01-31 21:41:11 +00:00
fn send_event ( & mut self , event : PlayerEvent ) {
let mut index = 0 ;
while index < self . event_senders . len ( ) {
2021-02-22 08:55:40 +00:00
match self . event_senders [ index ] . send ( event . clone ( ) ) {
2020-01-31 21:41:11 +00:00
Ok ( _ ) = > index + = 1 ,
Err ( _ ) = > {
self . event_senders . remove ( index ) ;
}
}
2019-11-01 19:46:28 +00:00
}
}
2021-02-20 21:14:15 +00:00
fn load_track (
2021-12-27 23:04:09 +00:00
& mut self ,
2019-11-11 07:22:41 +00:00
spotify_id : SpotifyId ,
2020-02-02 00:07:05 +00:00
position_ms : u32 ,
2021-01-22 21:51:41 +00:00
) -> impl Future < Output = Result < PlayerLoadedTrackData , ( ) > > + Send + 'static {
2020-01-31 21:41:11 +00:00
// This method creates a future that returns the loaded stream and associated info.
// Ideally all work should be done using asynchronous code. However, seek() on the
// audio stream is implemented in a blocking fashion. Thus, we can't turn it into future
// easily. Instead we spawn a thread to do the work and return a one-shot channel as the
// future to work with.
2021-02-20 21:14:15 +00:00
let loader = PlayerTrackLoader {
session : self . session . clone ( ) ,
config : self . config . clone ( ) ,
} ;
2019-11-01 19:48:18 +00:00
2021-02-20 21:14:15 +00:00
let ( result_tx , result_rx ) = oneshot ::channel ( ) ;
2021-01-21 21:22:32 +00:00
2021-12-27 23:04:09 +00:00
let load_handles_clone = self . load_handles . clone ( ) ;
2021-11-26 22:21:27 +00:00
let handle = tokio ::runtime ::Handle ::current ( ) ;
2021-12-27 23:04:09 +00:00
let load_handle = thread ::spawn ( move | | {
2021-11-26 22:21:27 +00:00
let data = handle . block_on ( loader . load_track ( spotify_id , position_ms ) ) ;
2021-03-10 21:32:24 +00:00
if let Some ( data ) = data {
let _ = result_tx . send ( data ) ;
}
2021-12-27 23:04:09 +00:00
let mut load_handles = load_handles_clone . lock ( ) ;
load_handles . remove ( & thread ::current ( ) . id ( ) ) ;
2021-02-20 21:14:15 +00:00
} ) ;
2017-01-29 14:11:20 +00:00
2021-12-27 23:04:09 +00:00
let mut load_handles = self . load_handles . lock ( ) ;
load_handles . insert ( load_handle . thread ( ) . id ( ) , load_handle ) ;
2021-02-20 21:14:15 +00:00
result_rx . map_err ( | _ | ( ) )
2020-01-31 21:41:11 +00:00
}
2019-11-07 13:02:53 +00:00
2021-12-26 20:18:42 +00:00
fn preload_data_before_playback ( & mut self ) -> PlayerResult {
2020-01-31 21:41:11 +00:00
if let PlayerState ::Playing {
2020-01-17 17:11:07 +00:00
bytes_per_second ,
2020-01-31 21:41:11 +00:00
ref mut stream_loader_controller ,
..
} = self . state
{
2022-01-05 19:44:08 +00:00
let ping_time = stream_loader_controller . ping_time ( ) . as_secs_f32 ( ) ;
2020-01-31 21:41:11 +00:00
// Request our read ahead range
let request_data_length = max (
2022-01-05 19:44:08 +00:00
( READ_AHEAD_DURING_PLAYBACK_ROUNDTRIPS * ping_time * bytes_per_second as f32 )
as usize ,
2021-05-31 20:32:39 +00:00
( READ_AHEAD_DURING_PLAYBACK . as_secs_f32 ( ) * bytes_per_second as f32 ) as usize ,
2020-01-31 21:41:11 +00:00
) ;
2022-01-05 19:44:08 +00:00
// Request the part we want to wait for blocking. This effectively means we wait for the previous request to partially complete.
2020-01-31 21:41:11 +00:00
let wait_for_data_length = max (
2022-01-05 19:44:08 +00:00
( READ_AHEAD_BEFORE_PLAYBACK_ROUNDTRIPS * ping_time * bytes_per_second as f32 )
as usize ,
2021-05-31 20:32:39 +00:00
( READ_AHEAD_BEFORE_PLAYBACK . as_secs_f32 ( ) * bytes_per_second as f32 ) as usize ,
2020-01-31 21:41:11 +00:00
) ;
2021-12-18 22:44:13 +00:00
stream_loader_controller
2022-01-05 19:44:08 +00:00
. fetch_next_and_wait ( request_data_length , wait_for_data_length )
2021-12-26 20:18:42 +00:00
. map_err ( Into ::into )
2021-12-18 22:44:13 +00:00
} else {
Ok ( ( ) )
2019-11-01 19:46:28 +00:00
}
2015-07-09 22:09:40 +00:00
}
2015-07-09 20:08:14 +00:00
}
2017-01-29 14:11:20 +00:00
2017-02-22 04:17:04 +00:00
impl Drop for PlayerInternal {
fn drop ( & mut self ) {
2020-01-31 21:41:11 +00:00
debug! ( " drop PlayerInternal[{}] " , self . session . session_id ( ) ) ;
2021-12-27 23:04:09 +00:00
let handles : Vec < thread ::JoinHandle < ( ) > > = {
// waiting for the thread while holding the mutex would result in a deadlock
let mut load_handles = self . load_handles . lock ( ) ;
load_handles
. drain ( )
. map ( | ( _thread_id , handle ) | handle )
. collect ( )
} ;
for handle in handles {
let _ = handle . join ( ) ;
}
2017-02-22 04:17:04 +00:00
}
}
2021-12-28 22:46:37 +00:00
impl fmt ::Debug for PlayerCommand {
fn fmt ( & self , f : & mut fmt ::Formatter ) -> fmt ::Result {
2017-02-09 01:27:52 +00:00
match * self {
2020-01-31 21:41:11 +00:00
PlayerCommand ::Load {
track_id ,
play ,
position_ms ,
..
} = > f
2018-07-03 11:08:42 +00:00
. debug_tuple ( " Load " )
2020-01-31 21:41:11 +00:00
. field ( & track_id )
2018-02-26 01:50:41 +00:00
. field ( & play )
2020-01-31 21:41:11 +00:00
. field ( & position_ms )
2018-02-26 01:50:41 +00:00
. finish ( ) ,
2020-01-31 21:41:11 +00:00
PlayerCommand ::Preload { track_id } = > {
f . debug_tuple ( " Preload " ) . field ( & track_id ) . finish ( )
}
2018-02-26 01:50:41 +00:00
PlayerCommand ::Play = > f . debug_tuple ( " Play " ) . finish ( ) ,
PlayerCommand ::Pause = > f . debug_tuple ( " Pause " ) . finish ( ) ,
PlayerCommand ::Stop = > f . debug_tuple ( " Stop " ) . finish ( ) ,
PlayerCommand ::Seek ( position ) = > f . debug_tuple ( " Seek " ) . field ( & position ) . finish ( ) ,
2020-01-31 21:41:11 +00:00
PlayerCommand ::AddEventSender ( _ ) = > f . debug_tuple ( " AddEventSender " ) . finish ( ) ,
2020-03-10 12:26:01 +00:00
PlayerCommand ::SetSinkEventCallback ( _ ) = > {
f . debug_tuple ( " SetSinkEventCallback " ) . finish ( )
}
2020-01-31 21:41:11 +00:00
PlayerCommand ::EmitVolumeSetEvent ( volume ) = > {
f . debug_tuple ( " VolumeSet " ) . field ( & volume ) . finish ( )
}
2021-09-20 17:22:02 +00:00
PlayerCommand ::SetAutoNormaliseAsAlbum ( setting ) = > f
. debug_tuple ( " SetAutoNormaliseAsAlbum " )
. field ( & setting )
. finish ( ) ,
2021-12-30 22:50:28 +00:00
PlayerCommand ::SkipExplicitContent ( ) = > f . debug_tuple ( " SkipExplicitContent " ) . finish ( ) ,
2017-02-09 01:27:52 +00:00
}
}
}
2018-02-10 15:26:08 +00:00
2021-12-28 22:46:37 +00:00
impl fmt ::Debug for PlayerState {
fn fmt ( & self , f : & mut fmt ::Formatter < '_ > ) -> fmt ::Result {
2020-05-09 11:59:28 +00:00
use PlayerState ::* ;
match * self {
Stopped = > f . debug_struct ( " Stopped " ) . finish ( ) ,
Loading {
track_id ,
play_request_id ,
..
} = > f
. debug_struct ( " Loading " )
. field ( " track_id " , & track_id )
. field ( " play_request_id " , & play_request_id )
. finish ( ) ,
Paused {
track_id ,
play_request_id ,
..
} = > f
. debug_struct ( " Paused " )
. field ( " track_id " , & track_id )
. field ( " play_request_id " , & play_request_id )
. finish ( ) ,
Playing {
track_id ,
play_request_id ,
..
} = > f
. debug_struct ( " Playing " )
. field ( " track_id " , & track_id )
. field ( " play_request_id " , & play_request_id )
. finish ( ) ,
EndOfTrack {
track_id ,
play_request_id ,
..
} = > f
. debug_struct ( " EndOfTrack " )
. field ( " track_id " , & track_id )
. field ( " play_request_id " , & play_request_id )
. finish ( ) ,
Invalid = > f . debug_struct ( " Invalid " ) . finish ( ) ,
}
}
}
2022-01-02 23:13:28 +00:00
2018-02-10 15:26:08 +00:00
struct Subfile < T : Read + Seek > {
stream : T ,
2022-01-03 21:20:29 +00:00
offset : u64 ,
2022-01-02 23:13:28 +00:00
length : u64 ,
2018-02-10 15:26:08 +00:00
}
impl < T : Read + Seek > Subfile < T > {
2022-01-06 20:55:08 +00:00
pub fn new ( mut stream : T , offset : u64 , length : u64 ) -> Result < Subfile < T > , io ::Error > {
2022-01-03 21:20:29 +00:00
let target = SeekFrom ::Start ( offset ) ;
2022-01-06 20:55:08 +00:00
stream . seek ( target ) ? ;
2021-12-28 22:46:37 +00:00
2022-01-06 20:55:08 +00:00
Ok ( Subfile {
2022-01-03 21:20:29 +00:00
stream ,
offset ,
length ,
2022-01-06 20:55:08 +00:00
} )
2018-02-10 15:26:08 +00:00
}
}
impl < T : Read + Seek > Read for Subfile < T > {
2021-01-21 21:22:32 +00:00
fn read ( & mut self , buf : & mut [ u8 ] ) -> io ::Result < usize > {
2018-02-10 15:26:08 +00:00
self . stream . read ( buf )
}
}
impl < T : Read + Seek > Seek for Subfile < T > {
2022-01-02 23:13:28 +00:00
fn seek ( & mut self , pos : SeekFrom ) -> io ::Result < u64 > {
2022-01-03 21:20:29 +00:00
let pos = match pos {
SeekFrom ::Start ( offset ) = > SeekFrom ::Start ( offset + self . offset ) ,
x = > x ,
} ;
let newpos = self . stream . seek ( pos ) ? ;
if newpos > = self . offset {
Ok ( newpos - self . offset )
} else {
Err ( io ::Error ::new (
io ::ErrorKind ::UnexpectedEof ,
" newpos < self.offset " ,
) )
}
2022-01-02 23:13:28 +00:00
}
}
2018-02-10 15:26:08 +00:00
2022-01-02 23:13:28 +00:00
impl < R > MediaSource for Subfile < R >
where
R : Read + Seek + Send ,
{
fn is_seekable ( & self ) -> bool {
true
}
2021-12-28 22:46:37 +00:00
2022-01-02 23:13:28 +00:00
fn byte_len ( & self ) -> Option < u64 > {
Some ( self . length )
2018-02-10 15:26:08 +00:00
}
}