implement Milestone 13: audio output with volume limits and concurrent sound management

This commit is contained in:
2026-01-18 16:02:31 +01:00
parent d61b8f0bd8
commit c2e8b8c212
6 changed files with 1236 additions and 1 deletions

View File

@@ -26,6 +26,7 @@ add_library(mosis-sandbox STATIC
../src/main/cpp/sandbox/websocket_manager.cpp
../src/main/cpp/sandbox/camera_interface.cpp
../src/main/cpp/sandbox/microphone_interface.cpp
../src/main/cpp/sandbox/audio_output.cpp
)
target_include_directories(mosis-sandbox PUBLIC
../src/main/cpp/sandbox

View File

@@ -16,6 +16,7 @@
#include "websocket_manager.h"
#include "camera_interface.h"
#include "microphone_interface.h"
#include "audio_output.h"
#include <filesystem>
#include <fstream>
#include <sstream>
@@ -2175,6 +2176,153 @@ bool Test_MicrophoneLuaIntegration(std::string& error_msg) {
return true;
}
//=============================================================================
// MILESTONE 13: Audio Output
//=============================================================================
bool Test_AudioPlaysSound(std::string& error_msg) {
mosis::AudioOutputInterface audio("test.app");
mosis::AudioData data;
data.sample_rate = 44100;
data.channels = 1;
data.bits_per_sample = 16;
data.data = std::vector<uint8_t>(1000); // Dummy data
mosis::PlaybackConfig config;
std::string err;
auto player = audio.Play(data, config, err);
EXPECT_TRUE(player != nullptr);
EXPECT_TRUE(player->GetState() == mosis::AudioState::Playing);
return true;
}
bool Test_AudioRespectsSystemVolume(std::string& error_msg) {
mosis::AudioOutputInterface audio("test.app");
audio.SetSystemVolume(0.5f);
mosis::AudioData data;
data.data = std::vector<uint8_t>(1000);
mosis::PlaybackConfig config;
config.volume = 1.0f; // App requests full volume
std::string err;
auto player = audio.Play(data, config, err);
// Player is created with requested volume
EXPECT_TRUE(player != nullptr);
EXPECT_TRUE(player->GetVolume() == 1.0f);
// But effective volume is capped by system
// (In real implementation, audio subsystem applies this)
return true;
}
bool Test_AudioLimitsConcurrent(std::string& error_msg) {
mosis::AudioOutputInterface audio("test.app");
mosis::AudioData data;
data.data = std::vector<uint8_t>(1000);
mosis::PlaybackConfig config;
// Create MAX_CONCURRENT_SOUNDS players
std::vector<std::shared_ptr<mosis::SoundPlayer>> players;
for (int i = 0; i < 10; i++) {
std::string err;
auto player = audio.Play(data, config, err);
EXPECT_TRUE(player != nullptr);
players.push_back(player);
}
// 11th should fail
std::string err;
auto extra = audio.Play(data, config, err);
EXPECT_TRUE(extra == nullptr);
EXPECT_TRUE(err.find("limit") != std::string::npos ||
err.find("concurrent") != std::string::npos);
return true;
}
bool Test_AudioStopAll(std::string& error_msg) {
mosis::AudioOutputInterface audio("test.app");
mosis::AudioData data;
data.data = std::vector<uint8_t>(1000);
mosis::PlaybackConfig config;
std::string err;
auto player1 = audio.Play(data, config, err);
auto player2 = audio.Play(data, config, err);
EXPECT_TRUE(audio.GetActivePlayerCount() == 2);
audio.StopAll();
EXPECT_TRUE(audio.GetActivePlayerCount() == 0);
return true;
}
bool Test_AudioStopsOnShutdown(std::string& error_msg) {
mosis::AudioOutputInterface audio("test.app");
mosis::AudioData data;
data.data = std::vector<uint8_t>(1000);
mosis::PlaybackConfig config;
std::string err;
auto player = audio.Play(data, config, err);
EXPECT_TRUE(player != nullptr);
// Simulate app stop
audio.Shutdown();
EXPECT_TRUE(audio.GetActivePlayerCount() == 0);
return true;
}
bool Test_AudioLuaIntegration(std::string& error_msg) {
SandboxContext ctx = TestContext();
LuaSandbox sandbox(ctx);
mosis::AudioOutputInterface audio("test.app");
mosis::RegisterAudioAPI(sandbox.GetState(), &audio);
std::string script = R"lua(
-- Test that audio global exists
if not audio then
error("audio global not found")
end
if not audio.play then
error("audio.play not found")
end
if not audio.stopAll then
error("audio.stopAll not found")
end
if not audio.getActiveCount then
error("audio.getActiveCount not found")
end
-- Active count should be 0 initially
if audio.getActiveCount() ~= 0 then
error("should have no active sounds initially")
end
)lua";
bool ok = sandbox.LoadString(script, "audio_test");
if (!ok) {
error_msg = "Lua test failed: " + sandbox.GetLastError();
return false;
}
return true;
}
//=============================================================================
// MAIN
//=============================================================================
@@ -2332,6 +2480,14 @@ int main(int argc, char* argv[]) {
harness.AddTest("MicrophoneStopsOnShutdown", Test_MicrophoneStopsOnShutdown);
harness.AddTest("MicrophoneLuaIntegration", Test_MicrophoneLuaIntegration);
// Milestone 13: Audio Output
harness.AddTest("AudioPlaysSound", Test_AudioPlaysSound);
harness.AddTest("AudioRespectsSystemVolume", Test_AudioRespectsSystemVolume);
harness.AddTest("AudioLimitsConcurrent", Test_AudioLimitsConcurrent);
harness.AddTest("AudioStopAll", Test_AudioStopAll);
harness.AddTest("AudioStopsOnShutdown", Test_AudioStopsOnShutdown);
harness.AddTest("AudioLuaIntegration", Test_AudioLuaIntegration);
// Run tests
auto results = harness.Run(filter);