File indexing completed on 2024-07-14 05:24:24

0001 /*
0002     SPDX-FileCopyrightText: 2014-2015 Harald Sitter <sitter@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #ifndef CONTEXT_H
0008 #define CONTEXT_H
0009 
0010 #include <QMutex>
0011 #include <QObject>
0012 #include <QSet>
0013 
0014 #include <pulse/ext-stream-restore.h>
0015 #include <pulse/glib-mainloop.h>
0016 #include <pulse/mainloop.h>
0017 #include <pulse/pulseaudio.h>
0018 
0019 #include "maps.h"
0020 #include "operation.h"
0021 
0022 namespace QPulseAudio
0023 {
0024 class Server;
0025 
0026 class Context : public QObject
0027 {
0028     Q_OBJECT
0029 public:
0030     explicit Context(QObject *parent = nullptr);
0031     ~Context() override;
0032 
0033     static Context *instance();
0034 
0035     static const qint64 NormalVolume;
0036     static const qint64 MinimalVolume;
0037     static const qint64 MaximalVolume;
0038 
0039     void ref();
0040     void unref();
0041 
0042     bool isValid()
0043     {
0044         return m_context && m_mainloop;
0045     }
0046 
0047     pa_context *context() const
0048     {
0049         return m_context;
0050     }
0051 
0052     const SinkMap &sinks() const
0053     {
0054         return m_sinks;
0055     }
0056     const SinkInputMap &sinkInputs() const
0057     {
0058         return m_sinkInputs;
0059     }
0060     const SourceMap &sources() const
0061     {
0062         return m_sources;
0063     }
0064     const SourceOutputMap &sourceOutputs() const
0065     {
0066         return m_sourceOutputs;
0067     }
0068     const ClientMap &clients() const
0069     {
0070         return m_clients;
0071     }
0072     const CardMap &cards() const
0073     {
0074         return m_cards;
0075     }
0076     const ModuleMap &modules() const
0077     {
0078         return m_modules;
0079     }
0080     const StreamRestoreMap &streamRestores() const
0081     {
0082         return m_streamRestores;
0083     }
0084     Server *server() const
0085     {
0086         return m_server;
0087     }
0088     QString newDefaultSink() const
0089     {
0090         return m_newDefaultSink;
0091     }
0092     QString newDefaultSource() const
0093     {
0094         return m_newDefaultSource;
0095     }
0096 
0097     void subscribeCallback(pa_context *context, pa_subscription_event_type_t type, uint32_t index);
0098     void contextStateCallback(pa_context *context);
0099 
0100     void sinkCallback(const pa_sink_info *info);
0101     void sinkInputCallback(const pa_sink_input_info *info);
0102     void sourceCallback(const pa_source_info *info);
0103     void sourceOutputCallback(const pa_source_output_info *info);
0104     void clientCallback(const pa_client_info *info);
0105     void cardCallback(const pa_card_info *info);
0106     void moduleCallback(const pa_module_info *info);
0107     void streamRestoreCallback(const pa_ext_stream_restore_info *info);
0108     void serverCallback(const pa_server_info *info);
0109 
0110     void setCardProfile(quint32 index, const QString &profile);
0111     void setDefaultSink(const QString &name);
0112     void setDefaultSource(const QString &name);
0113     void streamRestoreWrite(const pa_ext_stream_restore_info *info);
0114 
0115     static void setApplicationId(const QString &applicationId);
0116 
0117     template<typename PAFunction>
0118     void setGenericVolume(quint32 index, int channel, qint64 newVolume, pa_cvolume cVolume, PAFunction pa_set_volume)
0119     {
0120         if (!m_context) {
0121             return;
0122         }
0123         newVolume = qBound<qint64>(0, newVolume, PA_VOLUME_MAX);
0124         pa_cvolume newCVolume = cVolume;
0125         if (channel == -1) { // -1 all channels
0126             const qint64 orig = pa_cvolume_max(&cVolume);
0127             const qint64 diff = newVolume - orig;
0128             for (int i = 0; i < newCVolume.channels; ++i) {
0129                 const qint64 channel = newCVolume.values[i];
0130                 const qint64 channelDiff = orig == 0 ? diff : diff * channel / orig;
0131                 newCVolume.values[i] = qBound<qint64>(0, newCVolume.values[i] + channelDiff, PA_VOLUME_MAX);
0132             }
0133         } else {
0134             Q_ASSERT(newCVolume.channels > channel);
0135             newCVolume.values[channel] = newVolume;
0136         }
0137         if (!PAOperation(pa_set_volume(m_context, index, &newCVolume, nullptr, nullptr))) {
0138             qCWarning(PLASMAPA) << "pa_set_volume failed";
0139             return;
0140         }
0141     }
0142 
0143     template<typename PAFunction>
0144     void setGenericVolumes(quint32 index, QList<qint64> channelVolumes, pa_cvolume cVolume, PAFunction pa_set_volume)
0145     {
0146         if (!m_context) {
0147             return;
0148         }
0149         Q_ASSERT(channelVolumes.count() == cVolume.channels);
0150 
0151         pa_cvolume newCVolume = cVolume;
0152         for (int i = 0; i < channelVolumes.count(); ++i) {
0153             newCVolume.values[i] = qBound<qint64>(0, channelVolumes.at(i), PA_VOLUME_MAX);
0154         }
0155 
0156         if (!PAOperation(pa_set_volume(m_context, index, &newCVolume, nullptr, nullptr))) {
0157             qCWarning(PLASMAPA) << "pa_set_volume failed";
0158             return;
0159         }
0160     }
0161 
0162     template<typename PAFunction>
0163     void setGenericMute(quint32 index, bool mute, PAFunction pa_set_mute)
0164     {
0165         if (!m_context) {
0166             return;
0167         }
0168         if (!PAOperation(pa_set_mute(m_context, index, mute, nullptr, nullptr))) {
0169             qCWarning(PLASMAPA) << "pa_set_mute failed";
0170             return;
0171         }
0172     }
0173 
0174     template<typename PAFunction>
0175     void setGenericPort(quint32 index, const QString &portName, PAFunction pa_set_port)
0176     {
0177         if (!m_context) {
0178             return;
0179         }
0180         if (!PAOperation(pa_set_port(m_context, index, portName.toUtf8().constData(), nullptr, nullptr))) {
0181             qCWarning(PLASMAPA) << "pa_set_port failed";
0182             return;
0183         }
0184     }
0185 
0186     template<typename PAFunction>
0187     void setGenericDeviceForStream(quint32 streamIndex, quint32 deviceIndex, PAFunction pa_move_stream_to_device)
0188     {
0189         if (!m_context) {
0190             return;
0191         }
0192         if (!PAOperation(pa_move_stream_to_device(m_context, streamIndex, deviceIndex, nullptr, nullptr))) {
0193             qCWarning(PLASMAPA) << "pa_move_stream_to_device failed";
0194             return;
0195         }
0196     }
0197 
0198 private:
0199     void connectToDaemon();
0200     void reset();
0201 
0202     // Don't forget to add things to reset().
0203     SinkMap m_sinks;
0204     SinkInputMap m_sinkInputs;
0205     SourceMap m_sources;
0206     SourceOutputMap m_sourceOutputs;
0207     ClientMap m_clients;
0208     CardMap m_cards;
0209     ModuleMap m_modules;
0210     StreamRestoreMap m_streamRestores;
0211     Server *m_server;
0212 
0213     pa_context *m_context;
0214     pa_glib_mainloop *m_mainloop;
0215 
0216     QString m_newDefaultSink;
0217     QString m_newDefaultSource;
0218 
0219     int m_references;
0220     static Context *s_context;
0221     static QString s_applicationId;
0222 };
0223 
0224 } // QPulseAudio
0225 
0226 #endif // CONTEXT_H