File indexing completed on 2024-12-08 13:25:01
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 }