Skip to content
Snippets Groups Projects
AudioOutput.cc 6.14 KiB
Newer Older
  • Learn to ignore specific revisions
  • /****************************************************************************
     *
     *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
     *
     * QGroundControl is licensed according to the terms in the file
     * COPYING.md in the root of the source code directory.
     *
     ****************************************************************************/
    
    Lorenz Meier's avatar
    Lorenz Meier committed
    
    #include <QApplication>
    #include <QDebug>
    
    #include <QRegularExpression>
    
    Lorenz Meier's avatar
    Lorenz Meier committed
    
    
    #include "AudioOutput.h"
    
    #include "QGCApplication.h"
    #include "QGC.h"
    
    #include "SettingsManager.h"
    
    Lorenz Meier's avatar
    Lorenz Meier committed
    
    
    AudioOutput::AudioOutput(QGCApplication* app, QGCToolbox* toolbox)
    
        , _tts(new QTextToSpeech(this))
    
    Lorenz Meier's avatar
    Lorenz Meier committed
    {
    
        connect(_tts, &QTextToSpeech::stateChanged, this, &AudioOutput::_stateChanged);
    
    Lorenz Meier's avatar
    Lorenz Meier committed
    }
    
    
    bool AudioOutput::say(const QString& inText)
    
    Lorenz Meier's avatar
    Lorenz Meier committed
    {
    
        bool muted = qgcApp()->toolbox()->settingsManager()->appSettings()->audioMuted()->rawValue().toBool();
    
        muted |= qgcApp()->runningUnitTests();
    
    Don Gagne's avatar
    Don Gagne committed
        if (!muted && !qgcApp()->runningUnitTests()) {
    
            QString text = fixTextMessageForAudio(inText);
            if(_tts->state() == QTextToSpeech::Speaking) {
                if(!_texts.contains(text)) {
                    //-- Some arbitrary limit
                    if(_texts.size() > 20) {
                        _texts.removeFirst();
                    }
                    _texts.append(text);
                }
            } else {
                _tts->say(text);
    
    dogmaphobic's avatar
    dogmaphobic committed
            }
    
        return true;
    
    Lorenz Meier's avatar
    Lorenz Meier committed
    }
    
    void AudioOutput::_stateChanged(QTextToSpeech::State state)
    
    {
        if(state == QTextToSpeech::Ready) {
            if(_texts.size()) {
                QString text = _texts.first();
                _texts.removeFirst();
                _tts->say(text);
            }
        }
    }
    
    
    bool AudioOutput::getMillisecondString(const QString& string, QString& match, int& number) {
    
        static QRegularExpression re("([0-9]+ms)");
        QRegularExpressionMatchIterator i = re.globalMatch(string);
        while (i.hasNext()) {
            QRegularExpressionMatch qmatch = i.next();
            if (qmatch.hasMatch()) {
                match = qmatch.captured(0);
                number = qmatch.captured(0).replace("ms", "").toInt();
                return true;
            }
        }
        return false;
    }
    
    
    QString AudioOutput::fixTextMessageForAudio(const QString& string) {
    
        QString match;
        QString newNumber;
        QString result = string;
        //-- Look for codified terms
        if(result.contains("ERR ", Qt::CaseInsensitive)) {
            result.replace("ERR ", "error ", Qt::CaseInsensitive);
        }
        if(result.contains("ERR:", Qt::CaseInsensitive)) {
            result.replace("ERR:", "error.", Qt::CaseInsensitive);
        }
        if(result.contains("POSCTL", Qt::CaseInsensitive)) {
            result.replace("POSCTL", "Position Control", Qt::CaseInsensitive);
        }
        if(result.contains("ALTCTL", Qt::CaseInsensitive)) {
            result.replace("ALTCTL", "Altitude Control", Qt::CaseInsensitive);
        }
        if(result.contains("AUTO_RTL", Qt::CaseInsensitive)) {
            result.replace("AUTO_RTL", "auto Return To Launch", Qt::CaseInsensitive);
        } else if(result.contains("RTL", Qt::CaseInsensitive)) {
            result.replace("RTL", "Return To Launch", Qt::CaseInsensitive);
        }
        if(result.contains("ACCEL ", Qt::CaseInsensitive)) {
            result.replace("ACCEL ", "accelerometer ", Qt::CaseInsensitive);
        }
        if(result.contains("RC_MAP_MODE_SW", Qt::CaseInsensitive)) {
            result.replace("RC_MAP_MODE_SW", "RC mode switch", Qt::CaseInsensitive);
        }
        if(result.contains("REJ.", Qt::CaseInsensitive)) {
            result.replace("REJ.", "Rejected", Qt::CaseInsensitive);
        }
        if(result.contains("WP", Qt::CaseInsensitive)) {
            result.replace("WP", "way point", Qt::CaseInsensitive);
        }
        if(result.contains("CMD", Qt::CaseInsensitive)) {
            result.replace("CMD", "command", Qt::CaseInsensitive);
        }
        if(result.contains("COMPID", Qt::CaseInsensitive)) {
            result.replace("COMPID", "component eye dee", Qt::CaseInsensitive);
        }
        if(result.contains(" params ", Qt::CaseInsensitive)) {
            result.replace(" params ", " parameters ", Qt::CaseInsensitive);
        }
        if(result.contains(" id ", Qt::CaseInsensitive)) {
            result.replace(" id ", " eye dee ", Qt::CaseInsensitive);
        }
        if(result.contains(" ADSB ", Qt::CaseInsensitive)) {
            result.replace(" ADSB ", " Hey Dee Ess Bee ", Qt::CaseInsensitive);
        }
    
    
        // Convert negative numbers
        QRegularExpression re(QStringLiteral("(-)[0-9]*\\.?[0-9]"));
        QRegularExpressionMatch reMatch = re.match(result);
        while (reMatch.hasMatch()) {
            if (!reMatch.captured(1).isNull()) {
                // There is a negative prefix
                qDebug() << "negative" << reMatch.captured(1) << reMatch.capturedStart(1) << reMatch.capturedEnd(1);
                result.replace(reMatch.capturedStart(1), reMatch.capturedEnd(1) - reMatch.capturedStart(1), tr(" negative "));
                qDebug() << result;
            }
            reMatch = re.match(result);
        }
    
        // Convert meter postfix after real number
        re.setPattern(QStringLiteral("[0-9]*\\.?[0-9]\\s?(m)([^A-Za-z]|$)"));
        reMatch = re.match(result);
        while (reMatch.hasMatch()) {
            if (!reMatch.captured(1).isNull()) {
                // There is a meter postfix
                qDebug() << "meters" << reMatch.captured(1) << reMatch.capturedStart(1) << reMatch.capturedEnd(1);
                result.replace(reMatch.capturedStart(1), reMatch.capturedEnd(1) - reMatch.capturedStart(1), tr(" meters"));
                qDebug() << result;
            }
            reMatch = re.match(result);
        }
    
    
        int number;
        if(getMillisecondString(string, match, number) && number > 1000) {
            if(number < 60000) {
                int seconds = number / 1000;
                newNumber = QString("%1 second%2").arg(seconds).arg(seconds > 1 ? "s" : "");
            } else {
                int minutes = number / 60000;
                int seconds = (number - (minutes * 60000)) / 1000;
                if (!seconds) {
                    newNumber = QString("%1 minute%2").arg(minutes).arg(minutes > 1 ? "s" : "");
                } else {
                    newNumber = QString("%1 minute%2 and %3 second%4").arg(minutes).arg(minutes > 1 ? "s" : "").arg(seconds).arg(seconds > 1 ? "s" : "");
                }
            }
            result.replace(match, newNumber);
        }
        return result;
    }