react-native-audio-api 0.5.7 → 0.6.0-rc.1

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 (192) hide show
  1. package/RNAudioAPI.podspec +1 -1
  2. package/android/CMakeLists.txt +6 -3
  3. package/android/build.gradle +1 -0
  4. package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +0 -20
  5. package/android/src/main/cpp/audioapi/android/core/AudioPlayer.h +0 -2
  6. package/android/src/main/java/com/swmansion/audioapi/AudioAPIPackage.kt +13 -0
  7. package/android/src/main/java/com/swmansion/audioapi/AudioManagerModule.kt +62 -0
  8. package/android/src/main/java/com/swmansion/audioapi/system/AudioFocusListener.kt +60 -0
  9. package/android/src/main/java/com/swmansion/audioapi/system/LockScreenManager.kt +294 -0
  10. package/android/src/main/java/com/swmansion/audioapi/system/MediaNotificationManager.kt +279 -0
  11. package/android/src/main/java/com/swmansion/audioapi/system/MediaReceiver.kt +46 -0
  12. package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionCallback.kt +39 -0
  13. package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionEventEmitter.kt +84 -0
  14. package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionManager.kt +144 -0
  15. package/android/src/main/res/drawable/next.xml +9 -0
  16. package/android/src/main/res/drawable/pause.xml +9 -0
  17. package/android/src/main/res/drawable/play.xml +9 -0
  18. package/android/src/main/res/drawable/previous.xml +9 -0
  19. package/android/src/main/res/drawable/skip_backward_5.xml +9 -0
  20. package/android/src/main/res/drawable/skip_forward_5.xml +9 -0
  21. package/android/src/main/res/drawable/stop.xml +9 -0
  22. package/android/src/oldarch/NativeAudioManagerModuleSpec.java +99 -0
  23. package/app.plugin.js +1 -0
  24. package/common/cpp/audioapi/AudioAPIModuleInstaller.h +2 -6
  25. package/common/cpp/audioapi/core/AudioContext.cpp +1 -12
  26. package/common/cpp/audioapi/core/AudioContext.h +0 -1
  27. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +1 -6
  28. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +8 -4
  29. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +1 -0
  30. package/common/cpp/audioapi/core/utils/AudioNodeDestructor.cpp +3 -3
  31. package/ios/audioapi/ios/AudioAPIModule.mm +2 -3
  32. package/ios/audioapi/ios/AudioManagerModule.h +18 -0
  33. package/ios/audioapi/ios/AudioManagerModule.mm +94 -0
  34. package/ios/audioapi/ios/core/AudioPlayer.h +4 -12
  35. package/ios/audioapi/ios/core/AudioPlayer.m +26 -108
  36. package/ios/audioapi/ios/core/IOSAudioPlayer.h +1 -3
  37. package/ios/audioapi/ios/core/IOSAudioPlayer.mm +4 -28
  38. package/ios/audioapi/ios/system/AudioEngine.h +23 -0
  39. package/ios/audioapi/ios/system/AudioEngine.mm +137 -0
  40. package/ios/audioapi/ios/system/AudioSessionManager.h +22 -0
  41. package/ios/audioapi/ios/system/AudioSessionManager.mm +183 -0
  42. package/ios/audioapi/ios/system/LockScreenManager.h +23 -0
  43. package/ios/audioapi/ios/system/LockScreenManager.mm +295 -0
  44. package/ios/audioapi/ios/system/NotificationManager.h +22 -0
  45. package/ios/audioapi/ios/system/NotificationManager.mm +173 -0
  46. package/lib/commonjs/api.js +197 -0
  47. package/lib/commonjs/api.js.map +1 -0
  48. package/lib/commonjs/api.web.js +219 -0
  49. package/lib/commonjs/api.web.js.map +1 -0
  50. package/lib/commonjs/core/AnalyserNode.js +71 -0
  51. package/lib/commonjs/core/AnalyserNode.js.map +1 -0
  52. package/lib/commonjs/core/AudioBuffer.js +44 -0
  53. package/lib/commonjs/core/AudioBuffer.js.map +1 -0
  54. package/lib/commonjs/core/AudioBufferSourceNode.js +68 -0
  55. package/lib/commonjs/core/AudioBufferSourceNode.js.map +1 -0
  56. package/lib/commonjs/core/AudioContext.js +29 -0
  57. package/lib/commonjs/core/AudioContext.js.map +1 -0
  58. package/lib/commonjs/core/AudioDestinationNode.js +11 -0
  59. package/lib/commonjs/core/AudioDestinationNode.js.map +1 -0
  60. package/lib/commonjs/core/AudioNode.js +30 -0
  61. package/lib/commonjs/core/AudioNode.js.map +1 -0
  62. package/lib/commonjs/core/AudioParam.js +82 -0
  63. package/lib/commonjs/core/AudioParam.js.map +1 -0
  64. package/lib/commonjs/core/AudioScheduledSourceNode.js +38 -0
  65. package/lib/commonjs/core/AudioScheduledSourceNode.js.map +1 -0
  66. package/lib/commonjs/core/BaseAudioContext.js +80 -0
  67. package/lib/commonjs/core/BaseAudioContext.js.map +1 -0
  68. package/lib/commonjs/core/BiquadFilterNode.js +33 -0
  69. package/lib/commonjs/core/BiquadFilterNode.js.map +1 -0
  70. package/lib/commonjs/core/GainNode.js +17 -0
  71. package/lib/commonjs/core/GainNode.js.map +1 -0
  72. package/lib/commonjs/core/OfflineAudioContext.js +63 -0
  73. package/lib/commonjs/core/OfflineAudioContext.js.map +1 -0
  74. package/lib/commonjs/core/OscillatorNode.js +32 -0
  75. package/lib/commonjs/core/OscillatorNode.js.map +1 -0
  76. package/lib/commonjs/core/PeriodicWave.js +15 -0
  77. package/lib/commonjs/core/PeriodicWave.js.map +1 -0
  78. package/lib/commonjs/core/StereoPannerNode.js +17 -0
  79. package/lib/commonjs/core/StereoPannerNode.js.map +1 -0
  80. package/lib/commonjs/errors/IndexSizeError.js +14 -0
  81. package/lib/commonjs/errors/IndexSizeError.js.map +1 -0
  82. package/lib/commonjs/errors/InvalidAccessError.js +14 -0
  83. package/lib/commonjs/errors/InvalidAccessError.js.map +1 -0
  84. package/lib/commonjs/errors/InvalidStateError.js +14 -0
  85. package/lib/commonjs/errors/InvalidStateError.js.map +1 -0
  86. package/lib/commonjs/errors/NotSupportedError.js +14 -0
  87. package/lib/commonjs/errors/NotSupportedError.js.map +1 -0
  88. package/lib/commonjs/errors/RangeError.js +14 -0
  89. package/lib/commonjs/errors/RangeError.js.map +1 -0
  90. package/lib/commonjs/errors/index.js +42 -0
  91. package/lib/commonjs/errors/index.js.map +1 -0
  92. package/lib/commonjs/index.js +17 -0
  93. package/lib/commonjs/index.js.map +1 -0
  94. package/lib/commonjs/interfaces.js +6 -0
  95. package/lib/commonjs/interfaces.js.map +1 -0
  96. package/lib/commonjs/package.json +1 -0
  97. package/lib/commonjs/plugin/withAudioAPI.js +62 -0
  98. package/lib/commonjs/plugin/withAudioAPI.js.map +1 -0
  99. package/lib/commonjs/specs/NativeAudioAPIModule.js +9 -0
  100. package/lib/commonjs/specs/NativeAudioAPIModule.js.map +1 -0
  101. package/lib/commonjs/specs/NativeAudioManagerModule.js +33 -0
  102. package/lib/commonjs/specs/NativeAudioManagerModule.js.map +1 -0
  103. package/lib/commonjs/specs/index.js +27 -0
  104. package/lib/commonjs/specs/index.js.map +1 -0
  105. package/lib/commonjs/system/AudioManager.js +79 -0
  106. package/lib/commonjs/system/AudioManager.js.map +1 -0
  107. package/lib/commonjs/system/index.js +14 -0
  108. package/lib/commonjs/system/index.js.map +1 -0
  109. package/lib/commonjs/system/types.js +2 -0
  110. package/lib/commonjs/system/types.js.map +1 -0
  111. package/lib/commonjs/types.js +2 -0
  112. package/lib/commonjs/types.js.map +1 -0
  113. package/lib/commonjs/utils/index.js +10 -0
  114. package/lib/commonjs/utils/index.js.map +1 -0
  115. package/lib/commonjs/web-core/AnalyserNode.js +38 -0
  116. package/lib/commonjs/web-core/AnalyserNode.js.map +1 -0
  117. package/lib/commonjs/web-core/AudioBuffer.js +44 -0
  118. package/lib/commonjs/web-core/AudioBuffer.js.map +1 -0
  119. package/lib/commonjs/web-core/AudioBufferSourceNode.js +214 -0
  120. package/lib/commonjs/web-core/AudioBufferSourceNode.js.map +1 -0
  121. package/lib/commonjs/web-core/AudioContext.js +93 -0
  122. package/lib/commonjs/web-core/AudioContext.js.map +1 -0
  123. package/lib/commonjs/web-core/AudioDestinationNode.js +11 -0
  124. package/lib/commonjs/web-core/AudioDestinationNode.js.map +1 -0
  125. package/lib/commonjs/web-core/AudioNode.js +33 -0
  126. package/lib/commonjs/web-core/AudioNode.js.map +1 -0
  127. package/lib/commonjs/web-core/AudioParam.js +81 -0
  128. package/lib/commonjs/web-core/AudioParam.js.map +1 -0
  129. package/lib/commonjs/web-core/AudioScheduledSourceNode.js +41 -0
  130. package/lib/commonjs/web-core/AudioScheduledSourceNode.js.map +1 -0
  131. package/lib/commonjs/web-core/BaseAudioContext.js +2 -0
  132. package/lib/commonjs/web-core/BaseAudioContext.js.map +1 -0
  133. package/lib/commonjs/web-core/BiquadFilterNode.js +33 -0
  134. package/lib/commonjs/web-core/BiquadFilterNode.js.map +1 -0
  135. package/lib/commonjs/web-core/GainNode.js +17 -0
  136. package/lib/commonjs/web-core/GainNode.js.map +1 -0
  137. package/lib/commonjs/web-core/OfflineAudioContext.js +96 -0
  138. package/lib/commonjs/web-core/OfflineAudioContext.js.map +1 -0
  139. package/lib/commonjs/web-core/OscillatorNode.js +31 -0
  140. package/lib/commonjs/web-core/OscillatorNode.js.map +1 -0
  141. package/lib/commonjs/web-core/PeriodicWave.js +15 -0
  142. package/lib/commonjs/web-core/PeriodicWave.js.map +1 -0
  143. package/lib/commonjs/web-core/StereoPannerNode.js +17 -0
  144. package/lib/commonjs/web-core/StereoPannerNode.js.map +1 -0
  145. package/lib/commonjs/web-core/custom/LoadCustomWasm.js +37 -0
  146. package/lib/commonjs/web-core/custom/LoadCustomWasm.js.map +1 -0
  147. package/lib/commonjs/web-core/custom/index.js +14 -0
  148. package/lib/commonjs/web-core/custom/index.js.map +1 -0
  149. package/lib/commonjs/web-core/custom/signalsmithStretch/LICENSE.txt +21 -0
  150. package/lib/commonjs/web-core/custom/signalsmithStretch/README.md +46 -0
  151. package/lib/commonjs/web-core/custom/signalsmithStretch/SignalsmithStretch.mjs +826 -0
  152. package/lib/commonjs/web-core/custom/signalsmithStretch/SignalsmithStretch.mjs.map +1 -0
  153. package/lib/module/api.js +1 -0
  154. package/lib/module/api.js.map +1 -1
  155. package/lib/module/core/AudioContext.js +2 -1
  156. package/lib/module/core/AudioContext.js.map +1 -1
  157. package/lib/module/plugin/withAudioAPI.js +58 -0
  158. package/lib/module/plugin/withAudioAPI.js.map +1 -0
  159. package/lib/module/specs/NativeAudioManagerModule.js +30 -0
  160. package/lib/module/specs/NativeAudioManagerModule.js.map +1 -0
  161. package/lib/module/specs/index.js +6 -0
  162. package/lib/module/specs/index.js.map +1 -0
  163. package/lib/module/system/AudioManager.js +75 -0
  164. package/lib/module/system/AudioManager.js.map +1 -0
  165. package/lib/module/system/index.js +4 -0
  166. package/lib/module/system/index.js.map +1 -0
  167. package/lib/module/system/types.js +2 -0
  168. package/lib/module/system/types.js.map +1 -0
  169. package/lib/typescript/api.d.ts +1 -0
  170. package/lib/typescript/api.d.ts.map +1 -1
  171. package/lib/typescript/core/AudioContext.d.ts.map +1 -1
  172. package/lib/typescript/plugin/withAudioAPI.d.ts +9 -0
  173. package/lib/typescript/plugin/withAudioAPI.d.ts.map +1 -0
  174. package/lib/typescript/specs/NativeAudioManagerModule.d.ts +14 -0
  175. package/lib/typescript/specs/NativeAudioManagerModule.d.ts.map +1 -0
  176. package/lib/typescript/specs/index.d.ts +4 -0
  177. package/lib/typescript/specs/index.d.ts.map +1 -0
  178. package/lib/typescript/system/AudioManager.d.ts +14 -0
  179. package/lib/typescript/system/AudioManager.d.ts.map +1 -0
  180. package/lib/typescript/system/index.d.ts +2 -0
  181. package/lib/typescript/system/index.d.ts.map +1 -0
  182. package/lib/typescript/system/types.d.ts +40 -0
  183. package/lib/typescript/system/types.d.ts.map +1 -0
  184. package/package.json +7 -4
  185. package/src/api.ts +1 -0
  186. package/src/core/AudioContext.ts +6 -1
  187. package/src/plugin/withAudioAPI.ts +90 -0
  188. package/src/specs/NativeAudioManagerModule.ts +48 -0
  189. package/src/specs/index.ts +6 -0
  190. package/src/system/AudioManager.ts +149 -0
  191. package/src/system/index.ts +1 -0
  192. package/src/system/types.ts +84 -0
