Skip to content

Commit c9e2a60

Browse files
committed
override default output device for windows build, cult allolib changes and spatial root changes in realtime engine and app
1 parent 236ec8a commit c9e2a60

9 files changed

Lines changed: 244 additions & 103 deletions

File tree

PUBLIC_DOCS/API.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ While running, call `update()`, `queryStatus()`, and `consumeDiagnostics()` regu
9090
| --- | --- | --- | --- |
9191
| `sampleRate` | `int` | `48000` | Audio sample rate in Hz |
9292
| `bufferSize` | `int` | `512` | Frames per audio callback |
93-
| `outputDeviceName` | `std::string` | `""` | Exact device name; empty selects the system default |
93+
| `outputDeviceId` | `int` | `-1` | Stable backend output-device id; `-1` selects the system default |
94+
| `outputDeviceName` | `std::string` | `""` | Display name / CLI fallback; used only when no device id is provided |
9495
| `oscPort` | `int` | `9009` | OSC control port; set to `0` to disable OSC |
9596
| `elevationMode` | `ElevationMode` | `RescaleAtmosUp` | Vertical remapping mode |
9697

@@ -195,6 +196,7 @@ If `containsAudio.json` is present, it is the preferred source-to-file mapping c
195196
- `audioBackendLabel`
196197
- `requestedSampleRate`
197198
- `effectiveStreamSampleRate`
199+
- `outputDeviceId`
198200
- `outputDeviceName`
199201
- `outputDevicePreferredSampleRate`
200202

