react-native-audio-api 0.5.6 → 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.
Files changed (67) hide show
  1. package/RNAudioAPI.podspec +1 -1
  2. package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +0 -20
  3. package/android/src/main/cpp/audioapi/android/core/AudioPlayer.h +0 -2
  4. package/android/src/main/java/com/swmansion/audioapi/AudioAPIPackage.kt +13 -0
  5. package/android/src/main/java/com/swmansion/audioapi/AudioManagerModule.kt +59 -0
  6. package/android/src/oldarch/NativeAudioManagerModuleSpec.java +99 -0
  7. package/common/cpp/audioapi/AudioAPIModuleInstaller.h +2 -6
  8. package/common/cpp/audioapi/core/AudioContext.cpp +1 -12
  9. package/common/cpp/audioapi/core/AudioContext.h +0 -1
  10. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +1 -1
  11. package/common/cpp/audioapi/libs/signalsmith-stretch/fft-accelerate.h +326 -0
  12. package/common/cpp/audioapi/libs/signalsmith-stretch/fft.h +1257 -413
  13. package/common/cpp/audioapi/libs/signalsmith-stretch/signalsmith-stretch.h +398 -232
  14. package/common/cpp/audioapi/libs/signalsmith-stretch/stft.h +625 -0
  15. package/ios/audioapi/ios/AudioAPIModule.mm +2 -3
  16. package/ios/audioapi/ios/AudioManagerModule.h +18 -0
  17. package/ios/audioapi/ios/AudioManagerModule.mm +92 -0
  18. package/ios/audioapi/ios/core/AudioPlayer.h +4 -12
  19. package/ios/audioapi/ios/core/AudioPlayer.m +26 -108
  20. package/ios/audioapi/ios/core/IOSAudioPlayer.h +1 -3
  21. package/ios/audioapi/ios/core/IOSAudioPlayer.mm +4 -28
  22. package/ios/audioapi/ios/system/AudioEngine.h +23 -0
  23. package/ios/audioapi/ios/system/AudioEngine.mm +137 -0
  24. package/ios/audioapi/ios/system/AudioSessionManager.h +22 -0
  25. package/ios/audioapi/ios/system/AudioSessionManager.mm +183 -0
  26. package/ios/audioapi/ios/system/LockScreenManager.h +23 -0
  27. package/ios/audioapi/ios/system/LockScreenManager.mm +299 -0
  28. package/ios/audioapi/ios/system/NotificationManager.h +16 -0
  29. package/ios/audioapi/ios/system/NotificationManager.mm +151 -0
  30. package/lib/module/api.js +1 -0
  31. package/lib/module/api.js.map +1 -1
  32. package/lib/module/core/AudioContext.js +2 -1
  33. package/lib/module/core/AudioContext.js.map +1 -1
  34. package/lib/module/specs/NativeAudioManagerModule.js +31 -0
  35. package/lib/module/specs/NativeAudioManagerModule.js.map +1 -0
  36. package/lib/module/specs/index.js +6 -0
  37. package/lib/module/specs/index.js.map +1 -0
  38. package/lib/module/system/AudioManager.js +66 -0
  39. package/lib/module/system/AudioManager.js.map +1 -0
  40. package/lib/module/system/index.js +4 -0
  41. package/lib/module/system/index.js.map +1 -0
  42. package/lib/module/system/types.js +2 -0
  43. package/lib/module/system/types.js.map +1 -0
  44. package/lib/typescript/api.d.ts +1 -0
  45. package/lib/typescript/api.d.ts.map +1 -1
  46. package/lib/typescript/core/AudioContext.d.ts.map +1 -1
  47. package/lib/typescript/specs/NativeAudioManagerModule.d.ts +13 -0
  48. package/lib/typescript/specs/NativeAudioManagerModule.d.ts.map +1 -0
  49. package/lib/typescript/specs/index.d.ts +4 -0
  50. package/lib/typescript/specs/index.d.ts.map +1 -0
  51. package/lib/typescript/system/AudioManager.d.ts +12 -0
  52. package/lib/typescript/system/AudioManager.d.ts.map +1 -0
  53. package/lib/typescript/system/index.d.ts +2 -0
  54. package/lib/typescript/system/index.d.ts.map +1 -0
  55. package/lib/typescript/system/types.d.ts +28 -0
  56. package/lib/typescript/system/types.d.ts.map +1 -0
  57. package/package.json +2 -2
  58. package/src/api.ts +1 -0
  59. package/src/core/AudioContext.ts +6 -1
  60. package/src/specs/NativeAudioManagerModule.ts +51 -0
  61. package/src/specs/index.ts +6 -0
  62. package/src/system/AudioManager.ts +122 -0
  63. package/src/system/index.ts +1 -0
  64. package/src/system/types.ts +68 -0
  65. package/common/cpp/audioapi/libs/signalsmith-stretch/delay.h +0 -715
  66. package/common/cpp/audioapi/libs/signalsmith-stretch/perf.h +0 -82
  67. 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