@@ -0,0 +1,137 @@
1
+ #import <audioapi/ios/system/AudioEngine.h>
2
+
3
+ @implementation AudioEngine
4
+
5
+ static AudioEngine *_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 +[AudioEngine sharedInstance]" userInfo:nil];
19
+ return nil;
20
+ }
21
+
22
+ - (instancetype)initPrivate
23
+ {
24
+ if (self = [super init]) {
25
+ self.audioEngine = [[AVAudioEngine alloc] init];
26
+ self.audioEngine.mainMixerNode.outputVolume = 1;
27
+ self.sourceNodes = [[NSMutableDictionary alloc] init];
28
+ self.sourceFormats = [[NSMutableDictionary alloc] init];
29
+ }
30
+ return self;
31
+ }
32
+
33
+ - (void)cleanup
34
+ {
35
+ NSLog(@"[AudioEngine] cleanup");
36
+ if ([self.audioEngine isRunning]) {
37
+ [self.audioEngine stop];
38
+ }
39
+
40
+ self.audioEngine = nil;
41
+ self.sourceNodes = nil;
42
+ self.sourceFormats = nil;
43
+ }
44
+
45
+ - (bool)rebuildAudioEngine
46
+ {
47
+ NSError *error = nil;
48
+
49
+ if ([self.audioEngine isRunning]) {
50
+ return true;
51
+ }
52
+
53
+ for (id sourceNodeId in self.sourceNodes) {
54
+ AVAudioSourceNode *sourceNode = [self.sourceNodes valueForKey:sourceNodeId];
55
+ AVAudioFormat *format = [self.sourceFormats valueForKey:sourceNodeId];
56
+
57
+ [self.audioEngine attachNode:sourceNode];
58
+ [self.audioEngine connect:sourceNode to:self.audioEngine.mainMixerNode format:format];
59
+ }
60
+
61
+ if ([self.audioEngine isRunning]) {
62
+ return true;
63
+ }
64
+
65
+ if (![self.audioEngine startAndReturnError:&error]) {
66
+ NSLog(@"Error while rebuilding audio engine: %@", [error debugDescription]);
67
+ return false;
68
+ }
69
+
70
+ return true;
71
+ }
72
+
73
+ - (void)startEngine
74
+ {
75
+ NSLog(@"[AudioEngine] startEngine");
76
+ NSError *error = nil;
77
+
78
+ if ([self.audioEngine isRunning]) {
79
+ return;
80
+ }
81
+
82
+ [self.audioEngine startAndReturnError:&error];
83
+
84
+ if (error != nil) {
85
+ NSLog(@"Error while starting the audio engine: %@", [error debugDescription]);
86
+ }
87
+ }
88
+
89
+ - (void)stopEngine
90
+ {
91
+ NSLog(@"[AudioEngine] stopEngine");
92
+ if (![self.audioEngine isRunning]) {
93
+ return;
94
+ }
95
+
96
+ [self.audioEngine pause];
97
+ }
98
+
99
+ - (bool)isRunning
100
+ {
101
+ return [self.audioEngine isRunning];
102
+ }
103
+
104
+ - (NSString *)attachSourceNode:(AVAudioSourceNode *)sourceNode format:(AVAudioFormat *)format
105
+ {
106
+ NSString *sourceNodeId = [[NSUUID UUID] UUIDString];
107
+ NSLog(@"[AudioEngine] attaching new source node with ID: %@", sourceNodeId);
108
+
109
+ [self.sourceNodes setValue:sourceNode forKey:sourceNodeId];
110
+ [self.sourceFormats setValue:format forKey:sourceNodeId];
111
+
112
+ [self.audioEngine attachNode:sourceNode];
113
+ [self.audioEngine connect:sourceNode to:self.audioEngine.mainMixerNode format:format];
114
+
115
+ if ([self.sourceNodes count] == 1) {
116
+ [self startEngine];
117
+ }
118
+
119
+ return sourceNodeId;
120
+ }
121
+
122
+ - (void)detachSourceNodeWithId:(NSString *)sourceNodeId
123
+ {
124
+ NSLog(@"[AudioEngine] detaching source nde with ID: %@", sourceNodeId);
125
+
126
+ AVAudioSourceNode *sourceNode = [self.sourceNodes valueForKey:sourceNodeId];
127
+ [self.audioEngine detachNode:sourceNode];
128
+
129
+ [self.sourceNodes removeObjectForKey:sourceNodeId];
130
+ [self.sourceFormats removeObjectForKey:sourceNodeId];
131
+
132
+ if ([self.sourceNodes count] == 0) {
133
+ [self stopEngine];
134
+ }
135
+ }
136
+
137
+ @end
@@ -0,0 +1,22 @@
1
+ #pragma once
2
+
3
+ #import <AVFoundation/AVFoundation.h>
4
+ #import <Foundation/Foundation.h>
5
+
6
+ @interface AudioSessionManager : NSObject
7
+
8
+ @property (nonatomic, weak) AVAudioSession *audioSession;
9
+
10
+ @property (nonatomic, assign) AVAudioSessionMode sessionMode;
11
+ @property (nonatomic, assign) AVAudioSessionCategory sessionCategory;
12
+ @property (nonatomic, assign) AVAudioSessionCategoryOptions sessionOptions;
13
+
14
+ + (instancetype)sharedInstance;
15
+ - (void)cleanup;
16
+ - (bool)configureAudioSession;
17
+
18
+ - (NSNumber *)getDevicePreferredSampleRate;
19
+ - (void)setAudioSessionOptions:(NSString *)category mode:(NSString *)mode options:(NSArray *)options;
20
+ - (bool)setActive:(bool)active error:(NSError **)error;
21
+
22
+ @end
@@ -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,295 @@
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
+ @"duration" : MPMediaItemPropertyPlaybackDuration, \
10
+ @"title" : MPMediaItemPropertyTitle, \
11
+ @"speed" : MPNowPlayingInfoPropertyPlaybackRate, \
12
+ @"elapsedTime" : MPNowPlayingInfoPropertyElapsedPlaybackTime, \
13
+ }
14
+
15
+ @implementation LockScreenManager
16
+
17
+ static LockScreenManager *_sharedInstance = nil;
18
+
19
+ + (instancetype)sharedInstanceWithAudioManagerModule:(AudioManagerModule *)audioManagerModule
20
+ {
21
+ static dispatch_once_t onceToken;
22
+ dispatch_once(&onceToken, ^{
23
+ _sharedInstance = [[self alloc] initPrivateWithAudioManagerModule:audioManagerModule];
24
+ });
25
+ return _sharedInstance;
26
+ }
27
+
28
+ - (instancetype)init
29
+ {
30
+ @throw [NSException exceptionWithName:@"Singleton" reason:@"Use +[LockScreenManager sharedInstance]" userInfo:nil];
31
+ return nil;
32
+ }
33
+
34
+ - (instancetype)initPrivateWithAudioManagerModule:(AudioManagerModule *)audioManagerModule
35
+ {
36
+ if (self = [super init]) {
37
+ self.audioManagerModule = audioManagerModule;
38
+ self.playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];
39
+ [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
40
+ }
41
+ return self;
42
+ }
43
+
44
+ - (void)cleanup
45
+ {
46
+ NSLog(@"[LockScreenManager] cleanup");
47
+ [self resetLockScreenInfo];
48
+ }
49
+
50
+ - (void)setLockScreenInfo:(NSDictionary *)info
51
+ {
52
+ // now playing info(lock screen info)
53
+ NSMutableDictionary *lockScreenInfoDict;
54
+
55
+ if (self.playingInfoCenter.nowPlayingInfo == nil) {
56
+ lockScreenInfoDict = [NSMutableDictionary dictionary];
57
+ } else {
58
+ lockScreenInfoDict = [[NSMutableDictionary alloc] initWithDictionary:self.playingInfoCenter.nowPlayingInfo];
59
+ }
60
+
61
+ for (NSString *key in LOCK_SCREEN_INFO) {
62
+ if ([info objectForKey:key] != nil) {
63
+ [lockScreenInfoDict setValue:[info objectForKey:key] forKey:[LOCK_SCREEN_INFO objectForKey:key]];
64
+ }
65
+ }
66
+
67
+ self.playingInfoCenter.nowPlayingInfo = lockScreenInfoDict;
68
+
69
+ // playback state
70
+ NSString *state = [info objectForKey:@"state"];
71
+
72
+ if (state != nil) {
73
+ if ([state isEqualToString:@"state_playing"]) {
74
+ self.playingInfoCenter.playbackState = MPNowPlayingPlaybackStatePlaying;
75
+ } else if ([state isEqualToString:@"state_paused"]) {
76
+ self.playingInfoCenter.playbackState = MPNowPlayingPlaybackStatePaused;
77
+ }
78
+ }
79
+
80
+ // artwork
81
+ NSString *artworkUrl = [self getArtworkUrl:[info objectForKey:@"artwork"]];
82
+ [self updateArtworkIfNeeded:artworkUrl];
83
+ }
84
+
85
+ - (void)resetLockScreenInfo
86
+ {
87
+ self.playingInfoCenter.nowPlayingInfo = nil;
88
+ self.artworkUrl = nil;
89
+ }
90
+
91
+ - (NSString *)getArtworkUrl:(NSString *)artwork
92
+ {
93
+ NSString *artworkUrl = nil;
94
+
95
+ if (artwork) {
96
+ if ([artwork isKindOfClass:[NSString class]]) {
97
+ artworkUrl = artwork;
98
+ } else if ([[artwork valueForKey:@"uri"] isKindOfClass:[NSString class]]) {
99
+ artworkUrl = [artwork valueForKey:@"uri"];
100
+ }
101
+ }
102
+
103
+ return artworkUrl;
104
+ }
105
+
106
+ - (void)updateArtworkIfNeeded:(id)artworkUrl
107
+ {
108
+ if (artworkUrl == nil) {
109
+ return;
110
+ }
111
+
112
+ MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
113
+ if ([artworkUrl isEqualToString:self.artworkUrl] &&
114
+ [center.nowPlayingInfo objectForKey:MPMediaItemPropertyArtwork] != nil) {
115
+ return;
116
+ }
117
+
118
+ self.artworkUrl = artworkUrl;
119
+
120
+ // Custom handling of artwork in another thread, will be loaded async
121
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
122
+ UIImage *image = nil;
123
+
124
+ // check whether artwork path is present
125
+ if ([artworkUrl isEqual:@""]) {
126
+ return;
127
+ }
128
+
129
+ // artwork is url download from the interwebs
130
+ if ([artworkUrl hasPrefix:@"http://"] || [artworkUrl hasPrefix:@"https://"]) {
131
+ NSURL *imageURL = [NSURL URLWithString:artworkUrl];
132
+ NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
133
+ image = [UIImage imageWithData:imageData];
134
+ } else {
135
+ NSString *localArtworkUrl = [artworkUrl stringByReplacingOccurrencesOfString:@"file://" withString:@""];
136
+ BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:localArtworkUrl];
137
+ if (fileExists) {
138
+ image = [UIImage imageNamed:localArtworkUrl];
139
+ }
140
+ }
141
+
142
+ // Check if image was available otherwise don't do anything
143
+ if (image == nil) {
144
+ return;
145
+ }
146
+
147
+ // check whether image is loaded
148
+ CGImageRef cgref = [image CGImage];
149
+ CIImage *cim = [image CIImage];
150
+
151
+ if (cim == nil && cgref == NULL) {
152
+ return;
153
+ }
154
+
155
+ dispatch_async(dispatch_get_main_queue(), ^{
156
+ // Check if URL wasn't changed in the meantime
157
+ if (![artworkUrl isEqual:self.artworkUrl]) {
158
+ return;
159
+ }
160
+
161
+ MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
162
+ MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithBoundsSize:image.size
163
+ requestHandler:^UIImage *_Nonnull(CGSize size) {
164
+ return image;
165
+ }];
166
+ NSMutableDictionary *mediaDict = (center.nowPlayingInfo != nil)
167
+ ? [[NSMutableDictionary alloc] initWithDictionary:center.nowPlayingInfo]
168
+ : [NSMutableDictionary dictionary];
169
+ [mediaDict setValue:artwork forKey:MPMediaItemPropertyArtwork];
170
+ center.nowPlayingInfo = mediaDict;
171
+ });
172
+ });
173
+ }
174
+
175
+ - (void)enableRemoteCommand:(NSString *)name enabled:(BOOL)enabled
176
+ {
177
+ MPRemoteCommandCenter *remoteCenter = [MPRemoteCommandCenter sharedCommandCenter];
178
+
179
+ if ([name isEqual:@"play"]) {
180
+ [self enableCommand:remoteCenter.playCommand withSelector:@selector(onPlay:) enabled:enabled];
181
+ } else if ([name isEqual:@"pause"]) {
182
+ [self enableCommand:remoteCenter.pauseCommand withSelector:@selector(onPause:) enabled:enabled];
183
+ } else if ([name isEqual:@"stop"]) {
184
+ [self enableCommand:remoteCenter.stopCommand withSelector:@selector(onStop:) enabled:enabled];
185
+ } else if ([name isEqual:@"togglePlayPause"]) {
186
+ [self enableCommand:remoteCenter.togglePlayPauseCommand withSelector:@selector(onTogglePlayPause:) enabled:enabled];
187
+ } else if ([name isEqual:@"changePlaybackRate"]) {
188
+ [self enableCommand:remoteCenter.changePlaybackRateCommand
189
+ withSelector:@selector(onChangePlaybackRate:)
190
+ enabled:enabled];
191
+ } else if ([name isEqual:@"nextTrack"]) {
192
+ [self enableCommand:remoteCenter.nextTrackCommand withSelector:@selector(onNextTrack:) enabled:enabled];
193
+ } else if ([name isEqual:@"previousTrack"]) {
194
+ [self enableCommand:remoteCenter.previousTrackCommand withSelector:@selector(onPreviousTrack:) enabled:enabled];
195
+ } else if ([name isEqual:@"skipForward"]) {
196
+ [self enableCommand:remoteCenter.skipForwardCommand withSelector:@selector(onSkipForward:) enabled:enabled];
197
+ } else if ([name isEqual:@"skipBackward"]) {
198
+ [self enableCommand:remoteCenter.skipBackwardCommand withSelector:@selector(onSkipBackward:) enabled:enabled];
199
+ } else if ([name isEqual:@"seekForward"]) {
200
+ [self enableCommand:remoteCenter.seekForwardCommand withSelector:@selector(onSeekForward:) enabled:enabled];
201
+ } else if ([name isEqual:@"seekBackward"]) {
202
+ [self enableCommand:remoteCenter.seekBackwardCommand withSelector:@selector(onSeekBackward:) enabled:enabled];
203
+ } else if ([name isEqual:@"changePlaybackPosition"]) {
204
+ [self enableCommand:remoteCenter.changePlaybackPositionCommand
205
+ withSelector:@selector(onChangePlaybackPosition:)
206
+ enabled:enabled];
207
+ }
208
+ }
209
+
210
+ - (void)enableCommand:(MPRemoteCommand *)command withSelector:(SEL)selector enabled:(BOOL)enabled
211
+ {
212
+ [command removeTarget:self action:selector];
213
+ if (enabled) {
214
+ [command addTarget:self action:selector];
215
+ }
216
+ command.enabled = enabled;
217
+ }
218
+
219
+ - (MPRemoteCommandHandlerStatus)onPlay:(MPRemoteCommandEvent *)event
220
+ {
221
+ [self.audioManagerModule sendEventWithName:@"onRemotePlay" body:@{}];
222
+ return MPRemoteCommandHandlerStatusSuccess;
223
+ }
224
+
225
+ - (MPRemoteCommandHandlerStatus)onPause:(MPRemoteCommandEvent *)event
226
+ {
227
+ [self.audioManagerModule sendEventWithName:@"onRemotePause" body:@{}];
228
+ return MPRemoteCommandHandlerStatusSuccess;
229
+ }
230
+
231
+ - (MPRemoteCommandHandlerStatus)onStop:(MPRemoteCommandEvent *)event
232
+ {
233
+ [self.audioManagerModule sendEventWithName:@"onRemoteStop" body:@{}];
234
+ return MPRemoteCommandHandlerStatusSuccess;
235
+ }
236
+
237
+ - (MPRemoteCommandHandlerStatus)onTogglePlayPause:(MPRemoteCommandEvent *)event
238
+ {
239
+ [self.audioManagerModule sendEventWithName:@"onRemoteTogglePlayPause" body:@{}];
240
+ return MPRemoteCommandHandlerStatusSuccess;
241
+ }
242
+
243
+ - (MPRemoteCommandHandlerStatus)onChangePlaybackRate:(MPChangePlaybackRateCommandEvent *)event
244
+ {
245
+ [self.audioManagerModule sendEventWithName:@"onRemoteChangePlaybackRate"
246
+ body:@{@"value" : [NSNumber numberWithDouble:event.playbackRate]}];
247
+ return MPRemoteCommandHandlerStatusSuccess;
248
+ }
249
+
250
+ - (MPRemoteCommandHandlerStatus)onNextTrack:(MPRemoteCommandEvent *)event
251
+ {
252
+ [self.audioManagerModule sendEventWithName:@"onRemoteNextTrack" body:@{}];
253
+ return MPRemoteCommandHandlerStatusSuccess;
254
+ }
255
+
256
+ - (MPRemoteCommandHandlerStatus)onPreviousTrack:(MPRemoteCommandEvent *)event
257
+ {
258
+ [self.audioManagerModule sendEventWithName:@"onRemotePreviousTrack" body:@{}];
259
+ return MPRemoteCommandHandlerStatusSuccess;
260
+ }
261
+
262
+ - (MPRemoteCommandHandlerStatus)onSeekForward:(MPRemoteCommandEvent *)event
263
+ {
264
+ [self.audioManagerModule sendEventWithName:@"onRemoteSeekForward" body:nil];
265
+ return MPRemoteCommandHandlerStatusSuccess;
266
+ }
267
+
268
+ - (MPRemoteCommandHandlerStatus)onSeekBackward:(MPRemoteCommandEvent *)event
269
+ {
270
+ [self.audioManagerModule sendEventWithName:@"onRemoteSeekBackward" body:@{}];
271
+ return MPRemoteCommandHandlerStatusSuccess;
272
+ }
273
+
274
+ - (MPRemoteCommandHandlerStatus)onSkipForward:(MPSkipIntervalCommandEvent *)event
275
+ {
276
+ [self.audioManagerModule sendEventWithName:@"onRemoteSkipForward"
277
+ body:@{@"value" : [NSNumber numberWithDouble:event.interval]}];
278
+ return MPRemoteCommandHandlerStatusSuccess;
279
+ }
280
+
281
+ - (MPRemoteCommandHandlerStatus)onSkipBackward:(MPSkipIntervalCommandEvent *)event
282
+ {
283
+ [self.audioManagerModule sendEventWithName:@"onRemoteSkipBackward"
284
+ body:@{@"value" : [NSNumber numberWithDouble:event.interval]}];
285
+ return MPRemoteCommandHandlerStatusSuccess;
286
+ }
287
+
288
+ - (MPRemoteCommandHandlerStatus)onChangePlaybackPosition:(MPChangePlaybackPositionCommandEvent *)event
289
+ {
290
+ [self.audioManagerModule sendEventWithName:@"onRemoteChangePlaybackPosition"
291
+ body:@{@"value" : [NSNumber numberWithDouble:event.positionTime]}];
292
+ return MPRemoteCommandHandlerStatusSuccess;
293
+ }
294
+
295
+ @end
@@ -0,0 +1,22 @@
1
+ #pragma once
2
+
3
+ #import <AVFoundation/AVFoundation.h>
4
+ #import <Foundation/Foundation.h>
5
+
6
+ @class AudioManagerModule;
7
+
8
+ @interface NotificationManager : NSObject
9
+
10
+ @property (nonatomic, weak) AudioManagerModule *audioManagerModule;
11
+ @property (nonatomic, weak) NSNotificationCenter *notificationCenter;
12
+
13
+ @property (nonatomic, assign) bool isInterrupted;
14
+ @property (nonatomic, assign) bool hadConfigurationChange;
15
+ @property (nonatomic, assign) BOOL audioInterruptionsObserved;
16
+
17
+ + (instancetype)sharedInstanceWithAudioManagerModule:(AudioManagerModule *)audioManagerModule;
18
+ - (void)cleanup;
19
+
20
+ - (void)observeAudioInterruptions:(BOOL)enabled;
21
+
22
+ @end