#include "selfdrive/ui/soundd/sound.h" #include #include #include #include #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({"controlsState", "deviceState", "microphone"}) { 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->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 using ambient noise level if (sm.updated("microphone")) { float volume = util::map_val(sm["microphone"].getMicrophone().getFilteredSoundPressureWeightedDb(), 30.f, 56.f, 0.f, 1.f); volume = QAudio::convertVolume(volume, QAudio::LogarithmicVolumeScale, QAudio::LinearVolumeScale); Hardware::set_volume(volume); } 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(); } } }