@@ -26,6 +26,7 @@ export { default as BiquadFilterNode } from "./core/BiquadFilterNode.js";
26
26
  export { default as GainNode } from "./core/GainNode.js";
27
27
  export { default as OscillatorNode } from "./core/OscillatorNode.js";
28
28
  export { default as StereoPannerNode } from "./core/StereoPannerNode.js";
29
+ export { default as AudioManager } from "./system/index.js";
29
30
  export { OscillatorType, BiquadFilterType, ChannelCountMode, ChannelInterpretation, ContextState, WindowType, PeriodicWaveConstraints } from "./types.js";
30
31
  export { IndexSizeError, InvalidAccessError, InvalidStateError, RangeError, NotSupportedError } from "./errors/index.js";
31
32
  //# sourceMappingURL=api.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["NativeAudioAPIModule","global","createAudioContext","createOfflineAudioContext","Error","install","default","AudioBuffer","AudioBufferSourceNode","AudioContext","OfflineAudioContext","AudioDestinationNode","AudioNode","AnalyserNode","AudioParam","AudioScheduledSourceNode","BaseAudioContext","BiquadFilterNode","GainNode","OscillatorNode","StereoPannerNode","OscillatorType","BiquadFilterType","ChannelCountMode","ChannelInterpretation","ContextState","WindowType","PeriodicWaveConstraints","IndexSizeError","InvalidAccessError","InvalidStateError","RangeError","NotSupportedError"],"sourceRoot":"../../src","sources":["api.ts"],"mappings":";;AAAA,OAAOA,oBAAoB,MAAM,iCAA8B;;AAG/D;;AASA;;AAEA,IACEC,MAAM,CAACC,kBAAkB,IAAI,IAAI,IACjCD,MAAM,CAACE,yBAAyB,IAAI,IAAI,EACxC;EACA,IAAI,CAACH,oBAAoB,EAAE;IACzB,MAAM,IAAII,KAAK,CACb,iFACF,CAAC;EACH;EAEAJ,oBAAoB,CAACK,OAAO,CAAC,CAAC;AAChC;AAEA,SAASC,OAAO,IAAIC,WAAW,QAAQ,uBAAoB;AAC3D,SAASD,OAAO,IAAIE,qBAAqB,QAAQ,iCAA8B;AAC/E,SAASF,OAAO,IAAIG,YAAY,QAAQ,wBAAqB;AAC7D,SAASH,OAAO,IAAII,mBAAmB,QAAQ,+BAA4B;AAC3E,SAASJ,OAAO,IAAIK,oBAAoB,QAAQ,gCAA6B;AAC7E,SAASL,OAAO,IAAIM,SAAS,QAAQ,qBAAkB;AACvD,SAASN,OAAO,IAAIO,YAAY,QAAQ,wBAAqB;AAC7D,SAASP,OAAO,IAAIQ,UAAU,QAAQ,sBAAmB;AACzD,SAASR,OAAO,IAAIS,wBAAwB,QAAQ,oCAAiC;AACrF,SAAST,OAAO,IAAIU,gBAAgB,QAAQ,4BAAyB;AACrE,SAASV,OAAO,IAAIW,gBAAgB,QAAQ,4BAAyB;AACrE,SAASX,OAAO,IAAIY,QAAQ,QAAQ,oBAAiB;AACrD,SAASZ,OAAO,IAAIa,cAAc,QAAQ,0BAAuB;AACjE,SAASb,OAAO,IAAIc,gBAAgB,QAAQ,4BAAyB;AAErE,SACEC,cAAc,EACdC,gBAAgB,EAChBC,gBAAgB,EAChBC,qBAAqB,EACrBC,YAAY,EACZC,UAAU,EACVC,uBAAuB,QAClB,YAAS;AAEhB,SACEC,cAAc,EACdC,kBAAkB,EAClBC,iBAAiB,EACjBC,UAAU,EACVC,iBAAiB,QACZ,mBAAU","ignoreList":[]}
