@@ -760,7 +760,16 @@ impl PlayerTrackLoader {
760760 position_ms : u32 ,
761761 ) -> Option < PlayerLoadedTrackData > {
762762 let audio = match AudioItem :: get_audio_item ( & self . session , spotify_id) . await {
763- Ok ( audio) => audio,
763+ Ok ( audio) => match self . find_available_alternative ( audio) . await {
764+ Some ( audio) => audio,
765+ None => {
766+ warn ! (
767+ "<{}> is not available" ,
768+ spotify_id. to_uri( ) . unwrap_or_default( )
769+ ) ;
770+ return None ;
771+ }
772+ } ,
764773 Err ( e) => {
765774 error ! ( "Unable to load audio item: {:?}" , e) ;
766775 return None ;
@@ -769,17 +778,6 @@ impl PlayerTrackLoader {
769778
770779 info ! ( "Loading <{}> with Spotify URI <{}>" , audio. name, audio. uri) ;
771780
772- let audio = match self . find_available_alternative ( audio) . await {
773- Some ( audio) => audio,
774- None => {
775- warn ! (
776- "<{}> is not available" ,
777- spotify_id. to_uri( ) . unwrap_or_default( )
778- ) ;
779- return None ;
780- }
781- } ;
782-
783781 if audio. duration < 0 {
784782 error ! (
785783 "Track duration for <{}> cannot be {}" ,
@@ -809,26 +807,24 @@ impl PlayerTrackLoader {
809807 ] ,
810808 } ;
811809
812- let entry = formats. iter ( ) . find_map ( |format| {
813- if let Some ( & file_id) = audio. files . get ( format) {
814- Some ( ( * format, file_id) )
815- } else {
816- None
817- }
818- } ) ;
819-
820- let ( format, file_id) = match entry {
821- Some ( t) => t,
822- None => {
823- warn ! ( "<{}> is not available in any supported format" , audio. name) ;
824- return None ;
825- }
826- } ;
810+ let ( format, file_id) =
811+ match formats
812+ . iter ( )
813+ . find_map ( |format| match audio. files . get ( format) {
814+ Some ( & file_id) => Some ( ( * format, file_id) ) ,
815+ _ => None ,
816+ } ) {
817+ Some ( t) => t,
818+ None => {
819+ warn ! ( "<{}> is not available in any supported format" , audio. name) ;
820+ return None ;
821+ }
822+ } ;
827823
828824 let bytes_per_second = self . stream_data_rate ( format) ;
829825 let play_from_beginning = position_ms == 0 ;
830826
831- // This is only a loop to be able to reload the file if an error occured
827+ // This is only a loop to be able to reload the file if an error occurred
832828 // while opening a cached file.
833829 loop {
834830 let encrypted_file = AudioFile :: open (
@@ -1321,25 +1317,30 @@ impl PlayerInternal {
13211317 // For the basic normalisation method, a normalisation factor of 1.0 indicates that
13221318 // there is nothing to normalise (all samples should pass unaltered). For the
13231319 // dynamic method, there may still be peaks that we want to shave off.
1324- if self . config . normalisation
1325- && !( f64:: abs ( normalisation_factor - 1.0 ) <= f64:: EPSILON
1326- && self . config . normalisation_method == NormalisationMethod :: Basic )
1327- {
1328- // zero-cost shorthands
1329- let threshold_db = self . config . normalisation_threshold_dbfs ;
1330- let knee_db = self . config . normalisation_knee_db ;
1331- let attack_cf = self . config . normalisation_attack_cf ;
1332- let release_cf = self . config . normalisation_release_cf ;
1333-
1334- for sample in data. iter_mut ( ) {
1335- * sample *= normalisation_factor; // for both the basic and dynamic limiter
1336-
1337- // Feedforward limiter in the log domain
1338- // After: Giannoulis, D., Massberg, M., & Reiss, J.D. (2012). Digital Dynamic
1339- // Range Compressor Design—A Tutorial and Analysis. Journal of The Audio
1340- // Engineering Society, 60, 399-408.
1341- if self . config . normalisation_method == NormalisationMethod :: Dynamic
1342- {
1320+ if self . config . normalisation {
1321+ if self . config . normalisation_method == NormalisationMethod :: Basic
1322+ && normalisation_factor < 1.0
1323+ {
1324+ for sample in data. iter_mut ( ) {
1325+ * sample *= normalisation_factor;
1326+ }
1327+ } else if self . config . normalisation_method
1328+ == NormalisationMethod :: Dynamic
1329+ {
1330+ // zero-cost shorthands
1331+ let threshold_db = self . config . normalisation_threshold_dbfs ;
1332+ let knee_db = self . config . normalisation_knee_db ;
1333+ let attack_cf = self . config . normalisation_attack_cf ;
1334+ let release_cf = self . config . normalisation_release_cf ;
1335+
1336+ for sample in data. iter_mut ( ) {
1337+ * sample *= normalisation_factor;
1338+
1339+ // Feedforward limiter in the log domain
1340+ // After: Giannoulis, D., Massberg, M., & Reiss, J.D. (2012). Digital Dynamic
1341+ // Range Compressor Design—A Tutorial and Analysis. Journal of The Audio
1342+ // Engineering Society, 60, 399-408.
1343+
13431344 // Some tracks have samples that are precisely 0.0. That's silence
13441345 // and we know we don't need to limit that, in which we can spare
13451346 // the CPU cycles.
@@ -1348,22 +1349,26 @@ impl PlayerInternal {
13481349 // peak detector stuck. Also catch the unlikely case where a sample
13491350 // is decoded as `NaN` or some other non-normal value.
13501351 let limiter_db = if sample. is_normal ( ) {
1351- // step 1-2: half-wave rectification and conversion into dB
1352- let abs_sample_db = ratio_to_db ( sample. abs ( ) ) ;
1353-
1354- // step 3-4: gain computer with soft knee and subtractor
1355- let bias_db = abs_sample_db - threshold_db;
1352+ // step 1-4: half-wave rectification and conversion into dB
1353+ // and gain computer with soft knee and subtractor
1354+ let bias_db = ratio_to_db ( sample. abs ( ) ) - threshold_db;
13561355 let knee_boundary_db = bias_db * 2.0 ;
13571356
13581357 if knee_boundary_db < -knee_db {
13591358 0.0
13601359 } else if knee_boundary_db. abs ( ) <= knee_db {
1361- abs_sample_db
1362- - ( abs_sample_db
1363- - ( bias_db + knee_db / 2.0 ) . powi ( 2 )
1364- / ( 2.0 * knee_db) )
1360+ // The textbook equation:
1361+ // ratio_to_db(sample.abs()) - (ratio_to_db(sample.abs()) - (bias_db + knee_db / 2.0).powi(2) / (2.0 * knee_db))
1362+ // Simplifies to:
1363+ // ((2.0 * bias_db) + knee_db).powi(2) / (8.0 * knee_db)
1364+ // Which in our case further simplifies to:
1365+ // (knee_boundary_db + knee_db).powi(2) / (8.0 * knee_db)
1366+ // because knee_boundary_db is 2.0 * bias_db.
1367+ ( knee_boundary_db + knee_db) . powi ( 2 ) / ( 8.0 * knee_db)
13651368 } else {
1366- abs_sample_db - threshold_db
1369+ // Textbook:
1370+ // ratio_to_db(sample.abs()) - threshold_db, which is already our bias_db.
1371+ bias_db
13671372 }
13681373 } else {
13691374 0.0
@@ -1377,14 +1382,24 @@ impl PlayerInternal {
13771382 || self . normalisation_peak > 0.0
13781383 {
13791384 // step 5: smooth, decoupled peak detector
1385+ // Textbook:
1386+ // release_cf * self.normalisation_integrator + (1.0 - release_cf) * limiter_db
1387+ // Simplifies to:
1388+ // release_cf * self.normalisation_integrator - release_cf * limiter_db + limiter_db
13801389 self . normalisation_integrator = f64:: max (
13811390 limiter_db,
13821391 release_cf * self . normalisation_integrator
1383- + ( 1.0 - release_cf) * limiter_db,
1392+ - release_cf * limiter_db
1393+ + limiter_db,
13841394 ) ;
1395+ // Textbook:
1396+ // attack_cf * self.normalisation_peak + (1.0 - attack_cf) * self.normalisation_integrator
1397+ // Simplifies to:
1398+ // attack_cf * self.normalisation_peak - attack_cf * self.normalisation_integrator + self.normalisation_integrator
13851399 self . normalisation_peak = attack_cf
13861400 * self . normalisation_peak
1387- + ( 1.0 - attack_cf) * self . normalisation_integrator ;
1401+ - attack_cf * self . normalisation_integrator
1402+ + self . normalisation_integrator ;
13881403
13891404 // step 6: make-up gain applied later (volume attenuation)
13901405 // Applying the standard normalisation factor here won't work,
@@ -1897,15 +1912,8 @@ impl PlayerInternal {
18971912 }
18981913
18991914 fn send_event ( & mut self , event : PlayerEvent ) {
1900- let mut index = 0 ;
1901- while index < self . event_senders . len ( ) {
1902- match self . event_senders [ index] . send ( event. clone ( ) ) {
1903- Ok ( _) => index += 1 ,
1904- Err ( _) => {
1905- self . event_senders . remove ( index) ;
1906- }
1907- }
1908- }
1915+ self . event_senders
1916+ . retain ( |sender| sender. send ( event. clone ( ) ) . is_ok ( ) ) ;
19091917 }
19101918
19111919 fn load_track (
@@ -2079,10 +2087,7 @@ impl<T: Read + Seek> Seek for Subfile<T> {
20792087 } ;
20802088
20812089 let newpos = self . stream . seek ( pos) ?;
2082- if newpos > self . offset {
2083- Ok ( newpos - self . offset )
2084- } else {
2085- Ok ( 0 )
2086- }
2090+
2091+ Ok ( newpos. saturating_sub ( self . offset ) )
20872092 }
20882093}
0 commit comments