Skip to content

Commit f805b81

Browse files
committed
Adds OSC settings view
Adds a new settings view for configuring OSC targets, allowing users to specify custom targets (e.g., Resonite) or disable sending OSC messages to VRChat.
1 parent caa4504 commit f805b81

24 files changed

+607
-105
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project (now) adheres to [Calendar Versioning](https://calver.org/#sche
77

88
## [Unreleased]
99

10+
## Added
11+
12+
- OSC Settings view
13+
- Custom target for OSC messages (e.g. for use with Resonite, OSC routers, or when OSCQuery is not available)
14+
- VRChat (OSCQuery) target (allows for disabling OSC messages being sent to VRChat)
15+
1016
## [25.6.10]
1117

1218
### Fixed

src-ui/app/app-routing.module.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { SettingsIntegrationsViewComponent } from './views/dashboard-view/views/
2929
import { SettingsAdvancedViewComponent } from './views/dashboard-view/views/settings-advanced-view/settings-advanced-view.component';
3030
import { SettingsHotkeyViewComponent } from './views/dashboard-view/views/settings-hotkey-view/settings-hotkey-view.component';
3131
import { SettingsStatusInfoViewComponent } from './views/dashboard-view/views/settings-status-info-view/settings-status-info-view.component';
32+
import { SettingsOscViewComponent } from './views/dashboard-view/views/settings-osc-view/settings-osc-view.component';
3233
import { HmdAutomationsViewComponent } from './views/dashboard-view/views/hmd-automations-view/hmd-automations-view.component';
3334
import { JoinNotificationsViewComponent } from './views/dashboard-view/views/join-notifications-view/join-notifications-view.component';
3435
import { VRChatAvatarAutomationsViewComponent } from './views/dashboard-view/views/vrchat-avatar-automations-view/vrchat-avatar-automations-view.component';
@@ -154,6 +155,10 @@ const routes: Routes = [
154155
path: 'hotkeys',
155156
component: SettingsHotkeyViewComponent,
156157
},
158+
{
159+
path: 'osc',
160+
component: SettingsOscViewComponent,
161+
},
157162
{
158163
path: 'updates',
159164
component: SettingsUpdatesViewComponent,

src-ui/app/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ import { AudioDeviceAutomationsService } from './services/audio-device-automatio
154154
import { WindowsService } from './services/windows.service';
155155
import { SettingsAdvancedViewComponent } from './views/dashboard-view/views/settings-advanced-view/settings-advanced-view.component';
156156
import { SettingsNotificationsViewComponent } from './views/dashboard-view/views/settings-notifications-view/settings-notifications-view.component';
157+
import { SettingsOscViewComponent } from './views/dashboard-view/views/settings-osc-view/settings-osc-view.component';
157158
import { SettingsGeneralViewComponent } from './views/dashboard-view/views/settings-general-view/settings-general-view.component';
158159
import { SettingsUpdatesViewComponent } from './views/dashboard-view/views/settings-updates-view/settings-updates-view.component';
159160
import { StartWithSteamVRHowToModalComponent } from './views/dashboard-view/views/settings-general-view/start-with-steamvr-how-to-modal/start-with-steamvr-how-to-modal.component';
@@ -319,6 +320,7 @@ export function createTranslateLoader(http: HttpClient) {
319320
LanguageSelectModalComponent,
320321
SettingsGeneralViewComponent,
321322
SettingsNotificationsViewComponent,
323+
SettingsOscViewComponent,
322324
SettingsUpdatesViewComponent,
323325
SettingsAdvancedViewComponent,
324326
VRChatLoginModalComponent,

src-ui/app/components/dashboard-navbar/dashboard-navbar.component.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
'settings/vrchat',
121121
'settings/notifications',
122122
'settings/hotkeys',
123+
'settings/osc',
123124
'settings/updates',
124125
'settings/integrations',
125126
'settings/advanced',
@@ -346,6 +347,12 @@
346347
</div>
347348
<div class="menu-item-label" translate>comp.dashboard-navbar.settings.hotkeys</div>
348349
</div>
350+
<div class="menu-item" [routerLinkActive]="'active'" [routerLink]="['settings', 'osc']">
351+
<div class="menu-item-icon-prefix">
352+
<i class="material-icons-round">sync_alt</i>
353+
</div>
354+
<div class="menu-item-label" translate>comp.dashboard-navbar.settings.osc</div>
355+
</div>
349356
<div class="menu-item" [routerLinkActive]="'active'" [routerLink]="['settings', 'updates']">
350357
<div class="menu-item-icon-prefix">
351358
<i class="material-icons-round">update</i>

src-ui/app/models/settings.ts

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,39 @@ export interface AppSettings {
88
// General Settings
99
userLanguage: string;
1010
userLanguagePicked: boolean;
11-
lighthouseConsolePath: string;
1211
askForAdminOnStart: boolean;
1312
exitInSystemTray: boolean;
1413
startInSystemTray: boolean;
1514
sleepModeStartupBehaviour: 'PERSIST' | 'ACTIVE' | 'INACTIVE';
16-
notificationProvider: NotificationProvider;
17-
notificationsEnabled: { types: NotificationType[] };
1815
quitWithSteamVR: QuitWithSteamVRMode;
19-
generalNotificationVolume: number;
20-
v1LighthouseIdentifiers: {
21-
[deviceId: string]: string;
22-
};
2316
hotkeys: { [hotkeyId: string]: string[] };
24-
oscServerEnabled: boolean;
2517
playerListPresets: PlayerListPreset[];
2618
hideSnowverlay: boolean;
2719
openVrInitDelayFix: boolean;
2820
oneTimeFlags: OneTimeFlag[];
2921
eventLogTypesHidden: EventLogType[];
22+
// Notifications
23+
notificationProvider: NotificationProvider;
24+
notificationsEnabled: { types: NotificationType[] };
25+
generalNotificationVolume: number;
26+
// OSC
27+
oscServerEnabled: boolean;
28+
oscTargets: OSCTarget[];
29+
oscCustomTargetHost: string;
30+
oscCustomTargetPort: number;
3031
// Message center
3132
hiddenMessageIds: string[];
3233
// Overlay
3334
overlayMenuEnabled: boolean;
3435
overlayGpuAcceleration: boolean;
3536
overlayMenuOnlyOpenWhenVRChatIsRunning: boolean;
3637
// Lighthouse
38+
lighthouseConsolePath: string;
3739
lighthousePowerControl: boolean;
3840
lighthousePowerOffState: LighthouseDevicePowerState;
41+
v1LighthouseIdentifiers: {
42+
[deviceId: string]: string;
43+
};
3944
// Discord Rich Presence
4045
discordActivityMode: DiscordActivityMode;
4146
discordActivityOnlyWhileVRChatIsRunning: boolean;
@@ -60,6 +65,8 @@ export type DiscordActivityMode = 'ENABLED' | 'ONLY_ASLEEP' | 'DISABLED';
6065

6166
export type QuitWithSteamVRMode = 'DISABLED' | 'IMMEDIATELY' | 'AFTERDELAY';
6267

68+
export type OSCTarget = 'VRCHAT_OSCQUERY' | 'CUSTOM';
69+
6370
export type HotkeyId =
6471
| 'HOTKEY_TOGGLE_SLEEP_MODE'
6572
| 'HOTKEY_ENABLE_SLEEP_MODE'
@@ -83,46 +90,59 @@ export type NotificationType = (typeof NotificationTypes)[number];
8390

8491
export const APP_SETTINGS_DEFAULT: AppSettings = {
8592
version: 10,
93+
// General Settings
8694
userLanguage: 'en',
8795
userLanguagePicked: false,
8896
askForAdminOnStart: false,
89-
lighthouseConsolePath:
90-
'C:\\Program Files (x86)\\Steam\\steamapps\\common\\SteamVR\\tools\\lighthouse\\bin\\win64\\lighthouse_console.exe',
9197
exitInSystemTray: false,
9298
startInSystemTray: false,
93-
lighthousePowerControl: true,
94-
lighthousePowerOffState: 'sleep',
9599
sleepModeStartupBehaviour: 'PERSIST',
100+
quitWithSteamVR: 'DISABLED',
101+
hotkeys: {},
102+
playerListPresets: [],
103+
hideSnowverlay: false,
104+
openVrInitDelayFix: false,
105+
oneTimeFlags: [],
106+
eventLogTypesHidden: [],
107+
// Notifications
96108
notificationProvider: 'OYASUMIVR',
97109
notificationsEnabled: { types: [...NotificationTypes] as NotificationType[] },
98-
quitWithSteamVR: 'DISABLED',
110+
generalNotificationVolume: 100,
111+
// OSC
112+
oscServerEnabled: true,
113+
oscTargets: ['VRCHAT_OSCQUERY'],
114+
oscCustomTargetHost: '127.0.0.1',
115+
oscCustomTargetPort: 9000,
116+
// Message center
117+
hiddenMessageIds: [],
118+
// Overlay
99119
overlayMenuEnabled: true,
100120
overlayGpuAcceleration: true,
101121
overlayMenuOnlyOpenWhenVRChatIsRunning: false,
102-
generalNotificationVolume: 100,
122+
// Lighthouse
123+
lighthouseConsolePath:
124+
'C:\\Program Files (x86)\\Steam\\steamapps\\common\\SteamVR\\tools\\lighthouse\\bin\\win64\\lighthouse_console.exe',
125+
lighthousePowerControl: true,
126+
lighthousePowerOffState: 'sleep',
103127
v1LighthouseIdentifiers: {},
104-
hotkeys: {},
105-
hideSnowverlay: false,
106-
oscServerEnabled: true,
107-
valveIndexMaxBrightness: 160,
108-
bigscreenBeyondMaxBrightness: 150,
109-
bigscreenBeyondUnsafeBrightness: false,
110-
bigscreenBeyondBrightnessFanSafety: true,
128+
// Discord Rich Presence
111129
discordActivityMode: 'ENABLED',
112130
discordActivityOnlyWhileVRChatIsRunning: true,
113-
playerListPresets: [],
131+
// MQTT
114132
mqttEnabled: false,
115133
mqttHost: null,
116134
mqttPort: null,
117135
mqttUsername: null,
118136
mqttPassword: null,
119137
mqttSecureSocket: false,
120-
openVrInitDelayFix: false,
138+
// Brightness & CCT
121139
cctControlEnabled: true,
122140
cctSoftwareMode: false,
123-
oneTimeFlags: [],
124-
eventLogTypesHidden: [],
125-
hiddenMessageIds: [],
141+
// HW Specific
142+
valveIndexMaxBrightness: 160,
143+
bigscreenBeyondMaxBrightness: 150,
144+
bigscreenBeyondUnsafeBrightness: false,
145+
bigscreenBeyondBrightnessFanSafety: true,
126146
};
127147

128148
export type ExecutableReferenceStatus =

src-ui/app/services/osc.service.ts

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -184,35 +184,36 @@ export class OscService {
184184
}
185185

186186
async send_command(address: string, parameters: OscParameter[]) {
187-
const addr = this._vrchatOscAddress.value;
188-
if (!addr) return;
189-
// Replace spaces with underscores in address (This is default VRC behaviour for parameters, and spaces aren't supported in addresses according to the OSC spec anyways)
190-
address = address.trim().replace(/\s+/g, '_');
187+
const targetAddresses = this.getOscTargetAddresses();
188+
for (let targetAddr of targetAddresses) {
189+
// Replace spaces with underscores in address (This is default VRC behaviour for parameters, and spaces aren't supported in addresses according to the OSC spec anyways)
190+
address = address.trim().replace(/\s+/g, '_');
191191

192-
const _parameters = structuredClone(parameters); // copy parameter array because some parameters may be modified before sending
193-
_parameters.forEach((parameter) => {
194-
// handle "\n" in string values to insert newlines
195-
if (parameter.type === 'String') {
196-
parameter.value = parameter.value.replace(/\\n/g, '\n');
197-
}
198-
});
192+
const _parameters = structuredClone(parameters); // copy parameter array because some parameters may be modified before sending
193+
_parameters.forEach((parameter) => {
194+
// handle "\n" in string values to insert newlines
195+
if (parameter.type === 'String') {
196+
parameter.value = parameter.value.replace(/\\n/g, '\n');
197+
}
198+
});
199199

200-
const parametersString = _parameters
201-
.map((parameter) => `${parameter.type} => ${parameter.value}`)
202-
.join(', ');
200+
const parametersString = _parameters
201+
.map((parameter) => `${parameter.type} => ${parameter.value}`)
202+
.join(', ');
203203

204-
const addresses = [...this.getAddressAliasesForAvatarContext(address)];
205-
if (!addresses.length) addresses.push(address);
204+
const oscAddresses = [...this.getAddressAliasesForAvatarContext(address)];
205+
if (!oscAddresses.length) oscAddresses.push(address);
206206

207-
for (const oscAddr of addresses) {
208-
info(`[OSC] Sending {${parametersString}} to ${oscAddr}`);
207+
for (const oscAddr of oscAddresses) {
208+
info(`[OSC] Sending {${parametersString}} on ${oscAddr} to ${targetAddr}`);
209209

210-
await invoke('osc_send_command', {
211-
addr,
212-
oscAddr,
213-
types: _parameters.map((parameter) => parameter.type),
214-
values: _parameters.map((parameter) => parameter.value),
215-
});
210+
await invoke('osc_send_command', {
211+
addr: targetAddr,
212+
oscAddr,
213+
types: _parameters.map((parameter) => parameter.type),
214+
values: _parameters.map((parameter) => parameter.value),
215+
});
216+
}
216217
}
217218
}
218219

@@ -278,4 +279,17 @@ export class OscService {
278279
if (this.avatarContext.type !== 'VRCHAT') return [];
279280
return this.avatarContext.parameters.find((p) => p.address === address)?.modularAliases ?? [];
280281
}
282+
283+
private getOscTargetAddresses(): string[] {
284+
const targets: string[] = [];
285+
if (this.appSettings.settingsSync.oscTargets.includes('VRCHAT_OSCQUERY')) {
286+
targets.push(this._vrchatOscAddress.value ?? '');
287+
}
288+
if (this.appSettings.settingsSync.oscTargets.includes('CUSTOM')) {
289+
targets.push(
290+
`${this.appSettings.settingsSync.oscCustomTargetHost}:${this.appSettings.settingsSync.oscCustomTargetPort}`
291+
);
292+
}
293+
return targets.filter(Boolean);
294+
}
281295
}

src-ui/app/utils/cached-value.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,12 @@ export class CachedValue<T> {
6464
value: this.value,
6565
lastSet: this.lastSet,
6666
ttl: this.ttl,
67-
})
67+
});
6868
}
6969

