File indexing completed on 2024-12-08 08:02:48

0001 /*
0002     SPDX-FileCopyrightText: 2014-2015 Harald Sitter <sitter@kde.org>
0003     SPDX-FileCopyrightText: 2021 Nicolas Fella
0004 
0005     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #include "speakertest.h"
0009 
0010 #include "canberracontext.h"
0011 #include "soundthemeconfig.h"
0012 
0013 namespace
0014 {
0015 struct CallbackData {
0016     SpeakerTest *object = nullptr;
0017     QString name;
0018 };
0019 
0020 // Wrapper for camberra *ca_finish_callback_t
0021 void finish_callback(ca_context *c, unsigned int id, int error_code, void *userdata)
0022 {
0023     Q_UNUSED(c)
0024     Q_UNUSED(id)
0025 
0026     if (userdata == nullptr) {
0027         return;
0028     }
0029 
0030     auto *cb_data = static_cast<CallbackData *>(userdata);
0031     cb_data->object->playingFinished(cb_data->name, error_code);
0032 
0033     delete (cb_data);
0034 };
0035 }
0036 
0037 SpeakerTest::SpeakerTest(QObject *parent)
0038     : QObject(parent)
0039     , m_config(new SoundThemeConfig(this))
0040 {
0041 }
0042 
0043 QPulseAudio::Sink *SpeakerTest::sink() const
0044 {
0045     return m_sink;
0046 }
0047 
0048 void SpeakerTest::setSink(QPulseAudio::Sink *sink)
0049 {
0050     if (m_sink != sink) {
0051         m_sink = sink;
0052         Q_EMIT sinkChanged();
0053     }
0054 }
0055 
0056 void SpeakerTest::testChannel(const QString &name)
0057 {
0058     auto context = QPulseAudio::CanberraContext::instance()->canberra();
0059     if (!context) {
0060         return;
0061     }
0062 
0063     m_playingChannels << name;
0064     Q_EMIT playingChannelsChanged();
0065 
0066     ca_context_set_driver(context, "pulse");
0067 
0068     char dev[64];
0069     snprintf(dev, sizeof(dev), "%lu", (unsigned long)m_sink->index());
0070     ca_context_change_device(context, dev);
0071 
0072     void *cb_data = new CallbackData{this, name};
0073 
0074     ca_proplist *proplist;
0075     ca_proplist_create(&proplist);
0076 
0077     ca_proplist_sets(proplist, CA_PROP_MEDIA_ROLE, "test");
0078     ca_proplist_sets(proplist, CA_PROP_MEDIA_NAME, name.toLatin1().constData());
0079     ca_proplist_sets(proplist, CA_PROP_CANBERRA_FORCE_CHANNEL, name.toLatin1().data());
0080     ca_proplist_sets(proplist, CA_PROP_CANBERRA_ENABLE, "1");
0081     ca_proplist_sets(proplist, CA_PROP_CANBERRA_XDG_THEME_NAME, m_config->soundTheme().toLatin1().constData());
0082 
0083     // there is no subwoofer sound in the freedesktop theme https://gitlab.freedesktop.org/xdg/xdg-sound-theme/-/issues/7
0084     const QString sound_name = (name == QLatin1String("lfe")) ? QStringLiteral("audio-channel-rear-center") : QStringLiteral("audio-channel-%1").arg(name);
0085 
0086     int error_code = CA_SUCCESS;
0087     for (const QString &soundName : {sound_name,
0088                                      QStringLiteral("audio-test-signal"), // Fallback sounds
0089                                      QStringLiteral("bell-window-system"),
0090                                      QString()}) {
0091         if (soundName.isEmpty()) {
0092             // We are here because all of the fallback sounds failed to play
0093             playingFinished(name, error_code);
0094             break;
0095         }
0096 
0097         ca_proplist_sets(proplist, CA_PROP_EVENT_ID, soundName.toLatin1().data());
0098         error_code = ca_context_play_full(context, 0, proplist, finish_callback, cb_data);
0099         if (error_code == CA_SUCCESS) {
0100             break;
0101         }
0102     }
0103 
0104     ca_context_change_device(context, nullptr);
0105     ca_proplist_destroy(proplist);
0106 }
0107 
0108 QStringList SpeakerTest::playingChannels() const
0109 {
0110     return m_playingChannels;
0111 }
0112 
0113 void SpeakerTest::playingFinished(const QString &name, int errorCode)
0114 {
0115     m_playingChannels.removeOne(name);
0116     Q_EMIT playingChannelsChanged();
0117 
0118     if (errorCode != CA_SUCCESS) {
0119         Q_EMIT showErrorMessage(ca_strerror(errorCode));
0120     };
0121 }