#include "setdefault.h" #include #include #include #include #include #include #include #include #include #include #include #include static const QRegularExpression _splitter("[() \t-]"); static int countMatches(const QStringList &find, const QString &haystack); static void setDefaultSink(const QString &bestCardId, const QStringList &words); void setDefaultOutput(const QString &name) { auto *ctx = PulseAudioQt::Context::instance(); const QStringList words = name.toLower().split(_splitter, QString::SkipEmptyParts); int bestNum = -1000; int bestProfileIdx = -1; PulseAudioQt::Card *bestCard = nullptr; for (auto *card : ctx->cards()) { int i = -1; for (auto *profile : card->profiles()) { ++i; if (profile->sinks() == 0) continue; int matches = countMatches(words, profile->description()); // Prefer Duplex if (profile->description().contains(QLatin1String("Duplex"))) { matches += 2; } // Penaltize unavailable profiles if (profile->availability() != PulseAudioQt::Profile::Available) { matches -= 11; } qDebug() << matches << "Profile" << profile->description(); if (matches > bestNum) { bestNum = matches; bestCard = card; bestProfileIdx = i; } } } if (bestCard == nullptr) { qDebug() << "No match found"; qApp->quit(); return; } QString bestCardId = bestCard->name(); if (bestCard->activeProfileIndex() == bestProfileIdx) { setDefaultSink(bestCardId, words); return; } QObject::connect(bestCard, &PulseAudioQt::Card::activeProfileIndexChanged, [bestCardId, words]() { QTimer::singleShot(10, [bestCardId, words]() { setDefaultSink(bestCardId, words); }); }); qDebug() << "Switching card's profile..."; bestCard->setActiveProfileIndex(bestProfileIdx); // Wait for switch - add safety timeout QTimer::singleShot(1000, []() { qDebug() << "WARNING - timeout reached, exiting..."; qApp->exit(1); }); } static void setDefaultSink(const QString &bestCardId, const QStringList &words) { auto *ctx = PulseAudioQt::Context::instance(); int idx = -1; int bestCardIdx = -1; for (auto *card : ctx->cards()) { idx++; if (card->name() == bestCardId) { bestCardIdx = idx; break; } } int bestNum = -1000; int bestPortIdx = -1; PulseAudioQt::Sink *bestSink = nullptr; for (auto *sink : ctx->sinks()) { if (bestCardIdx != -1 && sink->cardIndex() != bestCardIdx) continue; // Not sink of card int sinkMatchVal = countMatches(words, sink->description()); int pi = -1; for (auto *port : sink->ports()) { pi++; int portMatchVal = sinkMatchVal + countMatches(words, port->description()); qDebug() << portMatchVal << "SinkPort" << sink->description() << port->description(); if (portMatchVal > bestNum) { bestNum = portMatchVal; bestSink = sink; bestPortIdx = pi; } } } if (bestSink == nullptr) { qDebug() << "No match found"; qApp->exit(1); } if (bestPortIdx != -1 && bestSink->activePortIndex() != bestPortIdx) { qDebug() << "Selecting port"; bestSink->setActivePortIndex(bestPortIdx); } QTimer::singleShot(10, [bestSink]() { qDebug() << "Unmuting sink and selecting as default:" << bestSink->description(); bestSink->setDefault(true); bestSink->setMuted(false); bestSink->setVolume(65535); }); QTimer::singleShot(20, []() { qDebug() << "Done"; qApp->quit(); }); } static int countMatches(const QStringList &find, const QString &haystack) { int ret = 0; QStringList hayList = haystack.toLower().split(_splitter, QString::SkipEmptyParts); qDebug() << "Comparing" << hayList; // Look for all the words we want to find for (const QString &word : find) { if (hayList.contains(word)) { ret += 10; } } if (ret == 0) { // If not a single word matched, bail out return -1000; } // Penaltize additonal words in haystack that we don't want to find for (const QString &word : hayList) { if (!find.contains(word)) { ret -= 1; } } return ret; }