File indexing completed on 2024-07-21 12:36:05

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