internalDocs/API_internal.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
| Struct | Purpose |
2121
|---|---|
22-
| `EngineOptions` | Core system settings (sample rate, buffer size, device name, OSC port, elevation mode) |
22+
| `EngineOptions` | Core system settings (sample rate, buffer size, device id/name, OSC port, elevation mode) |
2323
| `SceneInput` | Audio scene definition (LUSID scene path, sources folder, ADM file) |
2424
| `LayoutInput` | Speaker layout path and optional remap CSV path |
2525
| `RuntimeParams` | Initial gain/focus/mix values passed at configure time |
@@ -35,6 +35,7 @@
3535
struct EngineOptions {
3636
int sampleRate = 48000;
3737
int bufferSize = 512;
38+
int outputDeviceId = -1; // stable backend id; -1 = system default
3839
std::string outputDeviceName; // empty = system default
3940
int oscPort = 9009; // 0 = disable OSC entirely
4041
ElevationMode elevationMode = ElevationMode::RescaleAtmosUp;
@@ -83,6 +84,7 @@ std::string audioBackendLabel; // best-effort runtime backend/API l
8384
int requestedSampleRate = 48000; // requested engine rate
8485
double effectiveStreamSampleRate = 0.0; // actual running stream rate, if known
8586
bool effectiveStreamSampleRateKnown = false;
87+
int outputDeviceId = -1; // resolved backend output device id, if known
8688
std::string outputDeviceName; // selected output device, if known
8789
double outputDevicePreferredSampleRate = 0.0; // device preferred/default rate, if known
8890
bool outputDevicePreferredSampleRateKnown = false;
@@ -102,7 +104,7 @@ Semantics:
102104

103105
The engine enforces a strict, linear initialization sequence:
104106

105-
1. `configureEngine(const EngineOptions&)` — stores sampleRate, bufferSize, outputDeviceName, oscPort, elevationMode into `mConfig`. Always returns `true`.
107+
1. `configureEngine(const EngineOptions&)` — stores sampleRate, bufferSize, outputDeviceId, outputDeviceName, oscPort, elevationMode into `mConfig`. Always returns `true`.
106108
2. `loadScene(const SceneInput&)` — parses LUSID scene via `JSONLoader`, initializes `Streaming`. Returns `false` if scene file missing or no sources loaded.
107109
3. `applyLayout(const LayoutInput&)` — requires `loadScene` to have succeeded (`mSceneData` guard). Loads speaker layout, initializes `Pose` and `Spatializer`. Calls `configureOutputRouting()` internally.
108110
4. `configureRuntime(const RuntimeParams&)` — clamps and writes gain/focus/mix atomics to `mConfig`. **Does not perform output routing setup** (moved to `applyLayout()`). Safe before and after `start()`. Syncs OSC param values if the OSC server is already running. **OSC ParameterServer is NOT started here — it starts in `start()`.**

source/gui/imgui/src/App.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ App::App(std::string projectRoot, bool keepTempSessions, std::string tempRootOve
281281
tryLoadDefaultLayoutOnStartup();
282282

283283
appendEngineLog("[GUI] Select a source and layout, then click START.");
284+
scanDevices();
284285

285286
int lw = 0, lh = 0, lch = 0;
286287
unsigned char* logoData = stbi_load_from_memory(
@@ -584,6 +585,12 @@ void App::renderEngineTab() {
584585
(mDeviceIdx >= 0 && mDeviceIdx < static_cast<int>(mDeviceOutputChannels.size()))
585586
? mDeviceOutputChannels[mDeviceIdx]
586587
: 0;
588+
const int selectedDeviceBackendId =
589+
(mDeviceIdx >= 0 && mDeviceIdx < static_cast<int>(mDeviceBackendIds.size()))
590+
? mDeviceBackendIds[mDeviceIdx]
591+
: -1;
592+
const int activeDeviceBackendId =
593+
(mStatus.outputDeviceId >= 0) ? mStatus.outputDeviceId : selectedDeviceBackendId;
587594
const double scannedDeviceSampleRate =
588595
(mDeviceIdx >= 0 && mDeviceIdx < static_cast<int>(mDeviceSampleRates.size()))
589596
? mDeviceSampleRates[mDeviceIdx]
@@ -726,6 +733,9 @@ void App::renderEngineTab() {
726733
<< (audioReady ? "Ready" : (audioNotReady ? "Not Ready" : "Unknown")) << "\n"
727734
<< "Backend: " << backendLabel << "\n"
728735
<< "Device: " << activeDeviceName << "\n"
736+
<< "Device ID: "
737+
<< (activeDeviceBackendId >= 0 ? std::to_string(activeDeviceBackendId)
738+
: std::string("system default")) << "\n"
729739
<< "Output Channels: "
730740
<< (selectedOutputChannels > 0 ? std::to_string(selectedOutputChannels) : "unknown") << "\n"
731741
<< "Required sample rate: " << requiredSampleRate << " Hz\n"
@@ -755,6 +765,10 @@ void App::renderEngineTab() {
755765
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
756766
if (ImGui::Combo("##device", &mDeviceIdx, items.data(), (int)items.size())) {
757767
mDeviceName = (mDeviceIdx == 0) ? "" : mDeviceList[mDeviceIdx];
768+
mDeviceBackendId =
769+
(mDeviceIdx >= 0 && mDeviceIdx < static_cast<int>(mDeviceBackendIds.size()))
770+
? mDeviceBackendIds[mDeviceIdx]
771+
: -1;
758772
}
759773
}
760774
if (selectedOutputChannels > 0) {
@@ -1621,6 +1635,7 @@ void App::doLaunchEngine(const std::string& scenePath,
16211635
EngineOptions opts;
16221636
opts.sampleRate = 48000;
16231637
opts.bufferSize = kBufferSizes[mBufferSizeIdx];
1638+
opts.outputDeviceId = mDeviceBackendId;
16241639
opts.outputDeviceName = mDeviceName;
16251640
opts.oscPort = 9009;
16261641
opts.elevationMode = static_cast<ElevationMode>(mElevationMode);
@@ -1823,23 +1838,38 @@ void App::resetRuntimeToDefaults() {
18231838
}
18241839

18251840
void App::scanDevices() {
1841+
const int previousDeviceId = mDeviceBackendId;
1842+
const std::string previousDeviceName = mDeviceName;
18261843
mDeviceList.clear();
1844+
mDeviceBackendIds.clear();
18271845
mDeviceOutputChannels.clear();
18281846
mDeviceSampleRates.clear();
18291847
mDeviceList.push_back("(system default)");
1848+
mDeviceBackendIds.push_back(-1);
18301849
mDeviceOutputChannels.push_back(0);
18311850
mDeviceSampleRates.push_back(0.0);
18321851
const int n = al::AudioDevice::numDevices();
18331852
for (int i = 0; i < n; ++i) {
18341853
al::AudioDevice dev(i);
18351854
if (dev.valid() && dev.hasOutput()) {
18361855
mDeviceList.push_back(std::string(dev.name()));
1856+
mDeviceBackendIds.push_back(dev.id());
18371857
mDeviceOutputChannels.push_back(dev.channelsOutMax());
18381858
mDeviceSampleRates.push_back(dev.defaultSampleRate());
18391859
}
18401860
}
18411861
mDeviceIdx = 0;
1862+
mDeviceBackendId = -1;
18421863
mDeviceName = "";
1864+
for (int i = 1; i < static_cast<int>(mDeviceBackendIds.size()); ++i) {
1865+
if ((previousDeviceId >= 0 && mDeviceBackendIds[i] == previousDeviceId) ||
1866+
(!previousDeviceName.empty() && mDeviceList[i] == previousDeviceName)) {
1867+
mDeviceIdx = i;
1868+
mDeviceBackendId = mDeviceBackendIds[i];
1869+
mDeviceName = mDeviceList[i];
1870+
break;
1871+
}
1872+
}
18431873
}
18441874

18451875
void App::detectSource() {

source/gui/imgui/src/App.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,10 @@ class App {
9292
std::string mLayoutPath; // speaker layout JSON
9393
std::string mDeviceName; // "" = system default
9494
std::vector<std::string> mDeviceList; // populated by scanDevices(); [0] = system default
95+
std::vector<int> mDeviceBackendIds; // mirrors mDeviceList; -1 = system default
9596
std::vector<int> mDeviceOutputChannels; // mirrors mDeviceList; 0 = unknown/system default
9697
std::vector<double> mDeviceSampleRates; // mirrors mDeviceList; 0.0 = unknown/system default
98+
int mDeviceBackendId = -1;
9799
int mDeviceIdx = 0;
98100
int mBufferSizeIdx = 3; // index into kBufferSizes[]
99101
int mLayoutPreset = 0; // index into kLayoutNames[]

source/spatial_engine/realtimeEngine/src/EngineSession.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ bool EngineSession::configureEngine(const EngineOptions& opts)
182182
{
183183
mConfig.sampleRate = opts.sampleRate;
184184
mConfig.bufferSize = opts.bufferSize;
185+
mConfig.outputDeviceId = opts.outputDeviceId;
185186
mConfig.outputDeviceName = opts.outputDeviceName;
186187
mOscPort = opts.oscPort;
187188
mConfig.elevationMode.store(static_cast<int>(opts.elevationMode), std::memory_order_relaxed);
@@ -552,9 +553,11 @@ EngineStatus EngineSession::queryStatus() const
552553
st.paused = mConfig.paused.load(std::memory_order_relaxed);
553554
st.isExitRequested = (mBackend && !mBackend->isRunning());
554555
st.requestedSampleRate = mConfig.sampleRate;
556+
st.outputDeviceId = mConfig.outputDeviceId;
555557
st.outputDeviceName = mConfig.outputDeviceName.empty() ? "(system default)" : mConfig.outputDeviceName;
556558
if (mBackend) {
557559
st.audioBackendLabel = mBackend->backendDisplayLabel();
560+
st.outputDeviceId = mBackend->selectedDeviceId();
558561
st.outputDeviceName = mBackend->selectedDeviceName().empty()
559562
? st.outputDeviceName
560563
: mBackend->selectedDeviceName();

source/spatial_engine/realtimeEngine/src/EngineSession.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ struct EngineStatus {
3232
int requestedSampleRate = 48000;
3333
double effectiveStreamSampleRate = 0.0;
3434
bool effectiveStreamSampleRateKnown = false;
35+
int outputDeviceId = -1;
3536
std::string outputDeviceName;
3637
double outputDevicePreferredSampleRate = 0.0;
3738
bool outputDevicePreferredSampleRateKnown = false;
@@ -67,6 +68,7 @@ struct DiagnosticEvents {
6768
struct EngineOptions {
6869
int sampleRate = 48000;
6970
int bufferSize = 512;
71+
int outputDeviceId = -1;
7072
std::string outputDeviceName;
7173
int oscPort = 9009;
7274
ElevationMode elevationMode = ElevationMode::RescaleAtmosUp;

0 commit comments

Comments
 (0)