1 /*
2 * Strawberry Music Player
3 * Copyright 2017-2021, Jonas Kvinge <jonas@jkvinge.net>
4 *
5 * Strawberry is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * Strawberry is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #include "config.h"
21
22 #include <cstdio>
23 #include <cerrno>
24 #include <alsa/asoundlib.h>
25 #include <boost/scope_exit.hpp>
26
27 #include <QList>
28 #include <QVariant>
29 #include <QString>
30 #include <QtDebug>
31
32 #include <core/logging.h>
33
34 #include "devicefinder.h"
35 #include "alsadevicefinder.h"
36
AlsaDeviceFinder()37 AlsaDeviceFinder::AlsaDeviceFinder() : DeviceFinder("alsa", { "alsa","alsasink" }) {}
38
ListDevices()39 QList<DeviceFinder::Device> AlsaDeviceFinder::ListDevices() {
40
41 QList<Device> ret;
42
43 snd_pcm_stream_name(SND_PCM_STREAM_PLAYBACK);
44
45 int card = -1;
46 snd_ctl_card_info_t *cardinfo = nullptr;
47 snd_ctl_card_info_alloca(&cardinfo);
48 while (true) {
49
50 int result = snd_card_next(&card);
51 if (result < 0) {
52 qLog(Error) << "Unable to get soundcard:" << snd_strerror(result);
53 break;
54 }
55 if (card < 0) break;
56
57 char str[32];
58 snprintf(str, sizeof(str) - 1, "hw:%d", card);
59
60 snd_ctl_t *handle = nullptr;
61 result = snd_ctl_open(&handle, str, 0);
62 if (result < 0) {
63 qLog(Error) << "Unable to open soundcard" << card << ":" << snd_strerror(result);
64 continue;
65 }
66 BOOST_SCOPE_EXIT(&handle) { snd_ctl_close(handle); } // clazy:exclude=rule-of-three NOLINT(google-explicit-constructor)
67 BOOST_SCOPE_EXIT_END
68
69 result = snd_ctl_card_info(handle, cardinfo);
70 if (result < 0) {
71 qLog(Error) << "Control hardware failure for card" << card << ":" << snd_strerror(result);
72 continue;
73 }
74
75 int dev = -1;
76 snd_pcm_info_t *pcminfo = nullptr;
77 snd_pcm_info_alloca(&pcminfo);
78 while (true) {
79
80 result = snd_ctl_pcm_next_device(handle, &dev);
81 if (result < 0) {
82 qLog(Error) << "Unable to get PCM for card" << card << ":" << snd_strerror(result);
83 continue;
84 }
85 if (dev < 0) break;
86
87 snd_pcm_info_set_device(pcminfo, dev);
88 snd_pcm_info_set_subdevice(pcminfo, 0);
89 snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK);
90
91 result = snd_ctl_pcm_info(handle, pcminfo);
92 if (result < 0) {
93 if (result != -ENOENT) qLog(Error) << "Unable to get control digital audio info for card" << card << ":" << snd_strerror(result);
94 continue;
95 }
96
97 Device device;
98 device.description = QString("%1 %2").arg(snd_ctl_card_info_get_name(cardinfo), snd_pcm_info_get_name(pcminfo));
99 device.iconname = GuessIconName(device.description);
100 device.card = card;
101 device.device = dev;
102
103 device.value = QString("hw:%1,%2").arg(card).arg(dev);
104 ret.append(device);
105 device.value = QString("plughw:%1,%2").arg(card).arg(dev);
106 ret.append(device);
107
108 }
109 }
110
111 snd_config_update_free_global();
112
113 return ret;
114 }
115