소스 검색

finally found and fixed hotswapping bug on audio vizualizer

UwU 5 일 전
부모
커밋
72b356c343

+ 17 - 0
src/WallpaperEngine/Application/WallpaperApplication.cpp

@@ -564,6 +564,7 @@ void WallpaperApplication::advancePlaylist (
 	auto project = this->loadBackground (nextPath.string ());
 
 	this->setupPropertiesForProject (*project);
+	this->ensureAudioCapture (*project);
 
 	// the outgoing project must outlive setWallpaper(), the old wallpaper keeps a reference into it
 	auto outgoing = std::move (this->m_backgrounds[screen]);
@@ -971,6 +972,7 @@ void WallpaperApplication::checkHotswapRequest () {
 	    this->setupPropertiesForProject (*project);
 	    this->setupAudioSensitivityForProject (*project);
 	    this->setupSoundVolumeForProject (*project);
+	    this->ensureAudioCapture (*project);
 
 	    auto outgoing = std::move (background);
 	    background = std::move (project);
@@ -1959,6 +1961,7 @@ void WallpaperApplication::setupAudio () {
     if (audioProcessingRequired && this->m_context.settings.audio.audioprocessing) {
 	this->m_audioRecorder
 	    = std::make_unique<WallpaperEngine::Audio::Drivers::Recorders::PulseAudioPlaybackRecorder> ();
+	this->m_audioCapturing = true;
     } else {
 	this->m_audioRecorder = std::make_unique<WallpaperEngine::Audio::Drivers::Recorders::PlaybackRecorder> ();
     }
@@ -1979,6 +1982,20 @@ void WallpaperApplication::setupAudio () {
     m_audioContext = std::make_unique<WallpaperEngine::Audio::AudioContext> (*m_audioDriver);
 }
 
+void WallpaperApplication::ensureAudioCapture (const Project& project) {
+    if (this->m_audioCapturing || !project.supportsAudioProcessing || !this->m_context.settings.audio.audioprocessing
+	|| !this->m_audioDriver) {
+	return;
+    }
+
+    sLog.out ("Starting audio capture for ", project.title);
+
+    this->m_previousAudioRecorder = std::move (this->m_audioRecorder);
+    this->m_audioRecorder = std::make_unique<WallpaperEngine::Audio::Drivers::Recorders::PulseAudioPlaybackRecorder> ();
+    this->m_audioDriver->setRecorder (*this->m_audioRecorder);
+    this->m_audioCapturing = true;
+}
+
 void WallpaperApplication::prepareOutputs () {
     m_renderContext
 	= std::make_unique<WallpaperEngine::Render::RenderContext> (*m_videoDriver, *this, *this->m_mediaSource);

+ 5 - 0
src/WallpaperEngine/Application/WallpaperApplication.h

@@ -99,6 +99,8 @@ private:
     void setupBrowser ();
     void setupOutput ();
     void setupAudio ();
+    /** Starts audio capture for a wallpaper loaded after startup, setupAudio() only saw the first ones */
+    void ensureAudioCapture (const Project& project);
     void prepareOutputs ();
     void setupOpenGLDebugging ();
     void takeScreenshot (const std::filesystem::path& filename) const;
@@ -218,6 +220,9 @@ private:
     std::unique_ptr<WallpaperEngine::Audio::AudioContext> m_audioContext = nullptr;
     std::unique_ptr<WallpaperEngine::Audio::Drivers::SDLAudioDriver> m_audioDriver = nullptr;
     std::unique_ptr<WallpaperEngine::Audio::Drivers::Recorders::PlaybackRecorder> m_audioRecorder = nullptr;
+    // the recorder replaced by ensureAudioCapture(), the outgoing wallpaper's passes still point into it
+    std::unique_ptr<WallpaperEngine::Audio::Drivers::Recorders::PlaybackRecorder> m_previousAudioRecorder = nullptr;
+    bool m_audioCapturing = false;
     std::unique_ptr<WallpaperEngine::Render::RenderContext> m_renderContext = nullptr;
     std::unique_ptr<WallpaperEngine::Render::Drivers::VideoDriver> m_videoDriver = nullptr;
     std::unique_ptr<WallpaperEngine::Render::Drivers::Detectors::FullScreenDetector> m_fullScreenDetector = nullptr;

+ 5 - 3
src/WallpaperEngine/Audio/Drivers/AudioDriver.cpp

@@ -4,14 +4,14 @@ namespace WallpaperEngine::Audio::Drivers {
 AudioDriver::AudioDriver (
     Application::ApplicationContext& applicationContext, Detectors::AudioPlayingDetector& detector,
     Recorders::PlaybackRecorder& recorder
-) : m_applicationContext (applicationContext), m_detector (detector), m_recorder (recorder) {
+) : m_applicationContext (applicationContext), m_detector (detector), m_recorder (&recorder) {
     // perform a few update cycles to ensure data is ready before anything actually uses the audio
     this->AudioDriver::update ();
     this->AudioDriver::update ();
 }
 
 void AudioDriver::update () {
-    this->m_recorder.update ();
+    this->m_recorder->update ();
     this->m_detector.update ();
 }
 
@@ -19,5 +19,7 @@ Application::ApplicationContext& AudioDriver::getApplicationContext () const { r
 
 Detectors::AudioPlayingDetector& AudioDriver::getAudioDetector () const { return this->m_detector; }
 
-Recorders::PlaybackRecorder& AudioDriver::getRecorder () const { return this->m_recorder; }
+Recorders::PlaybackRecorder& AudioDriver::getRecorder () const { return *this->m_recorder; }
+
+void AudioDriver::setRecorder (Recorders::PlaybackRecorder& recorder) { this->m_recorder = &recorder; }
 } // namespace WallpaperEngine::Audio::Drivers

+ 2 - 1
src/WallpaperEngine/Audio/Drivers/AudioDriver.h

@@ -87,11 +87,12 @@ namespace Audio {
 	     * @return The audio recorder to use to capture stereo mix data
 	     */
 	    [[nodiscard]] Recorders::PlaybackRecorder& getRecorder () const;
+	    void setRecorder (Recorders::PlaybackRecorder& recorder);
 
 	private:
 	    Application::ApplicationContext& m_applicationContext;
 	    Detectors::AudioPlayingDetector& m_detector;
-	    Recorders::PlaybackRecorder& m_recorder;
+	    Recorders::PlaybackRecorder* m_recorder;
 	};
     } // namespace Drivers
 } // namespace Audio

+ 6 - 1
src/WallpaperEngine/Audio/Drivers/Recorders/PulseAudioPlaybackRecorder.cpp

@@ -257,8 +257,13 @@ PulseAudioPlaybackRecorder::PulseAudioPlaybackRecorder () :
 	return;
     }
 
-    // wait until the context is ready
+    // wait until the context is ready, a server that goes away mid-handshake would otherwise spin here forever
     while (pa_context_get_state (this->m_context) != PA_CONTEXT_READY) {
+	if (!PA_CONTEXT_IS_GOOD (pa_context_get_state (this->m_context))) {
+	    sLog.error ("PulseAudio connection failed! Audio processing is disabled");
+	    return;
+	}
+
 	pa_mainloop_iterate (this->m_mainloop, 1, nullptr);
     }
 

+ 3 - 2
src/WallpaperEngine/Render/Wallpapers/CWeb.cpp

@@ -64,7 +64,8 @@ CWeb::CWeb (
     // pushed from the recorder's capture thread, the host process only ever reads these atomics
     auto* shm = this->m_shm;
 
-    this->m_spectrumListenerId = this->getAudioContext ().getRecorder ().addSpectrumListener (
+    this->m_spectrumRecorder = &this->getAudioContext ().getRecorder ();
+    this->m_spectrumListenerId = this->m_spectrumRecorder->addSpectrumListener (
 	[shm] (const float* audio64) {
 	    for (std::size_t i = 0; i < WebHostSharedMemory::AUDIO_BANDS; i++) {
 		shm->audioBands[i].store (audio64[i], std::memory_order_relaxed);
@@ -385,7 +386,7 @@ void CWeb::updateMouse (const glm::ivec4& viewport) {
 
 CWeb::~CWeb () {
     // waits for a callback that's running right now, so the shared memory can be unmapped safely below
-    this->getAudioContext ().getRecorder ().removeSpectrumListener (this->m_spectrumListenerId);
+    this->m_spectrumRecorder->removeSpectrumListener (this->m_spectrumListenerId);
 
     this->m_shm->quitRequested.store (true, std::memory_order_release);
 

+ 2 - 0
src/WallpaperEngine/Render/Wallpapers/CWeb.h

@@ -85,6 +85,8 @@ private:
     // registered with the audio recorder so spectra are pushed to the host as soon as they're computed instead of
     // being sampled once per rendered frame
     int m_spectrumListenerId = 0;
+    // the application can swap recorders on a hotswap, the listener has to come off the one it went on
+    Audio::Drivers::Recorders::PlaybackRecorder* m_spectrumRecorder = nullptr;
     Media::MediaSource::MediaInfo m_lastMedia {};
     bool m_mediaPublished = false;
     uint32_t m_coverVersion = 0;