| 
					
				 | 
			
			
				@@ -1,3 +1,4 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use byteorder::{LittleEndian, ReadBytesExt}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use futures::sync::oneshot; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use futures::{future, Future}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use futures; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -61,6 +62,50 @@ pub enum PlayerEvent { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 type PlayerEventChannel = futures::sync::mpsc::UnboundedReceiver<PlayerEvent>; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#[derive(Clone, Copy, Debug)] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+struct NormalisationData { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    track_gain_db: f32, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    track_peak: f32, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    album_gain_db: f32, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    album_peak: f32, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+impl NormalisationData { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fn parse_from_file<T: Read + Seek>(mut file: T) -> Result<NormalisationData> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const SPOTIFY_NORMALIZATION_HEADER_START_OFFSET: u64 = 144; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        file.seek(SeekFrom::Start(SPOTIFY_NORMALIZATION_HEADER_START_OFFSET)).unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let track_gain_db = file.read_f32::<LittleEndian>().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let track_peak = file.read_f32::<LittleEndian>().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let album_gain_db = file.read_f32::<LittleEndian>().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let album_peak = file.read_f32::<LittleEndian>().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let r = NormalisationData { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            track_gain_db: track_gain_db, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            track_peak: track_peak, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            album_gain_db: album_gain_db, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            album_peak: album_peak, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Ok(r) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fn get_factor(config: &PlayerConfig, data: NormalisationData) -> f32 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let mut normalisation_factor = f32::powf(10.0, (data.track_gain_db + config.normalisation_pregain) / 20.0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if normalisation_factor * data.track_peak > 1.0 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            warn!("Reducing normalisation factor to prevent clipping. Please add negative pregain to avoid."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            normalisation_factor = 1.0 / data.track_peak; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        debug!("Normalisation Data: {:?}", data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        debug!("Applied normalisation factor: {}", normalisation_factor); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        normalisation_factor 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 impl Player { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     pub fn new<F>(config: PlayerConfig, session: Session, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                   audio_filter: Option<Box<AudioFilter + Send>>, 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -142,11 +187,13 @@ enum PlayerState { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         track_id: SpotifyId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         decoder: Decoder, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         end_of_track: oneshot::Sender<()>, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        normalisation_factor: f32, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     Playing { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         track_id: SpotifyId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         decoder: Decoder, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         end_of_track: oneshot::Sender<()>, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        normalisation_factor: f32, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     EndOfTrack { track_id: SpotifyId }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     Invalid, 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -176,7 +223,7 @@ impl PlayerState { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         use self::PlayerState::*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         match mem::replace(self, Invalid) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             Playing { track_id, end_of_track, ..} => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                end_of_track.send(()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                let _ = end_of_track.send(()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 *self = EndOfTrack { track_id }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             _ => panic!("Called playing_to_end_of_track in non-playing state.") 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -186,11 +233,12 @@ impl PlayerState { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     fn paused_to_playing(&mut self) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         use self::PlayerState::*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         match ::std::mem::replace(self, Invalid) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            Paused { decoder, end_of_track, track_id } => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Paused { track_id, decoder, end_of_track, normalisation_factor } => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 *self = Playing { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    track_id: track_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     decoder: decoder, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     end_of_track: end_of_track, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    track_id: track_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    normalisation_factor: normalisation_factor, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             _ => panic!("invalid state"), 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -200,11 +248,12 @@ impl PlayerState { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     fn playing_to_paused(&mut self) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         use self::PlayerState::*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         match ::std::mem::replace(self, Invalid) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            Playing { decoder, end_of_track, track_id } => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Playing { track_id, decoder, end_of_track, normalisation_factor } => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 *self = Paused { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    track_id: track_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     decoder: decoder, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     end_of_track: end_of_track, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    track_id: track_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    normalisation_factor: normalisation_factor, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             _ => panic!("invalid state"), 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -248,14 +297,17 @@ impl PlayerInternal { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if self.sink_running { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let packet = if let PlayerState::Playing { ref mut decoder, .. } = self.state { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                let mut current_normalisation_factor: f32 = 1.0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                let packet = if let PlayerState::Playing { ref mut decoder, normalisation_factor, .. } = self.state { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    current_normalisation_factor = normalisation_factor; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     Some(decoder.next_packet().expect("Vorbis error")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if let Some(packet) = packet { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    self.handle_packet(packet); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    self.handle_packet(packet, current_normalisation_factor); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -279,13 +331,19 @@ impl PlayerInternal { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self.sink_running = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    fn handle_packet(&mut self, packet: Option<VorbisPacket>) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fn handle_packet(&mut self, packet: Option<VorbisPacket>, normalisation_factor: f32) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         match packet { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             Some(mut packet) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if let Some(ref editor) = self.audio_filter { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     editor.modify_stream(&mut packet.data_mut()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if self.config.normalisation && normalisation_factor != 1.0 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    for x in packet.data_mut().iter_mut() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        *x = (*x as f32 * normalisation_factor) as i16; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if let Err(err) = self.sink.write(&packet.data()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     error!("Could not write audio: {}", err); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     self.stop_sink(); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -308,7 +366,7 @@ impl PlayerInternal { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 match self.load_track(track_id, position as i64) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    Some(decoder) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    Some((decoder, normalisation_factor)) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         if play { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             match self.state { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 PlayerState::Playing { track_id: old_track_id, ..} 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -326,12 +384,14 @@ impl PlayerInternal { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 track_id: track_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 decoder: decoder, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 end_of_track: end_of_track, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                normalisation_factor: normalisation_factor, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             self.state = PlayerState::Paused { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 track_id: track_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 decoder: decoder, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 end_of_track: end_of_track, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                normalisation_factor: normalisation_factor, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             match self.state { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 PlayerState::Playing { track_id: old_track_id, ..} 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -422,7 +482,7 @@ impl PlayerInternal { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    fn load_track(&self, track_id: SpotifyId, position: i64) -> Option<Decoder> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fn load_track(&self, track_id: SpotifyId, position: i64) -> Option<(Decoder, f32)> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let track = Track::get(&self.session, track_id).wait().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         info!("Loading track \"{}\"", track.name); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -452,7 +512,18 @@ impl PlayerInternal { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let key = self.session.audio_key().request(track.id, file_id).wait().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let encrypted_file = AudioFile::open(&self.session, file_id).wait().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let audio_file = Subfile::new(AudioDecrypt::new(key, encrypted_file), 0xa7); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let mut decrypted_file = AudioDecrypt::new(key, encrypted_file); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let normalisation_factor = match NormalisationData::parse_from_file(&mut decrypted_file) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Ok(normalisation_data) => NormalisationData::get_factor(&self.config, normalisation_data), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Err(_) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                warn!("Unable to extract normalisation data, using default value."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                1.0 as f32 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let audio_file = Subfile::new(decrypted_file, 0xa7); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let mut decoder = VorbisDecoder::new(audio_file).unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -463,7 +534,7 @@ impl PlayerInternal { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         info!("Track \"{}\" loaded", track.name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        Some(decoder) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Some((decoder, normalisation_factor)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 |