7070
private async clearFromDisk() {
7171
if (!this.persistenceKey) return;
72-
await CACHE_STORE.delete(this.persistenceKey)
72+
await CACHE_STORE.delete(this.persistenceKey);
7373
}
7474

7575
private async loadFromDisk() {

src-ui/app/views/dashboard-view/views/settings-advanced-view/settings-advanced-view.component.html

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,6 @@ <h2 translate>settings.advanced.application.title</h2>
2323
</label>
2424
</div>
2525
</div>
26-
<div class="setting-row">
27-
<div class="setting-row-label" translate>
28-
<span translate>settings.advanced.application.oscServerEnabled.title</span>
29-
<span translate>settings.advanced.application.oscServerEnabled.description</span>
30-
</div>
31-
<div class="setting-row-action">
32-
<label class="switch-toggle">
33-
<input
34-
type="checkbox"
35-
[checked]="oscServerEnabled"
36-
(change)="setOscServerEnabled(!oscServerEnabled)"
37-
/>
38-
<span class="switch-toggle-slider"></span>
39-
</label>
40-
</div>
41-
</div>
4226
<div class="setting-row">
4327
<div class="setting-row-label" translate>
4428
<span translate>settings.advanced.application.openVrInitDelayFix.title</span>

src-ui/app/views/dashboard-view/views/settings-advanced-view/settings-advanced-view.component.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ export class SettingsAdvancedViewComponent {
5656
];
5757
checkedPersistentStorageItems: string[] = [];
5858
memoryWatcherActive = FLAVOUR === 'DEV';
59-
oscServerEnabled = true;
6059
overlayGpuAcceleration = true;
6160
openVrInitDelayFix = false;
6261

@@ -66,11 +65,10 @@ export class SettingsAdvancedViewComponent {
6665
private modalService: ModalService,
6766
private eventLogService: EventLogService,
6867
private ipcService: IPCService,
69-
protected openvr: OpenVRService,
70-
private settingsService: AppSettingsService
68+
private settingsService: AppSettingsService,
69+
protected openvr: OpenVRService
7170
) {
7271
this.settingsService.settings.pipe(takeUntilDestroyed()).subscribe((settings) => {
73-
this.oscServerEnabled = settings.oscServerEnabled;
7472
this.overlayGpuAcceleration = settings.overlayGpuAcceleration;
7573
this.openVrInitDelayFix = settings.openVrInitDelayFix;
7674
});
@@ -295,12 +293,6 @@ export class SettingsAdvancedViewComponent {
295293
await invoke('open_dev_tools');
296294
}
297295

298-
setOscServerEnabled(enabled: boolean) {
299-
this.settingsService.updateSettings({
300-
oscServerEnabled: enabled,
301-
});
302-
}
303-
304296
setOverlayGpuAcceleration(enabled: boolean) {
305297
this.settingsService.updateSettings({ overlayGpuAcceleration: enabled });
306298
}

0 commit comments

Comments
 (0)