diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b8a0f58e..9804eed9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,10 @@ find_package(Gettext REQUIRED) find_package(PkgConfig REQUIRED) find_package(Protobuf REQUIRED) find_package(FFTW3) +find_package(ALSA) +if(ALSA_FOUND) + set(HAVE_ALSA ON) +endif() find_library(PROTOBUF_STATIC_LIBRARY libprotobuf.a libprotobuf) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f0a8a569b..8f258af17 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1187,6 +1187,12 @@ optional_source(HAVE_LIBPULSE engines/pulsedevicefinder.cpp ) +# ALSA integration +optional_source(HAVE_ALSA + SOURCES + engines/alsadevicefinder.cpp +) + # Hack to add Clementine to the Unity system tray whitelist optional_source(LINUX SOURCES core/ubuntuunityhack.cpp @@ -1305,6 +1311,10 @@ if(HAVE_LIBPULSE) target_link_libraries(clementine_lib ${LIBPULSE_LIBRARIES}) endif() +if(HAVE_ALSA) + target_link_libraries(clementine_lib ${ALSA_LIBRARIES}) +endif(HAVE_ALSA) + if (APPLE) target_link_libraries(clementine_lib "-framework AppKit" diff --git a/src/config.h.in b/src/config.h.in index 831d74a28..4475e28dc 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -36,6 +36,7 @@ #cmakedefine HAVE_LIBLASTFM1 #cmakedefine HAVE_LIBMTP #cmakedefine HAVE_LIBPULSE +#cmakedefine HAVE_ALSA #cmakedefine HAVE_MOODBAR #cmakedefine HAVE_SEAFILE #cmakedefine HAVE_SKYDRIVE diff --git a/src/engines/alsadevicefinder.cpp b/src/engines/alsadevicefinder.cpp new file mode 100644 index 000000000..929676d8f --- /dev/null +++ b/src/engines/alsadevicefinder.cpp @@ -0,0 +1,107 @@ +/* This file is part of Clementine. + Copyright 2017, Jonas Kvinge + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "alsadevicefinder.h" +#include "devicefinder.h" + +AlsaDeviceFinder::AlsaDeviceFinder() : DeviceFinder("alsasink") {} + +QList AlsaDeviceFinder::ListDevices() { + QList ret; + + snd_pcm_stream_name(SND_PCM_STREAM_PLAYBACK); + + int card = -1; + snd_ctl_card_info_t* cardinfo; + snd_ctl_card_info_alloca(&cardinfo); + while (true) { + int result = snd_card_next(&card); + if (result < 0) { + qLog(Error) << "Unable to get soundcard:" << snd_strerror(result); + break; + } + if (card < 0) break; + + char str[32]; + snprintf(str, sizeof(str) - 1, "hw:%d", card); + + snd_ctl_t* handle; + result = snd_ctl_open(&handle, str, 0); + if (result < 0) { + qLog(Error) << "Unable to open soundcard" << card << ":" + << snd_strerror(result); + continue; + } + BOOST_SCOPE_EXIT(&handle) { snd_ctl_close(handle); } + BOOST_SCOPE_EXIT_END + + result = snd_ctl_card_info(handle, cardinfo); + if (result < 0) { + qLog(Error) << "Control hardware failure for card" << card << ":" + << snd_strerror(result); + continue; + } + + int dev = -1; + snd_pcm_info_t* pcminfo; + snd_pcm_info_alloca(&pcminfo); + while (true) { + result = snd_ctl_pcm_next_device(handle, &dev); + if (result < 0) { + qLog(Error) << "Unable to get PCM for card" << card << ":" + << snd_strerror(result); + continue; + } + if (dev < 0) break; + + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK); + + result = snd_ctl_pcm_info(handle, pcminfo); + if (result < 0) { + if (result != -ENOENT) + qLog(Error) << "Unable to get control digital audio info for card" + << card << ":" << snd_strerror(result); + continue; + } + + Device device; + device.description = QString("%1 %2") + .arg(snd_ctl_card_info_get_name(cardinfo)) + .arg(snd_pcm_info_get_name(pcminfo)); + device.device_property_value = QString("hw:%1,%2").arg(card).arg(dev); + device.icon_name = GuessIconName(device.description); + ret.append(device); + } + } + + snd_config_update_free_global(); + + return ret; +} diff --git a/src/engines/alsadevicefinder.h b/src/engines/alsadevicefinder.h new file mode 100644 index 000000000..3148ade31 --- /dev/null +++ b/src/engines/alsadevicefinder.h @@ -0,0 +1,33 @@ +/* This file is part of Clementine. + Copyright 2017, Jonas Kvinge + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#ifndef ALSADEVICEFINDER_H +#define ALSADEVICEFINDER_H + +#include + +#include "devicefinder.h" + +class AlsaDeviceFinder : public DeviceFinder { + public: + AlsaDeviceFinder(); + + virtual bool Initialise() { return true; } + virtual QList ListDevices(); +}; + +#endif // ALSADEVICEFINDER_H diff --git a/src/engines/gstengine.cpp b/src/engines/gstengine.cpp index 86b712fc5..222e95943 100644 --- a/src/engines/gstengine.cpp +++ b/src/engines/gstengine.cpp @@ -58,6 +58,10 @@ #include "engines/pulsedevicefinder.h" #endif +#ifdef HAVE_ALSA +#include "engines/alsadevicefinder.h" +#endif + #ifdef Q_OS_DARWIN #include "engines/osxdevicefinder.h" #endif @@ -76,6 +80,7 @@ #endif using std::shared_ptr; +using std::unique_ptr; using std::vector; const char* GstEngine::kSettingsGroup = "GstEngine"; @@ -158,10 +163,23 @@ void GstEngine::InitialiseGstreamer() { plugin_names.insert(plugin.name); } - QList device_finders; + bool pa(false); #ifdef HAVE_LIBPULSE - device_finders.append(new PulseDeviceFinder); + unique_ptr finder_pulse(new PulseDeviceFinder); + if (plugin_names.contains(finder_pulse->gstreamer_sink()) && + finder_pulse->Initialise()) { + pa = true; + device_finders_.append(finder_pulse.release()); + } #endif + + QList device_finders; + if (!pa) { // Only add alsa devices if pulseaudio is not enabled to avoid + // confusion. +#ifdef HAVE_ALSA + device_finders.append(new AlsaDeviceFinder); +#endif + } #ifdef Q_OS_DARWIN device_finders.append(new OsxDeviceFinder); #endif