PulseAudio Qt Bindings

streamrestore.cpp
1/*
2 SPDX-FileCopyrightText: 2016 David Rosca <nowrep@gmail.com>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "streamrestore.h"
8#include "context.h"
9#include "context_p.h"
10#include "streamrestore_p.h"
11
12#include "debug.h"
13#include "pulseobject_p.h"
14
15namespace PulseAudioQt
16{
17StreamRestore::StreamRestore(quint32 index, const QVariantMap &properties, QObject *parent)
18 : PulseObject(parent)
19 , d(new StreamRestorePrivate(this))
20{
21 memset(&d->m_volume, 0, sizeof(d->m_volume));
22 memset(&d->m_channelMap, 0, sizeof(d->m_channelMap));
23
24 d->m_index = index;
25 PulseObject::d->m_properties = properties;
26}
27
28StreamRestore::~StreamRestore()
29{
30}
31
32StreamRestorePrivate::StreamRestorePrivate(StreamRestore *q)
33 : q(q)
34{
35}
36
37StreamRestorePrivate::~StreamRestorePrivate()
38{
39}
40
41void StreamRestorePrivate::update(const pa_ext_stream_restore_info *info)
42{
43 q->PulseObject::d->updatePulseObject(info);
44 m_cache.valid = false;
45
46 const QString infoDevice = QString::fromUtf8(info->device);
47 if (m_device != infoDevice) {
48 m_device = infoDevice;
49 Q_EMIT q->deviceChanged();
50 }
51 if (m_muted != info->mute) {
52 m_muted = info->mute;
53 Q_EMIT q->mutedChanged();
54 }
55 if (!pa_cvolume_equal(&m_volume, &info->volume)) {
56 m_volume = info->volume;
57 Q_EMIT q->volumeChanged();
58 Q_EMIT q->channelVolumesChanged();
59 }
60 if (!pa_channel_map_equal(&m_channelMap, &info->channel_map)) {
61 m_channels.clear();
62 m_channels.reserve(info->channel_map.channels);
63 for (int i = 0; i < info->channel_map.channels; ++i) {
64 m_channels << QString::fromUtf8(pa_channel_position_to_pretty_string(info->channel_map.map[i]));
65 }
66 m_channelMap = info->channel_map;
67 Q_EMIT q->channelsChanged();
68 }
69}
70
71QString StreamRestore::device() const
72{
73 return d->m_device;
74}
75
76void StreamRestore::setDevice(const QString &device)
77{
78 if (d->m_cache.valid) {
79 if (d->m_cache.device != device) {
80 d->writeChanges(d->m_cache.volume, d->m_cache.muted, device);
81 }
82 } else {
83 if (d->m_device != device) {
84 d->writeChanges(d->m_volume, d->m_muted, device);
85 }
86 }
87}
88
89qint64 StreamRestore::volume() const
90{
91 // FIXME: workaround for pipewire not supporting stream restore subscription
92 // revert when fix is released
93 // https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/3805
94 if (d->m_cache.valid) {
95 return d->m_cache.volume.values[0];
96 } else {
97 return d->m_volume.values[0];
98 }
99}
100
101void StreamRestore::setVolume(qint64 volume)
102{
103 pa_cvolume vol = d->m_cache.valid ? d->m_cache.volume : d->m_volume;
104
105 // If no channel exists force one. We need one to be able to control the volume
106 // See https://bugs.kde.org/show_bug.cgi?id=407397
107 if (vol.channels == 0) {
108 vol.channels = 1;
109 }
110
111 for (int i = 0; i < vol.channels; ++i) {
112 vol.values[i] = volume;
113 }
114
115 if (d->m_cache.valid) {
116 qCDebug(PULSEAUDIOQT) << "Changing cached volume of StreamRestore" << name() << " to " << volume;
117 d->writeChanges(vol, d->m_cache.muted, d->m_cache.device);
118 } else {
119 qCDebug(PULSEAUDIOQT) << "Changing uncached volume of StreamRestore" << name() << " to " << volume;
120 d->writeChanges(vol, d->m_muted, d->m_device);
121 }
122}
123
124bool StreamRestore::isMuted() const
125{
126 // FIXME: workaround for pipewire not supporting stream restore subscription
127 // revert when fix is released
128 // https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/3805
129 if (d->m_cache.valid) {
130 return d->m_cache.muted;
131 } else {
132 return d->m_muted;
133 }
134}
135
136void StreamRestore::setMuted(bool muted)
137{
138 if (d->m_cache.valid) {
139 if (d->m_cache.muted != muted) {
140 d->writeChanges(d->m_cache.volume, muted, d->m_cache.device);
141 }
142 } else {
143 if (d->m_muted != muted) {
144 d->writeChanges(d->m_volume, muted, d->m_device);
145 }
146 }
147}
148
149bool StreamRestore::hasVolume() const
150{
151 return true;
152}
153
154bool StreamRestore::isVolumeWritable() const
155{
156 return true;
157}
158
159QStringList StreamRestore::channels() const
160{
161 return d->m_channels;
162}
163
164QList<qreal> StreamRestore::channelVolumes() const
165{
166 QList<qreal> ret;
167 ret.reserve(d->m_volume.channels);
168 for (int i = 0; i < d->m_volume.channels; ++i) {
169 ret << d->m_volume.values[i];
170 }
171 return ret;
172}
173
174void StreamRestore::setChannelVolume(int channel, qint64 volume)
175{
176 Q_ASSERT(channel >= 0 && channel < d->m_volume.channels);
177 pa_cvolume vol = d->m_cache.valid ? d->m_cache.volume : d->m_volume;
178 vol.values[channel] = volume;
179
180 if (d->m_cache.valid) {
181 d->writeChanges(vol, d->m_cache.muted, d->m_cache.device);
182 } else {
183 d->writeChanges(vol, d->m_muted, d->m_device);
184 }
185}
186
187quint32 StreamRestore::deviceIndex() const
188{
189 return PA_INVALID_INDEX;
190}
191
192void StreamRestore::setDeviceIndex(quint32 deviceIndex)
193{
194 Q_UNUSED(deviceIndex);
195 qCWarning(PULSEAUDIOQT) << "Not implemented";
196}
197
198void StreamRestorePrivate::writeChanges(const pa_cvolume &volume, bool muted, const QString &device)
199{
200 const QByteArray nameData = q->name().toUtf8();
201 const QByteArray deviceData = device.toUtf8();
202
203 pa_ext_stream_restore_info info;
204 info.name = nameData.constData();
205 info.channel_map = m_channelMap;
206 info.volume = volume;
207 info.device = deviceData.isEmpty() ? nullptr : deviceData.constData();
208 info.mute = muted;
209
210 // If no channel exists force one. We need one to be able to control the volume
211 // See https://bugs.kde.org/show_bug.cgi?id=407397
212 if (info.channel_map.channels == 0) {
213 info.channel_map.channels = 1;
214 info.channel_map.map[0] = PA_CHANNEL_POSITION_MONO;
215 }
216
217 m_cache.valid = true;
218 m_cache.volume = volume;
219 m_cache.muted = muted;
220 m_cache.device = device;
221
222 Context::instance()->d->streamRestoreWrite(&info);
223}
224
225quint32 StreamRestore::index() const
226{
227 return d->m_index;
228}
229
230} // PulseAudioQt
QString name(StandardAction id)
KGuiItem properties()
The primary namespace of PulseAudioQt.
Definition card.cpp:17
const char * constData() const const
bool isEmpty() const const
void reserve(qsizetype size)
QString fromUtf8(QByteArrayView str)
QByteArray toUtf8() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:57:59 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.