# include "selfdrive/ui/soundd/sound.h"
# include <cmath>
# include <QAudio>
# include <QAudioDeviceInfo>
# include <QDebug>
# include "cereal/messaging/messaging.h"
# include "common/util.h"
// TODO: detect when we can't play sounds
// TODO: detect when we can't display the UI
Sound : : Sound ( QObject * parent ) : sm ( { " carState " , " controlsState " , " deviceState " } ) {
qInfo ( ) < < " default audio device: " < < QAudioDeviceInfo : : defaultOutputDevice ( ) . deviceName ( ) ;
for ( auto & [ alert , fn , loops ] : sound_list ) {
QSoundEffect * s = new QSoundEffect ( this ) ;
QObject : : connect ( s , & QSoundEffect : : statusChanged , [ = ] ( ) {
assert ( s - > status ( ) ! = QSoundEffect : : Error ) ;
} ) ;
s - > setVolume ( Hardware : : MIN_VOLUME ) ;
s - > setSource ( QUrl : : fromLocalFile ( " ../../assets/sounds/ " + fn ) ) ;
sounds [ alert ] = { s , loops } ;
}
QTimer * timer = new QTimer ( this ) ;
QObject : : connect ( timer , & QTimer : : timeout , this , & Sound : : update ) ;
timer - > start ( 1000 / UI_FREQ ) ;
} ;
void Sound : : update ( ) {
const bool started_prev = sm [ " deviceState " ] . getDeviceState ( ) . getStarted ( ) ;
sm . update ( 0 ) ;
const bool started = sm [ " deviceState " ] . getDeviceState ( ) . getStarted ( ) ;
if ( started & & ! started_prev ) {
started_frame = sm . frame ;
}
// no sounds while offroad
// also no sounds if nothing is alive in case thermald crashes while offroad
const bool crashed = ( sm . frame - std : : max ( sm . rcv_frame ( " deviceState " ) , sm . rcv_frame ( " controlsState " ) ) ) > 10 * UI_FREQ ;
if ( ! started | | crashed ) {
setAlert ( { } ) ;
return ;
}
// scale volume with speed
if ( sm . updated ( " carState " ) ) {
float volume = util : : map_val ( sm [ " carState " ] . getCarState ( ) . getVEgo ( ) , 11.f , 20.f , 0.f , 1.0f ) ;
volume = QAudio : : convertVolume ( volume , QAudio : : LogarithmicVolumeScale , QAudio : : LinearVolumeScale ) ;
volume = util : : map_val ( volume , 0.f , 1.f , Hardware : : MIN_VOLUME , Hardware : : MAX_VOLUME ) ;
for ( auto & [ s , loops ] : sounds ) {
s - > setVolume ( std : : round ( 100 * volume ) / 100 ) ;
}
}
setAlert ( Alert : : get ( sm , started_frame ) ) ;
}
void Sound : : setAlert ( const Alert & alert ) {
if ( ! current_alert . equal ( alert ) ) {
current_alert = alert ;
// stop sounds
for ( auto & [ s , loops ] : sounds ) {
// Only stop repeating sounds
if ( s - > loopsRemaining ( ) > 1 | | s - > loopsRemaining ( ) = = QSoundEffect : : Infinite ) {
s - > stop ( ) ;
}
}
// play sound
if ( alert . sound ! = AudibleAlert : : NONE ) {
auto & [ s , loops ] = sounds [ alert . sound ] ;
s - > setLoopCount ( loops ) ;
s - > play ( ) ;
}
}
}