Skip to content

Commit 337154f

Browse files
authored
Merge pull request #16590 from pri-yan-shu/pipewire
[GSoC] [WIP] PipeWire support
2 parents e3e1d64 + 033b92a commit 337154f

21 files changed

Lines changed: 1572 additions & 61 deletions

CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3741,6 +3741,20 @@ add_library(
37413741
target_include_directories(mixxx-lib SYSTEM PUBLIC lib/portaudio)
37423742
target_link_libraries(mixxx-lib PRIVATE PortAudioRingBuffer)
37433743

3744+
# PipeWire
3745+
default_option(PIPEWIRE "Enable the PipeWire backend" "UNIX AND NOT APPLE AND NOT ANDROID")
3746+
if(PIPEWIRE)
3747+
find_package(PipeWire REQUIRED)
3748+
target_link_libraries(mixxx-lib PUBLIC PipeWire::PipeWire)
3749+
target_compile_definitions(mixxx-lib PUBLIC __PIPEWIRE__)
3750+
target_sources(
3751+
mixxx-lib
3752+
PRIVATE
3753+
src/soundio/pipewireenumerator.cpp
3754+
src/soundio/sounddevicepipewire.cpp
3755+
)
3756+
endif()
3757+
37443758
# PortMidi
37453759
default_option(PORTMIDI "Enable the PortMidi backend for MIDI controllers" "NOT ANDROID")
37463760
if(PORTMIDI)

cmake/modules/FindPipeWire.cmake

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#[=======================================================================[.rst:
2+
FindPipeWire
3+
--------
4+
5+
Finds the PipeWire library.
6+
7+
Imported Targets
8+
^^^^^^^^^^^^^^^^
9+
10+
This module provides the following imported targets, if found:
11+
12+
``PipeWire::PipeWire``
13+
The PipeWire library
14+
15+
Result Variables
16+
^^^^^^^^^^^^^^^^
17+
18+
This will define the following variables:
19+
20+
``PipeWire_FOUND``
21+
True if the system has the PipeWire library.
22+
23+
#]=======================================================================]
24+
25+
find_package(PkgConfig REQUIRED)
26+
pkg_check_modules(PIPEWIRE REQUIRED IMPORTED_TARGET libpipewire-0.3)
27+
28+
include(FindPackageHandleStandardArgs)
29+
30+
find_package_handle_standard_args(
31+
PipeWire
32+
REQUIRED_VARS PIPEWIRE_FOUND
33+
VERSION_VAR PIPEWIRE_VERSION
34+
)
35+
36+
if(PipeWire_FOUND AND NOT TARGET PipeWire::PipeWire)
37+
add_library(PipeWire::PipeWire INTERFACE IMPORTED)
38+
set_target_properties(
39+
PipeWire::PipeWire
40+
PROPERTIES INTERFACE_LINK_LIBRARIES PkgConfig::PIPEWIRE
41+
)
42+
endif()

src/preferences/dialog/dlgprefsound.cpp

Lines changed: 96 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
#include <vector>
77

88
#include "control/controlproxy.h"
9+
#include "defs_urls.h"
910
#include "engine/enginebuffer.h"
1011
#include "engine/enginemixer.h"
1112
#include "mixer/playermanager.h"
1213
#include "moc_dlgprefsound.cpp"
1314
#include "preferences/dialog/dlgprefsounditem.h"
15+
#include "soundio/sounddevice.h"
1416
#include "soundio/soundmanager.h"
17+
#include "soundio/soundmanagerutil.h"
1518
#include "util/rlimit.h"
1619
#include "util/scopedoverridecursor.h"
1720

@@ -94,6 +97,21 @@ DlgPrefSound::DlgPrefSound(QWidget* pParent,
9497
this,
9598
&DlgPrefSound::refreshDevices);
9699

100+
connect(m_pSoundManager.get(),
101+
&SoundManager::deviceAdded,
102+
this,
103+
&DlgPrefSound::addDevice);
104+
105+
connect(m_pSoundManager.get(),
106+
&SoundManager::deviceRemoved,
107+
this,
108+
&DlgPrefSound::removeDevice);
109+
110+
connect(m_pSoundManager.get(),
111+
&SoundManager::deviceChannelsUpdated,
112+
this,
113+
&DlgPrefSound::updateDeviceChannels);
114+
97115
apiComboBox->clear();
98116
apiComboBox->addItem(SoundManagerConfig::kEmptyComboBox,
99117
SoundManagerConfig::kDefaultAPI);
@@ -108,16 +126,8 @@ DlgPrefSound::DlgPrefSound(QWidget* pParent,
108126
QStringLiteral("(?)"),
109127
MIXXX_MANUAL_SOUND_API_URL));
110128

