Skip to content

Commit c09dbe7

Browse files
committed
Implement preset auto-switching triggered by headphone connection
1 parent 75f2cbc commit c09dbe7

26 files changed

+1083
-30
lines changed

3rdparty/WAF/Animation/SideSlide/SideSlideAnimator.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "SideSlideAnimator.h"
1818
#include "SideSlideDecorator.h"
1919

20+
#include <cstring>
2021
#include <QEvent>
2122
#include <QPropertyAnimation>
2223
#include <QWidget>
@@ -74,7 +75,7 @@ void SideSlideAnimator::animateForward()
7475
{
7576
slideIn();
7677
}
77-
78+
#include <QDebug>
7879
void SideSlideAnimator::slideIn()
7980
{
8081
//
@@ -95,7 +96,7 @@ void SideSlideAnimator::slideIn()
9596
// Определим самый верхний виджет
9697
//
9798
QWidget* topWidget = widgetForSlide()->parentWidget();
98-
while (topWidget->parentWidget() != 0) {
99+
while (topWidget->parentWidget() != 0 && !topWidget->inherits("QDialog")) {
99100
topWidget = topWidget->parentWidget();
100101
}
101102

@@ -209,7 +210,7 @@ void SideSlideAnimator::slideOut()
209210
// Определим самый верхний виджет
210211
//
211212
QWidget* topWidget = widgetForSlide()->parentWidget();
212-
while (topWidget->parentWidget() != 0) {
213+
while (topWidget->parentWidget() != 0 && !topWidget->inherits("QDialog")) {
213214
topWidget = topWidget->parentWidget();
214215
}
215216

src/MainWindow.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,14 @@ MainWindow::MainWindow(QString exepath,
155155
_styleHelper = new StyleHelper(this);
156156
_eelEditor = new EELEditor(this);
157157

158+
connect(&PresetManager::instance(), &PresetManager::presetAutoloaded, this, [this](const QString& device){
159+
ui->info->setAnimatedText(QString("%1 connected - Preset loaded automatically").arg(device), true);
160+
});
161+
158162
connect(_audioService, &IAudioService::eelCompilationStarted, _eelEditor, &EELEditor::onCompilerStarted);
159163
connect(_audioService, &IAudioService::eelCompilationFinished, _eelEditor, &EELEditor::onCompilerFinished);
160164
connect(_audioService, &IAudioService::eelOutputReceived, _eelEditor, &EELEditor::onConsoleOutputReceived);
165+
connect(_audioService, &IAudioService::outputDeviceChanged, &PresetManager::instance(), &PresetManager::onOutputDeviceChanged);
161166

162167
// Convolver file info
163168
ConvolverInfoEventArgs ciArgs;
@@ -260,7 +265,7 @@ MainWindow::MainWindow(QString exepath,
260265

261266
_appMgrFragment = new FragmentHost<AppManagerFragment*>(new AppManagerFragment(_audioService->appManager(), this), WAF::BottomSide, this);
262267
_statusFragment = new FragmentHost<StatusFragment*>(new StatusFragment(this), WAF::BottomSide, this);
263-
_presetFragment = new FragmentHost<PresetFragment*>(new PresetFragment(this), WAF::LeftSide, this);
268+
_presetFragment = new FragmentHost<PresetFragment*>(new PresetFragment(_audioService, this), WAF::LeftSide, this);
264269
_settingsFragment = new FragmentHost<SettingsFragment*>(new SettingsFragment(_trayIcon, _audioService, this), WAF::BottomSide, this);
265270

266271
connect(_presetFragment->fragment(), &PresetFragment::wantsToWriteConfig, this, &MainWindow::applyConfig);

src/MainWindow.ui

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2240,7 +2240,7 @@
22402240
</sizepolicy>
22412241
</property>
22422242
<property name="text">
2243-
<string>'Galaxy Buds Pro' connected - Preset loaded automatically</string>
2243+
<string/>
22442244
</property>
22452245
<property name="wordWrap">
22462246
<bool>true</bool>

src/audio/base/IOutputDevice.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
class IOutputDevice
77
{
88
public:
9+
IOutputDevice(){}
10+
IOutputDevice(std::string name, std::string description) : name(name), description(description){}
11+
912
uint id = ((uint32_t)0xffffffff);
1013

1114
std::string name;

src/audio/pipewire/PipewireAudioService.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ PipewireAudioService::PipewireAudioService()
5555

5656
if (target_node.id != SPA_ID_INVALID)
5757
{
58-
emit outputDeviceChanged(QString::fromStdString(target_node.name), QString::fromStdString(device.output_route_name));
58+
emit outputDeviceChanged(QString::fromStdString(target_node.description), QString::fromStdString(device.name));
5959
}
6060
else
6161
{
@@ -98,7 +98,7 @@ void PipewireAudioService::onAppConfigUpdated(const AppConfig::Key &key, const Q
9898
{
9999
if (device.id == device_id)
100100
{
101-
emit outputDeviceChanged(name, QString::fromStdString(device.output_route_name));
101+
emit outputDeviceChanged(QString::fromStdString(device.description), name);
102102
break;
103103
}
104104
}

src/config/AppConfig.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ AppConfig::AppConfig()
4040
DEFINE_KEY(AudioAppBlocklist, QStringList());
4141
DEFINE_KEY(AudioAppBlocklistInvert, true);
4242

43+
DEFINE_KEY(AeqPlotDarkMode, false);
44+
4345
connect(this, &AppConfig::updated, this, &AppConfig::notify);
4446

4547
load();

src/data/PresetManager.cpp

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,17 @@
22

33
#include "config/AppConfig.h"
44
#include "config/DspConfig.h"
5+
#include "model/PresetListModel.h"
56

67
#include <QFile>
8+
#include <QJsonDocument>
9+
#include <QJsonArray>
10+
#include <QJsonObject>
711

8-
PresetManager::PresetManager(){}
12+
PresetManager::PresetManager() : _presetModel(new PresetListModel(this))
13+
{
14+
loadRules();
15+
}
916

1017
void PresetManager::load(const QString &filename)
1118
{
@@ -35,3 +42,78 @@ void PresetManager::save(const QString &filename)
3542
QFile::copy(src, dest);
3643
Log::debug("PresetManager::save: Saving to " + filename);
3744
}
45+
46+
void PresetManager::onOutputDeviceChanged(const QString &deviceName, const QString &deviceId)
47+
{
48+
for(const auto& rule : qAsConst(_rules))
49+
{
50+
if(rule.deviceId == deviceId)
51+
{
52+
load(AppConfig::instance().getPath("presets/" + rule.preset + ".conf"));
53+
emit presetAutoloaded(deviceName);
54+
break;
55+
}
56+
}
57+
}
58+
59+
QString PresetManager::rulesPath() const
60+
{
61+
return AppConfig::instance().getPath("preset_rules.json");
62+
}
63+
64+
void PresetManager::loadRules()
65+
{
66+
_rules.clear();
67+
68+
QFile indexJson(rulesPath());
69+
if(!indexJson.exists())
70+
{
71+
return;
72+
}
73+
74+
indexJson.open(QFile::ReadOnly);
75+
QJsonDocument d = QJsonDocument::fromJson(indexJson.readAll());
76+
QJsonArray root = d.array();
77+
78+
for(const auto& item : root)
79+
{
80+
_rules.append(PresetRule(item.toObject()));
81+
}
82+
83+
indexJson.close();
84+
}
85+
86+
void PresetManager::saveRules() const
87+
{
88+
QFile json(rulesPath());
89+
if(!json.open(QIODevice::WriteOnly)){
90+
Log::error("PresetRuleTableModel::save: Cannot open json file");
91+
return;
92+
}
93+
94+
QJsonArray root;
95+
for(const auto& item : _rules)
96+
{
97+
root.append(item.toJson());
98+
}
99+
100+
json.write(QJsonDocument(root).toJson(QJsonDocument::Indented));
101+
json.close();
102+
}
103+
104+
PresetListModel *PresetManager::presetModel() const
105+
{
106+
return _presetModel;
107+
}
108+
109+
void PresetManager::setRules(const QVector<PresetRule> &newRules)
110+
{
111+
_rules = newRules;
112+
saveRules();
113+
}
114+
115+
QVector<PresetRule> PresetManager::rules() const
116+
{
117+
return _rules;
118+
}
119+

src/data/PresetManager.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
#ifndef PRESETMANAGER_H
22
#define PRESETMANAGER_H
33

4+
#include "PresetRule.h"
5+
46
#include <QObject>
57

8+
class PresetListModel;
9+
610
class PresetManager : public QObject
711
{
812
Q_OBJECT
@@ -17,10 +21,28 @@ class PresetManager : public QObject
1721
PresetManager(PresetManager const &) = delete;
1822
PresetManager();
1923

24+
QVector<PresetRule> rules() const;
25+
26+
void setRules(const QVector<PresetRule> &newRules);
27+
28+
PresetListModel *presetModel() const;
29+
30+
signals:
31+
void presetAutoloaded(const QString& deviceName);
32+
2033
public slots:
2134
void save(const QString &filename);
2235
void load(const QString &filename);
2336

37+
void onOutputDeviceChanged(const QString& deviceName, const QString& deviceId);
38+
39+
void loadRules();
40+
void saveRules() const;
41+
private:
42+
QVector<PresetRule> _rules;
43+
PresetListModel* _presetModel;
44+
45+
QString rulesPath() const;
2446
};
2547

2648
#endif // PRESETMANAGER_H

src/data/PresetRule.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#ifndef PRESETRULE_H
2+
#define PRESETRULE_H
3+
4+
#include <IOutputDevice.h>
5+
#include <QJsonObject>
6+
7+
class PresetRule
8+
{
9+
public:
10+
PresetRule(){}
11+
PresetRule(QJsonObject pkg)
12+
{
13+
deviceName = pkg.value("deviceName").toString();
14+
deviceId = pkg.value("deviceId").toString();
15+
preset = pkg.value("preset").toString();
16+
}
17+
18+
PresetRule(IOutputDevice device, QString _preset)
19+
{
20+
deviceName = QString::fromStdString(device.description);
21+
deviceId = QString::fromStdString(device.name);
22+
preset = _preset;;
23+
}
24+
25+
QJsonObject toJson() const
26+
{
27+
QJsonObject pkg;
28+
pkg["deviceName"] = deviceName;
29+
pkg["deviceId"] = deviceId;
30+
pkg["preset"] = preset;
31+
return pkg;
32+
}
33+
34+
bool operator==(const PresetRule &rhs)
35+
{
36+
return this->deviceName == rhs.deviceName &&
37+
this->deviceId == rhs.deviceId &&
38+
this->preset == rhs.preset;
39+
}
40+
41+
QString deviceName;
42+
QString deviceId;
43+
QString preset;
44+
45+
};
46+
47+
#endif // PRESETRULE_H

src/data/PresetRuleTableDelegate.h

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#ifndef PRESETRULETABLEDELEGATE_H
2+
#define PRESETRULETABLEDELEGATE_H
3+
4+
#include <QComboBox>
5+
#include <QMessageBox>
6+
#include <QStyledItemDelegate>
7+
8+
#include "model/DeviceListModel.h"
9+
#include "model/PresetListModel.h"
10+
#include "model/PresetRuleTableModel.h"
11+
#include "utils/Log.h"
12+
13+
class PresetRuleTableDelegate : public QStyledItemDelegate {
14+
Q_OBJECT
15+
public:
16+
PresetRuleTableDelegate(QObject* parent = nullptr)
17+
: QStyledItemDelegate(parent) {}
18+
19+
void attachModels(DeviceListModel* devModel,
20+
PresetListModel* presetModel,
21+
PresetRuleTableModel* ruleModel)
22+
{
23+
_deviceModel = devModel;
24+
_presetModel = presetModel;
25+
_ruleModel = ruleModel;
26+
};
27+
28+
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option,
29+
const QModelIndex &index) const Q_DECL_OVERRIDE
30+
{
31+
Q_UNUSED(option)
32+
33+
QComboBox *cb = new QComboBox(parent);
34+
switch(index.column())
35+
{
36+
case 1:
37+
cb->setModel(_presetModel);
38+
break;
39+
}
40+
return cb;
41+
}
42+
43+
void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE
44+
{
45+
if(!_ruleModel)
46+
{
47+
Log::error("PresetRuleTableDelegate::setEditorData: Rule model not attached");
48+
return;
49+
}
50+
51+
auto rule = _ruleModel->at(index);
52+
const QString currentText = index.data(Qt::EditRole).toString();
53+
54+
QComboBox *cb = qobject_cast<QComboBox *>(editor);
55+
if(cb)
56+
{
57+
const int cbIndex = cb->findText(currentText);
58+
if (cbIndex >= 0)
59+
cb->setCurrentIndex(cbIndex);
60+
}
61+
}
62+
63+
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const Q_DECL_OVERRIDE
64+
{
65+
QComboBox *cb = qobject_cast<QComboBox *>(editor);
66+
if(cb)
67+
{
68+
model->setData(index, cb->currentText(), Qt::EditRole);
69+
}
70+
}
71+
72+
private:
73+
DeviceListModel* _deviceModel = nullptr;
74+
PresetRuleTableModel* _ruleModel = nullptr;
75+
PresetListModel* _presetModel = nullptr;
76+
77+
};
78+
79+
#endif // PRESETRULETABLEDELEGATE_H

0 commit comments

Comments
 (0)