android auto improvements

This commit is contained in:
Boof2015
2026-07-14 14:13:49 -04:00
parent 60a50d68fb
commit 36fadf9925
9 changed files with 64 additions and 3 deletions
+4
View File
@@ -105,6 +105,8 @@ const coordinator = new DspStartupCoordinator<
console.info('[dsp-startup] base-applied', {
at: Date.now(),
routeKey: route.key,
routeKind: route.kind,
nativeType: route.nativeType,
eqEnabled: eq.enabled,
preampDb: eq.preamp,
normalizationEnabled: settings.enabled,
@@ -134,6 +136,8 @@ const coordinator = new DspStartupCoordinator<
console.info('[dsp-startup] target-applied', {
at: Date.now(),
routeKey: route.key,
routeKind: route.kind,
nativeType: route.nativeType,
gainSource: resolved.source,
activation: target.activation,
hasTarget: Boolean(target.url),
+1
View File
@@ -25,6 +25,7 @@ function isRouteKind(value: unknown): value is AudioOutputRouteKind {
value === 'bluetooth' ||
value === 'usb' ||
value === 'hdmi' ||
value === 'remote' ||
value === 'unknown'
);
}
+7
View File
@@ -4,6 +4,7 @@ import type { AudioOutputRoute, EQBand } from '../types/audio.ts';
import {
buildAudioOutputRouteKey,
createEQRouteProfile,
isAudioOutputRouteUsable,
normalizeAudioOutputRoute,
parseEQRouteProfilesJson,
restoreEQRouteProfile,
@@ -55,10 +56,16 @@ test('normalizes named external routes and generic class routes', () => {
assert.equal(normalizeAudioOutputRoute({ kind: 'usb', label: 'USB DAC' })?.key, 'usb:name:usb-dac');
assert.equal(normalizeAudioOutputRoute({ kind: 'usb', label: 'USB audio' })?.key, 'usb');
assert.equal(normalizeAudioOutputRoute({ kind: 'hdmi', label: 'Living Room TV' })?.key, 'hdmi:name:living-room-tv');
assert.equal(normalizeAudioOutputRoute({ kind: 'remote', label: 'Remote audio' })?.key, 'remote');
assert.equal(normalizeAudioOutputRoute({ kind: 'speaker', label: 'Pixel speaker' })?.key, 'speaker');
assert.equal(normalizeAudioOutputRoute({ kind: 'nonsense', label: '' })?.key, 'unknown');
});
test('accepts concrete unclassified Android outputs without accepting a missing route', () => {
assert.equal(isAudioOutputRouteUsable(route({ kind: 'unknown', nativeType: 25 })), true);
assert.equal(isAudioOutputRouteUsable(route({ kind: 'unknown', nativeType: null })), false);
});
test('recovers from corrupt route profile storage', () => {
assert.deepEqual(parseEQRouteProfilesJson('{not-json', nextId), {});
assert.deepEqual(
+10
View File
@@ -49,6 +49,7 @@ function isRouteKind(value: unknown): value is AudioOutputRouteKind {
value === 'bluetooth' ||
value === 'usb' ||
value === 'hdmi' ||
value === 'remote' ||
value === 'unknown'
);
}
@@ -84,6 +85,8 @@ function defaultRouteLabel(kind: AudioOutputRouteKind): string {
return 'USB audio';
case 'hdmi':
return 'HDMI audio';
case 'remote':
return 'Remote audio';
default:
return 'Unknown output';
}
@@ -124,6 +127,8 @@ export function buildAudioOutputRouteKey(kind: AudioOutputRouteKind, label: stri
return 'usb';
case 'hdmi':
return 'hdmi';
case 'remote':
return 'remote';
default:
return 'unknown';
}
@@ -148,6 +153,11 @@ export function normalizeAudioOutputRoute(value: unknown): AudioOutputRoute | nu
};
}
/** A known kind or a concrete native type is enough to apply the loaded EQ safely. */
export function isAudioOutputRouteUsable(route: AudioOutputRoute): boolean {
return route.kind !== 'unknown' || route.nativeType != null;
}
function normalizeBands(value: unknown, createId: () => string): EQBand[] | null {
if (!Array.isArray(value) || value.length === 0) return null;
return value
+14 -1
View File
@@ -4,6 +4,7 @@ import {
} from '../../modules/astra-audio-route';
import { useEQStore } from '@/stores/eqStore';
import type { AudioOutputRoute } from '@/types/audio';
import { isAudioOutputRouteUsable } from '@/audio/eqRouteProfiles';
type Subscription = { remove: () => void };
@@ -30,7 +31,19 @@ export async function refreshEQRouteForPlayback(): Promise<AudioOutputRoute> {
await useEQStore.getState().load();
const route = AstraAudioRoute.getCurrentRoute();
if (!route) throw new Error('Current media output route unavailable');
if (route.kind === 'unknown') throw new Error('Current media output route unresolved');
if (!isAudioOutputRouteUsable(route)) {
throw new Error('Current media output route unresolved');
}
if (route.kind === 'unknown') {
// A real but newer Android route type is still safe: retain/reassert the
// loaded EQ state instead of permanently blocking playback. Android Auto
// used to reach this path because TYPE_REMOTE_SUBMIX was not classified.
console.warn('[eq-route] unclassified native output; retaining current EQ', {
nativeType: route.nativeType,
nativeId: route.nativeId,
label: route.label,
});
}
await useEQStore.getState().setOutputRoute(route);
return route;
}
+8 -1
View File
@@ -85,7 +85,14 @@ export interface EQPreset {
graphicGains?: number[];
}
export type AudioOutputRouteKind = 'speaker' | 'wired' | 'bluetooth' | 'usb' | 'hdmi' | 'unknown';
export type AudioOutputRouteKind =
| 'speaker'
| 'wired'
| 'bluetooth'
| 'usb'
| 'hdmi'
| 'remote'
| 'unknown';
export interface AudioOutputRoute {
key: string;