Add sfPlaybackDevice to manage the audio playback device

This commit is contained in:
Lukas Dürrenberger committed 2026-10-04 22:11:58 +02:00
1 parent b4220b39ff
commit 523c48cf98
7 files changed
+374

No files matched your search

+4
View File
@@ -62,9 +62,13 @@ jobs:
BUILD_TYPE: ${{ matrix.type.name }}
CMAKE_FLAGS: ${{ matrix.platform.flags }} ${{ matrix.config.flags }}
run: |
# Let the installed SFML libraries find each other, same as tools/nuget/build.linux.sh
RPATH_FLAGS=()
if [ "$RUNNER_OS" = "Linux" ]; then RPATH_FLAGS=('-DCMAKE_INSTALL_RPATH=$ORIGIN'); fi
cmake -S SFML -B SFML/build \
-DCMAKE_BUILD_TYPE="$BUILD_TYPE" \
-DCMAKE_INSTALL_PREFIX=SFML/install \
"${RPATH_FLAGS[@]}" \
$CMAKE_FLAGS
- name: Build SFML
+1
View File
@@ -30,6 +30,7 @@
#include <CSFML/Audio/Listener.h>
#include <CSFML/Audio/Music.h>
#include <CSFML/Audio/PlaybackDevice.h>
#include <CSFML/Audio/Sound.h>
#include <CSFML/Audio/SoundBuffer.h>
#include <CSFML/Audio/SoundBufferRecorder.h>
+205
View File
@@ -0,0 +1,205 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2026 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <CSFML/Audio/Export.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
////////////////////////////////////////////////////////////
/// \brief Enumeration of the playback device notifications
///
////////////////////////////////////////////////////////////
typedef enum
{
sfPlaybackDeviceStarted, ///< Playback device has been started
sfPlaybackDeviceStopped, ///< Playback device has been stopped
sfPlaybackDeviceRerouted, ///< Playback device has been rerouted (Generated on platforms that support automatic stream routing)
sfPlaybackDeviceInterruptionBegan, ///< Playback device interruption has begun (Generated on Apple mobile platforms)
sfPlaybackDeviceInterruptionEnded, ///< Playback device interruption has ended (Generated on Apple mobile platforms)
sfPlaybackDeviceUnlocked ///< Playback device has been unlocked (Generated by Emscripten/WebAudio)
} sfPlaybackDeviceNotification;
////////////////////////////////////////////////////////////
/// \brief Callback that is called to notify of changes to the playback device state
///
/// \param notification The notification
/// \param userData User data passed to sfPlaybackDevice_setNotificationCallback
///
////////////////////////////////////////////////////////////
typedef void (*sfPlaybackDeviceNotificationCallback)(sfPlaybackDeviceNotification notification, void* userData);
////////////////////////////////////////////////////////////
/// \brief Get a list of the names of all available audio playback devices
///
/// If the operating system reports multiple devices with
/// the same name, a number will be appended to the name
/// of all subsequent devices to distinguish them from each
/// other. This guarantees that every entry returned by this
/// function will represent a unique device.
///
/// The default device, if one is marked as such, will be
/// placed at the beginning of the list.
///
/// The returned array and strings stay valid until the next
/// call to this function.
///
/// \param count Pointer to a variable that will be filled with the number of devices
///
/// \return An array of strings containing the device names or NULL if no devices are available
///
////////////////////////////////////////////////////////////
CSFML_AUDIO_API const char* const* sfPlaybackDevice_getAvailableDevices(size_t* count);
////////////////////////////////////////////////////////////
/// \brief Get the name of the default audio playback device
///
/// Note that depending on when this function is called, the
/// default device reported by the operating system might
/// change e.g. when a USB audio device is plugged into or
/// unplugged from the system.
///
/// The returned string stays valid until the next call to
/// this function.
///
/// \return The name of the default audio playback device or NULL if there is none
///
////////////////////////////////////////////////////////////
CSFML_AUDIO_API const char* sfPlaybackDevice_getDefaultDevice(void);
////////////////////////////////////////////////////////////
/// \brief Set the audio playback device
///
/// This function sets the audio playback device to the device
/// with the given name. It can be called on the fly (i.e:
/// while sounds are playing).
///
/// If there are sounds playing when the audio playback
/// device is switched, the sounds will continue playing
/// uninterrupted on the new audio playback device.
///
/// \param name The name of the audio playback device
///
/// \return True, if it was able to set the requested device
///
////////////////////////////////////////////////////////////
CSFML_AUDIO_API bool sfPlaybackDevice_setDevice(const char* name);
////////////////////////////////////////////////////////////
/// \brief Set the audio playback device to the default
///
/// This function sets the audio playback device to the
/// default device. It can be called on the fly (i.e:
/// while sounds are playing).
///
/// When certain backends are used, using the default device
/// will enable automatic stream routing. When automatic
/// stream routing is enabled, audio data is automatically
/// sent to whichever physical audio device is currently
/// marked as the default on the system.
///
/// Automatic stream routing is currently supported when using
/// the WASAPI or DirectSound backend on Windows or the
/// Core Audio backend on macOS and iOS.
///
/// \return True, if it was able to set the audio playback device to the default device
///
////////////////////////////////////////////////////////////
CSFML_AUDIO_API bool sfPlaybackDevice_setDeviceToDefault(void);
////////////////////////////////////////////////////////////
/// \brief Set the audio playback device to the null device
///
/// This function sets the audio playback device to the
/// null device. It can be called on the fly (i.e:
/// while sounds are playing).
///
/// Audio data routed to the null device will be discarded
/// by the backend. This can be used to keep sounds playing
/// without having them actually output on a physical
/// audio playback device.
///
/// \return True, if it was able to set the audio playback device to the null device
///
////////////////////////////////////////////////////////////
CSFML_AUDIO_API bool sfPlaybackDevice_setDeviceToNull(void);
////////////////////////////////////////////////////////////
/// \brief Get the name of the current audio playback device
///
/// The returned string stays valid until the next call to
/// this function.
///
/// \return The name of the current audio playback device or NULL if there is none
///
////////////////////////////////////////////////////////////
CSFML_AUDIO_API const char* sfPlaybackDevice_getDevice(void);
////////////////////////////////////////////////////////////
/// \brief Get the sample rate of the current audio playback device
///
/// \return The sample rate of the current audio playback device or 0 if there is none
///
////////////////////////////////////////////////////////////
CSFML_AUDIO_API uint32_t sfPlaybackDevice_getDeviceSampleRate(void);
////////////////////////////////////////////////////////////
/// \brief Check if the current playback device is the default device
///
/// This function will return false if there is no
/// current playback device.
///
/// \return True, if the current playback device is the default device
///
////////////////////////////////////////////////////////////
CSFML_AUDIO_API bool sfPlaybackDevice_isDefaultDevice(void);
////////////////////////////////////////////////////////////
/// \brief Set a callback that should be called to notify of changes to the playback device state
///
/// Warning: Do not attempt to alter the device state from
/// within this callback. This includes changing the device
/// and even creating/destroying sound objects since that
/// could indirectly cause the playback device to be
/// created/destroyed. Also do not attempt to set the
/// notification callback from within this callback. Doing
/// so will result in a deadlock.
///
/// When receiving a notification via this callback, store
/// the information somewhere and react on it from another
/// thread e.g. the main thread within the application.
///
/// \param callback The callback to be called, pass NULL to remove the current callback
/// \param userData User data that will be passed to the callback
///
////////////////////////////////////////////////////////////
CSFML_AUDIO_API void sfPlaybackDevice_setNotificationCallback(sfPlaybackDeviceNotificationCallback callback, void* userData);
+2
View File
@@ -11,6 +11,8 @@ set(SRC
${SRCROOT}/Music.cpp
${SRCROOT}/MusicStruct.hpp
${INCROOT}/Music.h
${SRCROOT}/PlaybackDevice.cpp
${INCROOT}/PlaybackDevice.h
${SRCROOT}/Sound.cpp
${SRCROOT}/SoundStruct.hpp
${INCROOT}/Sound.h
+135
View File
@@ -0,0 +1,135 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2026 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <CSFML/Audio/PlaybackDevice.h>
#include <SFML/Audio/PlaybackDevice.hpp>
#include <cassert>
#include <optional>
#include <string>
#include <vector>
namespace
{
// Helper function for returning optional strings
[[nodiscard]] const char* toCString(std::string& storage, const std::optional<std::string>& string)
{
if (!string)
return nullptr;
storage = *string;
return storage.c_str();
}
} // namespace
////////////////////////////////////////////////////////////
const char* const* sfPlaybackDevice_getAvailableDevices(size_t* count)
{
static std::vector<std::string> stringDevices;
static std::vector<const char*> devices;
stringDevices = sf::PlaybackDevice::getAvailableDevices();
devices.clear();
devices.reserve(stringDevices.size());
for (const auto& stringDevice : stringDevices)
devices.push_back(stringDevice.c_str());
if (count)
*count = devices.size();
return !devices.empty() ? devices.data() : nullptr;
}
////////////////////////////////////////////////////////////
const char* sfPlaybackDevice_getDefaultDevice()
{
static std::string defaultDevice;
return toCString(defaultDevice, sf::PlaybackDevice::getDefaultDevice());
}
////////////////////////////////////////////////////////////
bool sfPlaybackDevice_setDevice(const char* name)
{
assert(name);
return sf::PlaybackDevice::setDevice(name);
}
////////////////////////////////////////////////////////////
bool sfPlaybackDevice_setDeviceToDefault()
{
return sf::PlaybackDevice::setDeviceToDefault();
}
////////////////////////////////////////////////////////////
bool sfPlaybackDevice_setDeviceToNull()
{
return sf::PlaybackDevice::setDeviceToNull();
}
////////////////////////////////////////////////////////////
const char* sfPlaybackDevice_getDevice()
{
static std::string device;
return toCString(device, sf::PlaybackDevice::getDevice());
}
////////////////////////////////////////////////////////////
uint32_t sfPlaybackDevice_getDeviceSampleRate()
{
return sf::PlaybackDevice::getDeviceSampleRate().value_or(0);
}
////////////////////////////////////////////////////////////
bool sfPlaybackDevice_isDefaultDevice()
{
return sf::PlaybackDevice::isDefaultDevice();
}
////////////////////////////////////////////////////////////
void sfPlaybackDevice_setNotificationCallback(sfPlaybackDeviceNotificationCallback callback, void* userData)
{
if (!callback)
{
sf::PlaybackDevice::setNotificationCallback({});
return;
}
sf::PlaybackDevice::setNotificationCallback(
[callback, userData](sf::PlaybackDevice::Notification notification)
{ callback(static_cast<sfPlaybackDeviceNotification>(notification), userData); });
}
+26
View File
@@ -0,0 +1,26 @@
#include <CSFML/Audio/PlaybackDevice.h>
#include <SFML/Audio/PlaybackDevice.hpp>
#include <catch2/catch_test_macros.hpp>
TEST_CASE("[Audio] sfPlaybackDevice")
{
SECTION("sfPlaybackDeviceNotification")
{
STATIC_CHECK(sfPlaybackDeviceStarted == static_cast<int>(sf::PlaybackDevice::Notification::DeviceStarted));
STATIC_CHECK(sfPlaybackDeviceStopped == static_cast<int>(sf::PlaybackDevice::Notification::DeviceStopped));
STATIC_CHECK(sfPlaybackDeviceRerouted == static_cast<int>(sf::PlaybackDevice::Notification::DeviceRerouted));
STATIC_CHECK(sfPlaybackDeviceInterruptionBegan ==
static_cast<int>(sf::PlaybackDevice::Notification::DeviceInterruptionBegan));
STATIC_CHECK(sfPlaybackDeviceInterruptionEnded ==
static_cast<int>(sf::PlaybackDevice::Notification::DeviceInterruptionEnded));
STATIC_CHECK(sfPlaybackDeviceUnlocked == static_cast<int>(sf::PlaybackDevice::Notification::DeviceUnlocked));
}
SECTION("sfPlaybackDevice_setNotificationCallback")
{
sfPlaybackDevice_setNotificationCallback([](sfPlaybackDeviceNotification, void*) {}, nullptr);
sfPlaybackDevice_setNotificationCallback(nullptr, nullptr);
}
}
+1
View File
@@ -76,6 +76,7 @@ set_target_warnings(test-csfml-network)
catch_discover_tests(test-csfml-network)
add_executable(test-csfml-audio
Audio/PlaybackDevice.test.cpp
Audio/SoundChannel.test.cpp
)
target_link_libraries(test-csfml-audio PRIVATE csfml-audio Catch2::Catch2WithMain SFML::Audio)