Squash merge feat/expo-app: app-expo, .cursor, workflows, package.json, .husky; remove app-android, app-ios, react-app
This commit is contained in:
83
app-expo/src/core/audio/audio-focus.ts
Normal file
83
app-expo/src/core/audio/audio-focus.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { setAudioModeAsync } from 'expo-audio';
|
||||
|
||||
type AudioOwner = 'recorder' | 'player' | null;
|
||||
type OwnerChangeListener = (owner: AudioOwner) => void;
|
||||
|
||||
let currentOwner: AudioOwner = null;
|
||||
const listeners = new Set<OwnerChangeListener>();
|
||||
|
||||
function notify() {
|
||||
for (const listener of listeners) {
|
||||
listener(currentOwner);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Global audio focus coordinator.
|
||||
* Ensures recording and playback are mutually exclusive at the app level,
|
||||
* not just within a single hook's lifecycle.
|
||||
*/
|
||||
export const audioFocus = {
|
||||
async acquireForRecording(): Promise<boolean> {
|
||||
if (currentOwner === 'recorder') return true;
|
||||
|
||||
if (currentOwner === 'player') {
|
||||
await this.release();
|
||||
}
|
||||
|
||||
await setAudioModeAsync({
|
||||
playsInSilentMode: true,
|
||||
allowsRecording: true,
|
||||
});
|
||||
|
||||
currentOwner = 'recorder';
|
||||
notify();
|
||||
return true;
|
||||
},
|
||||
|
||||
async acquireForPlayback(): Promise<boolean> {
|
||||
if (currentOwner === 'player') return true;
|
||||
|
||||
if (currentOwner === 'recorder') {
|
||||
return false;
|
||||
}
|
||||
|
||||
await setAudioModeAsync({
|
||||
playsInSilentMode: true,
|
||||
allowsRecording: false,
|
||||
});
|
||||
|
||||
currentOwner = 'player';
|
||||
notify();
|
||||
return true;
|
||||
},
|
||||
|
||||
async release(): Promise<void> {
|
||||
if (!currentOwner) return;
|
||||
|
||||
await setAudioModeAsync({
|
||||
playsInSilentMode: false,
|
||||
allowsRecording: false,
|
||||
});
|
||||
|
||||
currentOwner = null;
|
||||
notify();
|
||||
},
|
||||
|
||||
getCurrentOwner(): AudioOwner {
|
||||
return currentOwner;
|
||||
},
|
||||
|
||||
isRecording(): boolean {
|
||||
return currentOwner === 'recorder';
|
||||
},
|
||||
|
||||
isPlaying(): boolean {
|
||||
return currentOwner === 'player';
|
||||
},
|
||||
|
||||
onOwnerChange(listener: OwnerChangeListener): () => void {
|
||||
listeners.add(listener);
|
||||
return () => listeners.delete(listener);
|
||||
},
|
||||
} as const;
|
||||
Reference in New Issue
Block a user