1
+ {"version":3,"names":["NativeAudioAPIModule","global","createAudioContext","createOfflineAudioContext","Error","install","default","AudioBuffer","AudioBufferSourceNode","AudioContext","OfflineAudioContext","AudioDestinationNode","AudioNode","AnalyserNode","AudioParam","AudioScheduledSourceNode","BaseAudioContext","BiquadFilterNode","GainNode","OscillatorNode","StereoPannerNode","AudioManager","OscillatorType","BiquadFilterType","ChannelCountMode","ChannelInterpretation","ContextState","WindowType","PeriodicWaveConstraints","IndexSizeError","InvalidAccessError","InvalidStateError","RangeError","NotSupportedError"],"sourceRoot":"../../src","sources":["api.ts"],"mappings":";;AAAA,OAAOA,oBAAoB,MAAM,iCAA8B;;AAG/D;;AASA;;AAEA,IACEC,MAAM,CAACC,kBAAkB,IAAI,IAAI,IACjCD,MAAM,CAACE,yBAAyB,IAAI,IAAI,EACxC;EACA,IAAI,CAACH,oBAAoB,EAAE;IACzB,MAAM,IAAII,KAAK,CACb,iFACF,CAAC;EACH;EAEAJ,oBAAoB,CAACK,OAAO,CAAC,CAAC;AAChC;AAEA,SAASC,OAAO,IAAIC,WAAW,QAAQ,uBAAoB;AAC3D,SAASD,OAAO,IAAIE,qBAAqB,QAAQ,iCAA8B;AAC/E,SAASF,OAAO,IAAIG,YAAY,QAAQ,wBAAqB;AAC7D,SAASH,OAAO,IAAII,mBAAmB,QAAQ,+BAA4B;AAC3E,SAASJ,OAAO,IAAIK,oBAAoB,QAAQ,gCAA6B;AAC7E,SAASL,OAAO,IAAIM,SAAS,QAAQ,qBAAkB;AACvD,SAASN,OAAO,IAAIO,YAAY,QAAQ,wBAAqB;AAC7D,SAASP,OAAO,IAAIQ,UAAU,QAAQ,sBAAmB;AACzD,SAASR,OAAO,IAAIS,wBAAwB,QAAQ,oCAAiC;AACrF,SAAST,OAAO,IAAIU,gBAAgB,QAAQ,4BAAyB;AACrE,SAASV,OAAO,IAAIW,gBAAgB,QAAQ,4BAAyB;AACrE,SAASX,OAAO,IAAIY,QAAQ,QAAQ,oBAAiB;AACrD,SAASZ,OAAO,IAAIa,cAAc,QAAQ,0BAAuB;AACjE,SAASb,OAAO,IAAIc,gBAAgB,QAAQ,4BAAyB;AACrE,SAASd,OAAO,IAAIe,YAAY,QAAQ,mBAAU;AAElD,SACEC,cAAc,EACdC,gBAAgB,EAChBC,gBAAgB,EAChBC,qBAAqB,EACrBC,YAAY,EACZC,UAAU,EACVC,uBAAuB,QAClB,YAAS;AAEhB,SACEC,cAAc,EACdC,kBAAkB,EAClBC,iBAAiB,EACjBC,UAAU,EACVC,iBAAiB,QACZ,mBAAU","ignoreList":[]}