File indexing completed on 2024-12-01 05:05:12
0001 /* 0002 SPDX-FileCopyrightText: 2016 David Rosca <nowrep@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "streamrestore.h" 0008 #include "context.h" 0009 #include "debug.h" 0010 0011 namespace QPulseAudio 0012 { 0013 StreamRestore::StreamRestore(quint32 index, const QVariantMap &properties, QObject *parent) 0014 : PulseObject(parent) 0015 { 0016 memset(&m_volume, 0, sizeof(m_volume)); 0017 memset(&m_channelMap, 0, sizeof(m_channelMap)); 0018 0019 m_index = index; 0020 m_properties = properties; 0021 } 0022 0023 void StreamRestore::update(const pa_ext_stream_restore_info *info) 0024 { 0025 m_cache.valid = false; 0026 const QString infoName = QString::fromUtf8(info->name); 0027 if (m_name != infoName) { 0028 m_name = infoName; 0029 Q_EMIT nameChanged(); 0030 } 0031 const QString infoDevice = QString::fromUtf8(info->device); 0032 if (m_device != infoDevice) { 0033 m_device = infoDevice; 0034 Q_EMIT deviceChanged(); 0035 } 0036 if (m_muted != info->mute) { 0037 m_muted = info->mute; 0038 Q_EMIT mutedChanged(); 0039 } 0040 if (!pa_cvolume_equal(&m_volume, &info->volume)) { 0041 m_volume = info->volume; 0042 Q_EMIT volumeChanged(); 0043 Q_EMIT channelVolumesChanged(); 0044 } 0045 if (!pa_channel_map_equal(&m_channelMap, &info->channel_map)) { 0046 m_channels.clear(); 0047 m_channels.reserve(info->channel_map.channels); 0048 for (int i = 0; i < info->channel_map.channels; ++i) { 0049 m_channels << QString::fromUtf8(pa_channel_position_to_pretty_string(info->channel_map.map[i])); 0050 } 0051 m_channelMap = info->channel_map; 0052 Q_EMIT channelsChanged(); 0053 } 0054 } 0055 0056 QString StreamRestore::name() const 0057 { 0058 return m_name; 0059 } 0060 0061 QString StreamRestore::device() const 0062 { 0063 return m_device; 0064 } 0065 0066 void StreamRestore::setDevice(const QString &device) 0067 { 0068 if (m_cache.valid) { 0069 if (m_cache.device != device) { 0070 writeChanges(m_cache.volume, m_cache.muted, device); 0071 } 0072 } else { 0073 if (m_device != device) { 0074 writeChanges(m_volume, m_muted, device); 0075 } 0076 } 0077 } 0078 0079 qint64 StreamRestore::volume() const 0080 { 0081 // FIXME: workaround for pipewire not supporting stream restore subscription 0082 // revert when fix is released 0083 // https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/3805 0084 if (m_cache.valid) { 0085 return m_cache.volume.values[0]; 0086 } else { 0087 return m_volume.values[0]; 0088 } 0089 } 0090 0091 void StreamRestore::setVolume(qint64 volume) 0092 { 0093 pa_cvolume vol = m_cache.valid ? m_cache.volume : m_volume; 0094 0095 // If no channel exists force one. We need one to be able to control the volume 0096 // See https://bugs.kde.org/show_bug.cgi?id=407397 0097 if (vol.channels == 0) { 0098 vol.channels = 1; 0099 } 0100 0101 for (int i = 0; i < vol.channels; ++i) { 0102 vol.values[i] = volume; 0103 } 0104 0105 if (m_cache.valid) { 0106 writeChanges(vol, m_cache.muted, m_cache.device); 0107 } else { 0108 writeChanges(vol, m_muted, m_device); 0109 } 0110 } 0111 0112 bool StreamRestore::isMuted() const 0113 { 0114 // FIXME: workaround for pipewire not supporting stream restore subscription 0115 // revert when fix is released 0116 // https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/3805 0117 if (m_cache.valid) { 0118 return m_cache.muted; 0119 } else { 0120 return m_muted; 0121 } 0122 } 0123 0124 void StreamRestore::setMuted(bool muted) 0125 { 0126 if (m_cache.valid) { 0127 if (m_cache.muted != muted) { 0128 writeChanges(m_cache.volume, muted, m_cache.device); 0129 } 0130 } else { 0131 if (m_muted != muted) { 0132 writeChanges(m_volume, muted, m_device); 0133 } 0134 } 0135 } 0136 0137 bool StreamRestore::hasVolume() const 0138 { 0139 return true; 0140 } 0141 0142 bool StreamRestore::isVolumeWritable() const 0143 { 0144 return true; 0145 } 0146 0147 QStringList StreamRestore::channels() const 0148 { 0149 return m_channels; 0150 } 0151 0152 QList<qreal> StreamRestore::channelVolumes() const 0153 { 0154 QList<qreal> ret; 0155 ret.reserve(m_volume.channels); 0156 for (int i = 0; i < m_volume.channels; ++i) { 0157 ret << m_volume.values[i]; 0158 } 0159 return ret; 0160 } 0161 0162 void StreamRestore::setChannelVolume(int channel, qint64 volume) 0163 { 0164 Q_ASSERT(channel >= 0 && channel < m_volume.channels); 0165 pa_cvolume vol = m_cache.valid ? m_cache.volume : m_volume; 0166 vol.values[channel] = volume; 0167 0168 if (m_cache.valid) { 0169 writeChanges(vol, m_cache.muted, m_cache.device); 0170 } else { 0171 writeChanges(vol, m_muted, m_device); 0172 } 0173 } 0174 0175 quint32 StreamRestore::deviceIndex() const 0176 { 0177 return PA_INVALID_INDEX; 0178 } 0179 0180 void StreamRestore::setDeviceIndex(quint32 deviceIndex) 0181 { 0182 Q_UNUSED(deviceIndex); 0183 qCWarning(PLASMAPA) << "Not implemented"; 0184 } 0185 0186 void StreamRestore::writeChanges(const pa_cvolume &volume, bool muted, const QString &device) 0187 { 0188 const QByteArray nameData = m_name.toUtf8(); 0189 const QByteArray deviceData = device.toUtf8(); 0190 0191 pa_ext_stream_restore_info info; 0192 info.name = nameData.constData(); 0193 info.channel_map = m_channelMap; 0194 info.volume = volume; 0195 info.device = deviceData.isEmpty() ? nullptr : deviceData.constData(); 0196 info.mute = muted; 0197 0198 // If no channel exists force one. We need one to be able to control the volume 0199 // See https://bugs.kde.org/show_bug.cgi?id=407397 0200 if (info.channel_map.channels == 0) { 0201 info.channel_map.channels = 1; 0202 info.channel_map.map[0] = PA_CHANNEL_POSITION_MONO; 0203 } 0204 0205 m_cache.valid = true; 0206 m_cache.volume = volume; 0207 m_cache.muted = muted; 0208 m_cache.device = device; 0209 0210 context()->streamRestoreWrite(&info); 0211 } 0212 0213 } // QPulseAudio