-
Notifications
You must be signed in to change notification settings - Fork 8
Unified executable with runtime audio backend selection #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
struktured
wants to merge
12
commits into
master
Choose a base branch
from
feature/runtime-backend-selection
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 8 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
3a3af2f
Add unified executable with runtime audio backend selection
struktured d998017
Add runtime backend switching via Audio Backend menu
struktured 9cd54c9
Move backend selector to Settings > Audio Backend submenu
struktured b03e9d4
Fix crash when switching to unavailable audio backend
struktured 3ddb525
Fix PipeWire crash on backend re-initialization
struktured 20824c8
Cache backends to avoid teardown/re-init crashes
struktured 2e7c3fd
Fix freeze when switching to unavailable JACK server
struktured 2c6f4a4
Fix JACK connection freeze with timeout wrapper
struktured 48873b2
Address review: stop audio delivery when backend deactivated
struktured a21097e
Address review: real teardown in destructor at process exit
struktured 48ec487
Address review: replace QThread::terminate with safe detached pattern
struktured 666a87e
Address review: connectDeviceById, link dl_libs, drop unused includes
struktured File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| #ifndef QAUDIO_BACKEND_HPP | ||
| #define QAUDIO_BACKEND_HPP | ||
|
|
||
| #include <QObject> | ||
| #include <QString> | ||
| #include <QList> | ||
|
|
||
| class QProjectM_MainWindow; | ||
| class QMutex; | ||
|
|
||
| class QAudioBackend : public QObject | ||
| { | ||
| Q_OBJECT | ||
|
|
||
| public: | ||
| struct DeviceInfo { | ||
| QString id; | ||
| QString displayName; | ||
| }; | ||
|
|
||
| explicit QAudioBackend(QObject *parent = nullptr) : QObject(parent), m_active(false) {} | ||
| virtual ~QAudioBackend() {} | ||
|
|
||
| virtual bool start(QProjectM_MainWindow *mainWindow, QMutex *audioMutex) = 0; | ||
| virtual void stop() = 0; | ||
|
|
||
| bool isActive() const { return m_active; } | ||
| void setActive(bool active) { m_active = active; } | ||
|
|
||
| virtual QString backendName() const = 0; | ||
|
|
||
| virtual QList<DeviceInfo> devices() const = 0; | ||
| virtual QString currentDeviceId() const = 0; | ||
| virtual bool supportsDeviceSwitching() const = 0; | ||
|
|
||
| virtual void writeSettings() = 0; | ||
| virtual void readSettings() = 0; | ||
|
|
||
| protected: | ||
| bool m_active; | ||
|
|
||
| public slots: | ||
| virtual void selectDevice(const QString &deviceId) = 0; | ||
|
|
||
| signals: | ||
| void devicesChanged(); | ||
| void activeDeviceChanged(); | ||
| void errorOccurred(const QString &message); | ||
| }; | ||
|
|
||
| #endif // QAUDIO_BACKEND_HPP |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| /** | ||
| * projectM -- Milkdrop-esque visualisation SDK | ||
| * Copyright (C)2003-2004 projectM Team | ||
| * | ||
| * This library is free software; you can redistribute it and/or | ||
| * modify it under the terms of the GNU General Public | ||
| * License as published by the Free Software Foundation; either | ||
| * version 2.1 of the License, or (at your option) any later version. | ||
| * | ||
| * This library is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| * Lesser General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public | ||
| * License along with this library; if not, write to the Free Software | ||
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| * See 'LICENSE.txt' included within this release | ||
| * | ||
| */ | ||
|
|
||
| #include "QAudioDeviceChooser.hpp" | ||
| #include "QAudioDeviceModel.hpp" | ||
| #include "QAudioBackend.hpp" | ||
|
|
||
| #include <QCheckBox> | ||
| #include <QDialogButtonBox> | ||
| #include <QLabel> | ||
| #include <QListView> | ||
| #include <QSettings> | ||
| #include <QVBoxLayout> | ||
| #include <QHBoxLayout> | ||
| #include <QtDebug> | ||
|
|
||
| QAudioDeviceChooser::QAudioDeviceChooser(QAudioBackend *backend, QWidget *parent) | ||
| : QDialog(parent), | ||
| m_backend(backend), | ||
| m_deviceModel(new QAudioDeviceModel(backend, this)), | ||
| m_devicesListView(nullptr), | ||
| m_autoDetectCheckBox(nullptr), | ||
| m_infoLabel(nullptr) | ||
| { | ||
| buildUi(); | ||
| readSettings(); | ||
| } | ||
|
|
||
| void QAudioDeviceChooser::buildUi() | ||
| { | ||
| setWindowTitle(tr("Audio Device Settings")); | ||
| resize(380, 271); | ||
|
|
||
| QVBoxLayout *mainVBox = new QVBoxLayout(); | ||
|
|
||
| // Info / instruction label | ||
| m_infoLabel = new QLabel(tr("Select a source device below."), this); | ||
| mainVBox->addWidget(m_infoLabel); | ||
|
|
||
| // Device list view | ||
| m_devicesListView = new QListView(this); | ||
| m_devicesListView->setAutoFillBackground(true); | ||
| m_devicesListView->setToolTip(tr("Double click a source device to activate it.")); | ||
| m_devicesListView->setModel(m_deviceModel); | ||
| mainVBox->addWidget(m_devicesListView); | ||
|
|
||
| // Auto-detect checkbox | ||
| m_autoDetectCheckBox = new QCheckBox(tr("Auto-detect best source"), this); | ||
| m_autoDetectCheckBox->setToolTip( | ||
| tr("Automatically select the best available audio source on startup. " | ||
| "This is the recommended way to get projectM to visualize sound " | ||
| "without specifying a device explicitly.")); | ||
| mainVBox->addWidget(m_autoDetectCheckBox); | ||
|
|
||
| // If the backend does not support device switching, disable the list | ||
| if (!m_backend->supportsDeviceSwitching()) { | ||
| m_devicesListView->setEnabled(false); | ||
| m_autoDetectCheckBox->setEnabled(false); | ||
| m_infoLabel->setText( | ||
| tr("This audio backend does not support device switching.")); | ||
| } | ||
|
|
||
| // Horizontal layout: main content + button box | ||
| QHBoxLayout *hBox = new QHBoxLayout(this); | ||
| hBox->addLayout(mainVBox); | ||
|
|
||
| QDialogButtonBox *buttonBox = new QDialogButtonBox( | ||
| QDialogButtonBox::Ok, Qt::Vertical, this); | ||
| hBox->addWidget(buttonBox); | ||
|
|
||
| setLayout(hBox); | ||
|
|
||
| // Connections | ||
| connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); | ||
| connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); | ||
|
|
||
| connect(m_autoDetectCheckBox, SIGNAL(stateChanged(int)), | ||
| this, SLOT(onAutoDetectToggled(int))); | ||
|
|
||
| connect(m_devicesListView, SIGNAL(doubleClicked(const QModelIndex&)), | ||
| this, SLOT(onDeviceDoubleClicked(const QModelIndex&))); | ||
| } | ||
|
|
||
| void QAudioDeviceChooser::onAutoDetectToggled(int state) | ||
| { | ||
| m_devicesListView->setEnabled(state != Qt::Checked); | ||
| } | ||
|
|
||
| void QAudioDeviceChooser::onDeviceDoubleClicked(const QModelIndex &index) | ||
| { | ||
| if (!index.isValid()) | ||
| return; | ||
|
|
||
| QString deviceId = index.data(QAudioDeviceModel::DeviceIdRole).toString(); | ||
| if (!deviceId.isEmpty()) { | ||
| m_backend->selectDevice(deviceId); | ||
| } | ||
| } | ||
|
|
||
| void QAudioDeviceChooser::writeSettings() | ||
| { | ||
| QSettings settings("projectM", "qprojectM"); | ||
| settings.setValue("audioDeviceChooser/autoDetect", | ||
| m_autoDetectCheckBox->checkState() == Qt::Checked); | ||
|
|
||
| QString currentId = m_backend->currentDeviceId(); | ||
| if (!currentId.isEmpty()) { | ||
| settings.setValue("audioDeviceChooser/deviceId", currentId); | ||
| } | ||
| } | ||
|
|
||
| void QAudioDeviceChooser::readSettings() | ||
| { | ||
| QSettings settings("projectM", "qprojectM"); | ||
|
|
||
| bool autoDetect = settings.value("audioDeviceChooser/autoDetect", true).toBool(); | ||
| m_autoDetectCheckBox->setCheckState(autoDetect ? Qt::Checked : Qt::Unchecked); | ||
|
|
||
| if (autoDetect) { | ||
| m_devicesListView->setEnabled(false); | ||
| } else { | ||
| m_devicesListView->setEnabled(true); | ||
| } | ||
| } | ||
|
|
||
| void QAudioDeviceChooser::open() | ||
| { | ||
| m_deviceModel->refresh(); | ||
| show(); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| /** | ||
| * projectM -- Milkdrop-esque visualisation SDK | ||
| * Copyright (C)2003-2004 projectM Team | ||
| * | ||
| * This library is free software; you can redistribute it and/or | ||
| * modify it under the terms of the GNU General Public | ||
| * License as published by the Free Software Foundation; either | ||
| * version 2.1 of the License, or (at your option) any later version. | ||
| * | ||
| * This library is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| * Lesser General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public | ||
| * License along with this library; if not, write to the Free Software | ||
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| * See 'LICENSE.txt' included within this release | ||
| * | ||
| */ | ||
|
|
||
| #ifndef QAUDIO_DEVICE_CHOOSER_HPP | ||
| #define QAUDIO_DEVICE_CHOOSER_HPP | ||
|
|
||
| #include <QDialog> | ||
|
|
||
| class QAudioBackend; | ||
| class QAudioDeviceModel; | ||
| class QListView; | ||
| class QCheckBox; | ||
| class QLabel; | ||
|
|
||
| class QAudioDeviceChooser : public QDialog | ||
| { | ||
| Q_OBJECT | ||
|
|
||
| public: | ||
| QAudioDeviceChooser(QAudioBackend *backend, QWidget *parent = nullptr); | ||
|
|
||
| public slots: | ||
| void open(); | ||
| void writeSettings(); | ||
|
|
||
| private slots: | ||
| void readSettings(); | ||
| void onAutoDetectToggled(int state); | ||
| void onDeviceDoubleClicked(const QModelIndex &index); | ||
|
|
||
| private: | ||
| void buildUi(); | ||
|
|
||
| QAudioBackend *m_backend; | ||
| QAudioDeviceModel *m_deviceModel; | ||
|
|
||
| QListView *m_devicesListView; | ||
| QCheckBox *m_autoDetectCheckBox; | ||
| QLabel *m_infoLabel; | ||
| }; | ||
|
|
||
| #endif // QAUDIO_DEVICE_CHOOSER_HPP |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| /** | ||
| * projectM -- Milkdrop-esque visualisation SDK | ||
| * Copyright (C)2003-2004 projectM Team | ||
| * | ||
| * This library is free software; you can redistribute it and/or | ||
| * modify it under the terms of the GNU General Public | ||
| * License as published by the Free Software Foundation; either | ||
| * version 2.1 of the License, or (at your option) any later version. | ||
| * | ||
| * This library is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| * Lesser General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public | ||
| * License along with this library; if not, write to the Free Software | ||
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| * See 'LICENSE.txt' included within this release | ||
| * | ||
| */ | ||
|
|
||
| #include "QAudioDeviceModel.hpp" | ||
| #include "QAudioBackend.hpp" | ||
|
|
||
| #include <QColor> | ||
|
|
||
| QAudioDeviceModel::QAudioDeviceModel(QAudioBackend *backend, QObject *parent) | ||
| : QAbstractListModel(parent), m_backend(backend) | ||
| { | ||
| connect(m_backend, SIGNAL(devicesChanged()), this, SLOT(refresh())); | ||
| connect(m_backend, SIGNAL(activeDeviceChanged()), this, SLOT(refresh())); | ||
| } | ||
|
|
||
| QAudioDeviceModel::~QAudioDeviceModel() | ||
| { | ||
| } | ||
|
|
||
| void QAudioDeviceModel::refresh() | ||
| { | ||
| beginResetModel(); | ||
| endResetModel(); | ||
| } | ||
|
|
||
| int QAudioDeviceModel::rowCount(const QModelIndex &parent) const | ||
| { | ||
| Q_UNUSED(parent); | ||
| return m_backend->devices().size(); | ||
| } | ||
|
|
||
| QVariant QAudioDeviceModel::data(const QModelIndex &index, int role) const | ||
| { | ||
| QList<QAudioBackend::DeviceInfo> devs = m_backend->devices(); | ||
|
|
||
| if (!index.isValid() || index.row() >= devs.size()) | ||
| return QVariant(); | ||
|
|
||
| const QAudioBackend::DeviceInfo &info = devs[index.row()]; | ||
|
|
||
| switch (role) | ||
| { | ||
| case Qt::DisplayRole: | ||
| return info.displayName; | ||
|
|
||
| case Qt::ToolTipRole: | ||
| if (info.id == m_backend->currentDeviceId()) | ||
| return info.displayName + " (active)"; | ||
| else | ||
| return info.displayName; | ||
|
|
||
| case Qt::BackgroundRole: | ||
| if (info.id == m_backend->currentDeviceId()) { | ||
| QColor highlight(0, 200, 0, 80); | ||
| return highlight; | ||
| } | ||
| return QVariant(); | ||
|
|
||
| case DeviceIdRole: | ||
| return info.id; | ||
|
|
||
| default: | ||
| return QVariant(); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
QAudioDeviceModel::data()rebuilds a freshdevices()list on every call (androwCount()also callsdevices()). This can be expensive and can lead to inconsistent row->device mapping if the backend returns devices in non-stable order. Consider caching the device list in the model (update it inrefresh()), and haverowCount()/data()read from that cached list.