react-native-audio-api 0.5.5 → 0.6.0-rc.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/RNAudioAPI.podspec +1 -1
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +0 -20
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.h +0 -2
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIPackage.kt +13 -0
- package/android/src/main/java/com/swmansion/audioapi/AudioManagerModule.kt +59 -0
- package/android/src/oldarch/NativeAudioManagerModuleSpec.java +99 -0
- package/common/cpp/audioapi/AudioAPIModuleInstaller.h +30 -6
- package/common/cpp/audioapi/HostObjects/OfflineAudioContextHostObject.h +70 -0
- package/common/cpp/audioapi/core/AudioContext.cpp +1 -12
- package/common/cpp/audioapi/core/AudioContext.h +0 -1
- package/common/cpp/audioapi/core/OfflineAudioContext.cpp +117 -0
- package/common/cpp/audioapi/core/OfflineAudioContext.h +40 -0
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +3 -3
- package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +28 -2
- package/common/cpp/audioapi/core/utils/AudioNodeDestructor.cpp +53 -0
- package/common/cpp/audioapi/core/utils/AudioNodeDestructor.h +33 -0
- package/common/cpp/audioapi/core/utils/AudioNodeManager.cpp +13 -10
- package/common/cpp/audioapi/core/utils/AudioNodeManager.h +3 -0
- package/common/cpp/audioapi/libs/signalsmith-stretch/fft-accelerate.h +326 -0
- package/common/cpp/audioapi/libs/signalsmith-stretch/fft.h +1257 -413
- package/common/cpp/audioapi/libs/signalsmith-stretch/signalsmith-stretch.h +398 -232
- package/common/cpp/audioapi/libs/signalsmith-stretch/stft.h +625 -0
- package/ios/audioapi/ios/AudioAPIModule.mm +2 -3
- package/ios/audioapi/ios/AudioManagerModule.h +18 -0
- package/ios/audioapi/ios/AudioManagerModule.mm +92 -0
- package/ios/audioapi/ios/core/AudioPlayer.h +4 -12
- package/ios/audioapi/ios/core/AudioPlayer.m +26 -108
- package/ios/audioapi/ios/core/IOSAudioPlayer.h +1 -3
- package/ios/audioapi/ios/core/IOSAudioPlayer.mm +4 -28
- package/ios/audioapi/ios/system/AudioEngine.h +23 -0
- package/ios/audioapi/ios/system/AudioEngine.mm +137 -0
- package/ios/audioapi/ios/system/AudioSessionManager.h +22 -0
- package/ios/audioapi/ios/system/AudioSessionManager.mm +183 -0
- package/ios/audioapi/ios/system/LockScreenManager.h +23 -0
- package/ios/audioapi/ios/system/LockScreenManager.mm +299 -0
- package/ios/audioapi/ios/system/NotificationManager.h +16 -0
- package/ios/audioapi/ios/system/NotificationManager.mm +151 -0
- package/lib/module/api.js +3 -1
- package/lib/module/api.js.map +1 -1
- package/lib/module/api.web.js +1 -0
- package/lib/module/api.web.js.map +1 -1
- package/lib/module/core/AudioContext.js +2 -1
- package/lib/module/core/AudioContext.js.map +1 -1
- package/lib/module/core/OfflineAudioContext.js +57 -0
- package/lib/module/core/OfflineAudioContext.js.map +1 -0
- package/lib/module/specs/NativeAudioManagerModule.js +31 -0
- package/lib/module/specs/NativeAudioManagerModule.js.map +1 -0
- package/lib/module/specs/index.js +6 -0
- package/lib/module/specs/index.js.map +1 -0
- package/lib/module/system/AudioManager.js +66 -0
- package/lib/module/system/AudioManager.js.map +1 -0
- package/lib/module/system/index.js +4 -0
- package/lib/module/system/index.js.map +1 -0
- package/lib/module/system/types.js +2 -0
- package/lib/module/system/types.js.map +1 -0
- package/lib/module/web-core/OfflineAudioContext.js +90 -0
- package/lib/module/web-core/OfflineAudioContext.js.map +1 -0
- package/lib/typescript/api.d.ts +4 -1
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/api.web.d.ts +1 -0
- package/lib/typescript/api.web.d.ts.map +1 -1
- package/lib/typescript/core/AudioContext.d.ts.map +1 -1
- package/lib/typescript/core/OfflineAudioContext.d.ts +14 -0
- package/lib/typescript/core/OfflineAudioContext.d.ts.map +1 -0
- package/lib/typescript/interfaces.d.ts +6 -0
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/specs/NativeAudioManagerModule.d.ts +13 -0
- package/lib/typescript/specs/NativeAudioManagerModule.d.ts.map +1 -0
- package/lib/typescript/specs/index.d.ts +4 -0
- package/lib/typescript/specs/index.d.ts.map +1 -0
- package/lib/typescript/system/AudioManager.d.ts +12 -0
- package/lib/typescript/system/AudioManager.d.ts.map +1 -0
- package/lib/typescript/system/index.d.ts +2 -0
- package/lib/typescript/system/index.d.ts.map +1 -0
- package/lib/typescript/system/types.d.ts +28 -0
- package/lib/typescript/system/types.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +5 -0
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/web-core/OfflineAudioContext.d.ts +34 -0
- package/lib/typescript/web-core/OfflineAudioContext.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/api.ts +12 -2
- package/src/api.web.ts +1 -0
- package/src/core/AudioContext.ts +6 -1
- package/src/core/OfflineAudioContext.ts +94 -0
- package/src/interfaces.ts +11 -0
- package/src/specs/NativeAudioManagerModule.ts +51 -0
- package/src/specs/index.ts +6 -0
- package/src/system/AudioManager.ts +122 -0
- package/src/system/index.ts +1 -0
- package/src/system/types.ts +68 -0
- package/src/types.ts +6 -0
- package/src/web-core/OfflineAudioContext.tsx +163 -0
- package/common/cpp/audioapi/libs/signalsmith-stretch/delay.h +0 -715
- package/common/cpp/audioapi/libs/signalsmith-stretch/perf.h +0 -82
- package/common/cpp/audioapi/libs/signalsmith-stretch/spectral.h +0 -493
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#import <audioapi/ios/system/AudioSessionManager.h>
|
|
2
|
+
|
|
3
|
+
@implementation AudioSessionManager
|
|
4
|
+
|
|
5
|
+
static AudioSessionManager *_sharedInstance = nil;
|
|
6
|
+
|
|
7
|
+
+ (instancetype)sharedInstance
|
|
8
|
+
{
|
|
9
|
+
static dispatch_once_t onceToken;
|
|
10
|
+
dispatch_once(&onceToken, ^{
|
|
11
|
+
_sharedInstance = [[self alloc] initPrivate];
|
|
12
|
+
});
|
|
13
|
+
return _sharedInstance;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
- (instancetype)init
|
|
17
|
+
{
|
|
18
|
+
@throw [NSException exceptionWithName:@"Singleton" reason:@"Use +[AudioSessionManager sharedInstance]" userInfo:nil];
|
|
19
|
+
return nil;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
- (instancetype)initPrivate
|
|
23
|
+
{
|
|
24
|
+
if (self = [super init]) {
|
|
25
|
+
self.audioSession = [AVAudioSession sharedInstance];
|
|
26
|
+
|
|
27
|
+
self.sessionCategory = AVAudioSessionCategoryPlayback;
|
|
28
|
+
self.sessionMode = AVAudioSessionModeDefault;
|
|
29
|
+
self.sessionOptions = AVAudioSessionCategoryOptionDuckOthers | AVAudioSessionCategoryOptionAllowBluetooth;
|
|
30
|
+
|
|
31
|
+
[self configureAudioSession];
|
|
32
|
+
}
|
|
33
|
+
return self;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
- (void)cleanup
|
|
37
|
+
{
|
|
38
|
+
NSLog(@"[AudioSessionManager] cleanup");
|
|
39
|
+
|
|
40
|
+
self.audioSession = nil;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
- (NSNumber *)getDevicePreferredSampleRate
|
|
44
|
+
{
|
|
45
|
+
return [NSNumber numberWithFloat:[self.audioSession sampleRate]];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
- (void)setAudioSessionOptions:(NSString *)category mode:(NSString *)mode options:(NSArray *)options
|
|
49
|
+
{
|
|
50
|
+
AVAudioSessionCategory sessionCategory = self.sessionCategory;
|
|
51
|
+
AVAudioSessionMode sessionMode = self.sessionMode;
|
|
52
|
+
AVAudioSessionCategoryOptions sessionOptions = 0;
|
|
53
|
+
|
|
54
|
+
if ([category isEqualToString:@"record"]) {
|
|
55
|
+
sessionCategory = AVAudioSessionCategoryRecord;
|
|
56
|
+
} else if ([category isEqualToString:@"ambient"]) {
|
|
57
|
+
sessionCategory = AVAudioSessionCategoryAmbient;
|
|
58
|
+
} else if ([category isEqualToString:@"playback"]) {
|
|
59
|
+
sessionCategory = AVAudioSessionCategoryPlayback;
|
|
60
|
+
} else if ([category isEqualToString:@"multiRoute"]) {
|
|
61
|
+
sessionCategory = AVAudioSessionCategoryMultiRoute;
|
|
62
|
+
} else if ([category isEqualToString:@"soloAmbient"]) {
|
|
63
|
+
sessionCategory = AVAudioSessionCategorySoloAmbient;
|
|
64
|
+
} else if ([category isEqualToString:@"playAndRecord"]) {
|
|
65
|
+
sessionCategory = AVAudioSessionCategoryPlayAndRecord;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if ([mode isEqualToString:@"default"]) {
|
|
69
|
+
sessionMode = AVAudioSessionModeDefault;
|
|
70
|
+
} else if ([mode isEqualToString:@"gameChat"]) {
|
|
71
|
+
sessionMode = AVAudioSessionModeGameChat;
|
|
72
|
+
} else if ([mode isEqualToString:@"videoChat"]) {
|
|
73
|
+
sessionMode = AVAudioSessionModeVideoChat;
|
|
74
|
+
} else if ([mode isEqualToString:@"voiceChat"]) {
|
|
75
|
+
sessionMode = AVAudioSessionModeVoiceChat;
|
|
76
|
+
} else if ([mode isEqualToString:@"measurement"]) {
|
|
77
|
+
sessionMode = AVAudioSessionModeMeasurement;
|
|
78
|
+
} else if ([mode isEqualToString:@"voicePrompt"]) {
|
|
79
|
+
sessionMode = AVAudioSessionModeVoicePrompt;
|
|
80
|
+
} else if ([mode isEqualToString:@"spokenAudio"]) {
|
|
81
|
+
sessionMode = AVAudioSessionModeSpokenAudio;
|
|
82
|
+
} else if ([mode isEqualToString:@"moviePlayback"]) {
|
|
83
|
+
sessionMode = AVAudioSessionModeMoviePlayback;
|
|
84
|
+
} else if ([mode isEqualToString:@"videoRecording"]) {
|
|
85
|
+
sessionMode = AVAudioSessionModeVideoRecording;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (NSString *option in options) {
|
|
89
|
+
if ([option isEqualToString:@"duckOthers"]) {
|
|
90
|
+
sessionOptions |= AVAudioSessionCategoryOptionDuckOthers;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if ([option isEqualToString:@"allowAirPlay"]) {
|
|
94
|
+
sessionOptions |= AVAudioSessionCategoryOptionAllowAirPlay;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if ([option isEqualToString:@"mixWithOthers"]) {
|
|
98
|
+
sessionOptions |= AVAudioSessionCategoryOptionMixWithOthers;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if ([option isEqualToString:@"allowBluetooth"]) {
|
|
102
|
+
sessionOptions |= AVAudioSessionCategoryOptionAllowBluetooth;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if ([option isEqualToString:@"defaultToSpeaker"]) {
|
|
106
|
+
sessionOptions |= AVAudioSessionCategoryOptionDefaultToSpeaker;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if ([option isEqualToString:@"allowBluetoothA2DP"]) {
|
|
110
|
+
sessionOptions |= AVAudioSessionCategoryOptionAllowBluetoothA2DP;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if ([option isEqualToString:@"overrideMutedMicrophoneInterruption"]) {
|
|
114
|
+
sessionOptions |= AVAudioSessionCategoryOptionOverrideMutedMicrophoneInterruption;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if ([option isEqualToString:@"interruptSpokenAudioAndMixWithOthers"]) {
|
|
118
|
+
sessionOptions |= AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
bool hasDirtySettings = false;
|
|
123
|
+
|
|
124
|
+
if (self.sessionCategory != sessionCategory) {
|
|
125
|
+
hasDirtySettings = true;
|
|
126
|
+
self.sessionCategory = sessionCategory;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (self.sessionMode != sessionMode) {
|
|
130
|
+
hasDirtySettings = true;
|
|
131
|
+
self.sessionMode = sessionMode;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (self.sessionOptions != sessionOptions) {
|
|
135
|
+
hasDirtySettings = true;
|
|
136
|
+
self.sessionOptions = sessionOptions;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (hasDirtySettings) {
|
|
140
|
+
[self configureAudioSession];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
- (bool)setActive:(bool)active error:(NSError **)error
|
|
145
|
+
{
|
|
146
|
+
return [self.audioSession setActive:active error:error];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
- (bool)configureAudioSession
|
|
150
|
+
{
|
|
151
|
+
NSLog(
|
|
152
|
+
@"[AudioSessionManager] configureAudioSession, category: %@, mode: %@, options: %lu",
|
|
153
|
+
self.sessionCategory,
|
|
154
|
+
self.sessionMode,
|
|
155
|
+
(unsigned long)self.sessionOptions);
|
|
156
|
+
|
|
157
|
+
NSError *error = nil;
|
|
158
|
+
|
|
159
|
+
[self.audioSession setPreferredIOBufferDuration:0.022 error:&error];
|
|
160
|
+
|
|
161
|
+
if (error != nil) {
|
|
162
|
+
NSLog(@"Error while setting preffered IO buffer duration: %@", [error debugDescription]);
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
[self.audioSession setCategory:self.sessionCategory mode:self.sessionMode options:self.sessionOptions error:&error];
|
|
167
|
+
|
|
168
|
+
if (error != nil) {
|
|
169
|
+
NSLog(@"Error while configuring audio session: %@", [error debugDescription]);
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
[self setActive:true error:&error];
|
|
174
|
+
|
|
175
|
+
if (error != nil) {
|
|
176
|
+
NSLog(@"Error while activating audio session: %@", [error debugDescription]);
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
@end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#import <AVFoundation/AVFoundation.h>
|
|
4
|
+
#import <Foundation/Foundation.h>
|
|
5
|
+
#import <MediaPlayer/MediaPlayer.h>
|
|
6
|
+
|
|
7
|
+
@class AudioManagerModule;
|
|
8
|
+
|
|
9
|
+
@interface LockScreenManager : NSObject
|
|
10
|
+
|
|
11
|
+
@property (nonatomic, weak) AudioManagerModule *audioManagerModule;
|
|
12
|
+
|
|
13
|
+
@property (nonatomic, weak) MPNowPlayingInfoCenter *playingInfoCenter;
|
|
14
|
+
@property (nonatomic, copy) NSString *artworkUrl;
|
|
15
|
+
|
|
16
|
+
+ (instancetype)sharedInstanceWithAudioManagerModule:(AudioManagerModule *)audioManagerModule;
|
|
17
|
+
- (void)cleanup;
|
|
18
|
+
|
|
19
|
+
- (void)setLockScreenInfo:(NSDictionary *)info;
|
|
20
|
+
- (void)resetLockScreenInfo;
|
|
21
|
+
- (void)enableRemoteCommand:(NSString *)name enabled:(BOOL)enabled;
|
|
22
|
+
|
|
23
|
+
@end
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
#import <MediaPlayer/MediaPlayer.h>
|
|
2
|
+
#import <audioapi/ios/AudioManagerModule.h>
|
|
3
|
+
#import <audioapi/ios/system/LockScreenManager.h>
|
|
4
|
+
|
|
5
|
+
#define LOCK_SCREEN_INFO \
|
|
6
|
+
@{ \
|
|
7
|
+
@"album" : MPMediaItemPropertyAlbumTitle, \
|
|
8
|
+
@"artist" : MPMediaItemPropertyArtist, \
|
|
9
|
+
@"genre" : MPMediaItemPropertyGenre, \
|
|
10
|
+
@"duration" : MPMediaItemPropertyPlaybackDuration, \
|
|
11
|
+
@"title" : MPMediaItemPropertyTitle, \
|
|
12
|
+
@"isLiveStream" : MPNowPlayingInfoPropertyIsLiveStream, \
|
|
13
|
+
@"speed" : MPNowPlayingInfoPropertyPlaybackRate, \
|
|
14
|
+
@"elapsedTime" : MPNowPlayingInfoPropertyElapsedPlaybackTime, \
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@implementation LockScreenManager
|
|
18
|
+
|
|
19
|
+
static LockScreenManager *_sharedInstance = nil;
|
|
20
|
+
|
|
21
|
+
+ (instancetype)sharedInstanceWithAudioManagerModule:(AudioManagerModule *)audioManagerModule
|
|
22
|
+
{
|
|
23
|
+
static dispatch_once_t onceToken;
|
|
24
|
+
dispatch_once(&onceToken, ^{
|
|
25
|
+
_sharedInstance = [[self alloc] initPrivateWithAudioManagerModule:audioManagerModule];
|
|
26
|
+
});
|
|
27
|
+
return _sharedInstance;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
- (instancetype)init
|
|
31
|
+
{
|
|
32
|
+
@throw [NSException exceptionWithName:@"Singleton" reason:@"Use +[LockScreenManager sharedInstance]" userInfo:nil];
|
|
33
|
+
return nil;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
- (instancetype)initPrivateWithAudioManagerModule:(AudioManagerModule *)audioManagerModule
|
|
37
|
+
{
|
|
38
|
+
if (self = [super init]) {
|
|
39
|
+
self.audioManagerModule = audioManagerModule;
|
|
40
|
+
self.playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];
|
|
41
|
+
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
|
|
42
|
+
}
|
|
43
|
+
return self;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
- (void)cleanup
|
|
47
|
+
{
|
|
48
|
+
NSLog(@"[LockScreenManager] cleanup");
|
|
49
|
+
[self resetLockScreenInfo];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
- (void)setLockScreenInfo:(NSDictionary *)info
|
|
53
|
+
{
|
|
54
|
+
// now playing info(lock screen info)
|
|
55
|
+
NSMutableDictionary *lockScreenInfoDict;
|
|
56
|
+
|
|
57
|
+
if (self.playingInfoCenter.nowPlayingInfo == nil) {
|
|
58
|
+
lockScreenInfoDict = [NSMutableDictionary dictionary];
|
|
59
|
+
} else {
|
|
60
|
+
lockScreenInfoDict = [[NSMutableDictionary alloc] initWithDictionary:self.playingInfoCenter.nowPlayingInfo];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (NSString *key in LOCK_SCREEN_INFO) {
|
|
64
|
+
if ([info objectForKey:key] != nil) {
|
|
65
|
+
[lockScreenInfoDict setValue:[info objectForKey:key] forKey:[LOCK_SCREEN_INFO objectForKey:key]];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
self.playingInfoCenter.nowPlayingInfo = lockScreenInfoDict;
|
|
70
|
+
|
|
71
|
+
// playback state
|
|
72
|
+
NSString *state = [info objectForKey:@"state"];
|
|
73
|
+
|
|
74
|
+
if (state != nil) {
|
|
75
|
+
if ([state isEqualToString:@"state_playing"]) {
|
|
76
|
+
self.playingInfoCenter.playbackState = MPNowPlayingPlaybackStatePlaying;
|
|
77
|
+
} else if ([state isEqualToString:@"state_paused"]) {
|
|
78
|
+
self.playingInfoCenter.playbackState = MPNowPlayingPlaybackStatePaused;
|
|
79
|
+
} else if ([state isEqualToString:@"state_stopped"]) {
|
|
80
|
+
self.playingInfoCenter.playbackState = MPNowPlayingPlaybackStateStopped;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// artwork
|
|
85
|
+
NSString *artworkUrl = [self getArtworkUrl:[info objectForKey:@"artwork"]];
|
|
86
|
+
[self updateArtworkIfNeeded:artworkUrl];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
- (void)resetLockScreenInfo
|
|
90
|
+
{
|
|
91
|
+
self.playingInfoCenter.nowPlayingInfo = nil;
|
|
92
|
+
self.artworkUrl = nil;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
- (NSString *)getArtworkUrl:(NSString *)artwork
|
|
96
|
+
{
|
|
97
|
+
NSString *artworkUrl = nil;
|
|
98
|
+
|
|
99
|
+
if (artwork) {
|
|
100
|
+
if ([artwork isKindOfClass:[NSString class]]) {
|
|
101
|
+
artworkUrl = artwork;
|
|
102
|
+
} else if ([[artwork valueForKey:@"uri"] isKindOfClass:[NSString class]]) {
|
|
103
|
+
artworkUrl = [artwork valueForKey:@"uri"];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return artworkUrl;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
- (void)updateArtworkIfNeeded:(id)artworkUrl
|
|
111
|
+
{
|
|
112
|
+
if (artworkUrl == nil) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
|
|
117
|
+
if ([artworkUrl isEqualToString:self.artworkUrl] &&
|
|
118
|
+
[center.nowPlayingInfo objectForKey:MPMediaItemPropertyArtwork] != nil) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
self.artworkUrl = artworkUrl;
|
|
123
|
+
|
|
124
|
+
// Custom handling of artwork in another thread, will be loaded async
|
|
125
|
+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
|
126
|
+
UIImage *image = nil;
|
|
127
|
+
|
|
128
|
+
// check whether artwork path is present
|
|
129
|
+
if ([artworkUrl isEqual:@""]) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// artwork is url download from the interwebs
|
|
134
|
+
if ([artworkUrl hasPrefix:@"http://"] || [artworkUrl hasPrefix:@"https://"]) {
|
|
135
|
+
NSURL *imageURL = [NSURL URLWithString:artworkUrl];
|
|
136
|
+
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
|
|
137
|
+
image = [UIImage imageWithData:imageData];
|
|
138
|
+
} else {
|
|
139
|
+
NSString *localArtworkUrl = [artworkUrl stringByReplacingOccurrencesOfString:@"file://" withString:@""];
|
|
140
|
+
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:localArtworkUrl];
|
|
141
|
+
if (fileExists) {
|
|
142
|
+
image = [UIImage imageNamed:localArtworkUrl];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check if image was available otherwise don't do anything
|
|
147
|
+
if (image == nil) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// check whether image is loaded
|
|
152
|
+
CGImageRef cgref = [image CGImage];
|
|
153
|
+
CIImage *cim = [image CIImage];
|
|
154
|
+
|
|
155
|
+
if (cim == nil && cgref == NULL) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
160
|
+
// Check if URL wasn't changed in the meantime
|
|
161
|
+
if (![artworkUrl isEqual:self.artworkUrl]) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
|
|
166
|
+
MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithBoundsSize:image.size
|
|
167
|
+
requestHandler:^UIImage *_Nonnull(CGSize size) {
|
|
168
|
+
return image;
|
|
169
|
+
}];
|
|
170
|
+
NSMutableDictionary *mediaDict = (center.nowPlayingInfo != nil)
|
|
171
|
+
? [[NSMutableDictionary alloc] initWithDictionary:center.nowPlayingInfo]
|
|
172
|
+
: [NSMutableDictionary dictionary];
|
|
173
|
+
[mediaDict setValue:artwork forKey:MPMediaItemPropertyArtwork];
|
|
174
|
+
center.nowPlayingInfo = mediaDict;
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
- (void)enableRemoteCommand:(NSString *)name enabled:(BOOL)enabled
|
|
180
|
+
{
|
|
181
|
+
MPRemoteCommandCenter *remoteCenter = [MPRemoteCommandCenter sharedCommandCenter];
|
|
182
|
+
|
|
183
|
+
if ([name isEqual:@"play"]) {
|
|
184
|
+
[self enableCommand:remoteCenter.playCommand withSelector:@selector(onPlay:) enabled:enabled];
|
|
185
|
+
} else if ([name isEqual:@"pause"]) {
|
|
186
|
+
[self enableCommand:remoteCenter.pauseCommand withSelector:@selector(onPause:) enabled:enabled];
|
|
187
|
+
} else if ([name isEqual:@"stop"]) {
|
|
188
|
+
[self enableCommand:remoteCenter.stopCommand withSelector:@selector(onStop:) enabled:enabled];
|
|
189
|
+
} else if ([name isEqual:@"togglePlayPause"]) {
|
|
190
|
+
[self enableCommand:remoteCenter.togglePlayPauseCommand withSelector:@selector(onTogglePlayPause:) enabled:enabled];
|
|
191
|
+
} else if ([name isEqual:@"changePlaybackRate"]) {
|
|
192
|
+
[self enableCommand:remoteCenter.changePlaybackRateCommand
|
|
193
|
+
withSelector:@selector(onChangePlaybackRate:)
|
|
194
|
+
enabled:enabled];
|
|
195
|
+
} else if ([name isEqual:@"nextTrack"]) {
|
|
196
|
+
[self enableCommand:remoteCenter.nextTrackCommand withSelector:@selector(onNextTrack:) enabled:enabled];
|
|
197
|
+
} else if ([name isEqual:@"previousTrack"]) {
|
|
198
|
+
[self enableCommand:remoteCenter.previousTrackCommand withSelector:@selector(onPreviousTrack:) enabled:enabled];
|
|
199
|
+
} else if ([name isEqual:@"skipForward"]) {
|
|
200
|
+
[self enableCommand:remoteCenter.skipForwardCommand withSelector:@selector(onSkipForward:) enabled:enabled];
|
|
201
|
+
} else if ([name isEqual:@"skipBackward"]) {
|
|
202
|
+
[self enableCommand:remoteCenter.skipBackwardCommand withSelector:@selector(onSkipBackward:) enabled:enabled];
|
|
203
|
+
} else if ([name isEqual:@"seekForward"]) {
|
|
204
|
+
[self enableCommand:remoteCenter.seekForwardCommand withSelector:@selector(onSeekForward:) enabled:enabled];
|
|
205
|
+
} else if ([name isEqual:@"seekBackward"]) {
|
|
206
|
+
[self enableCommand:remoteCenter.seekBackwardCommand withSelector:@selector(onSeekBackward:) enabled:enabled];
|
|
207
|
+
} else if ([name isEqual:@"changePlaybackPosition"]) {
|
|
208
|
+
[self enableCommand:remoteCenter.changePlaybackPositionCommand
|
|
209
|
+
withSelector:@selector(onChangePlaybackPosition:)
|
|
210
|
+
enabled:enabled];
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
- (void)enableCommand:(MPRemoteCommand *)command withSelector:(SEL)selector enabled:(BOOL)enabled
|
|
215
|
+
{
|
|
216
|
+
[command removeTarget:self action:selector];
|
|
217
|
+
if (enabled) {
|
|
218
|
+
[command addTarget:self action:selector];
|
|
219
|
+
}
|
|
220
|
+
command.enabled = enabled;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
- (MPRemoteCommandHandlerStatus)onPlay:(MPRemoteCommandEvent *)event
|
|
224
|
+
{
|
|
225
|
+
[self.audioManagerModule sendEventWithName:@"onRemotePlay" body:@{}];
|
|
226
|
+
return MPRemoteCommandHandlerStatusSuccess;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
- (MPRemoteCommandHandlerStatus)onPause:(MPRemoteCommandEvent *)event
|
|
230
|
+
{
|
|
231
|
+
[self.audioManagerModule sendEventWithName:@"onRemotePause" body:@{}];
|
|
232
|
+
return MPRemoteCommandHandlerStatusSuccess;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
- (MPRemoteCommandHandlerStatus)onStop:(MPRemoteCommandEvent *)event
|
|
236
|
+
{
|
|
237
|
+
[self.audioManagerModule sendEventWithName:@"onRemoteStop" body:@{}];
|
|
238
|
+
return MPRemoteCommandHandlerStatusSuccess;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
- (MPRemoteCommandHandlerStatus)onTogglePlayPause:(MPRemoteCommandEvent *)event
|
|
242
|
+
{
|
|
243
|
+
[self.audioManagerModule sendEventWithName:@"onRemoteTogglePlayPause" body:@{}];
|
|
244
|
+
return MPRemoteCommandHandlerStatusSuccess;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
- (MPRemoteCommandHandlerStatus)onChangePlaybackRate:(MPChangePlaybackRateCommandEvent *)event
|
|
248
|
+
{
|
|
249
|
+
[self.audioManagerModule sendEventWithName:@"onRemoteChangePlaybackRate"
|
|
250
|
+
body:@{@"playbackRate" : [NSNumber numberWithDouble:event.playbackRate]}];
|
|
251
|
+
return MPRemoteCommandHandlerStatusSuccess;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
- (MPRemoteCommandHandlerStatus)onNextTrack:(MPRemoteCommandEvent *)event
|
|
255
|
+
{
|
|
256
|
+
[self.audioManagerModule sendEventWithName:@"onRemoteNextTrack" body:@{}];
|
|
257
|
+
return MPRemoteCommandHandlerStatusSuccess;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
- (MPRemoteCommandHandlerStatus)onPreviousTrack:(MPRemoteCommandEvent *)event
|
|
261
|
+
{
|
|
262
|
+
[self.audioManagerModule sendEventWithName:@"onRemotePreviousTrack" body:@{}];
|
|
263
|
+
return MPRemoteCommandHandlerStatusSuccess;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
- (MPRemoteCommandHandlerStatus)onSeekForward:(MPRemoteCommandEvent *)event
|
|
267
|
+
{
|
|
268
|
+
[self.audioManagerModule sendEventWithName:@"onRemoteSeekForward" body:nil];
|
|
269
|
+
return MPRemoteCommandHandlerStatusSuccess;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
- (MPRemoteCommandHandlerStatus)onSeekBackward:(MPRemoteCommandEvent *)event
|
|
273
|
+
{
|
|
274
|
+
[self.audioManagerModule sendEventWithName:@"onRemoteSeekBackward" body:@{}];
|
|
275
|
+
return MPRemoteCommandHandlerStatusSuccess;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
- (MPRemoteCommandHandlerStatus)onSkipForward:(MPSkipIntervalCommandEvent *)event
|
|
279
|
+
{
|
|
280
|
+
[self.audioManagerModule sendEventWithName:@"onRemoteSkipForward"
|
|
281
|
+
body:@{@"interval" : [NSNumber numberWithDouble:event.interval]}];
|
|
282
|
+
return MPRemoteCommandHandlerStatusSuccess;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
- (MPRemoteCommandHandlerStatus)onSkipBackward:(MPSkipIntervalCommandEvent *)event
|
|
286
|
+
{
|
|
287
|
+
[self.audioManagerModule sendEventWithName:@"onRemoteSkipBackward"
|
|
288
|
+
body:@{@"interval" : [NSNumber numberWithDouble:event.interval]}];
|
|
289
|
+
return MPRemoteCommandHandlerStatusSuccess;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
- (MPRemoteCommandHandlerStatus)onChangePlaybackPosition:(MPChangePlaybackPositionCommandEvent *)event
|
|
293
|
+
{
|
|
294
|
+
[self.audioManagerModule sendEventWithName:@"onRemoteChangePlaybackPosition"
|
|
295
|
+
body:@{@"positionTime" : [NSNumber numberWithDouble:event.positionTime]}];
|
|
296
|
+
return MPRemoteCommandHandlerStatusSuccess;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
@end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#import <AVFoundation/AVFoundation.h>
|
|
4
|
+
#import <Foundation/Foundation.h>
|
|
5
|
+
|
|
6
|
+
@interface NotificationManager : NSObject
|
|
7
|
+
|
|
8
|
+
@property (nonatomic, weak) NSNotificationCenter *notificationCenter;
|
|
9
|
+
|
|
10
|
+
@property (nonatomic, assign) bool isInterrupted;
|
|
11
|
+
@property (nonatomic, assign) bool hadConfigurationChange;
|
|
12
|
+
|
|
13
|
+
+ (instancetype)sharedInstance;
|
|
14
|
+
- (void)cleanup;
|
|
15
|
+
|
|
16
|
+
@end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#import <audioapi/ios/system/AudioEngine.h>
|
|
2
|
+
#import <audioapi/ios/system/AudioSessionManager.h>
|
|
3
|
+
#import <audioapi/ios/system/NotificationManager.h>
|
|
4
|
+
|
|
5
|
+
@implementation NotificationManager
|
|
6
|
+
|
|
7
|
+
static NotificationManager *_sharedInstance = nil;
|
|
8
|
+
|
|
9
|
+
+ (instancetype)sharedInstance
|
|
10
|
+
{
|
|
11
|
+
static dispatch_once_t onceToken;
|
|
12
|
+
dispatch_once(&onceToken, ^{
|
|
13
|
+
_sharedInstance = [[self alloc] initPrivate];
|
|
14
|
+
});
|
|
15
|
+
return _sharedInstance;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
- (instancetype)init
|
|
19
|
+
{
|
|
20
|
+
@throw [NSException exceptionWithName:@"Singleton" reason:@"Use +[NotificationManager sharedInstance]" userInfo:nil];
|
|
21
|
+
return nil;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
- (instancetype)initPrivate
|
|
25
|
+
{
|
|
26
|
+
if (self = [super init]) {
|
|
27
|
+
self.notificationCenter = [NSNotificationCenter defaultCenter];
|
|
28
|
+
|
|
29
|
+
[self configureNotifications];
|
|
30
|
+
}
|
|
31
|
+
return self;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
- (void)cleanup
|
|
35
|
+
{
|
|
36
|
+
NSLog(@"[NotificationManager] cleanup");
|
|
37
|
+
|
|
38
|
+
self.notificationCenter = nil;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
- (void)configureNotifications
|
|
42
|
+
{
|
|
43
|
+
[self.notificationCenter addObserver:self
|
|
44
|
+
selector:@selector(handleInterruption:)
|
|
45
|
+
name:AVAudioSessionInterruptionNotification
|
|
46
|
+
object:nil];
|
|
47
|
+
[self.notificationCenter addObserver:self
|
|
48
|
+
selector:@selector(handleRouteChange:)
|
|
49
|
+
name:AVAudioSessionRouteChangeNotification
|
|
50
|
+
object:nil];
|
|
51
|
+
[self.notificationCenter addObserver:self
|
|
52
|
+
selector:@selector(handleMediaServicesReset:)
|
|
53
|
+
name:AVAudioSessionMediaServicesWereResetNotification
|
|
54
|
+
object:nil];
|
|
55
|
+
[self.notificationCenter addObserver:self
|
|
56
|
+
selector:@selector(handleEngineConfigurationChange:)
|
|
57
|
+
name:AVAudioEngineConfigurationChangeNotification
|
|
58
|
+
object:nil];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
- (void)handleInterruption:(NSNotification *)notification
|
|
62
|
+
{
|
|
63
|
+
NSError *error;
|
|
64
|
+
UInt8 type = [[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] intValue];
|
|
65
|
+
UInt8 option = [[notification.userInfo valueForKey:AVAudioSessionInterruptionOptionKey] intValue];
|
|
66
|
+
AudioEngine *audioEngine = [AudioEngine sharedInstance];
|
|
67
|
+
AudioSessionManager *audioSessionManager = [AudioSessionManager sharedInstance];
|
|
68
|
+
|
|
69
|
+
if (type == AVAudioSessionInterruptionTypeBegan) {
|
|
70
|
+
self.isInterrupted = true;
|
|
71
|
+
NSLog(@"[NotificationManager] Detected interruption, stopping the engine");
|
|
72
|
+
|
|
73
|
+
[audioEngine stopEngine];
|
|
74
|
+
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (type != AVAudioSessionInterruptionTypeEnded) {
|
|
79
|
+
NSLog(@"[NotificationManager] Unexpected interruption state, chilling");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
self.isInterrupted = false;
|
|
84
|
+
|
|
85
|
+
if (option != AVAudioSessionInterruptionOptionShouldResume) {
|
|
86
|
+
NSLog(@"[NotificationManager] Interruption ended, but engine is not allowed to resume");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
NSLog(@"[NotificationManager] Interruption ended, resuming the engine");
|
|
91
|
+
bool success = [audioSessionManager setActive:true error:&error];
|
|
92
|
+
|
|
93
|
+
if (!success) {
|
|
94
|
+
NSLog(@"[NotificationManager] Unable to activate the audio session, reason: %@", [error debugDescription]);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (self.hadConfigurationChange) {
|
|
99
|
+
[audioEngine rebuildAudioEngine];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
[audioEngine startEngine];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
- (void)handleRouteChange:(NSNotification *)notification
|
|
106
|
+
{
|
|
107
|
+
UInt8 reason = [[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] intValue];
|
|
108
|
+
NSLog(@"[NotificationManager] Route change detected, reason: %u", reason);
|
|
109
|
+
AudioEngine *audioEngine = [AudioEngine sharedInstance];
|
|
110
|
+
|
|
111
|
+
if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
|
|
112
|
+
NSLog(@"[NotificationManager] The previously used audio device became unavailable. Audio engine paused");
|
|
113
|
+
[audioEngine stopEngine];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
- (void)handleMediaServicesReset:(NSNotification *)notification
|
|
118
|
+
{
|
|
119
|
+
NSLog(@"[NotificationManager] Media services have been reset, tearing down and rebuilding everything.");
|
|
120
|
+
AudioEngine *audioEngine = [AudioEngine sharedInstance];
|
|
121
|
+
AudioSessionManager *audioSessionManager = [AudioSessionManager sharedInstance];
|
|
122
|
+
|
|
123
|
+
[self cleanup];
|
|
124
|
+
[audioSessionManager configureAudioSession];
|
|
125
|
+
[self configureNotifications];
|
|
126
|
+
[audioEngine rebuildAudioEngine];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
- (void)handleEngineConfigurationChange:(NSNotification *)notification
|
|
130
|
+
{
|
|
131
|
+
AudioEngine *audioEngine = [AudioEngine sharedInstance];
|
|
132
|
+
|
|
133
|
+
if (![audioEngine isRunning]) {
|
|
134
|
+
NSLog(
|
|
135
|
+
@"[NotificationManager] detected engine configuration change when engine is not running, marking for rebuild");
|
|
136
|
+
self.hadConfigurationChange = true;
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (self.isInterrupted) {
|
|
141
|
+
NSLog(@"[NotificationManager] detected engine configuration change during interruption, marking for rebuild");
|
|
142
|
+
self.hadConfigurationChange = true;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
147
|
+
[audioEngine rebuildAudioEngine];
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@end
|
package/lib/module/api.js
CHANGED
|
@@ -6,7 +6,7 @@ import NativeAudioAPIModule from "./specs/NativeAudioAPIModule.js";
|
|
|
6
6
|
|
|
7
7
|
/* eslint-disable no-var */
|
|
8
8
|
|
|
9
|
-
if (global.createAudioContext == null) {
|
|
9
|
+
if (global.createAudioContext == null || global.createOfflineAudioContext == null) {
|
|
10
10
|
if (!NativeAudioAPIModule) {
|
|
11
11
|
throw new Error(`Failed to install react-native-audio-api: The native module could not be found.`);
|
|
12
12
|
}
|
|
@@ -15,6 +15,7 @@ if (global.createAudioContext == null) {
|
|
|
15
15
|
export { default as AudioBuffer } from "./core/AudioBuffer.js";
|
|
16
16
|
export { default as AudioBufferSourceNode } from "./core/AudioBufferSourceNode.js";
|
|
17
17
|
export { default as AudioContext } from "./core/AudioContext.js";
|
|
18
|
+
export { default as OfflineAudioContext } from "./core/OfflineAudioContext.js";
|
|
18
19
|
export { default as AudioDestinationNode } from "./core/AudioDestinationNode.js";
|
|
19
20
|
export { default as AudioNode } from "./core/AudioNode.js";
|
|
20
21
|
export { default as AnalyserNode } from "./core/AnalyserNode.js";
|
|
@@ -25,6 +26,7 @@ export { default as BiquadFilterNode } from "./core/BiquadFilterNode.js";
|
|
|
25
26
|
export { default as GainNode } from "./core/GainNode.js";
|
|
26
27
|
export { default as OscillatorNode } from "./core/OscillatorNode.js";
|
|
27
28
|
export { default as StereoPannerNode } from "./core/StereoPannerNode.js";
|
|
29
|
+
export { default as AudioManager } from "./system/index.js";
|
|
28
30
|
export { OscillatorType, BiquadFilterType, ChannelCountMode, ChannelInterpretation, ContextState, WindowType, PeriodicWaveConstraints } from "./types.js";
|
|
29
31
|
export { IndexSizeError, InvalidAccessError, InvalidStateError, RangeError, NotSupportedError } from "./errors/index.js";
|
|
30
32
|
//# sourceMappingURL=api.js.map
|