@symbiote-native/audio 0.1.0
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/LICENSE +21 -0
- package/README.md +249 -0
- package/build/angular/index.d.ts +1 -0
- package/build/angular/index.js +4 -0
- package/build/core/audio-module.d.ts +26 -0
- package/build/core/audio-module.js +64 -0
- package/build/core/audio-player.d.ts +13 -0
- package/build/core/audio-player.js +31 -0
- package/build/core/audio-playlist.d.ts +8 -0
- package/build/core/audio-playlist.js +14 -0
- package/build/core/audio-recorder.d.ts +5 -0
- package/build/core/audio-recorder.js +40 -0
- package/build/core/audio-stream.d.ts +13 -0
- package/build/core/audio-stream.js +16 -0
- package/build/core/index.d.ts +9 -0
- package/build/core/index.js +8 -0
- package/build/core/native-module.d.ts +134 -0
- package/build/core/native-module.js +2 -0
- package/build/core/recording-presets.d.ts +50 -0
- package/build/core/recording-presets.js +94 -0
- package/build/core/resolve-source.d.ts +10 -0
- package/build/core/resolve-source.js +31 -0
- package/build/core/types.d.ts +218 -0
- package/build/core/types.js +8 -0
- package/build-ngc/angular/index.d.ts +1 -0
- package/build-ngc/angular/index.js +5 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/core/audio-module.d.ts +26 -0
- package/build-ngc/core/audio-module.js +65 -0
- package/build-ngc/core/audio-module.js.map +1 -0
- package/build-ngc/core/audio-player.d.ts +13 -0
- package/build-ngc/core/audio-player.js +32 -0
- package/build-ngc/core/audio-player.js.map +1 -0
- package/build-ngc/core/audio-playlist.d.ts +8 -0
- package/build-ngc/core/audio-playlist.js +15 -0
- package/build-ngc/core/audio-playlist.js.map +1 -0
- package/build-ngc/core/audio-recorder.d.ts +5 -0
- package/build-ngc/core/audio-recorder.js +41 -0
- package/build-ngc/core/audio-recorder.js.map +1 -0
- package/build-ngc/core/audio-stream.d.ts +13 -0
- package/build-ngc/core/audio-stream.js +17 -0
- package/build-ngc/core/audio-stream.js.map +1 -0
- package/build-ngc/core/index.d.ts +9 -0
- package/build-ngc/core/index.js +9 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/native-module.d.ts +134 -0
- package/build-ngc/core/native-module.js +3 -0
- package/build-ngc/core/native-module.js.map +1 -0
- package/build-ngc/core/recording-presets.d.ts +50 -0
- package/build-ngc/core/recording-presets.js +95 -0
- package/build-ngc/core/recording-presets.js.map +1 -0
- package/build-ngc/core/resolve-source.d.ts +10 -0
- package/build-ngc/core/resolve-source.js +32 -0
- package/build-ngc/core/resolve-source.js.map +1 -0
- package/build-ngc/core/types.d.ts +218 -0
- package/build-ngc/core/types.js +9 -0
- package/build-ngc/core/types.js.map +1 -0
- package/native-link.json +47 -0
- package/package.json +150 -0
- package/src/angular/index.ts +4 -0
- package/src/core/audio-module.ts +82 -0
- package/src/core/audio-player.ts +53 -0
- package/src/core/audio-playlist.ts +20 -0
- package/src/core/audio-recorder.ts +46 -0
- package/src/core/audio-stream.ts +22 -0
- package/src/core/index.ts +62 -0
- package/src/core/native-module.ts +181 -0
- package/src/core/recording-presets.ts +99 -0
- package/src/core/resolve-source.ts +41 -0
- package/src/core/types.ts +282 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export { AudioPlayer, createAudioPlayer } from './audio-player';
|
|
2
|
+
export { AudioRecorder } from './audio-recorder';
|
|
3
|
+
export { AudioPlaylist, createAudioPlaylist } from './audio-playlist';
|
|
4
|
+
export { AudioStream, createAudioStream } from './audio-stream';
|
|
5
|
+
export {
|
|
6
|
+
setIsAudioActiveAsync,
|
|
7
|
+
setAudioModeAsync,
|
|
8
|
+
requestRecordingPermissionsAsync,
|
|
9
|
+
requestNotificationPermissionsAsync,
|
|
10
|
+
getRecordingPermissionsAsync,
|
|
11
|
+
preload,
|
|
12
|
+
clearPreloadedSource,
|
|
13
|
+
clearAllPreloadedSources,
|
|
14
|
+
getPreloadedSources,
|
|
15
|
+
} from './audio-module';
|
|
16
|
+
export { resolveSource, resolveSources } from './resolve-source';
|
|
17
|
+
export {
|
|
18
|
+
RecordingPresets,
|
|
19
|
+
IOSOutputFormat,
|
|
20
|
+
AudioQuality,
|
|
21
|
+
} from './recording-presets';
|
|
22
|
+
export {
|
|
23
|
+
PLAYBACK_STATUS_UPDATE,
|
|
24
|
+
AUDIO_SAMPLE_UPDATE,
|
|
25
|
+
RECORDING_STATUS_UPDATE,
|
|
26
|
+
PLAYLIST_STATUS_UPDATE,
|
|
27
|
+
TRACK_CHANGED,
|
|
28
|
+
AUDIO_STREAM_BUFFER,
|
|
29
|
+
AUDIO_STREAM_STATUS,
|
|
30
|
+
} from './types';
|
|
31
|
+
export type {
|
|
32
|
+
IAudioSource,
|
|
33
|
+
IAudioSourceInfo,
|
|
34
|
+
IAudioPlayerOptions,
|
|
35
|
+
IPreloadOptions,
|
|
36
|
+
IRecordingInput,
|
|
37
|
+
IPitchCorrectionQuality,
|
|
38
|
+
IAudioStatus,
|
|
39
|
+
IRecordingStatus,
|
|
40
|
+
IRecorderState,
|
|
41
|
+
IAndroidOutputFormat,
|
|
42
|
+
IAndroidAudioEncoder,
|
|
43
|
+
IRecordingStartOptions,
|
|
44
|
+
IRecordingDirectory,
|
|
45
|
+
IRecordingOptions,
|
|
46
|
+
IRecordingOptionsWeb,
|
|
47
|
+
IRecordingOptionsIos,
|
|
48
|
+
IRecordingOptionsAndroid,
|
|
49
|
+
IRecordingSource,
|
|
50
|
+
IAudioMode,
|
|
51
|
+
IInterruptionMode,
|
|
52
|
+
IAudioMetadata,
|
|
53
|
+
IAudioPlaylistLoopMode,
|
|
54
|
+
IAudioPlaylistOptions,
|
|
55
|
+
IAudioPlaylistStatus,
|
|
56
|
+
IAudioLockScreenOptions,
|
|
57
|
+
IAudioSample,
|
|
58
|
+
IAudioStreamEncoding,
|
|
59
|
+
IAudioStreamOptions,
|
|
60
|
+
IAudioStreamBuffer,
|
|
61
|
+
IAudioStreamStatus,
|
|
62
|
+
} from './types';
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { requireNativeModule, SharedObject } from 'expo-modules-core';
|
|
2
|
+
import type { PermissionResponse } from 'expo-modules-core';
|
|
3
|
+
import type {
|
|
4
|
+
IAudioMetadata,
|
|
5
|
+
IAudioMode,
|
|
6
|
+
IAudioPlaylistLoopMode,
|
|
7
|
+
IAudioSample,
|
|
8
|
+
IAudioSource,
|
|
9
|
+
IAudioStatus,
|
|
10
|
+
IAudioLockScreenOptions,
|
|
11
|
+
IAudioPlaylistStatus,
|
|
12
|
+
IPitchCorrectionQuality,
|
|
13
|
+
IRecorderState,
|
|
14
|
+
IRecordingInput,
|
|
15
|
+
IRecordingOptions,
|
|
16
|
+
IRecordingStartOptions,
|
|
17
|
+
IRecordingStatus,
|
|
18
|
+
} from './types';
|
|
19
|
+
|
|
20
|
+
// `declare class ... extends SharedObject<Events>` (matching upstream's own AudioModule.types.ts
|
|
21
|
+
// pattern, and packages/media-library/src/next/native-module.ts's precedent for this shape), not
|
|
22
|
+
// a flat object type with a self-referential `new(...): T` construct signature — the latter makes
|
|
23
|
+
// TypeScript report spurious `override` errors on the JS classes that `extends` these.
|
|
24
|
+
// `SharedObject` is a REAL export of expo-modules-core (its JSI-backed base class) — we never
|
|
25
|
+
// reimplement it, only declare the per-class surface these native classes carry.
|
|
26
|
+
|
|
27
|
+
export type NativeAudioPlayerEvents = {
|
|
28
|
+
playbackStatusUpdate(status: IAudioStatus): void;
|
|
29
|
+
audioSampleUpdate(data: IAudioSample): void;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export declare class NativeAudioPlayer extends SharedObject<NativeAudioPlayerEvents> {
|
|
33
|
+
constructor(
|
|
34
|
+
source: IAudioSource,
|
|
35
|
+
updateInterval: number,
|
|
36
|
+
keepAudioSessionActive: boolean,
|
|
37
|
+
preferredForwardBufferDuration: number,
|
|
38
|
+
);
|
|
39
|
+
id: string;
|
|
40
|
+
playing: boolean;
|
|
41
|
+
muted: boolean;
|
|
42
|
+
loop: boolean;
|
|
43
|
+
paused: boolean;
|
|
44
|
+
isLoaded: boolean;
|
|
45
|
+
isAudioSamplingSupported: boolean;
|
|
46
|
+
isBuffering: boolean;
|
|
47
|
+
currentTime: number;
|
|
48
|
+
duration: number;
|
|
49
|
+
volume: number;
|
|
50
|
+
playbackRate: number;
|
|
51
|
+
shouldCorrectPitch: boolean;
|
|
52
|
+
currentStatus: IAudioStatus;
|
|
53
|
+
play(): void;
|
|
54
|
+
pause(): void;
|
|
55
|
+
replace(source: IAudioSource): void;
|
|
56
|
+
seekTo(
|
|
57
|
+
seconds: number,
|
|
58
|
+
toleranceMillisBefore?: number,
|
|
59
|
+
toleranceMillisAfter?: number,
|
|
60
|
+
): Promise<void>;
|
|
61
|
+
setPlaybackRate(
|
|
62
|
+
rate: number,
|
|
63
|
+
pitchCorrectionQuality?: IPitchCorrectionQuality,
|
|
64
|
+
): void;
|
|
65
|
+
setAudioSamplingEnabled(enabled: boolean): void;
|
|
66
|
+
setActiveForLockScreen(
|
|
67
|
+
active: boolean,
|
|
68
|
+
metadata?: IAudioMetadata,
|
|
69
|
+
options?: IAudioLockScreenOptions,
|
|
70
|
+
): void;
|
|
71
|
+
updateLockScreenMetadata(metadata: IAudioMetadata): void;
|
|
72
|
+
clearLockScreenControls(): void;
|
|
73
|
+
remove(): void;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type NativeAudioRecorderEvents = {
|
|
77
|
+
recordingStatusUpdate: (status: IRecordingStatus) => void;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export declare class NativeAudioRecorder extends SharedObject<NativeAudioRecorderEvents> {
|
|
81
|
+
constructor(options: Partial<IRecordingOptions>);
|
|
82
|
+
id: string;
|
|
83
|
+
currentTime: number;
|
|
84
|
+
isRecording: boolean;
|
|
85
|
+
uri: string | null;
|
|
86
|
+
record(options?: IRecordingStartOptions): void;
|
|
87
|
+
stop(): Promise<void>;
|
|
88
|
+
pause(): void;
|
|
89
|
+
getAvailableInputs(): IRecordingInput[];
|
|
90
|
+
getCurrentInput(): Promise<IRecordingInput>;
|
|
91
|
+
setInput(inputUid: string): void;
|
|
92
|
+
getStatus(): IRecorderState;
|
|
93
|
+
/** @deprecated Use `record({ atTime: seconds })` instead. */
|
|
94
|
+
startRecordingAtTime(seconds: number): void;
|
|
95
|
+
prepareToRecordAsync(options?: Partial<IRecordingOptions>): Promise<void>;
|
|
96
|
+
/** @deprecated Use `record({ forDuration: seconds })` instead. */
|
|
97
|
+
recordForDuration(seconds: number): void;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type NativeAudioPlaylistEvents = {
|
|
101
|
+
playlistStatusUpdate(status: IAudioPlaylistStatus): void;
|
|
102
|
+
trackChanged(data: { previousIndex: number; currentIndex: number }): void;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export declare class NativeAudioPlaylist extends SharedObject<NativeAudioPlaylistEvents> {
|
|
106
|
+
constructor(
|
|
107
|
+
sources: IAudioSource[],
|
|
108
|
+
updateInterval: number,
|
|
109
|
+
loop: IAudioPlaylistLoopMode,
|
|
110
|
+
);
|
|
111
|
+
id: string;
|
|
112
|
+
readonly currentIndex: number;
|
|
113
|
+
readonly trackCount: number;
|
|
114
|
+
readonly sources: { uri?: string; name?: string }[];
|
|
115
|
+
playing: boolean;
|
|
116
|
+
muted: boolean;
|
|
117
|
+
isLoaded: boolean;
|
|
118
|
+
isBuffering: boolean;
|
|
119
|
+
currentTime: number;
|
|
120
|
+
duration: number;
|
|
121
|
+
volume: number;
|
|
122
|
+
playbackRate: number;
|
|
123
|
+
loop: IAudioPlaylistLoopMode;
|
|
124
|
+
currentStatus: IAudioPlaylistStatus;
|
|
125
|
+
play(): void;
|
|
126
|
+
pause(): void;
|
|
127
|
+
next(): void;
|
|
128
|
+
previous(): void;
|
|
129
|
+
skipTo(index: number): void;
|
|
130
|
+
seekTo(seconds: number): Promise<void>;
|
|
131
|
+
add(source: IAudioSource): void;
|
|
132
|
+
insert(source: IAudioSource, index: number): void;
|
|
133
|
+
remove(index: number): void;
|
|
134
|
+
clear(): void;
|
|
135
|
+
destroy(): void;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export type NativeAudioStreamEvents = {
|
|
139
|
+
audioStreamBuffer(buffer: {
|
|
140
|
+
data: ArrayBuffer;
|
|
141
|
+
sampleRate: number;
|
|
142
|
+
channels: number;
|
|
143
|
+
timestamp: number;
|
|
144
|
+
}): void;
|
|
145
|
+
audioStreamStatus(status: { isStreaming: boolean }): void;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export declare class NativeAudioStream extends SharedObject<NativeAudioStreamEvents> {
|
|
149
|
+
constructor(options: {
|
|
150
|
+
sampleRate: number;
|
|
151
|
+
channels: number;
|
|
152
|
+
encoding: string;
|
|
153
|
+
});
|
|
154
|
+
id: string;
|
|
155
|
+
readonly sampleRate: number;
|
|
156
|
+
readonly channels: number;
|
|
157
|
+
readonly isStreaming: boolean;
|
|
158
|
+
start(): Promise<void>;
|
|
159
|
+
stop(): void;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export type INativeAudioModule = {
|
|
163
|
+
readonly AudioPlayer: typeof NativeAudioPlayer;
|
|
164
|
+
readonly AudioRecorder: typeof NativeAudioRecorder;
|
|
165
|
+
readonly AudioPlaylist: typeof NativeAudioPlaylist;
|
|
166
|
+
readonly AudioStream: typeof NativeAudioStream;
|
|
167
|
+
setIsAudioActiveAsync(active: boolean): Promise<void>;
|
|
168
|
+
setAudioModeAsync(mode: Partial<IAudioMode>): Promise<void>;
|
|
169
|
+
requestRecordingPermissionsAsync(): Promise<PermissionResponse>;
|
|
170
|
+
requestNotificationPermissionsAsync(): Promise<PermissionResponse>;
|
|
171
|
+
getRecordingPermissionsAsync(): Promise<PermissionResponse>;
|
|
172
|
+
preload(
|
|
173
|
+
source: IAudioSource,
|
|
174
|
+
preferredForwardBufferDuration: number,
|
|
175
|
+
): Promise<void>;
|
|
176
|
+
clearPreloadedSource(source: IAudioSource): Promise<void>;
|
|
177
|
+
clearAllPreloadedSources(): Promise<void>;
|
|
178
|
+
getPreloadedSources(): Promise<string[]>;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export const expoAudio = requireNativeModule<INativeAudioModule>('ExpoAudio');
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Ported verbatim from expo-audio's RecordingConstants.ts.
|
|
2
|
+
import type { IRecordingOptions } from './types';
|
|
3
|
+
|
|
4
|
+
/** @platform ios */
|
|
5
|
+
export enum IOSOutputFormat {
|
|
6
|
+
LINEARPCM = 'lpcm',
|
|
7
|
+
AC3 = 'ac-3',
|
|
8
|
+
'60958AC3' = 'cac3',
|
|
9
|
+
APPLEIMA4 = 'ima4',
|
|
10
|
+
MPEG4AAC = 'aac ',
|
|
11
|
+
MPEG4CELP = 'celp',
|
|
12
|
+
MPEG4HVXC = 'hvxc',
|
|
13
|
+
MPEG4TWINVQ = 'twvq',
|
|
14
|
+
MACE3 = 'MAC3',
|
|
15
|
+
MACE6 = 'MAC6',
|
|
16
|
+
ULAW = 'ulaw',
|
|
17
|
+
ALAW = 'alaw',
|
|
18
|
+
QDESIGN = 'QDMC',
|
|
19
|
+
QDESIGN2 = 'QDM2',
|
|
20
|
+
QUALCOMM = 'Qclp',
|
|
21
|
+
MPEGLAYER1 = '.mp1',
|
|
22
|
+
MPEGLAYER2 = '.mp2',
|
|
23
|
+
MPEGLAYER3 = '.mp3',
|
|
24
|
+
APPLELOSSLESS = 'alac',
|
|
25
|
+
MPEG4AAC_HE = 'aach',
|
|
26
|
+
MPEG4AAC_LD = 'aacl',
|
|
27
|
+
MPEG4AAC_ELD = 'aace',
|
|
28
|
+
MPEG4AAC_ELD_SBR = 'aacf',
|
|
29
|
+
MPEG4AAC_ELD_V2 = 'aacg',
|
|
30
|
+
MPEG4AAC_HE_V2 = 'aacp',
|
|
31
|
+
MPEG4AAC_SPATIAL = 'aacs',
|
|
32
|
+
AMR = 'samr',
|
|
33
|
+
AMR_WB = 'sawb',
|
|
34
|
+
AUDIBLE = 'AUDB',
|
|
35
|
+
ILBC = 'ilbc',
|
|
36
|
+
DVIINTELIMA = 0x6d730011,
|
|
37
|
+
MICROSOFTGSM = 0x6d730031,
|
|
38
|
+
AES3 = 'aes3',
|
|
39
|
+
ENHANCEDAC3 = 'ec-3',
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export enum AudioQuality {
|
|
43
|
+
MIN = 0,
|
|
44
|
+
LOW = 0x20,
|
|
45
|
+
MEDIUM = 0x40,
|
|
46
|
+
HIGH = 0x60,
|
|
47
|
+
MAX = 0x7f,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const HIGH_QUALITY: IRecordingOptions = {
|
|
51
|
+
extension: '.m4a',
|
|
52
|
+
sampleRate: 44100,
|
|
53
|
+
numberOfChannels: 2,
|
|
54
|
+
bitRate: 128000,
|
|
55
|
+
android: {
|
|
56
|
+
outputFormat: 'mpeg4',
|
|
57
|
+
audioEncoder: 'aac',
|
|
58
|
+
},
|
|
59
|
+
ios: {
|
|
60
|
+
outputFormat: IOSOutputFormat.MPEG4AAC,
|
|
61
|
+
audioQuality: AudioQuality.MAX,
|
|
62
|
+
linearPCMBitDepth: 16,
|
|
63
|
+
linearPCMIsBigEndian: false,
|
|
64
|
+
linearPCMIsFloat: false,
|
|
65
|
+
},
|
|
66
|
+
web: {
|
|
67
|
+
mimeType: 'audio/webm',
|
|
68
|
+
bitsPerSecond: 128000,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const LOW_QUALITY: IRecordingOptions = {
|
|
73
|
+
extension: '.m4a',
|
|
74
|
+
sampleRate: 44100,
|
|
75
|
+
numberOfChannels: 2,
|
|
76
|
+
bitRate: 64000,
|
|
77
|
+
android: {
|
|
78
|
+
extension: '.3gp',
|
|
79
|
+
outputFormat: '3gp',
|
|
80
|
+
audioEncoder: 'amr_nb',
|
|
81
|
+
},
|
|
82
|
+
ios: {
|
|
83
|
+
audioQuality: AudioQuality.MIN,
|
|
84
|
+
outputFormat: IOSOutputFormat.MPEG4AAC,
|
|
85
|
+
linearPCMBitDepth: 16,
|
|
86
|
+
linearPCMIsBigEndian: false,
|
|
87
|
+
linearPCMIsFloat: false,
|
|
88
|
+
},
|
|
89
|
+
web: {
|
|
90
|
+
mimeType: 'audio/webm',
|
|
91
|
+
bitsPerSecond: 128000,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/** The two preset `IRecordingOptions` bundles expo-audio ships. */
|
|
96
|
+
export const RecordingPresets = {
|
|
97
|
+
HIGH_QUALITY,
|
|
98
|
+
LOW_QUALITY,
|
|
99
|
+
} as const;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { resolveAssetSource } from '@symbiote-native/engine';
|
|
2
|
+
import type { IAudioSource } from './types';
|
|
3
|
+
|
|
4
|
+
type IResolvedAudioSourceObject = Exclude<IAudioSource, string | number | null>;
|
|
5
|
+
|
|
6
|
+
// resolveAssetSource returns `unknown` by design (the seam serves every require()'d-asset
|
|
7
|
+
// consumer, not just this package) — narrow it instead of casting.
|
|
8
|
+
function isAudioSourceObject(
|
|
9
|
+
value: unknown,
|
|
10
|
+
): value is IResolvedAudioSourceObject {
|
|
11
|
+
return typeof value === 'object' && value !== null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Normalizes a source into the shape the native player accepts. A `number` (a `require()`'d
|
|
16
|
+
* local asset) resolves via `@symbiote-native/engine`'s `resolveAssetSource` seam — RN's own
|
|
17
|
+
* generic asset-registry lookup, wired by `bootstrapHost`. An actual `expo-asset` `Asset`
|
|
18
|
+
* instance is still not ported, see `types.ts`'s header comment on `IAudioSource`. A plain
|
|
19
|
+
* string is treated as a URI, matching upstream.
|
|
20
|
+
*/
|
|
21
|
+
export function resolveSource(source?: IAudioSource): IAudioSource | null {
|
|
22
|
+
if (source == null) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
if (typeof source === 'number') {
|
|
26
|
+
const resolved = resolveAssetSource(source);
|
|
27
|
+
return isAudioSourceObject(resolved) ? resolved : null;
|
|
28
|
+
}
|
|
29
|
+
if (typeof source === 'string') {
|
|
30
|
+
return { uri: source };
|
|
31
|
+
}
|
|
32
|
+
return source;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function resolveSources(
|
|
36
|
+
sources: IAudioSource[],
|
|
37
|
+
): NonNullable<IAudioSource>[] {
|
|
38
|
+
return sources
|
|
39
|
+
.map(source => resolveSource(source))
|
|
40
|
+
.filter((source): source is NonNullable<IAudioSource> => source != null);
|
|
41
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
// Ported from expo-audio's Audio.types.ts / AudioModule.types.ts / AudioConstants.ts /
|
|
2
|
+
// RecordingConstants.ts / AudioStream.types.ts / AudioEventKeys.ts, renamed with this repo's
|
|
3
|
+
// `I`-prefix convention for object/union-shaped exported types (enums keep their upstream name,
|
|
4
|
+
// matching packages/local-auth's AuthenticationType/SecurityLevel).
|
|
5
|
+
//
|
|
6
|
+
// `IAudioSource`'s `number` variant (a `require()`'d local asset) IS supported: it resolves via
|
|
7
|
+
// `@symbiote-native/engine`'s `resolveAssetSource` seam, which wraps RN's own generic
|
|
8
|
+
// `Image.resolveAssetSource` — an ordinary Metro asset-registry lookup, nothing image-specific
|
|
9
|
+
// despite the name, and `mp3` is already in Metro's default `assetExts`. Only an actual `Asset`
|
|
10
|
+
// CLASS instance (as opposed to the plain numeric id `require()` returns) is deliberately NOT
|
|
11
|
+
// ported, along with the `downloadFirst` player option — both need `expo-asset`'s own
|
|
12
|
+
// remote-caching layer, which pulls in `expo-constants` and peers on the `expo` meta-package this
|
|
13
|
+
// project never depends on (root CLAUDE.md's dependency-scope invariant, and the
|
|
14
|
+
// `symbiote-expo-native-module` skill §1). See the package README.
|
|
15
|
+
import type { AudioQuality, IOSOutputFormat } from './recording-presets';
|
|
16
|
+
|
|
17
|
+
/** Audio source information returned from native when reading sources from a queue. */
|
|
18
|
+
export type IAudioSourceInfo = {
|
|
19
|
+
uri?: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A URI string, an object describing a remote/local (by URI) audio source, or the numeric asset
|
|
25
|
+
* id `require('./song.mp3')` produces — resolved via `resolveSource`/`resolveSources`.
|
|
26
|
+
*/
|
|
27
|
+
export type IAudioSource =
|
|
28
|
+
| string
|
|
29
|
+
| number
|
|
30
|
+
| null
|
|
31
|
+
| {
|
|
32
|
+
uri?: string;
|
|
33
|
+
headers?: Record<string, string>;
|
|
34
|
+
name?: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type IAudioPlayerOptions = {
|
|
38
|
+
/** How often (in milliseconds) to emit playback status updates. @default 500 */
|
|
39
|
+
updateInterval?: number;
|
|
40
|
+
/** @platform web */
|
|
41
|
+
crossOrigin?: 'anonymous' | 'use-credentials';
|
|
42
|
+
/**
|
|
43
|
+
* If `true`, the audio session is not deactivated when this player pauses or finishes.
|
|
44
|
+
* @platform ios
|
|
45
|
+
* @default false
|
|
46
|
+
*/
|
|
47
|
+
keepAudioSessionActive?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Seconds to buffer ahead of the current playback position.
|
|
50
|
+
* @platform ios
|
|
51
|
+
* @platform android
|
|
52
|
+
* @default 0
|
|
53
|
+
*/
|
|
54
|
+
preferredForwardBufferDuration?: number;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type IPreloadOptions = {
|
|
58
|
+
/** @default 10 */
|
|
59
|
+
preferredForwardBufferDuration?: number;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export type IRecordingInput = {
|
|
63
|
+
name: string;
|
|
64
|
+
type: string;
|
|
65
|
+
uid: string;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export type IPitchCorrectionQuality = 'low' | 'medium' | 'high';
|
|
69
|
+
|
|
70
|
+
export type IAudioStatus = {
|
|
71
|
+
id: string;
|
|
72
|
+
currentTime: number;
|
|
73
|
+
playbackState: string;
|
|
74
|
+
timeControlStatus: string;
|
|
75
|
+
reasonForWaitingToPlay: string;
|
|
76
|
+
mute: boolean;
|
|
77
|
+
duration: number;
|
|
78
|
+
playing: boolean;
|
|
79
|
+
loop: boolean;
|
|
80
|
+
didJustFinish: boolean;
|
|
81
|
+
isBuffering: boolean;
|
|
82
|
+
isLoaded: boolean;
|
|
83
|
+
playbackRate: number;
|
|
84
|
+
shouldCorrectPitch: boolean;
|
|
85
|
+
/** @platform ios */
|
|
86
|
+
mediaServicesDidReset?: boolean;
|
|
87
|
+
isLive: boolean;
|
|
88
|
+
currentOffsetFromLive: number | null;
|
|
89
|
+
error: string | null;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export type IRecordingStatus = {
|
|
93
|
+
id: string;
|
|
94
|
+
isFinished: boolean;
|
|
95
|
+
hasError: boolean;
|
|
96
|
+
error: string | null;
|
|
97
|
+
url: string | null;
|
|
98
|
+
/** @platform ios */
|
|
99
|
+
mediaServicesDidReset?: boolean;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export type IRecorderState = {
|
|
103
|
+
canRecord: boolean;
|
|
104
|
+
isRecording: boolean;
|
|
105
|
+
durationMillis: number;
|
|
106
|
+
mediaServicesDidReset: boolean;
|
|
107
|
+
metering?: number;
|
|
108
|
+
url: string | null;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/** @platform android */
|
|
112
|
+
export type IAndroidOutputFormat =
|
|
113
|
+
| 'default'
|
|
114
|
+
| '3gp'
|
|
115
|
+
| 'mpeg4'
|
|
116
|
+
| 'amrnb'
|
|
117
|
+
| 'amrwb'
|
|
118
|
+
| 'aac_adts'
|
|
119
|
+
| 'mpeg2ts'
|
|
120
|
+
| 'webm';
|
|
121
|
+
|
|
122
|
+
/** @platform android */
|
|
123
|
+
export type IAndroidAudioEncoder =
|
|
124
|
+
'default' | 'amr_nb' | 'amr_wb' | 'aac' | 'he_aac' | 'aac_eld';
|
|
125
|
+
|
|
126
|
+
export type IRecordingStartOptions = {
|
|
127
|
+
/** @platform ios @platform android @platform web */
|
|
128
|
+
forDuration?: number;
|
|
129
|
+
/** @platform ios */
|
|
130
|
+
atTime?: number;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/** @platform android @platform ios */
|
|
134
|
+
export type IRecordingDirectory = 'cache' | 'document';
|
|
135
|
+
|
|
136
|
+
export type IRecordingOptionsWeb = {
|
|
137
|
+
mimeType?: string;
|
|
138
|
+
bitsPerSecond?: number;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/** @platform ios */
|
|
142
|
+
export type IRecordingOptionsIos = {
|
|
143
|
+
extension?: string;
|
|
144
|
+
sampleRate?: number;
|
|
145
|
+
outputFormat?: string | IOSOutputFormat | number;
|
|
146
|
+
audioQuality: AudioQuality | number;
|
|
147
|
+
bitRateStrategy?: number;
|
|
148
|
+
bitDepthHint?: number;
|
|
149
|
+
linearPCMBitDepth?: number;
|
|
150
|
+
linearPCMIsBigEndian?: boolean;
|
|
151
|
+
linearPCMIsFloat?: boolean;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/** @platform android */
|
|
155
|
+
export type IRecordingSource =
|
|
156
|
+
| 'camcorder'
|
|
157
|
+
| 'default'
|
|
158
|
+
| 'mic'
|
|
159
|
+
| 'remote_submix'
|
|
160
|
+
| 'unprocessed'
|
|
161
|
+
| 'voice_communication'
|
|
162
|
+
| 'voice_performance'
|
|
163
|
+
| 'voice_recognition';
|
|
164
|
+
|
|
165
|
+
/** @platform android */
|
|
166
|
+
export type IRecordingOptionsAndroid = {
|
|
167
|
+
extension?: string;
|
|
168
|
+
sampleRate?: number;
|
|
169
|
+
outputFormat: IAndroidOutputFormat;
|
|
170
|
+
audioEncoder: IAndroidAudioEncoder;
|
|
171
|
+
maxFileSize?: number;
|
|
172
|
+
audioSource?: IRecordingSource;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export type IRecordingOptions = {
|
|
176
|
+
/** @default 'cache' */
|
|
177
|
+
directory?: IRecordingDirectory;
|
|
178
|
+
isMeteringEnabled?: boolean;
|
|
179
|
+
extension: string;
|
|
180
|
+
sampleRate: number;
|
|
181
|
+
numberOfChannels: number;
|
|
182
|
+
bitRate: number;
|
|
183
|
+
android: IRecordingOptionsAndroid;
|
|
184
|
+
ios: IRecordingOptionsIos;
|
|
185
|
+
web: IRecordingOptionsWeb;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export type IAudioMode = {
|
|
189
|
+
/** @default true */
|
|
190
|
+
playsInSilentMode: boolean;
|
|
191
|
+
/** @default 'mixWithOthers' */
|
|
192
|
+
interruptionMode: IInterruptionMode;
|
|
193
|
+
/** @default false @platform ios */
|
|
194
|
+
allowsRecording: boolean;
|
|
195
|
+
/** @default false */
|
|
196
|
+
shouldPlayInBackground: boolean;
|
|
197
|
+
/** @default false */
|
|
198
|
+
shouldRouteThroughEarpiece: boolean;
|
|
199
|
+
/** @default false @platform ios @platform android */
|
|
200
|
+
allowsBackgroundRecording?: boolean;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export type IInterruptionMode = 'mixWithOthers' | 'doNotMix' | 'duckOthers';
|
|
204
|
+
|
|
205
|
+
export type IAudioMetadata = {
|
|
206
|
+
title?: string;
|
|
207
|
+
artist?: string;
|
|
208
|
+
albumTitle?: string;
|
|
209
|
+
artworkUrl?: string;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
export type IAudioPlaylistLoopMode = 'none' | 'single' | 'all';
|
|
213
|
+
|
|
214
|
+
export type IAudioPlaylistOptions = {
|
|
215
|
+
/** @default [] */
|
|
216
|
+
sources?: IAudioSource[];
|
|
217
|
+
/** @default 500 */
|
|
218
|
+
updateInterval?: number;
|
|
219
|
+
/** @default 'none' */
|
|
220
|
+
loop?: IAudioPlaylistLoopMode;
|
|
221
|
+
/** @platform web */
|
|
222
|
+
crossOrigin?: 'anonymous' | 'use-credentials';
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export type IAudioPlaylistStatus = {
|
|
226
|
+
id: string;
|
|
227
|
+
currentIndex: number;
|
|
228
|
+
trackCount: number;
|
|
229
|
+
currentTime: number;
|
|
230
|
+
duration: number;
|
|
231
|
+
playing: boolean;
|
|
232
|
+
isBuffering: boolean;
|
|
233
|
+
isLoaded: boolean;
|
|
234
|
+
playbackRate: number;
|
|
235
|
+
muted: boolean;
|
|
236
|
+
volume: number;
|
|
237
|
+
loop: IAudioPlaylistLoopMode;
|
|
238
|
+
didJustFinish: boolean;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
/** From AudioConstants.ts — which lock-screen controls to show while a player is active. */
|
|
242
|
+
export type IAudioLockScreenOptions = {
|
|
243
|
+
showSeekForward?: boolean;
|
|
244
|
+
showSeekBackward?: boolean;
|
|
245
|
+
isLiveStream?: boolean;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
export type IAudioSample = {
|
|
249
|
+
channels: { frames: number[] }[];
|
|
250
|
+
timestamp: number;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export type IAudioStreamEncoding = 'float32' | 'int16';
|
|
254
|
+
|
|
255
|
+
export type IAudioStreamOptions = {
|
|
256
|
+
/** @default 48000 */
|
|
257
|
+
sampleRate?: number;
|
|
258
|
+
/** @default 1 */
|
|
259
|
+
channels?: number;
|
|
260
|
+
/** @default 'float32' */
|
|
261
|
+
encoding?: IAudioStreamEncoding;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
export type IAudioStreamBuffer = {
|
|
265
|
+
data: ArrayBuffer;
|
|
266
|
+
sampleRate: number;
|
|
267
|
+
channels: number;
|
|
268
|
+
timestamp: number;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
export type IAudioStreamStatus = {
|
|
272
|
+
isStreaming: boolean;
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
/** From AudioEventKeys.ts — event names the `SharedObject` classes' `addListener` accepts. */
|
|
276
|
+
export const PLAYBACK_STATUS_UPDATE = 'playbackStatusUpdate';
|
|
277
|
+
export const AUDIO_SAMPLE_UPDATE = 'audioSampleUpdate';
|
|
278
|
+
export const RECORDING_STATUS_UPDATE = 'recordingStatusUpdate';
|
|
279
|
+
export const PLAYLIST_STATUS_UPDATE = 'playlistStatusUpdate';
|
|
280
|
+
export const TRACK_CHANGED = 'trackChanged';
|
|
281
|
+
export const AUDIO_STREAM_BUFFER = 'audioStreamBuffer';
|
|
282
|
+
export const AUDIO_STREAM_STATUS = 'audioStreamStatus';
|