Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca7b8b7e6f | ||
|
|
bf2d4d1f89 |
+8
-1
@@ -112,7 +112,14 @@ add_executable(test_provisioning
|
|||||||
tests/test_provisioning.cpp
|
tests/test_provisioning.cpp
|
||||||
)
|
)
|
||||||
target_include_directories(test_provisioning PRIVATE src)
|
target_include_directories(test_provisioning PRIVATE src)
|
||||||
target_link_libraries(test_provisioning PRIVATE SDL3::SDL3)
|
# provisioning.h pulls in app_state.h, so the test compiles against the SDL3,
|
||||||
|
# SDL3_mixer, and SQLite headers transitively (app_state.h -> audio_manager.h ->
|
||||||
|
# SDL3_mixer; app_state.h -> database_manager.h -> sqlite3.h).
|
||||||
|
target_link_libraries(test_provisioning PRIVATE
|
||||||
|
SDL3::SDL3
|
||||||
|
SDL3_mixer::SDL3_mixer
|
||||||
|
SQLite3::SQLite3
|
||||||
|
)
|
||||||
|
|
||||||
add_executable(test_assets
|
add_executable(test_assets
|
||||||
tests/test_assets.cpp
|
tests/test_assets.cpp
|
||||||
|
|||||||
@@ -112,6 +112,18 @@ in
|
|||||||
export WLR_RENDERER=pixman
|
export WLR_RENDERER=pixman
|
||||||
export WLR_NO_HARDWARE_CURSORS=1
|
export WLR_NO_HARDWARE_CURSORS=1
|
||||||
|
|
||||||
|
# Prefer the PipeWire audio backend so SDL enumerates every sink
|
||||||
|
# (built-in analog, HDMI, and the loopback virtual speaker) instead of
|
||||||
|
# silently falling back to an ALSA-only device view that hides the
|
||||||
|
# PipeWire sinks and makes the device selector show a single entry.
|
||||||
|
export SDL_AUDIODRIVER=pipewire
|
||||||
|
if [ -n "$${XDG_RUNTIME_DIR:-}" ]; then
|
||||||
|
for i in {1..20}; do
|
||||||
|
[ -S "$XDG_RUNTIME_DIR/pipewire-0" ] && break
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
cage -- ${radiantWaveApp}/bin/radiantwave > /tmp/kiosk.log 2>&1
|
cage -- ${radiantWaveApp}/bin/radiantwave > /tmp/kiosk.log 2>&1
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ namespace radiantwave::audio
|
|||||||
if (lower.find("analog") != std::string::npos ||
|
if (lower.find("analog") != std::string::npos ||
|
||||||
lower.find("built-in") != std::string::npos ||
|
lower.find("built-in") != std::string::npos ||
|
||||||
lower.find("builtin") != std::string::npos ||
|
lower.find("builtin") != std::string::npos ||
|
||||||
|
lower.find("virtual") != std::string::npos ||
|
||||||
lower.find("realtek") != std::string::npos ||
|
lower.find("realtek") != std::string::npos ||
|
||||||
lower.find("hda") != std::string::npos ||
|
lower.find("hda") != std::string::npos ||
|
||||||
lower.find("speaker") != std::string::npos ||
|
lower.find("speaker") != std::string::npos ||
|
||||||
@@ -85,6 +86,34 @@ namespace radiantwave::audio
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Startup diagnostics: which audio backend SDL picked (PipeWire, ALSA,
|
||||||
|
// Pulse, dummy, ...) and what playback devices it actually sees. This is
|
||||||
|
// the first place a "only the built-in speakers show up" report points;
|
||||||
|
// a single device here means SDL itself never enumerated the HDMI sink.
|
||||||
|
const char *driver = SDL_GetCurrentAudioDriver();
|
||||||
|
std::string driverName = driver ? driver : "(none)";
|
||||||
|
int deviceCount = 0;
|
||||||
|
SDL_AudioDeviceID *devices = SDL_GetAudioPlaybackDevices(&deviceCount);
|
||||||
|
std::string deviceNames;
|
||||||
|
if (devices)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < deviceCount; ++i)
|
||||||
|
{
|
||||||
|
if (i)
|
||||||
|
{
|
||||||
|
deviceNames += ", ";
|
||||||
|
}
|
||||||
|
const char *n = SDL_GetAudioDeviceName(devices[i]);
|
||||||
|
deviceNames += n ? n : "(unnamed)";
|
||||||
|
}
|
||||||
|
SDL_free(devices);
|
||||||
|
}
|
||||||
|
logger::Logger::instance().info(
|
||||||
|
"Audio",
|
||||||
|
"SDL audio driver: " + driverName + "; " +
|
||||||
|
std::to_string(deviceCount) + " playback device(s) [" +
|
||||||
|
(deviceNames.empty() ? "(none)" : deviceNames) + "]");
|
||||||
|
|
||||||
// Try to open the mixer on the device previously persisted in config;
|
// Try to open the mixer on the device previously persisted in config;
|
||||||
// fall back to the system default when the configured device is gone.
|
// fall back to the system default when the configured device is gone.
|
||||||
SDL_AudioDeviceID targetId = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
|
SDL_AudioDeviceID targetId = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
|
||||||
@@ -247,7 +276,21 @@ namespace radiantwave::audio
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
list.push_back({"", "Built-In Speakers", 0});
|
if (devices)
|
||||||
|
{
|
||||||
|
SDL_free(devices);
|
||||||
|
}
|
||||||
|
const char *driver = SDL_GetCurrentAudioDriver();
|
||||||
|
// Zero enumerated devices is a symptom, not a real output. Surface
|
||||||
|
// the truth (a single "System Default" entry that still routes to
|
||||||
|
// whatever the OS default is) instead of pretending a fabricated
|
||||||
|
// device, and say which backend was active so kiosk logs show why.
|
||||||
|
logger::Logger::instance().debug(
|
||||||
|
"Audio",
|
||||||
|
"No playback devices enumerated (audio driver: " +
|
||||||
|
std::string(driver ? driver : "none") +
|
||||||
|
"); falling back to the system default");
|
||||||
|
list.push_back({"", "System Default", 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
|
|||||||
@@ -531,7 +531,7 @@ void AudioSettingsPage::render(SDL_Renderer* renderer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string devName = (currentDeviceIndex_ >= 0 && currentDeviceIndex_ < static_cast<int>(devices_.size()))
|
std::string devName = (currentDeviceIndex_ >= 0 && currentDeviceIndex_ < static_cast<int>(devices_.size()))
|
||||||
? devices_[currentDeviceIndex_].friendly_name : "Built-In Speakers";
|
? devices_[currentDeviceIndex_].friendly_name : "System Default";
|
||||||
deviceText_.ensure(renderer, fontHeader_, "Device: " + devName, white);
|
deviceText_.ensure(renderer, fontHeader_, "Device: " + devName, white);
|
||||||
if (deviceText_.texture) {
|
if (deviceText_.texture) {
|
||||||
SDL_FRect dst{500.0f, currentY, deviceText_.width, deviceText_.height};
|
SDL_FRect dst{500.0f, currentY, deviceText_.width, deviceText_.height};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ TEST(AudioTest, FriendlyDeviceNameMapping) {
|
|||||||
EXPECT_EQ(mapToFriendlyDeviceName("AMD Radeon HD Audio Controller HDMI"), "TV / HDMI Monitor");
|
EXPECT_EQ(mapToFriendlyDeviceName("AMD Radeon HD Audio Controller HDMI"), "TV / HDMI Monitor");
|
||||||
EXPECT_EQ(mapToFriendlyDeviceName("USB Audio Headset Dongle"), "USB Audio Device");
|
EXPECT_EQ(mapToFriendlyDeviceName("USB Audio Headset Dongle"), "USB Audio Device");
|
||||||
EXPECT_EQ(mapToFriendlyDeviceName("bluez_sink.00_11_22_33_44_55"), "Bluetooth Audio");
|
EXPECT_EQ(mapToFriendlyDeviceName("bluez_sink.00_11_22_33_44_55"), "Bluetooth Audio");
|
||||||
|
EXPECT_EQ(mapToFriendlyDeviceName("Virtual Speaker"), "Built-In Speakers");
|
||||||
EXPECT_EQ(mapToFriendlyDeviceName(""), "Default Audio Output");
|
EXPECT_EQ(mapToFriendlyDeviceName(""), "Default Audio Output");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ TEST(AudioTest, GetPlaybackDevicesFallsBackWithIdZeroWhenNone) {
|
|||||||
for (const auto& d : devices) {
|
for (const auto& d : devices) {
|
||||||
if (d.raw_name.empty()) {
|
if (d.raw_name.empty()) {
|
||||||
EXPECT_EQ(d.id, 0u);
|
EXPECT_EQ(d.id, 0u);
|
||||||
|
EXPECT_EQ(d.friendly_name, "System Default");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user