Matthias Nott
2026-03-02 a0f39302919fbacf7a0d407f01b1a50413ea6f70
services/audio.ts
....@@ -1,112 +1,65 @@
1
-import { Audio, AVPlaybackStatus } from "expo-av";
1
+import {
2
+ createAudioPlayer,
3
+ requestRecordingPermissionsAsync,
4
+ setAudioModeAsync,
5
+} from "expo-audio";
26
37 export interface RecordingResult {
48 uri: string;
59 durationMs: number;
610 }
711
8
-let currentRecording: Audio.Recording | null = null;
9
-let currentSound: Audio.Sound | null = null;
12
+let currentPlayer: ReturnType<typeof createAudioPlayer> | null = null;
1013
11
-async function requestPermissions(): Promise<boolean> {
12
- const { status } = await Audio.requestPermissionsAsync();
14
+export async function requestPermissions(): Promise<boolean> {
15
+ const { status } = await requestRecordingPermissionsAsync();
1316 return status === "granted";
14
-}
15
-
16
-export async function startRecording(): Promise<Audio.Recording | null> {
17
- const granted = await requestPermissions();
18
- if (!granted) return null;
19
-
20
- try {
21
- await Audio.setAudioModeAsync({
22
- allowsRecordingIOS: true,
23
- playsInSilentModeIOS: true,
24
- });
25
-
26
- const { recording } = await Audio.Recording.createAsync(
27
- Audio.RecordingOptionsPresets.HIGH_QUALITY
28
- );
29
-
30
- currentRecording = recording;
31
- return recording;
32
- } catch (error) {
33
- console.error("Failed to start recording:", error);
34
- return null;
35
- }
36
-}
37
-
38
-export async function stopRecording(): Promise<RecordingResult | null> {
39
- if (!currentRecording) return null;
40
-
41
- try {
42
- await currentRecording.stopAndUnloadAsync();
43
- const status = await currentRecording.getStatusAsync();
44
- const uri = currentRecording.getURI();
45
- currentRecording = null;
46
-
47
- await Audio.setAudioModeAsync({
48
- allowsRecordingIOS: false,
49
- });
50
-
51
- if (!uri) return null;
52
-
53
- const durationMs = (status as { durationMillis?: number }).durationMillis ?? 0;
54
- return { uri, durationMs };
55
- } catch (error) {
56
- console.error("Failed to stop recording:", error);
57
- currentRecording = null;
58
- return null;
59
- }
6017 }
6118
6219 export async function playAudio(
6320 uri: string,
6421 onFinish?: () => void
65
-): Promise<Audio.Sound | null> {
22
+): Promise<void> {
6623 try {
6724 await stopPlayback();
6825
69
- await Audio.setAudioModeAsync({
70
- allowsRecordingIOS: false,
71
- playsInSilentModeIOS: true,
26
+ await setAudioModeAsync({
27
+ playsInSilentMode: true,
7228 });
7329
74
- const { sound } = await Audio.Sound.createAsync(
75
- { uri },
76
- { shouldPlay: true }
77
- );
30
+ const player = createAudioPlayer(uri);
31
+ currentPlayer = player;
7832
79
- currentSound = sound;
80
-
81
- sound.setOnPlaybackStatusUpdate((status: AVPlaybackStatus) => {
82
- if (status.isLoaded && status.didJustFinish) {
33
+ player.addListener("playbackStatusUpdate", (status) => {
34
+ if (!status.playing && status.currentTime >= status.duration && status.duration > 0) {
8335 onFinish?.();
84
- sound.unloadAsync().catch(() => {});
85
- currentSound = null;
36
+ player.remove();
37
+ if (currentPlayer === player) currentPlayer = null;
8638 }
8739 });
8840
89
- return sound;
41
+ player.play();
9042 } catch (error) {
9143 console.error("Failed to play audio:", error);
92
- return null;
9344 }
9445 }
9546
9647 export async function stopPlayback(): Promise<void> {
97
- if (currentSound) {
48
+ if (currentPlayer) {
9849 try {
99
- await currentSound.stopAsync();
100
- await currentSound.unloadAsync();
50
+ currentPlayer.pause();
51
+ currentPlayer.remove();
10152 } catch {
102
- // Ignore errors during cleanup
53
+ // Ignore cleanup errors
10354 }
104
- currentSound = null;
55
+ currentPlayer = null;
10556 }
10657 }
10758
108
-export function encodeAudioToBase64(uri: string): Promise<string> {
109
- // In React Native, we'd use FileSystem from expo-file-system
110
- // For now, return the URI as-is since we may not have expo-file-system
111
- return Promise.resolve(uri);
59
+export async function encodeAudioToBase64(uri: string): Promise<string> {
60
+ const FileSystem = await import("expo-file-system");
61
+ const result = await FileSystem.readAsStringAsync(uri, {
62
+ encoding: FileSystem.EncodingType.Base64,
63
+ });
64
+ return result;
11265 }