Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3741,6 +3741,20 @@ add_library(
target_include_directories(mixxx-lib SYSTEM PUBLIC lib/portaudio)
target_link_libraries(mixxx-lib PRIVATE PortAudioRingBuffer)

# PipeWire
default_option(PIPEWIRE "Enable the PipeWire backend" "UNIX AND NOT APPLE AND NOT ANDROID")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please disable Pipewire by default!
The proposal is still not approved and thus we shouldn't make this feature available till we agreed we want to keep it such a way.

CC @mixxxdj/developers

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(just stumbled upon this because I don't have libpipewire-3, yet)

I agree. Default off until it's mature / safe/polished enough for test use.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done: #16713

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ronso0 we require actually libpipewire-0.3
Was it just a typo and you have libpipewire-0.3 or is there a distro out there that does not provide it?
I am asking because then we may adjust the CMake code accordingly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, libpipewire-0.3
Just set the flag to Off until I update my distro.
If I require it before that I'll find a way.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, no problem we can make Pipwire optional, depending on the existence of that file.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heads up this makes pipewire dependency optional, it is compiled in by default (helpful for CI) and pipewire features can be accessed by --developer flag. Is that fine?

if(PIPEWIRE)
find_package(PipeWire REQUIRED)
target_link_libraries(mixxx-lib PUBLIC PipeWire::PipeWire)
target_compile_definitions(mixxx-lib PUBLIC __PIPEWIRE__)
target_sources(
mixxx-lib
PRIVATE
src/soundio/pipewireenumerator.cpp
src/soundio/sounddevicepipewire.cpp
)
endif()

# PortMidi
default_option(PORTMIDI "Enable the PortMidi backend for MIDI controllers" "NOT ANDROID")
if(PORTMIDI)
Expand Down
42 changes: 42 additions & 0 deletions cmake/modules/FindPipeWire.cmake
Comment thread
daschuer marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#[=======================================================================[.rst:
FindPipeWire
--------

Finds the PipeWire library.

Imported Targets
^^^^^^^^^^^^^^^^

This module provides the following imported targets, if found:

``PipeWire::PipeWire``
The PipeWire library

Result Variables
^^^^^^^^^^^^^^^^

This will define the following variables:

``PipeWire_FOUND``
True if the system has the PipeWire library.

#]=======================================================================]

find_package(PkgConfig REQUIRED)
pkg_check_modules(PIPEWIRE REQUIRED IMPORTED_TARGET libpipewire-0.3)

include(FindPackageHandleStandardArgs)

find_package_handle_standard_args(
PipeWire
REQUIRED_VARS PIPEWIRE_FOUND
VERSION_VAR PIPEWIRE_VERSION
)

if(PipeWire_FOUND AND NOT TARGET PipeWire::PipeWire)
add_library(PipeWire::PipeWire INTERFACE IMPORTED)
set_target_properties(
PipeWire::PipeWire
PROPERTIES INTERFACE_LINK_LIBRARIES PkgConfig::PIPEWIRE
)
endif()
105 changes: 96 additions & 9 deletions src/preferences/dialog/dlgprefsound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
#include <vector>

#include "control/controlproxy.h"
#include "defs_urls.h"
#include "engine/enginebuffer.h"
#include "engine/enginemixer.h"
#include "mixer/playermanager.h"
#include "moc_dlgprefsound.cpp"
#include "preferences/dialog/dlgprefsounditem.h"
#include "soundio/sounddevice.h"
#include "soundio/soundmanager.h"
#include "soundio/soundmanagerutil.h"
#include "util/rlimit.h"
#include "util/scopedoverridecursor.h"

Expand Down Expand Up @@ -94,6 +97,21 @@ DlgPrefSound::DlgPrefSound(QWidget* pParent,
this,
&DlgPrefSound::refreshDevices);

connect(m_pSoundManager.get(),
&SoundManager::deviceAdded,
this,
&DlgPrefSound::addDevice);

connect(m_pSoundManager.get(),
&SoundManager::deviceRemoved,
this,
&DlgPrefSound::removeDevice);

connect(m_pSoundManager.get(),
&SoundManager::deviceChannelsUpdated,
this,
&DlgPrefSound::updateDeviceChannels);

apiComboBox->clear();
apiComboBox->addItem(SoundManagerConfig::kEmptyComboBox,
SoundManagerConfig::kDefaultAPI);
Expand All @@ -108,16 +126,8 @@ DlgPrefSound::DlgPrefSound(QWidget* pParent,
QStringLiteral("(?)"),
MIXXX_MANUAL_SOUND_API_URL));

sampleRateComboBox->clear();
const auto sampleRates = m_pSoundManager->getSampleRates();
for (const auto& sampleRate : sampleRates) {
if (sampleRate.isValid()) {
// no ridiculous sample rate values. prohibiting zero means
// avoiding a potential div-by-0 error in ::updateLatencies
sampleRateComboBox->addItem(tr("%1 Hz").arg(sampleRate.value()),
QVariant::fromValue(sampleRate));
}
}
updateSampleRates(sampleRates);
connect(sampleRateComboBox,
QOverload<int>::of(&QComboBox::currentIndexChanged),
this,
Expand Down Expand Up @@ -508,14 +518,23 @@ void DlgPrefSound::connectSoundItem(DlgPrefSoundItem* pItem) {
connect(this, &DlgPrefSound::writePaths, pItem, &DlgPrefSoundItem::writePath);
if (pItem->isInput()) {
connect(this, &DlgPrefSound::refreshInputDevices, pItem, &DlgPrefSoundItem::refreshDevices);
connect(this, &DlgPrefSound::addInputDevice, pItem, &DlgPrefSoundItem::addDevice);
connect(this, &DlgPrefSound::removeInputDevice, pItem, &DlgPrefSoundItem::removeDevice);
} else {
connect(this,
&DlgPrefSound::refreshOutputDevices,
pItem,
&DlgPrefSoundItem::refreshDevices);
connect(this, &DlgPrefSound::addOutputDevice, pItem, &DlgPrefSoundItem::addDevice);
connect(this, &DlgPrefSound::removeOutputDevice, pItem, &DlgPrefSoundItem::removeDevice);
}
connect(this, &DlgPrefSound::updatingAPI, pItem, &DlgPrefSoundItem::save);
connect(this, &DlgPrefSound::updatedAPI, pItem, &DlgPrefSoundItem::reload);
connect(this,
&DlgPrefSound::deviceChannelsUpdated,
pItem,
&DlgPrefSoundItem::updateDeviceChannels);
connect(this, &DlgPrefSound::deviceRouteUpdated, pItem, &DlgPrefSoundItem::updateDeviceRoute);
}

void DlgPrefSound::insertItem(DlgPrefSoundItem *pItem, QVBoxLayout *pLayout) {
Expand Down Expand Up @@ -797,6 +816,62 @@ void DlgPrefSound::refreshDevices() {
emit refreshInputDevices(m_inputDevices);
}

void DlgPrefSound::addDevice(SoundDevicePointer pDevice) {
const bool hasInputs = pDevice->getNumInputChannels().isValid();
const bool hasOutputs = pDevice->getNumOutputChannels().isValid();

if (hasInputs) {
m_inputDevices.append(pDevice);
emit addInputDevice(pDevice);
}
if (hasOutputs) {
m_outputDevices.append(pDevice);
emit addOutputDevice(pDevice);
}
}

void DlgPrefSound::removeDevice(SoundDevicePointer pDevice) {
const bool hasInputs = pDevice->getNumInputChannels().isValid();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An old issue: isValid() does not read like a book. Maybe we should introduce an alias: hasChannels() or something more descriptive.

const bool hasOutputs = pDevice->getNumOutputChannels().isValid();

if (hasInputs && m_inputDevices.removeOne(pDevice)) {
emit removeInputDevice(pDevice);
}

if (hasOutputs && m_outputDevices.removeOne(pDevice)) {
emit removeOutputDevice(pDevice);
}
}

void DlgPrefSound::updateDeviceChannels(SoundDevicePointer pDevice) {
const bool hasInputs = pDevice->getNumInputChannels().isValid();
const bool hasOutputs = pDevice->getNumOutputChannels().isValid();
const bool hadInputs = m_inputDevices.contains(pDevice);
const bool hadOutputs = m_outputDevices.contains(pDevice);
const bool listsModified = (hasInputs ^ hadInputs) || (hasOutputs ^ hadOutputs);

if (!listsModified) {
emit deviceChannelsUpdated(pDevice);
return;
}

if (hadInputs && !hasInputs) {
m_inputDevices.removeOne(pDevice);
emit removeInputDevice(pDevice);
} else if (!hadInputs && hasInputs) {
m_inputDevices.append(pDevice);
emit addInputDevice(pDevice);
}

if (hadOutputs && !hasOutputs) {
m_outputDevices.removeOne(pDevice);
emit removeOutputDevice(pDevice);
} else if (!hadOutputs && hasOutputs) {
m_outputDevices.append(pDevice);
emit addOutputDevice(pDevice);
}
}

/// Called when any of the combo boxes in this dialog are changed. Enables the
/// apply button and marks that settings have been changed so that
/// DlgPrefSound::slotApply knows to apply them.
Expand Down Expand Up @@ -1099,3 +1174,15 @@ void DlgPrefSound::checkLatencyCompensation() {
bool DlgPrefSound::okayToClose() const {
return m_configValid;
}

void DlgPrefSound::updateSampleRates(const QList<mixxx::audio::SampleRate>& sampleRates) {
sampleRateComboBox->clear();
for (const auto& sampleRate : sampleRates) {
if (sampleRate.isValid()) {
// no ridiculous sample rate values. prohibiting zero means
// avoiding a potential div-by-0 error in ::updateLatencies
sampleRateComboBox->addItem(tr("%1 Hz").arg(sampleRate.value()),
QVariant::fromValue(sampleRate));
}
}
}
12 changes: 10 additions & 2 deletions src/preferences/dialog/dlgprefsound.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
#include <memory>

#include "control/pollingcontrolproxy.h"
#include "defs_urls.h"
#include "preferences/constants.h"
#include "preferences/dialog/dlgpreferencepage.h"
#include "preferences/dialog/ui_dlgprefsounddlg.h"
#include "preferences/usersettings.h"
#include "soundio/sounddevice.h"
#include "soundio/sounddevicestatus.h"
#include "soundio/soundmanagerconfig.h"
#include "util/parented_ptr.h"

Expand Down Expand Up @@ -42,8 +40,14 @@ class DlgPrefSound : public DlgPreferencePage, public Ui::DlgPrefSoundDlg {
void writePaths(SoundManagerConfig *config);
void refreshOutputDevices(const QList<SoundDevicePointer>& devices);
void refreshInputDevices(const QList<SoundDevicePointer>& devices);
void addOutputDevice(SoundDevicePointer pDevice);
void addInputDevice(SoundDevicePointer pDevice);
void removeOutputDevice(SoundDevicePointer pDevice);
void removeInputDevice(SoundDevicePointer pDevice);
void updatingAPI();
void updatedAPI();
void deviceRouteUpdated(const SoundDeviceId& device, const AudioPath* pPath);
void deviceChannelsUpdated(SoundDevicePointer devices);

public slots:
void slotUpdate() override; // called on show
Expand Down Expand Up @@ -83,6 +87,10 @@ class DlgPrefSound : public DlgPreferencePage, public Ui::DlgPrefSoundDlg {
void updateKeylockDualThreadingCheckbox();
void updateKeylockMultithreading(bool enabled);
#endif
void addDevice(SoundDevicePointer pDevice);
void removeDevice(SoundDevicePointer pDevice);
void updateDeviceChannels(SoundDevicePointer pDevice);
void updateSampleRates(const QList<mixxx::audio::SampleRate>& sampleRates);

private:
void initializePaths();
Expand Down
72 changes: 72 additions & 0 deletions src/preferences/dialog/dlgprefsounditem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "moc_dlgprefsounditem.cpp"
#include "soundio/sounddevice.h"
#include "soundio/soundmanagerconfig.h"
#include "util/assert.h"

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

void DlgPrefSoundItem::addDevice(const SoundDevicePointer pDevice) {
// SoundDeviceId oldDev =
// deviceComboBox->itemData(deviceComboBox->currentIndex()).value<SoundDeviceId>();
deviceComboBox->addItem(pDevice->getDisplayName(), QVariant::fromValue(pDevice->getDeviceId()));

// int newIndex = deviceComboBox->findData(QVariant::fromValue(oldDev));
// deviceComboBox->setCurrentIndex(newIndex);

m_devices.push_back(pDevice);
}

void DlgPrefSoundItem::removeDevice(const SoundDevicePointer pDevice) {
int removeIndex = deviceComboBox->findData(QVariant::fromValue(pDevice->getDeviceId()));
int currentIndex = deviceComboBox->currentIndex();

if (currentIndex == removeIndex) {
deviceComboBox->setCurrentIndex(0);
deviceComboBox->removeItem(removeIndex);
} else {
SoundDeviceId oldDev = deviceComboBox->itemData(currentIndex).value<SoundDeviceId>();
deviceComboBox->removeItem(removeIndex);

int newIndex = deviceComboBox->findData(QVariant::fromValue(oldDev));
if (newIndex != currentIndex) {
deviceComboBox->setCurrentIndex(newIndex);
}
}
m_devices.removeOne(pDevice);
}

void DlgPrefSoundItem::updateDeviceChannels(SoundDevicePointer pDevice) {
const auto& id = pDevice->getDeviceId();
int index = deviceComboBox->findData(QVariant::fromValue(id));
if (index >= 0 && deviceComboBox->currentIndex() == index) {
// if changed device is not selected no need to update
int currentIndex = channelComboBox->currentIndex();
auto channelData = channelComboBox->itemData(currentIndex).value<QPoint>();
deviceChanged(index);
auto newIndex = channelComboBox->findData(QVariant::fromValue(channelData));

m_emitSettingChanged = false;
channelComboBox->setCurrentIndex(newIndex);
m_emitSettingChanged = true;
}
}

void DlgPrefSoundItem::updateDeviceRoute(const SoundDeviceId& id, const AudioPath* pPath) {
if (pPath->getType() != m_type || pPath->getIndex() != m_index) {
return;
}

// qWarning() << "DlgPrefSoundItem::updateDevice" << id.name;
int index = deviceComboBox->findData(QVariant::fromValue(id));

VERIFY_OR_DEBUG_ASSERT(index >= 0) {
return;
}

if (index != deviceComboBox->currentIndex()) {
deviceComboBox->blockSignals(true);
deviceComboBox->setCurrentIndex(index);
deviceComboBox->blockSignals(false);
deviceChanged(index);
}

auto channelGroup = pPath->getChannelGroup();
QPoint point = QPoint(channelGroup.getChannelBase(), channelGroup.getChannelCount());
int channelIndex = channelComboBox->findData(QVariant::fromValue(point));
channelComboBox->setCurrentIndex(channelIndex);
}

/// Slot called when the device combo box selection changes. Updates the channel
/// combo box.
void DlgPrefSoundItem::deviceChanged(int index) {
Expand Down
6 changes: 5 additions & 1 deletion src/preferences/dialog/dlgprefsounditem.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class DlgPrefSoundItem : public QWidget, public Ui::DlgPrefSoundItem {
return channelComboBox->currentIndex();
}
void selectFirstUnusedChannelIndex(const QList<int>& selectedChannels);
void setDevice(const SoundDeviceId& device);

signals:
void selectedDeviceChanged();
Expand All @@ -46,10 +47,13 @@ class DlgPrefSoundItem : public QWidget, public Ui::DlgPrefSoundItem {
void writePath(SoundManagerConfig *config) const;
void save();
void reload();
void addDevice(SoundDevicePointer pDevice);
void removeDevice(SoundDevicePointer pDevice);
void updateDeviceChannels(SoundDevicePointer pDevice);
void updateDeviceRoute(const SoundDeviceId& pDevice, const AudioPath* pPath);

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

Expand Down
6 changes: 3 additions & 3 deletions src/soundio/networkenumerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
#include "engine/sidechain/enginenetworkstream.h"
#include "soundio/sounddevice.h"

NetworkEnumerator::NetworkEnumerator(UserSettingsPointer config,
SoundManager* sm)
NetworkEnumerator::NetworkEnumerator(UserSettingsPointer pConfig,
SoundManager* pSoundManager)
: m_pNetworkStream(QSharedPointer<EngineNetworkStream>::create(2, 0)),
m_pDevice(QSharedPointer<SoundDeviceNetwork>::create(
config, sm, m_pNetworkStream)) {
pConfig, pSoundManager, m_pNetworkStream)) {
}

NetworkEnumerator::~NetworkEnumerator() {
Expand Down
4 changes: 2 additions & 2 deletions src/soundio/networkenumerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

class NetworkEnumerator : public SoundDeviceEnumerator {
public:
NetworkEnumerator(UserSettingsPointer config,
SoundManager* sm);
NetworkEnumerator(UserSettingsPointer pConfig,
SoundManager* pSoundManager);
~NetworkEnumerator() override;

std::vector<SoundDevicePointer> queryDevices() const override;
Expand Down
Loading
Loading