openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

110 lines
3.6 KiB

#include <sys/resource.h>
#include <map>
#include <QApplication>
#include <QString>
#include <QSoundEffect>
#include "cereal/messaging/messaging.h"
#include "selfdrive/common/util.h"
#include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/ui.h"
// TODO: detect when we can't play sounds
// TODO: detect when we can't display the UI
class Sound : public QObject {
public:
explicit Sound(QObject *parent = 0) {
// TODO: merge again and add EQ in the amp config
const QString sound_asset_path = Hardware::TICI ? "../assets/sounds_tici/" : "../assets/sounds/";
std::tuple<AudibleAlert, QString, bool> sound_list[] = {
{AudibleAlert::CHIME_DISENGAGE, sound_asset_path + "disengaged.wav", false},
{AudibleAlert::CHIME_ENGAGE, sound_asset_path + "engaged.wav", false},
{AudibleAlert::CHIME_WARNING1, sound_asset_path + "warning_1.wav", false},
{AudibleAlert::CHIME_WARNING2, sound_asset_path + "warning_2.wav", false},
{AudibleAlert::CHIME_WARNING2_REPEAT, sound_asset_path + "warning_2.wav", true},
{AudibleAlert::CHIME_WARNING_REPEAT, sound_asset_path + "warning_repeat.wav", true},
{AudibleAlert::CHIME_ERROR, sound_asset_path + "error.wav", false},
{AudibleAlert::CHIME_PROMPT, sound_asset_path + "error.wav", false}
};
for (auto &[alert, fn, loops] : sound_list) {
sounds[alert].first.setSource(QUrl::fromLocalFile(fn));
sounds[alert].second = loops ? QSoundEffect::Infinite : 0;
QObject::connect(&sounds[alert].first, &QSoundEffect::statusChanged, this, &Sound::checkStatus);
}
sm = new SubMaster({"carState", "controlsState"});
QTimer *timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, this, &Sound::update);
timer->start();
};
~Sound() {
delete sm;
};
private slots:
void checkStatus() {
for (auto &[alert, kv] : sounds) {
assert(kv.first.status() != QSoundEffect::Error);
}
}
void update() {
sm->update(100);
if (sm->updated("carState")) {
// scale volume with speed
volume = util::map_val((*sm)["carState"].getCarState().getVEgo(), 0.f, 20.f,
Hardware::MIN_VOLUME, Hardware::MAX_VOLUME);
}
if (sm->updated("controlsState")) {
const cereal::ControlsState::Reader &cs = (*sm)["controlsState"].getControlsState();
setAlert({QString::fromStdString(cs.getAlertText1()),
QString::fromStdString(cs.getAlertText2()),
QString::fromStdString(cs.getAlertType()),
cs.getAlertSize(), cs.getAlertSound()});
} else if (sm->rcv_frame("controlsState") > 0 && (*sm)["controlsState"].getControlsState().getEnabled() &&
((nanos_since_boot() - sm->rcv_time("controlsState")) / 1e9 > CONTROLS_TIMEOUT)) {
setAlert(CONTROLS_UNRESPONSIVE_ALERT);
}
}
void setAlert(Alert a) {
if (!alert.equal(a)) {
alert = a;
// stop sounds
for (auto &kv : sounds) {
// Only stop repeating sounds
auto &[sound, loops] = kv.second;
if (sound.loopsRemaining() == QSoundEffect::Infinite) {
sound.stop();
}
}
// play sound
if (alert.sound != AudibleAlert::NONE) {
auto &[sound, loops] = sounds[alert.sound];
sound.setLoopCount(loops);
sound.setVolume(volume);
sound.play();
}
}
}
private:
Alert alert;
float volume = Hardware::MIN_VOLUME;
std::map<AudibleAlert, std::pair<QSoundEffect, int>> sounds;
SubMaster *sm;
};
int main(int argc, char **argv) {
setpriority(PRIO_PROCESS, 0, -20);
QApplication a(argc, argv);
Sound sound;
return a.exec();
}