diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 8eaa9b9..eb32fed 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/checkout@v3 - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y install libxrandr-dev libfreeimage-dev libxinerama-dev libxcursor-dev libxi-dev libgl-dev libglew-dev freeglut3-dev libsdl2-dev liblz4-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libxxf86vm-dev libglm-dev libglfw3-dev libmpv-dev mpv libmpv1 + run: sudo apt-get update && sudo apt-get -y install libxrandr-dev libfreeimage-dev libxinerama-dev libxcursor-dev libxi-dev libgl-dev libglew-dev freeglut3-dev libsdl2-dev liblz4-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libxxf86vm-dev libglm-dev libglfw3-dev libmpv-dev mpv libmpv1 libpulse-dev libpulse0 - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. diff --git a/CMakeLists.txt b/CMakeLists.txt index 82ff662..652c07b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ find_package(MPV REQUIRED) find_package(LZ4 REQUIRED) find_package(FFMPEG REQUIRED) find_package(FreeImage REQUIRED) +find_package(PulseAudio REQUIRED) include_directories( ${MPV_INCLUDE_DIR} @@ -36,6 +37,7 @@ include_directories( ${SDL2_INCLUDE_DIRS} ${FFMPEG_INCLUDE_DIR} ${FREEIMAGE_INCLUDE_DIR} + ${PULSEAUDIO_INCLUDE_DIR} src include) @@ -75,6 +77,11 @@ add_executable( src/WallpaperEngine/Core/Core.h src/WallpaperEngine/Core/Core.cpp + src/WallpaperEngine/Audio/Detectors/CAudioPlayingDetector.cpp + src/WallpaperEngine/Audio/Detectors/CAudioPlayingDetector.h + src/WallpaperEngine/Audio/Detectors/CPulseAudioPlayingDetector.cpp + src/WallpaperEngine/Audio/Detectors/CPulseAudioPlayingDetector.cpp + src/WallpaperEngine/Audio/Drivers/CAudioDriver.cpp src/WallpaperEngine/Audio/Drivers/CAudioDriver.h src/WallpaperEngine/Audio/Drivers/CSDLAudioDriver.cpp @@ -266,6 +273,7 @@ target_link_libraries(linux-wallpaperengine ${FFMPEG_LIBRARIES} ${FREEIMAGE_LIBRARIES} ${MPV_LIBRARY} + ${PULSEAUDIO_LIBRARY} glfw) file(CREATE_LINK linux-wallpaperengine wallengine SYMBOLIC) diff --git a/CMakeModules/FindPulseAudio.cmake b/CMakeModules/FindPulseAudio.cmake new file mode 100644 index 0000000..35bcbc2 --- /dev/null +++ b/CMakeModules/FindPulseAudio.cmake @@ -0,0 +1,71 @@ +# Try to find the PulseAudio library +# +# Once done this will define: +# +# PULSEAUDIO_FOUND - system has the PulseAudio library +# PULSEAUDIO_INCLUDE_DIR - the PulseAudio include directory +# PULSEAUDIO_LIBRARY - the libraries needed to use PulseAudio +# PULSEAUDIO_MAINLOOP_LIBRARY - the libraries needed to use PulsAudio Mainloop +# +# The minimum required version of PulseAudio can be specified using the +# standard syntax, e.g. find_package(PulseAudio 1.0) + +# Copyright (c) 2008, Matthias Kretz, +# Copyright (c) 2009, Marcus Hufgard, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +# Support PULSEAUDIO_MINIMUM_VERSION for compatibility: +if(NOT PulseAudio_FIND_VERSION) + set(PulseAudio_FIND_VERSION "${PULSEAUDIO_MINIMUM_VERSION}") +endif(NOT PulseAudio_FIND_VERSION) + +# the minimum version of PulseAudio we require +if(NOT PulseAudio_FIND_VERSION) + set(PulseAudio_FIND_VERSION "0.9.9") +endif(NOT PulseAudio_FIND_VERSION) + +if (NOT WIN32) + include(FindPkgConfig) + pkg_check_modules(PC_PULSEAUDIO QUIET libpulse>=${PulseAudio_FIND_VERSION}) + pkg_check_modules(PC_PULSEAUDIO_MAINLOOP QUIET libpulse-mainloop-glib) +endif (NOT WIN32) + +find_path(PULSEAUDIO_INCLUDE_DIR pulse/pulseaudio.h + HINTS + ${PC_PULSEAUDIO_INCLUDEDIR} + ${PC_PULSEAUDIO_INCLUDE_DIRS} + ) + +find_library(PULSEAUDIO_LIBRARY NAMES pulse libpulse + HINTS + ${PC_PULSEAUDIO_LIBDIR} + ${PC_PULSEAUDIO_LIBRARY_DIRS} + ) + +find_library(PULSEAUDIO_MAINLOOP_LIBRARY NAMES pulse-mainloop pulse-mainloop-glib libpulse-mainloop-glib + HINTS + ${PC_PULSEAUDIO_LIBDIR} + ${PC_PULSEAUDIO_LIBRARY_DIRS} + ) + +# Store the version number in the cache, so we don't have to search every time again: +if (PULSEAUDIO_INCLUDE_DIR AND NOT PULSEAUDIO_VERSION) + + # get PulseAudio's version from its version.h, and compare it with our minimum version + file(STRINGS "${PULSEAUDIO_INCLUDE_DIR}/pulse/version.h" pulse_version_h + REGEX ".*pa_get_headers_version\\(\\).*" + ) + string(REGEX REPLACE ".*pa_get_headers_version\\(\\)\ \\(\"([0-9]+\\.[0-9]+\\.[0-9]+)[^\"]*\"\\).*" "\\1" + _PULSEAUDIO_VERSION "${pulse_version_h}") + + set(PULSEAUDIO_VERSION "${_PULSEAUDIO_VERSION}" CACHE STRING "Version number of PulseAudio" FORCE) +endif (PULSEAUDIO_INCLUDE_DIR AND NOT PULSEAUDIO_VERSION) + +# Use the new extended syntax of find_package_handle_standard_args(), which also handles version checking: +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(PulseAudio REQUIRED_VARS PULSEAUDIO_LIBRARY PULSEAUDIO_INCLUDE_DIR + VERSION_VAR PULSEAUDIO_VERSION ) + +mark_as_advanced(PULSEAUDIO_INCLUDE_DIR PULSEAUDIO_LIBRARY PULSEAUDIO_MAINLOOP_LIBRARY) diff --git a/README.md b/README.md index be6ac3f..3a28e70 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Wallpaper Engine is a software designed by [Kristjan Skutta](https://store.steam - GLUT - FreeImage - MPV +- PulseAudio ## Commands ``` diff --git a/src/WallpaperEngine/Application/CApplicationContext.cpp b/src/WallpaperEngine/Application/CApplicationContext.cpp index cc8c9f3..f056dfb 100644 --- a/src/WallpaperEngine/Application/CApplicationContext.cpp +++ b/src/WallpaperEngine/Application/CApplicationContext.cpp @@ -25,6 +25,7 @@ struct option long_options[] = { { "screenshot", required_argument, nullptr, 'c' }, { "list-properties", no_argument, nullptr, 'l' }, { "set-property", required_argument, nullptr, 'o' }, + { "noautomute", no_argument, nullptr, 'm' }, { nullptr, 0, nullptr, 0 } }; @@ -66,7 +67,8 @@ CApplicationContext::CApplicationContext (int argc, char* argv[]) .audio = { .enabled = true, - .volume = 127, + .volume = 15, + .automute = true }, .screenshot = { @@ -80,7 +82,7 @@ CApplicationContext::CApplicationContext (int argc, char* argv[]) std::string lastScreen; - while ((c = getopt_long (argc, argv, "b:r:p:d:shf:a:w:", long_options, nullptr)) != -1) + while ((c = getopt_long (argc, argv, "b:r:p:d:shf:a:w:m", long_options, nullptr)) != -1) { switch (c) { @@ -170,9 +172,12 @@ CApplicationContext::CApplicationContext (int argc, char* argv[]) case 'c': this->settings.screenshot.take = true; - this->settings.screenshot.path = stringPathFixes (optarg); + this->settings.screenshot.path = stringPathFixes (optarg); break; + case 'm': + this->settings.audio.automute = false; + break; default: sLog.out ("Default on path parsing: ", optarg); break; @@ -259,6 +264,7 @@ void CApplicationContext::printHelp (const char* route) sLog.out ("options:"); sLog.out ("\t--silent\t\t\t\t\tMutes all the sound the wallpaper might produce"); sLog.out ("\t--volume \t\t\tSets the volume for all the sounds in the background"); + sLog.out ("\t--noautomute\t\t\t\tDisables the automute when an app is playing sound"); sLog.out ("\t--screen-root \tDisplay as screen's background"); sLog.out ("\t--window \tRuns in window mode, geometry has to be XxYxWxH and sets the position and size of the window"); sLog.out ("\t--fps \t\t\tLimits the FPS to the given number, useful to keep battery consumption low"); diff --git a/src/WallpaperEngine/Application/CApplicationContext.h b/src/WallpaperEngine/Application/CApplicationContext.h index b9901c1..043aca9 100644 --- a/src/WallpaperEngine/Application/CApplicationContext.h +++ b/src/WallpaperEngine/Application/CApplicationContext.h @@ -78,6 +78,8 @@ namespace WallpaperEngine::Application bool enabled; /** Sound volume (0-128) */ int volume; + /** If the audio must be muted if something else is playing sound */ + bool automute; } audio; /** diff --git a/src/WallpaperEngine/Application/CWallpaperApplication.cpp b/src/WallpaperEngine/Application/CWallpaperApplication.cpp index cb2091e..1a5edf9 100644 --- a/src/WallpaperEngine/Application/CWallpaperApplication.cpp +++ b/src/WallpaperEngine/Application/CWallpaperApplication.cpp @@ -11,6 +11,7 @@ #include "WallpaperEngine/Render/Drivers/Output/CX11Output.h" #include "WallpaperEngine/Application/CApplicationState.h" #include "WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.h" +#include "WallpaperEngine/Audio/Detectors/CPulseAudioPlayingDetector.h" #include @@ -265,8 +266,10 @@ namespace WallpaperEngine::Application void CWallpaperApplication::show () { + // audio playing detector + WallpaperEngine::Audio::Detectors::CPulseAudioPlayingDetector audioDetector (this->m_context); // initialize sdl audio driver - WallpaperEngine::Audio::Drivers::CSDLAudioDriver audioDriver (this->m_context); + WallpaperEngine::Audio::Drivers::CSDLAudioDriver audioDriver (this->m_context, audioDetector); // initialize audio context WallpaperEngine::Audio::CAudioContext audioContext (audioDriver); // initialize OpenGL driver diff --git a/src/WallpaperEngine/Audio/Detectors/CAudioPlayingDetector.cpp b/src/WallpaperEngine/Audio/Detectors/CAudioPlayingDetector.cpp new file mode 100644 index 0000000..33f70e2 --- /dev/null +++ b/src/WallpaperEngine/Audio/Detectors/CAudioPlayingDetector.cpp @@ -0,0 +1,14 @@ +#include "CAudioPlayingDetector.h" + +namespace WallpaperEngine::Audio::Detectors +{ + CAudioPlayingDetector::CAudioPlayingDetector (Application::CApplicationContext& appContext) : + m_applicationContext (appContext) + { + } + + Application::CApplicationContext& CAudioPlayingDetector::getApplicationContext () + { + return this->m_applicationContext; + } +} \ No newline at end of file diff --git a/src/WallpaperEngine/Audio/Detectors/CAudioPlayingDetector.h b/src/WallpaperEngine/Audio/Detectors/CAudioPlayingDetector.h new file mode 100644 index 0000000..1d32521 --- /dev/null +++ b/src/WallpaperEngine/Audio/Detectors/CAudioPlayingDetector.h @@ -0,0 +1,29 @@ +#pragma once + +#include "WallpaperEngine/Application/CApplicationContext.h" + +namespace WallpaperEngine::Application +{ + class CApplicationContext; +} + +namespace WallpaperEngine::Audio::Detectors +{ + class CAudioPlayingDetector + { + public: + CAudioPlayingDetector (Application::CApplicationContext& appContext); + + /** + * @return If any kind of sound is currently playing on the default audio device + */ + virtual bool anythingPlaying () = 0; + /** + * @return The application context using this detector + */ + [[nodiscard]] Application::CApplicationContext& getApplicationContext (); + + private: + Application::CApplicationContext& m_applicationContext; + }; +} diff --git a/src/WallpaperEngine/Audio/Detectors/CPulseAudioPlayingDetector.cpp b/src/WallpaperEngine/Audio/Detectors/CPulseAudioPlayingDetector.cpp new file mode 100644 index 0000000..2699378 --- /dev/null +++ b/src/WallpaperEngine/Audio/Detectors/CPulseAudioPlayingDetector.cpp @@ -0,0 +1,87 @@ +#include "CPulseAudioPlayingDetector.h" +#include "WallpaperEngine/Logging/CLog.h" + +namespace WallpaperEngine::Audio::Detectors +{ + void sinkInputInfoCallback (pa_context* context, const pa_sink_input_info* info, int eol, void* userdata) + { + auto* detector = static_cast (userdata); + + if (info == nullptr) + return; + + if (info->proplist == nullptr) + return; + + // get processid + const char* value = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_PROCESS_ID); + + if (atoi (value) != getpid () && pa_cvolume_avg (&info->volume) != PA_VOLUME_MUTED) + detector->setIsPlaying (true); + } + + void defaultSinkInfoCallback (pa_context* context, const pa_server_info* info, void* userdata) + { + if (info == nullptr) + return; + + pa_operation* op = pa_context_get_sink_input_info_list (context, sinkInputInfoCallback, userdata); + + pa_operation_unref (op); + } + + CPulseAudioPlayingDetector::CPulseAudioPlayingDetector (Application::CApplicationContext& appContext) : + CAudioPlayingDetector (appContext), + m_mainloop (nullptr), + m_mainloopApi (nullptr), + m_context (nullptr), + m_isPlaying (false) + { + this->m_mainloop = pa_mainloop_new (); + this->m_mainloopApi = pa_mainloop_get_api (this->m_mainloop); + this->m_context = pa_context_new (this->m_mainloopApi, "wallpaperengine"); + + pa_context_connect (this->m_context, nullptr, PA_CONTEXT_NOFLAGS, nullptr); + + // lock until pulseaudio allows connection + while (pa_context_get_state (this->m_context) != PA_CONTEXT_READY) + pa_mainloop_iterate (this->m_mainloop, 1, nullptr); + } + + CPulseAudioPlayingDetector::~CPulseAudioPlayingDetector () + { + if (this->m_context) + { + pa_context_disconnect (this->m_context); + pa_context_unref (this->m_context); + } + + if (this->m_mainloop) + pa_mainloop_free (this->m_mainloop); + } + + void CPulseAudioPlayingDetector::setIsPlaying (bool newState) + { + this->m_isPlaying = newState; + } + + bool CPulseAudioPlayingDetector::anythingPlaying () + { + if (!this->getApplicationContext ().settings.audio.automute) + return false; + + // reset playing state + this->setIsPlaying (false); + + // start discovery of sinks + pa_operation* op = pa_context_get_server_info (this->m_context, defaultSinkInfoCallback, (void*) this); + + // wait until all the operations are done + while (pa_operation_get_state (op) == PA_OPERATION_RUNNING) + pa_mainloop_iterate (this->m_mainloop, 1, nullptr); + + pa_operation_unref (op); + + return this->m_isPlaying; + } +} \ No newline at end of file diff --git a/src/WallpaperEngine/Audio/Detectors/CPulseAudioPlayingDetector.h b/src/WallpaperEngine/Audio/Detectors/CPulseAudioPlayingDetector.h new file mode 100644 index 0000000..6c40003 --- /dev/null +++ b/src/WallpaperEngine/Audio/Detectors/CPulseAudioPlayingDetector.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include "CAudioPlayingDetector.h" +#include + +namespace WallpaperEngine::Audio::Detectors +{ + class CPulseAudioPlayingDetector : public CAudioPlayingDetector + { + public: + explicit CPulseAudioPlayingDetector (Application::CApplicationContext& appContext); + ~CPulseAudioPlayingDetector (); + + [[nodiscard]] bool anythingPlaying () override; + void setIsPlaying (bool newState); + + private: + bool m_isPlaying; + + pa_mainloop* m_mainloop; + pa_mainloop_api* m_mainloopApi; + pa_context* m_context; + pa_operation* m_operation; + }; +} diff --git a/src/WallpaperEngine/Audio/Drivers/CAudioDriver.cpp b/src/WallpaperEngine/Audio/Drivers/CAudioDriver.cpp index 21b5dc9..5041aa2 100644 --- a/src/WallpaperEngine/Audio/Drivers/CAudioDriver.cpp +++ b/src/WallpaperEngine/Audio/Drivers/CAudioDriver.cpp @@ -2,8 +2,9 @@ namespace WallpaperEngine::Audio::Drivers { - CAudioDriver::CAudioDriver (Application::CApplicationContext& applicationContext) : - m_applicationContext (applicationContext) + CAudioDriver::CAudioDriver (Application::CApplicationContext& applicationContext, Detectors::CAudioPlayingDetector& detector) : + m_applicationContext (applicationContext), + m_detector (detector) { } @@ -11,4 +12,8 @@ namespace WallpaperEngine::Audio::Drivers { return this->m_applicationContext; } + Detectors::CAudioPlayingDetector& CAudioDriver::getAudioDetector () + { + return this->m_detector; + } } diff --git a/src/WallpaperEngine/Audio/Drivers/CAudioDriver.h b/src/WallpaperEngine/Audio/Drivers/CAudioDriver.h index f3ade47..e860b9d 100644 --- a/src/WallpaperEngine/Audio/Drivers/CAudioDriver.h +++ b/src/WallpaperEngine/Audio/Drivers/CAudioDriver.h @@ -2,30 +2,26 @@ #include +#include "WallpaperEngine/Audio/Detectors/CAudioPlayingDetector.h" #include "WallpaperEngine/Application/CApplicationContext.h" #include "WallpaperEngine/Audio/CAudioStream.h" -namespace WallpaperEngine::Audio -{ - class CAudioStream; -} - -namespace WallpaperEngine::Application -{ - class CApplicationContext; -} - namespace WallpaperEngine { namespace Application { - + class CApplicationContext; } namespace Audio { class CAudioStream; + namespace Detectors + { + class CAudioPlayingDetector; + } + namespace Drivers { /** @@ -34,7 +30,7 @@ namespace WallpaperEngine class CAudioDriver { public: - explicit CAudioDriver (Application::CApplicationContext& applicationContext); + explicit CAudioDriver (Application::CApplicationContext& applicationContext, Detectors::CAudioPlayingDetector& detector); /** * Registers the given stream in the driver for playing @@ -61,9 +57,14 @@ namespace WallpaperEngine * @return The application context under which the audio driver is initialized */ Application::CApplicationContext& getApplicationContext (); + /** + * @return The audio playing detector to use to stop playing sound when something else starts playing + */ + [[nodiscard]] Detectors::CAudioPlayingDetector& getAudioDetector (); private: Application::CApplicationContext& m_applicationContext; + Detectors::CAudioPlayingDetector& m_detector; }; } } diff --git a/src/WallpaperEngine/Audio/Drivers/CSDLAudioDriver.cpp b/src/WallpaperEngine/Audio/Drivers/CSDLAudioDriver.cpp index b3c9254..82386ba 100644 --- a/src/WallpaperEngine/Audio/Drivers/CSDLAudioDriver.cpp +++ b/src/WallpaperEngine/Audio/Drivers/CSDLAudioDriver.cpp @@ -13,6 +13,10 @@ void audio_callback (void* userdata, uint8_t* streamData, int length) memset (streamData, 0, length); + // if audio is playing do not do anything here! + if (driver->getAudioDetector ().anythingPlaying ()) + return; + for (const auto& buffer : driver->getStreams ()) { uint8_t* streamDataPointer = streamData; @@ -70,8 +74,8 @@ void audio_callback (void* userdata, uint8_t* streamData, int length) } } -CSDLAudioDriver::CSDLAudioDriver (Application::CApplicationContext& applicationContext) : - CAudioDriver (applicationContext), +CSDLAudioDriver::CSDLAudioDriver (Application::CApplicationContext& applicationContext, Detectors::CAudioPlayingDetector& detector) : + CAudioDriver (applicationContext, detector), m_initialized (false), m_audioSpec () { diff --git a/src/WallpaperEngine/Audio/Drivers/CSDLAudioDriver.h b/src/WallpaperEngine/Audio/Drivers/CSDLAudioDriver.h index 21dcd9c..bef26ec 100644 --- a/src/WallpaperEngine/Audio/Drivers/CSDLAudioDriver.h +++ b/src/WallpaperEngine/Audio/Drivers/CSDLAudioDriver.h @@ -29,7 +29,7 @@ namespace WallpaperEngine::Audio::Drivers class CSDLAudioDriver : public CAudioDriver { public: - CSDLAudioDriver (Application::CApplicationContext& applicationContext); + CSDLAudioDriver (Application::CApplicationContext& applicationContext, Detectors::CAudioPlayingDetector& detector); ~CSDLAudioDriver (); /** @inheritdoc */ diff --git a/src/WallpaperEngine/Render/Shaders/Compiler.h b/src/WallpaperEngine/Render/Shaders/Compiler.h index 356e0cf..0a6f311 100644 --- a/src/WallpaperEngine/Render/Shaders/Compiler.h +++ b/src/WallpaperEngine/Render/Shaders/Compiler.h @@ -83,15 +83,15 @@ namespace WallpaperEngine::Render::Shaders /** * @return The list of parameters available for this shader with their default values */ - const std::vector & getParameters () const; + [[nodiscard]] const std::vector & getParameters () const; /** * @return The list of combos available for this shader after compilation */ - std::map * getCombos () const; + [[nodiscard]] std::map * getCombos () const; /** * @return The list of textures inferred from the shader's code */ - const std::map & getTextures () const; + [[nodiscard]] const std::map & getTextures () const; private: /**