@idealyst/audio 1.2.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +69 -0
- package/src/constants.ts +161 -0
- package/src/context/AudioContext.native.ts +84 -0
- package/src/context/AudioContext.web.ts +97 -0
- package/src/context/index.native.ts +1 -0
- package/src/context/index.ts +1 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useAudio.ts +129 -0
- package/src/hooks/usePlayer.ts +247 -0
- package/src/hooks/useRecorder.ts +176 -0
- package/src/index.native.ts +114 -0
- package/src/index.ts +114 -0
- package/src/index.web.ts +8 -0
- package/src/playback/Player.native.ts +517 -0
- package/src/playback/Player.web.ts +518 -0
- package/src/playback/index.native.ts +1 -0
- package/src/playback/index.ts +1 -0
- package/src/recording/Recorder.native.ts +330 -0
- package/src/recording/Recorder.web.ts +399 -0
- package/src/recording/index.native.ts +1 -0
- package/src/recording/index.ts +1 -0
- package/src/session/AudioSession.native.ts +204 -0
- package/src/session/AudioSession.web.ts +69 -0
- package/src/session/index.native.ts +5 -0
- package/src/session/index.ts +1 -0
- package/src/types.ts +470 -0
- package/src/utils.ts +379 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* usePlayer Hook
|
|
3
|
+
*
|
|
4
|
+
* Provides audio playback functionality for files and PCM streaming.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
8
|
+
import type {
|
|
9
|
+
UsePlayerOptions,
|
|
10
|
+
UsePlayerResult,
|
|
11
|
+
PlayerStatus,
|
|
12
|
+
AudioError,
|
|
13
|
+
AudioConfig,
|
|
14
|
+
} from '../types';
|
|
15
|
+
import {
|
|
16
|
+
DEFAULT_PLAYER_STATUS,
|
|
17
|
+
DEFAULT_POSITION_UPDATE_INTERVAL,
|
|
18
|
+
} from '../constants';
|
|
19
|
+
import { getAudioContext } from '../context';
|
|
20
|
+
import { createPlayer } from '../playback';
|
|
21
|
+
|
|
22
|
+
export function usePlayer(options: UsePlayerOptions = {}): UsePlayerResult {
|
|
23
|
+
const {
|
|
24
|
+
autoPlay = false,
|
|
25
|
+
volume: initialVolume = 1.0,
|
|
26
|
+
positionUpdateInterval = DEFAULT_POSITION_UPDATE_INTERVAL,
|
|
27
|
+
} = options;
|
|
28
|
+
|
|
29
|
+
const [status, setStatus] = useState<PlayerStatus>({
|
|
30
|
+
...DEFAULT_PLAYER_STATUS,
|
|
31
|
+
volume: initialVolume,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const audioContextRef = useRef(getAudioContext());
|
|
35
|
+
const playerRef = useRef<ReturnType<typeof createPlayer> | null>(null);
|
|
36
|
+
const mountedRef = useRef(true);
|
|
37
|
+
const autoPlayPendingRef = useRef(false);
|
|
38
|
+
|
|
39
|
+
// Initialize player lazily
|
|
40
|
+
const getPlayer = useCallback(() => {
|
|
41
|
+
if (!playerRef.current) {
|
|
42
|
+
playerRef.current = createPlayer(audioContextRef.current);
|
|
43
|
+
// Set initial volume
|
|
44
|
+
playerRef.current.setVolume(initialVolume);
|
|
45
|
+
}
|
|
46
|
+
return playerRef.current;
|
|
47
|
+
}, [initialVolume]);
|
|
48
|
+
|
|
49
|
+
// Load file
|
|
50
|
+
const loadFile = useCallback(async (uri: string) => {
|
|
51
|
+
const player = getPlayer();
|
|
52
|
+
const audioContext = audioContextRef.current;
|
|
53
|
+
|
|
54
|
+
// Ensure audio context is initialized
|
|
55
|
+
if (!audioContext.isInitialized) {
|
|
56
|
+
await audioContext.initialize();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
await player.loadFile(uri);
|
|
60
|
+
|
|
61
|
+
if (autoPlay) {
|
|
62
|
+
autoPlayPendingRef.current = true;
|
|
63
|
+
}
|
|
64
|
+
}, [getPlayer, autoPlay]);
|
|
65
|
+
|
|
66
|
+
// Unload
|
|
67
|
+
const unload = useCallback(() => {
|
|
68
|
+
const player = playerRef.current;
|
|
69
|
+
if (player) {
|
|
70
|
+
player.unload();
|
|
71
|
+
}
|
|
72
|
+
}, []);
|
|
73
|
+
|
|
74
|
+
// Load PCM stream
|
|
75
|
+
const loadPCMStream = useCallback(async (config: AudioConfig) => {
|
|
76
|
+
const player = getPlayer();
|
|
77
|
+
const audioContext = audioContextRef.current;
|
|
78
|
+
|
|
79
|
+
// Ensure audio context is initialized
|
|
80
|
+
if (!audioContext.isInitialized) {
|
|
81
|
+
await audioContext.initialize();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
await player.loadPCMStream(config);
|
|
85
|
+
|
|
86
|
+
if (autoPlay) {
|
|
87
|
+
autoPlayPendingRef.current = true;
|
|
88
|
+
}
|
|
89
|
+
}, [getPlayer, autoPlay]);
|
|
90
|
+
|
|
91
|
+
// Feed PCM data
|
|
92
|
+
const feedPCMData = useCallback((data: ArrayBuffer | Int16Array) => {
|
|
93
|
+
const player = playerRef.current;
|
|
94
|
+
if (player) {
|
|
95
|
+
player.feedPCMData(data);
|
|
96
|
+
}
|
|
97
|
+
}, []);
|
|
98
|
+
|
|
99
|
+
// Flush
|
|
100
|
+
const flush = useCallback(async () => {
|
|
101
|
+
const player = playerRef.current;
|
|
102
|
+
if (player) {
|
|
103
|
+
await player.flush();
|
|
104
|
+
}
|
|
105
|
+
}, []);
|
|
106
|
+
|
|
107
|
+
// Play
|
|
108
|
+
const play = useCallback(async () => {
|
|
109
|
+
const player = getPlayer();
|
|
110
|
+
await player.play();
|
|
111
|
+
}, [getPlayer]);
|
|
112
|
+
|
|
113
|
+
// Pause
|
|
114
|
+
const pause = useCallback(() => {
|
|
115
|
+
const player = playerRef.current;
|
|
116
|
+
if (player) {
|
|
117
|
+
player.pause();
|
|
118
|
+
}
|
|
119
|
+
}, []);
|
|
120
|
+
|
|
121
|
+
// Stop
|
|
122
|
+
const stop = useCallback(() => {
|
|
123
|
+
const player = playerRef.current;
|
|
124
|
+
if (player) {
|
|
125
|
+
player.stop();
|
|
126
|
+
}
|
|
127
|
+
}, []);
|
|
128
|
+
|
|
129
|
+
// Seek
|
|
130
|
+
const seek = useCallback(async (positionMs: number) => {
|
|
131
|
+
const player = playerRef.current;
|
|
132
|
+
if (player) {
|
|
133
|
+
await player.seek(positionMs);
|
|
134
|
+
}
|
|
135
|
+
}, []);
|
|
136
|
+
|
|
137
|
+
// Set volume
|
|
138
|
+
const setVolume = useCallback((vol: number) => {
|
|
139
|
+
const player = playerRef.current;
|
|
140
|
+
if (player) {
|
|
141
|
+
player.setVolume(vol);
|
|
142
|
+
}
|
|
143
|
+
}, []);
|
|
144
|
+
|
|
145
|
+
// Toggle mute
|
|
146
|
+
const toggleMute = useCallback(() => {
|
|
147
|
+
const player = playerRef.current;
|
|
148
|
+
if (player) {
|
|
149
|
+
player.setMuted(!status.muted);
|
|
150
|
+
}
|
|
151
|
+
}, [status.muted]);
|
|
152
|
+
|
|
153
|
+
// Setup on mount
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
mountedRef.current = true;
|
|
156
|
+
|
|
157
|
+
const player = getPlayer();
|
|
158
|
+
|
|
159
|
+
// Subscribe to state changes
|
|
160
|
+
const unsubState = player.onStateChange((newStatus) => {
|
|
161
|
+
if (mountedRef.current) {
|
|
162
|
+
setStatus(newStatus);
|
|
163
|
+
|
|
164
|
+
// Handle autoplay
|
|
165
|
+
if (autoPlayPendingRef.current && newStatus.state === 'ready') {
|
|
166
|
+
autoPlayPendingRef.current = false;
|
|
167
|
+
player.play().catch(console.error);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Subscribe to position updates
|
|
173
|
+
const unsubPosition = player.onPosition((position) => {
|
|
174
|
+
if (mountedRef.current) {
|
|
175
|
+
setStatus((prev) => ({ ...prev, position }));
|
|
176
|
+
}
|
|
177
|
+
}, positionUpdateInterval);
|
|
178
|
+
|
|
179
|
+
// Subscribe to buffer changes
|
|
180
|
+
const unsubBuffer = player.onBufferChange((buffered) => {
|
|
181
|
+
if (mountedRef.current) {
|
|
182
|
+
setStatus((prev) => ({ ...prev, buffered }));
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Subscribe to ended event
|
|
187
|
+
const unsubEnded = player.onEnded(() => {
|
|
188
|
+
if (mountedRef.current) {
|
|
189
|
+
// Status will be updated via onStateChange
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
return () => {
|
|
194
|
+
mountedRef.current = false;
|
|
195
|
+
unsubState();
|
|
196
|
+
unsubPosition();
|
|
197
|
+
unsubBuffer();
|
|
198
|
+
unsubEnded();
|
|
199
|
+
|
|
200
|
+
// Dispose player on unmount
|
|
201
|
+
if (playerRef.current) {
|
|
202
|
+
playerRef.current.dispose();
|
|
203
|
+
playerRef.current = null;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
}, [getPlayer, positionUpdateInterval]);
|
|
207
|
+
|
|
208
|
+
// Derived state
|
|
209
|
+
const isPlaying = status.isPlaying;
|
|
210
|
+
const isPaused = status.isPaused;
|
|
211
|
+
const isLoading = status.state === 'loading';
|
|
212
|
+
const position = status.position;
|
|
213
|
+
const duration = status.duration;
|
|
214
|
+
const volume = status.volume;
|
|
215
|
+
const error = status.error || null;
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
// State
|
|
219
|
+
status,
|
|
220
|
+
isPlaying,
|
|
221
|
+
isPaused,
|
|
222
|
+
isLoading,
|
|
223
|
+
position,
|
|
224
|
+
duration,
|
|
225
|
+
volume,
|
|
226
|
+
error,
|
|
227
|
+
|
|
228
|
+
// File playback
|
|
229
|
+
loadFile,
|
|
230
|
+
unload,
|
|
231
|
+
|
|
232
|
+
// PCM streaming
|
|
233
|
+
loadPCMStream,
|
|
234
|
+
feedPCMData,
|
|
235
|
+
flush,
|
|
236
|
+
|
|
237
|
+
// Playback control
|
|
238
|
+
play,
|
|
239
|
+
pause,
|
|
240
|
+
stop,
|
|
241
|
+
seek,
|
|
242
|
+
|
|
243
|
+
// Volume
|
|
244
|
+
setVolume,
|
|
245
|
+
toggleMute,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useRecorder Hook
|
|
3
|
+
*
|
|
4
|
+
* Provides recording functionality with PCM data streaming.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
8
|
+
import type {
|
|
9
|
+
UseRecorderOptions,
|
|
10
|
+
UseRecorderResult,
|
|
11
|
+
RecorderStatus,
|
|
12
|
+
PermissionStatus,
|
|
13
|
+
AudioLevel,
|
|
14
|
+
AudioError,
|
|
15
|
+
AudioConfig,
|
|
16
|
+
RecorderDataCallback,
|
|
17
|
+
} from '../types';
|
|
18
|
+
import {
|
|
19
|
+
DEFAULT_RECORDER_STATUS,
|
|
20
|
+
DEFAULT_AUDIO_LEVEL,
|
|
21
|
+
DEFAULT_LEVEL_UPDATE_INTERVAL,
|
|
22
|
+
} from '../constants';
|
|
23
|
+
import { getAudioContext } from '../context';
|
|
24
|
+
import { createRecorder } from '../recording';
|
|
25
|
+
|
|
26
|
+
export function useRecorder(options: UseRecorderOptions = {}): UseRecorderResult {
|
|
27
|
+
const {
|
|
28
|
+
config,
|
|
29
|
+
autoRequestPermission = false,
|
|
30
|
+
levelUpdateInterval = DEFAULT_LEVEL_UPDATE_INTERVAL,
|
|
31
|
+
} = options;
|
|
32
|
+
|
|
33
|
+
const [status, setStatus] = useState<RecorderStatus>(DEFAULT_RECORDER_STATUS);
|
|
34
|
+
|
|
35
|
+
const audioContextRef = useRef(getAudioContext());
|
|
36
|
+
const recorderRef = useRef<ReturnType<typeof createRecorder> | null>(null);
|
|
37
|
+
const mountedRef = useRef(true);
|
|
38
|
+
|
|
39
|
+
// Initialize recorder lazily
|
|
40
|
+
const getRecorder = useCallback(() => {
|
|
41
|
+
if (!recorderRef.current) {
|
|
42
|
+
recorderRef.current = createRecorder(audioContextRef.current);
|
|
43
|
+
}
|
|
44
|
+
return recorderRef.current;
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
// Check permission
|
|
48
|
+
const checkPermission = useCallback(async (): Promise<PermissionStatus> => {
|
|
49
|
+
const recorder = getRecorder();
|
|
50
|
+
return recorder.checkPermission();
|
|
51
|
+
}, [getRecorder]);
|
|
52
|
+
|
|
53
|
+
// Request permission
|
|
54
|
+
const requestPermission = useCallback(async (): Promise<PermissionStatus> => {
|
|
55
|
+
const recorder = getRecorder();
|
|
56
|
+
return recorder.requestPermission();
|
|
57
|
+
}, [getRecorder]);
|
|
58
|
+
|
|
59
|
+
// Start recording
|
|
60
|
+
const start = useCallback(async (startConfig?: Partial<AudioConfig>) => {
|
|
61
|
+
const recorder = getRecorder();
|
|
62
|
+
const audioContext = audioContextRef.current;
|
|
63
|
+
|
|
64
|
+
// Ensure audio context is initialized
|
|
65
|
+
if (!audioContext.isInitialized) {
|
|
66
|
+
await audioContext.initialize();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
await recorder.start(startConfig || config);
|
|
70
|
+
}, [getRecorder, config]);
|
|
71
|
+
|
|
72
|
+
// Stop recording
|
|
73
|
+
const stop = useCallback(async () => {
|
|
74
|
+
const recorder = getRecorder();
|
|
75
|
+
await recorder.stop();
|
|
76
|
+
}, [getRecorder]);
|
|
77
|
+
|
|
78
|
+
// Pause recording
|
|
79
|
+
const pause = useCallback(async () => {
|
|
80
|
+
const recorder = getRecorder();
|
|
81
|
+
await recorder.pause();
|
|
82
|
+
}, [getRecorder]);
|
|
83
|
+
|
|
84
|
+
// Resume recording
|
|
85
|
+
const resume = useCallback(async () => {
|
|
86
|
+
const recorder = getRecorder();
|
|
87
|
+
await recorder.resume();
|
|
88
|
+
}, [getRecorder]);
|
|
89
|
+
|
|
90
|
+
// Subscribe to data
|
|
91
|
+
const subscribeToData = useCallback((callback: RecorderDataCallback): (() => void) => {
|
|
92
|
+
const recorder = getRecorder();
|
|
93
|
+
return recorder.onData(callback);
|
|
94
|
+
}, [getRecorder]);
|
|
95
|
+
|
|
96
|
+
// Reset peak level
|
|
97
|
+
const resetPeakLevel = useCallback(() => {
|
|
98
|
+
const recorder = recorderRef.current;
|
|
99
|
+
if (recorder) {
|
|
100
|
+
recorder.resetPeakLevel();
|
|
101
|
+
}
|
|
102
|
+
}, []);
|
|
103
|
+
|
|
104
|
+
// Setup on mount
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
mountedRef.current = true;
|
|
107
|
+
|
|
108
|
+
const recorder = getRecorder();
|
|
109
|
+
|
|
110
|
+
// Subscribe to state changes
|
|
111
|
+
const unsubState = recorder.onStateChange((newStatus) => {
|
|
112
|
+
if (mountedRef.current) {
|
|
113
|
+
setStatus(newStatus);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Subscribe to level updates
|
|
118
|
+
const unsubLevel = recorder.onLevel((level) => {
|
|
119
|
+
if (mountedRef.current) {
|
|
120
|
+
setStatus((prev) => ({ ...prev, level }));
|
|
121
|
+
}
|
|
122
|
+
}, levelUpdateInterval);
|
|
123
|
+
|
|
124
|
+
// Auto request permission if enabled
|
|
125
|
+
if (autoRequestPermission) {
|
|
126
|
+
requestPermission().catch(console.error);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return () => {
|
|
130
|
+
mountedRef.current = false;
|
|
131
|
+
unsubState();
|
|
132
|
+
unsubLevel();
|
|
133
|
+
|
|
134
|
+
// Dispose recorder on unmount
|
|
135
|
+
if (recorderRef.current) {
|
|
136
|
+
recorderRef.current.dispose();
|
|
137
|
+
recorderRef.current = null;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}, [getRecorder, autoRequestPermission, requestPermission, levelUpdateInterval]);
|
|
141
|
+
|
|
142
|
+
// Derived state
|
|
143
|
+
const isRecording = status.isRecording;
|
|
144
|
+
const isPaused = status.isPaused;
|
|
145
|
+
const permission = status.permission;
|
|
146
|
+
const duration = status.duration;
|
|
147
|
+
const level = status.level;
|
|
148
|
+
const error = status.error || null;
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
// State
|
|
152
|
+
status,
|
|
153
|
+
isRecording,
|
|
154
|
+
isPaused,
|
|
155
|
+
permission,
|
|
156
|
+
duration,
|
|
157
|
+
level,
|
|
158
|
+
error,
|
|
159
|
+
|
|
160
|
+
// Actions
|
|
161
|
+
start,
|
|
162
|
+
stop,
|
|
163
|
+
pause,
|
|
164
|
+
resume,
|
|
165
|
+
|
|
166
|
+
// Permissions
|
|
167
|
+
checkPermission,
|
|
168
|
+
requestPermission,
|
|
169
|
+
|
|
170
|
+
// Data subscription
|
|
171
|
+
subscribeToData,
|
|
172
|
+
|
|
173
|
+
// Utilities
|
|
174
|
+
resetPeakLevel,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @idealyst/audio
|
|
3
|
+
*
|
|
4
|
+
* Unified audio package for recording and playback on web and native.
|
|
5
|
+
* Native implementation using react-native-audio-api.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Types
|
|
9
|
+
export type {
|
|
10
|
+
// Audio configuration
|
|
11
|
+
SampleRate,
|
|
12
|
+
BitDepth,
|
|
13
|
+
ChannelCount,
|
|
14
|
+
AudioConfig,
|
|
15
|
+
|
|
16
|
+
// Audio session (native)
|
|
17
|
+
AudioSessionCategory,
|
|
18
|
+
AudioSessionCategoryOption,
|
|
19
|
+
AudioSessionMode,
|
|
20
|
+
AudioSessionConfig,
|
|
21
|
+
AudioSessionState,
|
|
22
|
+
AudioSessionInterruption,
|
|
23
|
+
AudioSessionRouteChange,
|
|
24
|
+
|
|
25
|
+
// PCM data
|
|
26
|
+
PCMData,
|
|
27
|
+
AudioLevel,
|
|
28
|
+
|
|
29
|
+
// Recorder
|
|
30
|
+
RecorderState,
|
|
31
|
+
PermissionStatus,
|
|
32
|
+
RecorderStatus,
|
|
33
|
+
RecorderDataCallback,
|
|
34
|
+
RecorderLevelCallback,
|
|
35
|
+
RecorderStateCallback,
|
|
36
|
+
IRecorder,
|
|
37
|
+
|
|
38
|
+
// Player
|
|
39
|
+
PlayerState,
|
|
40
|
+
PlayerStatus,
|
|
41
|
+
PlayerStateCallback,
|
|
42
|
+
PlayerPositionCallback,
|
|
43
|
+
PlayerBufferCallback,
|
|
44
|
+
PlayerEndedCallback,
|
|
45
|
+
IPlayer,
|
|
46
|
+
|
|
47
|
+
// Audio context
|
|
48
|
+
IAudioContext,
|
|
49
|
+
IAudioSessionManager,
|
|
50
|
+
|
|
51
|
+
// Errors
|
|
52
|
+
AudioErrorCode,
|
|
53
|
+
AudioError,
|
|
54
|
+
|
|
55
|
+
// Hook types
|
|
56
|
+
UseAudioOptions,
|
|
57
|
+
UseAudioResult,
|
|
58
|
+
UseRecorderOptions,
|
|
59
|
+
UseRecorderResult,
|
|
60
|
+
UsePlayerOptions,
|
|
61
|
+
UsePlayerResult,
|
|
62
|
+
|
|
63
|
+
// Presets
|
|
64
|
+
AudioProfiles,
|
|
65
|
+
SessionPresets,
|
|
66
|
+
} from './types';
|
|
67
|
+
|
|
68
|
+
// Constants
|
|
69
|
+
export {
|
|
70
|
+
// Audio profiles
|
|
71
|
+
AUDIO_PROFILES,
|
|
72
|
+
|
|
73
|
+
// Session presets
|
|
74
|
+
SESSION_PRESETS,
|
|
75
|
+
|
|
76
|
+
// Defaults
|
|
77
|
+
DEFAULT_AUDIO_CONFIG,
|
|
78
|
+
DEFAULT_AUDIO_LEVEL,
|
|
79
|
+
DEFAULT_RECORDER_STATUS,
|
|
80
|
+
DEFAULT_PLAYER_STATUS,
|
|
81
|
+
DEFAULT_SESSION_STATE,
|
|
82
|
+
DEFAULT_LEVEL_UPDATE_INTERVAL,
|
|
83
|
+
DEFAULT_POSITION_UPDATE_INTERVAL,
|
|
84
|
+
} from './constants';
|
|
85
|
+
|
|
86
|
+
// Context (native)
|
|
87
|
+
export { getAudioContext, NativeAudioContextManager } from './context/index.native';
|
|
88
|
+
|
|
89
|
+
// Session (native)
|
|
90
|
+
export { getAudioSessionManager, NativeAudioSessionManager } from './session/index.native';
|
|
91
|
+
|
|
92
|
+
// Recording (native)
|
|
93
|
+
export { createRecorder, NativeRecorder } from './recording/index.native';
|
|
94
|
+
|
|
95
|
+
// Playback (native)
|
|
96
|
+
export { createPlayer, NativePlayer } from './playback/index.native';
|
|
97
|
+
|
|
98
|
+
// Hooks
|
|
99
|
+
export { useAudio, useRecorder, usePlayer } from './hooks';
|
|
100
|
+
|
|
101
|
+
// Utilities
|
|
102
|
+
export {
|
|
103
|
+
createAudioError,
|
|
104
|
+
pcmToFloat32,
|
|
105
|
+
float32ToInt16,
|
|
106
|
+
resampleLinear,
|
|
107
|
+
calculateAudioLevels,
|
|
108
|
+
clamp,
|
|
109
|
+
durationToSamples,
|
|
110
|
+
samplesToDuration,
|
|
111
|
+
createWavHeader,
|
|
112
|
+
arrayBufferToBase64,
|
|
113
|
+
base64ToArrayBuffer,
|
|
114
|
+
} from './utils';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @idealyst/audio
|
|
3
|
+
*
|
|
4
|
+
* Unified audio package for recording and playback on web and native.
|
|
5
|
+
* Web implementation using Web Audio API.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Types
|
|
9
|
+
export type {
|
|
10
|
+
// Audio configuration
|
|
11
|
+
SampleRate,
|
|
12
|
+
BitDepth,
|
|
13
|
+
ChannelCount,
|
|
14
|
+
AudioConfig,
|
|
15
|
+
|
|
16
|
+
// Audio session (native)
|
|
17
|
+
AudioSessionCategory,
|
|
18
|
+
AudioSessionCategoryOption,
|
|
19
|
+
AudioSessionMode,
|
|
20
|
+
AudioSessionConfig,
|
|
21
|
+
AudioSessionState,
|
|
22
|
+
AudioSessionInterruption,
|
|
23
|
+
AudioSessionRouteChange,
|
|
24
|
+
|
|
25
|
+
// PCM data
|
|
26
|
+
PCMData,
|
|
27
|
+
AudioLevel,
|
|
28
|
+
|
|
29
|
+
// Recorder
|
|
30
|
+
RecorderState,
|
|
31
|
+
PermissionStatus,
|
|
32
|
+
RecorderStatus,
|
|
33
|
+
RecorderDataCallback,
|
|
34
|
+
RecorderLevelCallback,
|
|
35
|
+
RecorderStateCallback,
|
|
36
|
+
IRecorder,
|
|
37
|
+
|
|
38
|
+
// Player
|
|
39
|
+
PlayerState,
|
|
40
|
+
PlayerStatus,
|
|
41
|
+
PlayerStateCallback,
|
|
42
|
+
PlayerPositionCallback,
|
|
43
|
+
PlayerBufferCallback,
|
|
44
|
+
PlayerEndedCallback,
|
|
45
|
+
IPlayer,
|
|
46
|
+
|
|
47
|
+
// Audio context
|
|
48
|
+
IAudioContext,
|
|
49
|
+
IAudioSessionManager,
|
|
50
|
+
|
|
51
|
+
// Errors
|
|
52
|
+
AudioErrorCode,
|
|
53
|
+
AudioError,
|
|
54
|
+
|
|
55
|
+
// Hook types
|
|
56
|
+
UseAudioOptions,
|
|
57
|
+
UseAudioResult,
|
|
58
|
+
UseRecorderOptions,
|
|
59
|
+
UseRecorderResult,
|
|
60
|
+
UsePlayerOptions,
|
|
61
|
+
UsePlayerResult,
|
|
62
|
+
|
|
63
|
+
// Presets
|
|
64
|
+
AudioProfiles,
|
|
65
|
+
SessionPresets,
|
|
66
|
+
} from './types';
|
|
67
|
+
|
|
68
|
+
// Constants
|
|
69
|
+
export {
|
|
70
|
+
// Audio profiles
|
|
71
|
+
AUDIO_PROFILES,
|
|
72
|
+
|
|
73
|
+
// Session presets
|
|
74
|
+
SESSION_PRESETS,
|
|
75
|
+
|
|
76
|
+
// Defaults
|
|
77
|
+
DEFAULT_AUDIO_CONFIG,
|
|
78
|
+
DEFAULT_AUDIO_LEVEL,
|
|
79
|
+
DEFAULT_RECORDER_STATUS,
|
|
80
|
+
DEFAULT_PLAYER_STATUS,
|
|
81
|
+
DEFAULT_SESSION_STATE,
|
|
82
|
+
DEFAULT_LEVEL_UPDATE_INTERVAL,
|
|
83
|
+
DEFAULT_POSITION_UPDATE_INTERVAL,
|
|
84
|
+
} from './constants';
|
|
85
|
+
|
|
86
|
+
// Context
|
|
87
|
+
export { getAudioContext, WebAudioContextManager } from './context';
|
|
88
|
+
|
|
89
|
+
// Session
|
|
90
|
+
export { getAudioSessionManager, WebAudioSessionManager } from './session';
|
|
91
|
+
|
|
92
|
+
// Recording
|
|
93
|
+
export { createRecorder, WebRecorder } from './recording';
|
|
94
|
+
|
|
95
|
+
// Playback
|
|
96
|
+
export { createPlayer, WebPlayer } from './playback';
|
|
97
|
+
|
|
98
|
+
// Hooks
|
|
99
|
+
export { useAudio, useRecorder, usePlayer } from './hooks';
|
|
100
|
+
|
|
101
|
+
// Utilities
|
|
102
|
+
export {
|
|
103
|
+
createAudioError,
|
|
104
|
+
pcmToFloat32,
|
|
105
|
+
float32ToInt16,
|
|
106
|
+
resampleLinear,
|
|
107
|
+
calculateAudioLevels,
|
|
108
|
+
clamp,
|
|
109
|
+
durationToSamples,
|
|
110
|
+
samplesToDuration,
|
|
111
|
+
createWavHeader,
|
|
112
|
+
arrayBufferToBase64,
|
|
113
|
+
base64ToArrayBuffer,
|
|
114
|
+
} from './utils';
|