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 }