micd: scale sound volume with ambient noise level (#26399)
	
		
	
				
					
				
			* test changing sound volume * test changing sound volume * create system/hardware/pc/hardware.h * implement Hardware::set_volume using pactl * soundd: use Hardware::set_volume * add sounddevice dependency * sounddevice example * simple micd * cleanup * remove this * fix process config * add to release files * hardware: get sound input device * no more offroad * debug * calculate volume from all measurements since last update * use microphone noise level to update sound volume * fix scale * mute microphone during alerts * log raw noise level * hardware: reduce tici min volume * improve scale * add package * clear measurements on muted * change default to min volume and respond quicker * fixes Co-authored-by: Shane Smiskol <shane@smiskol.com> * logarithmic scaling * fix * respond quicker * fixes * tweak scaling * specify default device * Revert "hardware: get sound input device" This reverts commit 50f594f7a3bab005023482bc793147a8c8dae5d7. * tuning * forgot to update submaster * tuning * don't mute microphone, and clip measurement * remove submaster * fixes * tuning * implement Hardware::set_volume using pactl * Revert "test changing sound volume" This reverts commit 4bbd870746ec86d1c9871a6175def96cf7f751a6. * draft * draft * calculate sound pressure level in dB * fix setting * faster filter * start at initial value * don't run command in background * pactl: use default sink * use sound pressure db * tuning * bump up max volume threshold * update filter slower * fix divide by zero * bump cereal Co-authored-by: Shane Smiskol <shane@smiskol.com>pull/214/head
							parent
							
								
									3d2f6c1dc0
								
							
						
					
					
						commit
						108ff15f5d
					
				
				 9 changed files with 98 additions and 15 deletions
			
			
		| @ -1 +1 @@ | ||||
| Subproject commit 19a0c46b71150a8dabc5644eb24f261feee45b9c | ||||
| Subproject commit dbc9846ac9c9e735ee2f4a281ce079cfff7ea285 | ||||
| @ -0,0 +1,67 @@ | ||||
| #!/usr/bin/env python3 | ||||
| import sounddevice as sd | ||||
| import numpy as np | ||||
| 
 | ||||
| from cereal import messaging | ||||
| from common.filter_simple import FirstOrderFilter | ||||
| from common.realtime import Ratekeeper | ||||
| from system.swaglog import cloudlog | ||||
| 
 | ||||
| RATE = 10 | ||||
| DT_MIC = 1. / RATE | ||||
| REFERENCE_SPL = 2 * 10 ** -5  # newtons/m^2 | ||||
| 
 | ||||
| 
 | ||||
| class Mic: | ||||
|   def __init__(self, pm): | ||||
|     self.pm = pm | ||||
|     self.rk = Ratekeeper(RATE) | ||||
| 
 | ||||
|     self.measurements = np.empty(0) | ||||
|     self.spl_filter = FirstOrderFilter(0, 4, DT_MIC, initialized=False) | ||||
| 
 | ||||
|   def update(self): | ||||
|     # self.measurements contains amplitudes from -1 to 1 which we use to | ||||
|     # calculate an uncalibrated sound pressure level | ||||
|     if len(self.measurements) > 0: | ||||
|       # https://www.engineeringtoolbox.com/sound-pressure-d_711.html | ||||
|       sound_pressure = np.sqrt(np.mean(self.measurements ** 2))  # RMS of amplitudes | ||||
|       sound_pressure_level = 20 * np.log10(sound_pressure / REFERENCE_SPL) if sound_pressure > 0 else 0  # dB | ||||
|       self.spl_filter.update(sound_pressure_level) | ||||
|     else: | ||||
|       sound_pressure = 0 | ||||
|       sound_pressure_level = 0 | ||||
| 
 | ||||
|     self.measurements = np.empty(0) | ||||
| 
 | ||||
|     msg = messaging.new_message('microphone') | ||||
|     msg.microphone.soundPressure = float(sound_pressure) | ||||
|     msg.microphone.soundPressureDb = float(sound_pressure_level) | ||||
|     msg.microphone.filteredSoundPressureDb = float(self.spl_filter.x) | ||||
| 
 | ||||
|     self.pm.send('microphone', msg) | ||||
|     self.rk.keep_time() | ||||
| 
 | ||||
|   def callback(self, indata, frames, time, status): | ||||
|     self.measurements = np.concatenate((self.measurements, indata[:, 0])) | ||||
| 
 | ||||
|   def micd_thread(self, device=None): | ||||
|     if device is None: | ||||
|       device = "sysdefault" | ||||
| 
 | ||||
|     with sd.InputStream(device=device, channels=1, samplerate=44100, callback=self.callback) as stream: | ||||
|       cloudlog.info(f"micd stream started: {stream.samplerate=} {stream.channels=} {stream.dtype=} {stream.device=}") | ||||
|       while True: | ||||
|         self.update() | ||||
| 
 | ||||
| 
 | ||||
| def main(pm=None, sm=None): | ||||
|   if pm is None: | ||||
|     pm = messaging.PubMaster(['microphone']) | ||||
| 
 | ||||
|   mic = Mic(pm) | ||||
|   mic.micd_thread() | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|   main() | ||||
					Loading…
					
					
				
		Reference in new issue