File indexing completed on 2024-12-08 10:59:35

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