Commit 5c8f1269 authored by Ismael Asensio's avatar Ismael Asensio
Browse files

Speaker Test: Show playback errors in the UI

Errors produced when trying to play a sound were being ignored.
Show them in the spaker test UI, so it can help users to find
their cause.

The error strings are provided directly by libcanberra, so i18n
will be probably missing for them.

CCBUG: 451995
parent d915cbd6
Pipeline #157396 failed with stage
in 1 minute and 31 seconds
......@@ -268,6 +268,17 @@ ScrollViewKCM {
}
}
SpeakerTest {
id: tester
sink: testOverlay.sinkObject
onShowErrorMessage: message => {
testError.text = i18ndc("kcm_pulseaudio",
"%1 is an error string produced by an external component, and probably untranslated",
"Error trying to play a test sound. \nThe system said: \"%1\"", message);
testError.visible = true;
}
}
Kirigami.OverlaySheet {
id: testOverlay
......@@ -338,85 +349,89 @@ ScrollViewKCM {
}
}
GridLayout {
id: layoutTest
columns: 3
rowSpacing: Kirigami.Units.gridUnit
ColumnLayout {
Kirigami.InlineMessage {
id: testError
type: Kirigami.MessageType.Error
showCloseButton: true
Layout.fillWidth: true
}
LayoutMirroring.enabled: false // To preserve spacial layout on RTL
GridLayout {
columns: 3
rowSpacing: Kirigami.Units.gridUnit
Kirigami.Avatar {
KCoreAddons.KUser {
id: kuser
}
source: kuser.faceIconUrl
implicitWidth: Kirigami.Units.gridUnit * 4
implicitHeight: implicitWidth
sourceSize.width: implicitWidth
sourceSize.height: implicitWidth
Layout.row: 1
Layout.column: 1
Layout.alignment: Qt.AlignCenter
LayoutMirroring.enabled: false // To preserve spacial layout on RTL
}
Kirigami.Avatar {
KCoreAddons.KUser {
id: kuser
}
source: kuser.faceIconUrl
implicitWidth: Kirigami.Units.gridUnit * 4
implicitHeight: implicitWidth
sourceSize.width: implicitWidth
sourceSize.height: implicitWidth
SpeakerTest {
id: tester
sink: testOverlay.sinkObject
}
Layout.row: 1
Layout.column: 1
Layout.alignment: Qt.AlignCenter
Repeater {
model: testOverlay.sinkObject && testOverlay.sinkObject.rawChannels
delegate: ToolButton {
readonly property var channelData: testOverlay.channelData(modelData)
readonly property bool isPlaying: tester.playingChannels.includes(modelData)
Layout.row: channelData.row
Layout.column: channelData.column
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: Kirigami.Units.gridUnit * 8
Layout.minimumHeight: Kirigami.Units.gridUnit * 4
contentItem: ColumnLayout {
spacing: 0
Kirigami.Icon {
source: "audio-speakers-symbolic"
color: isPlaying ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor
implicitWidth: Kirigami.Units.iconSizes.medium
implicitHeight: Kirigami.Units.iconSizes.medium
Layout.fillWidth: true
Layout.margins: Kirigami.Units.smallSpacing
rotation: channelData.angle
}
Repeater {
model: testOverlay.sinkObject && testOverlay.sinkObject.rawChannels
delegate: ToolButton {
readonly property var channelData: testOverlay.channelData(modelData)
readonly property bool isPlaying: tester.playingChannels.includes(modelData)
Layout.row: channelData.row
Layout.column: channelData.column
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: Kirigami.Units.gridUnit * 8
Layout.minimumHeight: Kirigami.Units.gridUnit * 4
contentItem: ColumnLayout {
spacing: 0
Kirigami.Icon {
source: "audio-speakers-symbolic"
color: isPlaying ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor
implicitWidth: Kirigami.Units.iconSizes.medium
implicitHeight: Kirigami.Units.iconSizes.medium
Layout.fillWidth: true
Layout.margins: Kirigami.Units.smallSpacing
rotation: channelData.angle
}
Label {
text: channelData.text
color: isPlaying ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: Kirigami.Units.smallSpacing
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignTop
wrapMode: Text.WordWrap
}
}
Label {
text: channelData.text
color: isPlaying ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: Kirigami.Units.smallSpacing
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignTop
wrapMode: Text.WordWrap
onClicked: {
testError.visible = false;
// there is no subwoofer sound in the freedesktop theme https://gitlab.freedesktop.org/xdg/xdg-sound-theme/-/issues/7
tester.testChannel(modelData === "lfe" ? "rear-center" : modelData);
}
}
// there is no subwoofer sound in the freedesktop theme https://gitlab.freedesktop.org/xdg/xdg-sound-theme/-/issues/7
onClicked: tester.testChannel(modelData === "lfe" ? "rear-center" : modelData)
}
}
Label {
text: i18nd("kcm_pulseaudio", "Click on any speaker to test sound")
font: Kirigami.Theme.smallFont
Layout.row: 3
Layout.columnSpan: 3
Layout.alignment: Qt.AlignCenter
Layout.topMargin: Kirigami.Units.gridUnit
}
}
}
......
......@@ -21,14 +21,13 @@ void finish_callback(ca_context *c, unsigned int id, int error_code, void *userd
{
Q_UNUSED(c)
Q_UNUSED(id)
Q_UNUSED(error_code)
if (userdata == nullptr) {
return;
}
CallbackData *cb_data = static_cast<CallbackData *>(userdata);
cb_data->object->playingFinished(cb_data->name);
cb_data->object->playingFinished(cb_data->name, error_code);
delete (cb_data);
};
......@@ -63,7 +62,7 @@ void SpeakerTest::testChannel(const QString &name)
snprintf(dev, sizeof(dev), "%lu", (unsigned long)m_sink->index());
ca_context_change_device(context, dev);
QString sound_name = QStringLiteral("audio-channel-") + name;
QString sound_name = QStringLiteral("audio-channel-%1").arg(name);
void *cb_data = new CallbackData{this, name};
ca_proplist *proplist;
......@@ -74,14 +73,21 @@ void SpeakerTest::testChannel(const QString &name)
ca_proplist_sets(proplist, CA_PROP_CANBERRA_FORCE_CHANNEL, name.toLatin1().data());
ca_proplist_sets(proplist, CA_PROP_CANBERRA_ENABLE, "1");
ca_proplist_sets(proplist, CA_PROP_EVENT_ID, sound_name.toLatin1().data());
if (ca_context_play_full(context, 0, proplist, finish_callback, cb_data) != CA_SUCCESS) {
// Try a different sound name.
ca_proplist_sets(proplist, CA_PROP_EVENT_ID, "audio-test-signal");
if (ca_context_play_full(context, 0, proplist, finish_callback, cb_data) != CA_SUCCESS) {
// Finaly try this... if this doesn't work, then stuff it.
ca_proplist_sets(proplist, CA_PROP_EVENT_ID, "bell-window-system");
ca_context_play_full(context, 0, proplist, finish_callback, cb_data);
int error_code = CA_SUCCESS;
for (const QString &soundName : {sound_name,
QStringLiteral("audio-test-signal"), // Fallback sounds
QStringLiteral("bell-window-system"),
QString()}) {
if (soundName.isEmpty()) {
// We are here because all of the fallback sounds failed to play
playingFinished(name, error_code);
break;
}
ca_proplist_sets(proplist, CA_PROP_EVENT_ID, soundName.toLatin1().data());
error_code = ca_context_play_full(context, 0, proplist, finish_callback, cb_data);
if (error_code == CA_SUCCESS) {
break;
}
}
......@@ -94,8 +100,12 @@ QStringList SpeakerTest::playingChannels() const
return m_playingChannels;
}
void SpeakerTest::playingFinished(const QString &name)
void SpeakerTest::playingFinished(const QString &name, int errorCode)
{
m_playingChannels.removeOne(name);
Q_EMIT playingChannelsChanged();
if (errorCode != CA_SUCCESS) {
Q_EMIT showErrorMessage(ca_strerror(errorCode));
};
}
......@@ -23,9 +23,10 @@ public:
QStringList playingChannels() const;
Q_SIGNAL void playingChannelsChanged();
void playingFinished(const QString &name);
void playingFinished(const QString &name, int errorCode);
Q_INVOKABLE void testChannel(const QString &name);
Q_SIGNAL void showErrorMessage(const QString &message);
private:
QPulseAudio::Sink *m_sink;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment