Skip to content

Commit

Permalink
AudioOutput Formatting and Dependencies (#11366)
Browse files Browse the repository at this point in the history
  • Loading branch information
HTRamsey authored Apr 24, 2024
1 parent f143bcc commit d785993
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 81 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ find_package(Qt6
Sql
Svg
Test
TextToSpeech
Widgets
Xml
OPTIONAL_COMPONENTS
Expand Down
136 changes: 68 additions & 68 deletions src/Audio/AudioOutput.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

#define MAX_TEXT_QUEUE_SIZE 20U

QGC_LOGGING_CATEGORY( AudioOutputLog, "qgc.audio.audiooutput" );
QGC_LOGGING_CATEGORY(AudioOutputLog, "qgc.audio.audiooutput");
// qt.speech.tts.flite
// qt.speech.tts.android

Expand All @@ -38,36 +38,36 @@ const QHash<QString, QString> AudioOutput::s_textHash = {
{ "PITOT", "pee toe" },
};

Q_APPLICATION_STATIC( AudioOutput, s_audioOutput );
Q_APPLICATION_STATIC(AudioOutput, s_audioOutput);

AudioOutput* AudioOutput::instance()
{
return s_audioOutput();
}

AudioOutput::AudioOutput( QObject* parent )
: QTextToSpeech( QStringLiteral("none"), parent )
AudioOutput::AudioOutput(QObject* parent)
: QTextToSpeech(QStringLiteral("none"), parent)
{
( void ) connect( this, &QTextToSpeech::stateChanged, []( QTextToSpeech::State state ) {
qCDebug( AudioOutputLog ) << Q_FUNC_INFO << "State:" << state;
(void) connect(this, &QTextToSpeech::stateChanged, [](QTextToSpeech::State state) {
qCDebug(AudioOutputLog) << Q_FUNC_INFO << "State:" << state;
});
( void ) connect( this, &QTextToSpeech::errorOccurred, []( QTextToSpeech::ErrorReason reason, const QString &errorString ) {
qCDebug( AudioOutputLog ) << Q_FUNC_INFO << "Error: (" << reason << ") " << errorString;
(void) connect(this, &QTextToSpeech::errorOccurred, [](QTextToSpeech::ErrorReason reason, const QString &errorString) {
qCDebug(AudioOutputLog) << Q_FUNC_INFO << "Error: (" << reason << ") " << errorString;
});
( void ) connect( this, &QTextToSpeech::volumeChanged, []( double volume ) {
qCDebug( AudioOutputLog ) << Q_FUNC_INFO << "volume:" << volume;
(void) connect(this, &QTextToSpeech::volumeChanged, [](double volume) {
qCDebug(AudioOutputLog) << Q_FUNC_INFO << "volume:" << volume;
});

if ( !QTextToSpeech::availableEngines().isEmpty() ) {
if ( setEngine( QString() ) ) { // Autoselect engine by priority
qCDebug( AudioOutputLog ) << Q_FUNC_INFO << "engine:" << engine();
if ( availableLocales().contains( QLocale( "en_US" ) ) ) {
setLocale( QLocale( "en_US" ) );
if (!QTextToSpeech::availableEngines().isEmpty()) {
if (setEngine(QString())) { // Autoselect engine by priority
qCDebug(AudioOutputLog) << Q_FUNC_INFO << "engine:" << engine();
if (availableLocales().contains(QLocale("en_US"))) {
setLocale(QLocale("en_US"));
}

( void ) connect( this, &AudioOutput::mutedChanged, [ this ]( bool muted ) {
qCDebug( AudioOutputLog ) << Q_FUNC_INFO << "muted:" << muted;
( void ) QMetaObject::invokeMethod( this, "setVolume", Qt::AutoConnection, muted ? 0. : 1. );
(void) connect(this, &AudioOutput::mutedChanged, [ this ](bool muted) {
qCDebug(AudioOutputLog) << Q_FUNC_INFO << "muted:" << muted;
(void) QMetaObject::invokeMethod(this, "setVolume", Qt::AutoConnection, muted ? 0. : 1.);
});
}
}
Expand All @@ -78,52 +78,52 @@ bool AudioOutput::isMuted() const
return m_muted;
}

void AudioOutput::setMuted( bool enable )
void AudioOutput::setMuted(bool enable)
{
if ( enable != isMuted() ) {
if (enable != isMuted()) {
m_muted = enable;
emit mutedChanged( m_muted );
emit mutedChanged(m_muted);
}
}

void AudioOutput::read( const QString& text, AudioOutput::TextMods textMods )
void AudioOutput::read(const QString& text, AudioOutput::TextMods textMods)
{
if( m_muted ) {
if(m_muted) {
return;
}

if ( !engineCapabilities().testFlag( QTextToSpeech::Capability::Speak ) ) {
qCWarning( AudioOutputLog ) << Q_FUNC_INFO << "Speech Not Supported:" << text;
if (!engineCapabilities().testFlag(QTextToSpeech::Capability::Speak)) {
qCWarning(AudioOutputLog) << Q_FUNC_INFO << "Speech Not Supported:" << text;
return;
}

if ( m_textQueueSize > MAX_TEXT_QUEUE_SIZE ) {
( void ) QMetaObject::invokeMethod( this, "stop", Qt::AutoConnection, QTextToSpeech::BoundaryHint::Default );
if (m_textQueueSize > MAX_TEXT_QUEUE_SIZE) {
(void) QMetaObject::invokeMethod(this, "stop", Qt::AutoConnection, QTextToSpeech::BoundaryHint::Default);
}

QString outText = AudioOutput::fixTextMessageForAudio( text );
QString outText = AudioOutput::fixTextMessageForAudio(text);

if ( textMods.testFlag( AudioOutput::TextMod::Translate ) ) {
outText = QCoreApplication::translate("AudioOutput", outText.toStdString().c_str() );
if (textMods.testFlag(AudioOutput::TextMod::Translate)) {
outText = QCoreApplication::translate("AudioOutput", outText.toStdString().c_str());
}

qsizetype index = -1;
( void ) QMetaObject::invokeMethod( this, "enqueue", Qt::AutoConnection, qReturnArg( m_textQueueSize ), outText );
if ( index != -1 ) {
(void) QMetaObject::invokeMethod(this, "enqueue", Qt::AutoConnection, qReturnArg(m_textQueueSize), outText);
if (index != -1) {
m_textQueueSize = index;
}
}

bool AudioOutput::getMillisecondString( const QString& string, QString& match, int& number )
bool AudioOutput::getMillisecondString(const QString& string, QString& match, int& number)
{
static const QRegularExpression regexp( "([0-9]+ms)" );
static const QRegularExpression regexp("([0-9]+ms)");

bool result = false;

for ( const QRegularExpressionMatch &tempMatch : regexp.globalMatch( string ) ) {
if ( tempMatch.hasMatch() ) {
match = tempMatch.captured( 0 );
number = tempMatch.captured( 0 ).replace( "ms", "" ).toInt();
for (const QRegularExpressionMatch &tempMatch : regexp.globalMatch(string)) {
if (tempMatch.hasMatch()) {
match = tempMatch.captured(0);
number = tempMatch.captured(0).replace("ms", "").toInt();
result = true;
break;
}
Expand All @@ -132,63 +132,63 @@ bool AudioOutput::getMillisecondString( const QString& string, QString& match, i
return result;
}

QString AudioOutput::fixTextMessageForAudio( const QString& string )
QString AudioOutput::fixTextMessageForAudio(const QString& string)
{
QString result = string;

for ( const QString& word: string.split( ' ', Qt::SkipEmptyParts ) ) {
if ( s_textHash.contains( word.toUpper() ) ) {
result.replace( word, s_textHash.value( word.toUpper() ) );
for (const QString& word: string.split(' ', Qt::SkipEmptyParts)) {
if (s_textHash.contains(word.toUpper())) {
result.replace(word, s_textHash.value(word.toUpper()));
}
}

// Convert negative numbers
static const QRegularExpression negNumRegex( QStringLiteral( "(-)[0-9]*\\.?[0-9]" ) );
QRegularExpressionMatch negNumRegexMatch = negNumRegex.match( result );
while ( negNumRegexMatch.hasMatch() ) {
if ( !negNumRegexMatch.captured( 1 ).isNull() ) {
result.replace( negNumRegexMatch.capturedStart( 1 ), negNumRegexMatch.capturedEnd( 1 ) - negNumRegexMatch.capturedStart( 1 ), tr(" negative "));
static const QRegularExpression negNumRegex(QStringLiteral("(-)[0-9]*\\.?[0-9]"));
QRegularExpressionMatch negNumRegexMatch = negNumRegex.match(result);
while (negNumRegexMatch.hasMatch()) {
if (!negNumRegexMatch.captured(1).isNull()) {
result.replace(negNumRegexMatch.capturedStart(1), negNumRegexMatch.capturedEnd(1) - negNumRegexMatch.capturedStart(1), tr(" negative "));
}
negNumRegexMatch = negNumRegex.match( result );
negNumRegexMatch = negNumRegex.match(result);
}

// Convert real number with decimal point
static const QRegularExpression realNumRegex( QStringLiteral( "([0-9]+)(\\.)([0-9]+)" ) );
QRegularExpressionMatch realNumRegexMatch = realNumRegex.match( result );
while ( realNumRegexMatch.hasMatch() ) {
if ( !realNumRegexMatch.captured( 2 ).isNull() ) {
result.replace(realNumRegexMatch.capturedStart( 2 ), realNumRegexMatch.capturedEnd( 2 ) - realNumRegexMatch.capturedStart( 2 ), tr( " point " ));
static const QRegularExpression realNumRegex(QStringLiteral("([0-9]+)(\\.)([0-9]+)"));
QRegularExpressionMatch realNumRegexMatch = realNumRegex.match(result);
while (realNumRegexMatch.hasMatch()) {
if (!realNumRegexMatch.captured(2).isNull()) {
result.replace(realNumRegexMatch.capturedStart(2), realNumRegexMatch.capturedEnd(2) - realNumRegexMatch.capturedStart(2), tr(" point "));
}
realNumRegexMatch = realNumRegex.match( result );
realNumRegexMatch = realNumRegex.match(result);
}

// Convert meter postfix after real number
static const QRegularExpression realNumMeterRegex( QStringLiteral( "[0-9]*\\.?[0-9]\\s?(m)([^A-Za-z]|$)" ) );
QRegularExpressionMatch realNumMeterRegexMatch = realNumMeterRegex.match( result );
while ( realNumMeterRegexMatch.hasMatch() ) {
if ( !realNumMeterRegexMatch.captured( 1 ).isNull( ) ) {
result.replace( realNumMeterRegexMatch.capturedStart( 1 ), realNumMeterRegexMatch.capturedEnd( 1 ) - realNumMeterRegexMatch.capturedStart( 1 ), tr(" meters"));
static const QRegularExpression realNumMeterRegex(QStringLiteral("[0-9]*\\.?[0-9]\\s?(m)([^A-Za-z]|$)"));
QRegularExpressionMatch realNumMeterRegexMatch = realNumMeterRegex.match(result);
while (realNumMeterRegexMatch.hasMatch()) {
if (!realNumMeterRegexMatch.captured(1).isNull()) {
result.replace(realNumMeterRegexMatch.capturedStart(1), realNumMeterRegexMatch.capturedEnd(1) - realNumMeterRegexMatch.capturedStart(1), tr(" meters"));
}
realNumMeterRegexMatch = realNumMeterRegex.match( result );
realNumMeterRegexMatch = realNumMeterRegex.match(result);
}

QString match;
int number;
if ( getMillisecondString( string, match, number ) && ( number > 1000 ) ) {
if (getMillisecondString(string, match, number) && (number > 1000)) {
QString newNumber;
if ( number < 60000 ) {
if (number < 60000) {
const int seconds = number / 1000;
newNumber = QString( "%1 second%2" ).arg( seconds ).arg( seconds > 1 ? "s" : "" );
newNumber = QString("%1 second%2").arg(seconds).arg(seconds > 1 ? "s" : "");
} else {
const int minutes = number / 60000;
const int seconds = ( number - ( minutes * 60000 ) ) / 1000;
if ( !seconds ) {
newNumber = QString( "%1 minute%2" ).arg( minutes ).arg( minutes > 1 ? "s" : "" );
const 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" : "" );
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 );
result.replace(match, newNumber);
}

return result;
Expand Down
24 changes: 12 additions & 12 deletions src/Audio/AudioOutput.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,41 +13,41 @@
#include <QtQmlIntegration/QtQmlIntegration>
#include <QtTextToSpeech/QTextToSpeech>

Q_DECLARE_LOGGING_CATEGORY( AudioOutputLog )
Q_DECLARE_LOGGING_CATEGORY(AudioOutputLog)

class AudioOutput : public QTextToSpeech
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE( "" )
QML_UNCREATABLE("")

Q_PROPERTY( bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged )
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)

public:
enum class TextMod {
None = 0,
Translate = 1 << 0,
};
Q_DECLARE_FLAGS( TextMods, TextMod )
Q_FLAG( TextMod )
Q_DECLARE_FLAGS(TextMods, TextMod)
Q_FLAG(TextMod)

explicit AudioOutput( QObject* parent = nullptr );
explicit AudioOutput(QObject* parent = nullptr);

bool isMuted() const;
void setMuted( bool enable );
void setMuted(bool enable);

void read( const QString& text, AudioOutput::TextMods textMods = TextMod::None );
void read(const QString& text, AudioOutput::TextMods textMods = TextMod::None);

static AudioOutput* instance();
static bool getMillisecondString( const QString& string, QString& match, int& number );
static QString fixTextMessageForAudio( const QString& string );
static bool getMillisecondString(const QString& string, QString& match, int& number);
static QString fixTextMessageForAudio(const QString& string);

signals:
void mutedChanged( bool muted );
void mutedChanged(bool muted);

private:
qsizetype m_textQueueSize = 0;
bool m_muted = false;
static const QHash<QString, QString> s_textHash;
};
Q_DECLARE_OPERATORS_FOR_FLAGS( AudioOutput::TextMods )
Q_DECLARE_OPERATORS_FOR_FLAGS(AudioOutput::TextMods)
6 changes: 5 additions & 1 deletion tools/setup/ubuntu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ sudo DEBIAN_FRONTEND=noninteractive apt-get -y --quiet --no-install-recommends i
libxcb-xinerama0 \
libxkbcommon-x11-0 \
libxcb-cursor0 \
libdrm-dev
libdrm-dev \
libspeechd2 \
flite \
speech-dispatcher \
speech-dispatcher-flite

0 comments on commit d785993

Please sign in to comment.