File indexing completed on 2024-12-01 13:40:43

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     return m_volume.values[0];
0082 }
0083 
0084 void StreamRestore::setVolume(qint64 volume)
0085 {
0086     pa_cvolume vol = m_cache.valid ? m_cache.volume : m_volume;
0087 
0088     // If no channel exists force one. We need one to be able to control the volume
0089     // See https://bugs.kde.org/show_bug.cgi?id=407397
0090     if (vol.channels == 0) {
0091         vol.channels = 1;
0092     }
0093 
0094     for (int i = 0; i < vol.channels; ++i) {
0095         vol.values[i] = volume;
0096     }
0097 
0098     if (m_cache.valid) {
0099         writeChanges(vol, m_cache.muted, m_cache.device);
0100     } else {
0101         writeChanges(vol, m_muted, m_device);
0102     }
0103 }
0104 
0105 bool StreamRestore::isMuted() const
0106 {
0107     return m_muted;
0108 }
0109 
0110 void StreamRestore::setMuted(bool muted)
0111 {
0112     if (m_cache.valid) {
0113         if (m_cache.muted != muted) {
0114             writeChanges(m_cache.volume, muted, m_cache.device);
0115         }
0116     } else {
0117         if (m_muted != muted) {
0118             writeChanges(m_volume, muted, m_device);
0119         }
0120     }
0121 }
0122 
0123 bool StreamRestore::hasVolume() const
0124 {
0125     return true;
0126 }
0127 
0128 bool StreamRestore::isVolumeWritable() const
0129 {
0130     return true;
0131 }
0132 
0133 QStringList StreamRestore::channels() const
0134 {
0135     return m_channels;
0136 }
0137 
0138 QList<qreal> StreamRestore::channelVolumes() const
0139 {
0140     QList<qreal> ret;
0141     ret.reserve(m_volume.channels);
0142     for (int i = 0; i < m_volume.channels; ++i) {
0143         ret << m_volume.values[i];
0144     }
0145     return ret;
0146 }
0147 
0148 void StreamRestore::setChannelVolume(int channel, qint64 volume)
0149 {
0150     Q_ASSERT(channel >= 0 && channel < m_volume.channels);
0151     pa_cvolume vol = m_cache.valid ? m_cache.volume : m_volume;
0152     vol.values[channel] = volume;
0153 
0154     if (m_cache.valid) {
0155         writeChanges(vol, m_cache.muted, m_cache.device);
0156     } else {
0157         writeChanges(vol, m_muted, m_device);
0158     }
0159 }
0160 
0161 quint32 StreamRestore::deviceIndex() const
0162 {
0163     return PA_INVALID_INDEX;
0164 }
0165 
0166 void StreamRestore::setDeviceIndex(quint32 deviceIndex)
0167 {
0168     Q_UNUSED(deviceIndex);
0169     qCWarning(PLASMAPA) << "Not implemented";
0170 }
0171 
0172 void StreamRestore::writeChanges(const pa_cvolume &volume, bool muted, const QString &device)
0173 {
0174     const QByteArray nameData = m_name.toUtf8();
0175     const QByteArray deviceData = device.toUtf8();
0176 
0177     pa_ext_stream_restore_info info;
0178     info.name = nameData.constData();
0179     info.channel_map = m_channelMap;
0180     info.volume = volume;
0181     info.device = deviceData.isEmpty() ? nullptr : deviceData.constData();
0182     info.mute = muted;
0183 
0184     // If no channel exists force one. We need one to be able to control the volume
0185     // See https://bugs.kde.org/show_bug.cgi?id=407397
0186     if (info.channel_map.channels == 0) {
0187         info.channel_map.channels = 1;
0188         info.channel_map.map[0] = PA_CHANNEL_POSITION_MONO;
0189     }
0190 
0191     m_cache.valid = true;
0192     m_cache.volume = volume;
0193     m_cache.muted = muted;
0194     m_cache.device = device;
0195 
0196     context()->streamRestoreWrite(&info);
0197 }
0198 
0199 } // QPulseAudio