summaryrefslogblamecommitdiffstats
path: root/src/setdefault.cpp
blob: 21b368e76cfaff3c854160fb0997a13cfdd232d7 (plain) (tree)






















































































































































                                                                                                      
#include "setdefault.h"

#include <PulseAudioQt/Context>
#include <PulseAudioQt/Server>
#include <PulseAudioQt/Profile>
#include <PulseAudioQt/PulseObject>
#include <PulseAudioQt/SinkInput>
#include <PulseAudioQt/Sink>
#include <PulseAudioQt/Card>
#include <PulseAudioQt/Port>

#include <QRegularExpression>
#include <QDebug>
#include <QCoreApplication>
#include <QTimer>

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;
}