111-
sampleRateComboBox->clear();
112129
const auto sampleRates = m_pSoundManager->getSampleRates();
113-
for (const auto& sampleRate : sampleRates) {
114-
if (sampleRate.isValid()) {
115-
// no ridiculous sample rate values. prohibiting zero means
116-
// avoiding a potential div-by-0 error in ::updateLatencies
117-
sampleRateComboBox->addItem(tr("%1 Hz").arg(sampleRate.value()),
118-
QVariant::fromValue(sampleRate));
119-
}
120-
}
130+
updateSampleRates(sampleRates);
121131
connect(sampleRateComboBox,
122132
QOverload<int>::of(&QComboBox::currentIndexChanged),
123133
this,
@@ -508,14 +518,23 @@ void DlgPrefSound::connectSoundItem(DlgPrefSoundItem* pItem) {
508518
connect(this, &DlgPrefSound::writePaths, pItem, &DlgPrefSoundItem::writePath);
509519
if (pItem->isInput()) {
510520
connect(this, &DlgPrefSound::refreshInputDevices, pItem, &DlgPrefSoundItem::refreshDevices);
521+
connect(this, &DlgPrefSound::addInputDevice, pItem, &DlgPrefSoundItem::addDevice);
522+
connect(this, &DlgPrefSound::removeInputDevice, pItem, &DlgPrefSoundItem::removeDevice);
511523
} else {
512524
connect(this,
513525
&DlgPrefSound::refreshOutputDevices,
514526
pItem,
515527
&DlgPrefSoundItem::refreshDevices);
528+
connect(this, &DlgPrefSound::addOutputDevice, pItem, &DlgPrefSoundItem::addDevice);
529+
connect(this, &DlgPrefSound::removeOutputDevice, pItem, &DlgPrefSoundItem::removeDevice);
516530
}
517531
connect(this, &DlgPrefSound::updatingAPI, pItem, &DlgPrefSoundItem::save);
518532
connect(this, &DlgPrefSound::updatedAPI, pItem, &DlgPrefSoundItem::reload);
533+
connect(this,
534+
&DlgPrefSound::deviceChannelsUpdated,
535+
pItem,
536+
&DlgPrefSoundItem::updateDeviceChannels);
537+
connect(this, &DlgPrefSound::deviceRouteUpdated, pItem, &DlgPrefSoundItem::updateDeviceRoute);
519538
}
520539

521540
void DlgPrefSound::insertItem(DlgPrefSoundItem *pItem, QVBoxLayout *pLayout) {
@@ -797,6 +816,62 @@ void DlgPrefSound::refreshDevices() {
797816
emit refreshInputDevices(m_inputDevices);
798817
}
799818

819+
void DlgPrefSound::addDevice(SoundDevicePointer pDevice) {
820+
const bool hasInputs = pDevice->getNumInputChannels().isValid();
821+
const bool hasOutputs = pDevice->getNumOutputChannels().isValid();
822+
823+
if (hasInputs) {
824+
m_inputDevices.append(pDevice);
825+
emit addInputDevice(pDevice);
826+
}
827+
if (hasOutputs) {
828+
m_outputDevices.append(pDevice);
829+
emit addOutputDevice(pDevice);
830+
}
831+
}
832+
833+
void DlgPrefSound::removeDevice(SoundDevicePointer pDevice) {
834+
const bool hasInputs = pDevice->getNumInputChannels().isValid();
835+
const bool hasOutputs = pDevice->getNumOutputChannels().isValid();
836+
837+
if (hasInputs && m_inputDevices.removeOne(pDevice)) {
838+
emit removeInputDevice(pDevice);
839+
}
840+
841+
if (hasOutputs && m_outputDevices.removeOne(pDevice)) {
842+
emit removeOutputDevice(pDevice);
843+
}
844+
}
845+
846+
void DlgPrefSound::updateDeviceChannels(SoundDevicePointer pDevice) {
847+
const bool hasInputs = pDevice->getNumInputChannels().isValid();
848+
const bool hasOutputs = pDevice->getNumOutputChannels().isValid();
849+
const bool hadInputs = m_inputDevices.contains(pDevice);
850+
const bool hadOutputs = m_outputDevices.contains(pDevice);
851+
const bool listsModified = (hasInputs ^ hadInputs) || (hasOutputs ^ hadOutputs);
852+
853+
if (!listsModified) {
854+
emit deviceChannelsUpdated(pDevice);
855+
return;
856+
}
857+
858+
if (hadInputs && !hasInputs) {
859+
m_inputDevices.removeOne(pDevice);
860+
emit removeInputDevice(pDevice);
861+
} else if (!hadInputs && hasInputs) {
862+
m_inputDevices.append(pDevice);
863+
emit addInputDevice(pDevice);
864+
}
865+
866+
if (hadOutputs && !hasOutputs) {
867+
m_outputDevices.removeOne(pDevice);
868+
emit removeOutputDevice(pDevice);
869+
} else if (!hadOutputs && hasOutputs) {
870+
m_outputDevices.append(pDevice);
871+
emit addOutputDevice(pDevice);
872+
}
873+
}
874+
800875
/// Called when any of the combo boxes in this dialog are changed. Enables the
801876
/// apply button and marks that settings have been changed so that
802877
/// DlgPrefSound::slotApply knows to apply them.
@@ -1099,3 +1174,15 @@ void DlgPrefSound::checkLatencyCompensation() {
10991174
bool DlgPrefSound::okayToClose() const {
11001175
return m_configValid;
11011176
}
1177+
1178+
void DlgPrefSound::updateSampleRates(const QList<mixxx::audio::SampleRate>& sampleRates) {
1179+
sampleRateComboBox->clear();
1180+
for (const auto& sampleRate : sampleRates) {
1181+
if (sampleRate.isValid()) {
1182+
// no ridiculous sample rate values. prohibiting zero means
1183+
// avoiding a potential div-by-0 error in ::updateLatencies
1184+
sampleRateComboBox->addItem(tr("%1 Hz").arg(sampleRate.value()),
1185+
QVariant::fromValue(sampleRate));
1186+
}
1187+
}
1188+
}

src/preferences/dialog/dlgprefsound.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@
33
#include <memory>
44

55
#include "control/pollingcontrolproxy.h"
6-
#include "defs_urls.h"
76
#include "preferences/constants.h"
87
#include "preferences/dialog/dlgpreferencepage.h"
98
#include "preferences/dialog/ui_dlgprefsounddlg.h"
109
#include "preferences/usersettings.h"
1110
#include "soundio/sounddevice.h"
12-
#include "soundio/sounddevicestatus.h"
1311
#include "soundio/soundmanagerconfig.h"
1412
#include "util/parented_ptr.h"
1513

@@ -42,8 +40,14 @@ class DlgPrefSound : public DlgPreferencePage, public Ui::DlgPrefSoundDlg {
4240
void writePaths(SoundManagerConfig *config);
4341
void refreshOutputDevices(const QList<SoundDevicePointer>& devices);
4442
void refreshInputDevices(const QList<SoundDevicePointer>& devices);
43+
void addOutputDevice(SoundDevicePointer pDevice);
44+
void addInputDevice(SoundDevicePointer pDevice);
45+
void removeOutputDevice(SoundDevicePointer pDevice);
46+
void removeInputDevice(SoundDevicePointer pDevice);
4547
void updatingAPI();
4648
void updatedAPI();
49+
void deviceRouteUpdated(const SoundDeviceId& device, const AudioPath* pPath);
50+
void deviceChannelsUpdated(SoundDevicePointer devices);
4751

4852
public slots:
4953
void slotUpdate() override; // called on show
@@ -83,6 +87,10 @@ class DlgPrefSound : public DlgPreferencePage, public Ui::DlgPrefSoundDlg {
8387
void updateKeylockDualThreadingCheckbox();
8488
void updateKeylockMultithreading(bool enabled);
8589
#endif
90+
void addDevice(SoundDevicePointer pDevice);
91+
void removeDevice(SoundDevicePointer pDevice);
92+
void updateDeviceChannels(SoundDevicePointer pDevice);
93+
void updateSampleRates(const QList<mixxx::audio::SampleRate>& sampleRates);
8694

8795
private:
8896
void initializePaths();

src/preferences/dialog/dlgprefsounditem.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "moc_dlgprefsounditem.cpp"
66
#include "soundio/sounddevice.h"
77
#include "soundio/soundmanagerconfig.h"
8+
#include "util/assert.h"
89

910
/// Constructs a new preferences sound item, representing an AudioPath and SoundDevice
1011
/// with a label and two combo boxes.
@@ -68,6 +69,77 @@ void DlgPrefSoundItem::refreshDevices(const QList<SoundDevicePointer>& devices)
6869
}
6970
}
7071

72+
void DlgPrefSoundItem::addDevice(const SoundDevicePointer pDevice) {
73+
// SoundDeviceId oldDev =
74+
// deviceComboBox->itemData(deviceComboBox->currentIndex()).value<SoundDeviceId>();
75+
deviceComboBox->addItem(pDevice->getDisplayName(), QVariant::fromValue(pDevice->getDeviceId()));
76+
77+
// int newIndex = deviceComboBox->findData(QVariant::fromValue(oldDev));
78+
// deviceComboBox->setCurrentIndex(newIndex);
79+
80+
m_devices.push_back(pDevice);
81+
}
82+
83+
void DlgPrefSoundItem::removeDevice(const SoundDevicePointer pDevice) {
84+
int removeIndex = deviceComboBox->findData(QVariant::fromValue(pDevice->getDeviceId()));
85+
int currentIndex = deviceComboBox->currentIndex();
86+
87+
if (currentIndex == removeIndex) {
88+
deviceComboBox->setCurrentIndex(0);
89+
deviceComboBox->removeItem(removeIndex);
90+
} else {
91+
SoundDeviceId oldDev = deviceComboBox->itemData(currentIndex).value<SoundDeviceId>();
92+
deviceComboBox->removeItem(removeIndex);
93+
94+
int newIndex = deviceComboBox->findData(QVariant::fromValue(oldDev));
95+
if (newIndex != currentIndex) {
96+
deviceComboBox->setCurrentIndex(newIndex);
97+
}
98+
}
99+
m_devices.removeOne(pDevice);
100+
}
101+
102+
void DlgPrefSoundItem::updateDeviceChannels(SoundDevicePointer pDevice) {
103+
const auto& id = pDevice->getDeviceId();
104+
int index = deviceComboBox->findData(QVariant::fromValue(id));
105+
if (index >= 0 && deviceComboBox->currentIndex() == index) {
106+
// if changed device is not selected no need to update
107+
int currentIndex = channelComboBox->currentIndex();
108+
auto channelData = channelComboBox->itemData(currentIndex).value<QPoint>();
109+
deviceChanged(index);
110+
auto newIndex = channelComboBox->findData(QVariant::fromValue(channelData));
111+
112+
m_emitSettingChanged = false;
113+
channelComboBox->setCurrentIndex(newIndex);
114+
m_emitSettingChanged = true;
115+
}
116+
}
117+
118+
void DlgPrefSoundItem::updateDeviceRoute(const SoundDeviceId& id, const AudioPath* pPath) {
119+
if (pPath->getType() != m_type || pPath->getIndex() != m_index) {
120+
return;
121+
}
122+
123+
// qWarning() << "DlgPrefSoundItem::updateDevice" << id.name;
124+
int index = deviceComboBox->findData(QVariant::fromValue(id));
125+
126+
VERIFY_OR_DEBUG_ASSERT(index >= 0) {
127+
return;
128+
}
129+
130+
if (index != deviceComboBox->currentIndex()) {
131+
deviceComboBox->blockSignals(true);
132+
deviceComboBox->setCurrentIndex(index);
133+
deviceComboBox->blockSignals(false);
134+
deviceChanged(index);
135+
}
136+
137+
auto channelGroup = pPath->getChannelGroup();
138+
QPoint point = QPoint(channelGroup.getChannelBase(), channelGroup.getChannelCount());
139+
int channelIndex = channelComboBox->findData(QVariant::fromValue(point));
140+
channelComboBox->setCurrentIndex(channelIndex);
141+
}
142+
71143
/// Slot called when the device combo box selection changes. Updates the channel
72144
/// combo box.
73145
void DlgPrefSoundItem::deviceChanged(int index) {

src/preferences/dialog/dlgprefsounditem.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class DlgPrefSoundItem : public QWidget, public Ui::DlgPrefSoundItem {
3232
return channelComboBox->currentIndex();
3333
}
3434
void selectFirstUnusedChannelIndex(const QList<int>& selectedChannels);
35+
void setDevice(const SoundDeviceId& device);
3536

3637
signals:
3738
void selectedDeviceChanged();
@@ -46,10 +47,13 @@ class DlgPrefSoundItem : public QWidget, public Ui::DlgPrefSoundItem {
4647
void writePath(SoundManagerConfig *config) const;
4748
void save();
4849
void reload();
50+
void addDevice(SoundDevicePointer pDevice);
51+
void removeDevice(SoundDevicePointer pDevice);
52+
void updateDeviceChannels(SoundDevicePointer pDevice);
53+
void updateDeviceRoute(const SoundDeviceId& pDevice, const AudioPath* pPath);
4954

5055
private:
5156
SoundDevicePointer getDevice() const; // if this returns NULL, we don't have a valid AudioPath
52-
void setDevice(const SoundDeviceId& device);
5357
void setChannel(unsigned int channelBase, unsigned int channels);
5458
int hasSufficientChannels(const SoundDevice& device) const;
5559

src/soundio/networkenumerator.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
#include "engine/sidechain/enginenetworkstream.h"
44
#include "soundio/sounddevice.h"
55

6-
NetworkEnumerator::NetworkEnumerator(UserSettingsPointer config,
7-
SoundManager* sm)
6+
NetworkEnumerator::NetworkEnumerator(UserSettingsPointer pConfig,
7+
SoundManager* pSoundManager)
88
: m_pNetworkStream(QSharedPointer<EngineNetworkStream>::create(2, 0)),
99
m_pDevice(QSharedPointer<SoundDeviceNetwork>::create(
10-
config, sm, m_pNetworkStream)) {
10+
pConfig, pSoundManager, m_pNetworkStream)) {
1111
}
1212

1313
NetworkEnumerator::~NetworkEnumerator() {

src/soundio/networkenumerator.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
class NetworkEnumerator : public SoundDeviceEnumerator {
1010
public:
11-
NetworkEnumerator(UserSettingsPointer config,
12-
SoundManager* sm);
11+
NetworkEnumerator(UserSettingsPointer pConfig,
12+
SoundManager* pSoundManager);
1313
~NetworkEnumerator() override;
1414

1515
std::vector<SoundDevicePointer> queryDevices() const override;

0 commit comments

Comments
 (0)