react-native-audio-api 0.11.0-alpha.3 → 0.11.0-alpha.5
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/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +34 -6
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.cpp +4 -0
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/ptrs.hpp +8 -0
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/utils.cpp +4 -0
- package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.h +1 -0
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +164 -16
- package/android/src/main/java/com/swmansion/audioapi/core/NativeAudioPlayer.kt +10 -8
- package/android/src/main/java/com/swmansion/audioapi/core/NativeAudioRecorder.kt +10 -8
- package/android/src/main/java/com/swmansion/audioapi/system/AudioFocusListener.kt +3 -4
- package/android/src/main/java/com/swmansion/audioapi/system/CentralizedForegroundService.kt +128 -0
- package/android/src/main/java/com/swmansion/audioapi/system/ForegroundServiceManager.kt +116 -0
- package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionManager.kt +115 -107
- package/android/src/main/java/com/swmansion/audioapi/system/PermissionRequestListener.kt +2 -1
- package/android/src/main/java/com/swmansion/audioapi/system/notification/BaseNotification.kt +47 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/NotificationRegistry.kt +191 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotification.kt +669 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotificationReceiver.kt +33 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/RecordingNotification.kt +303 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/RecordingNotificationReceiver.kt +45 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/SimpleNotification.kt +119 -0
- package/common/cpp/audioapi/core/utils/AudioFileWriter.h +1 -0
- package/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +1 -0
- package/common/cpp/audioapi/utils/AudioFileProperties.h +17 -17
- package/ios/audioapi/ios/AudioAPIModule.h +2 -2
- package/ios/audioapi/ios/AudioAPIModule.mm +108 -18
- package/ios/audioapi/ios/core/IOSAudioRecorder.mm +8 -7
- package/ios/audioapi/ios/core/NativeAudioPlayer.m +1 -1
- package/ios/audioapi/ios/core/NativeAudioRecorder.m +9 -2
- package/ios/audioapi/ios/system/AudioEngine.h +2 -0
- package/ios/audioapi/ios/system/AudioEngine.mm +49 -6
- package/ios/audioapi/ios/system/AudioSessionManager.mm +12 -9
- package/ios/audioapi/ios/system/NotificationManager.mm +7 -4
- package/ios/audioapi/ios/system/notification/BaseNotification.h +58 -0
- package/ios/audioapi/ios/system/notification/NotificationRegistry.h +70 -0
- package/ios/audioapi/ios/system/notification/NotificationRegistry.mm +172 -0
- package/ios/audioapi/ios/system/notification/PlaybackNotification.h +27 -0
- package/ios/audioapi/ios/system/notification/PlaybackNotification.mm +427 -0
- package/lib/commonjs/api.js +72 -1
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/api.web.js +27 -14
- package/lib/commonjs/api.web.js.map +1 -1
- package/lib/commonjs/specs/NativeAudioAPIModule.js.map +1 -1
- package/lib/commonjs/system/AudioManager.js +6 -9
- package/lib/commonjs/system/AudioManager.js.map +1 -1
- package/lib/commonjs/system/index.js +13 -0
- package/lib/commonjs/system/index.js.map +1 -1
- package/lib/commonjs/system/notification/PlaybackNotificationManager.js +135 -0
- package/lib/commonjs/system/notification/PlaybackNotificationManager.js.map +1 -0
- package/lib/commonjs/system/notification/RecordingNotificationManager.js +182 -0
- package/lib/commonjs/system/notification/RecordingNotificationManager.js.map +1 -0
- package/lib/commonjs/system/notification/SimpleNotificationManager.js +122 -0
- package/lib/commonjs/system/notification/SimpleNotificationManager.js.map +1 -0
- package/lib/commonjs/system/notification/index.js +45 -0
- package/lib/commonjs/system/notification/index.js.map +1 -0
- package/lib/commonjs/system/notification/types.js +6 -0
- package/lib/commonjs/system/notification/types.js.map +1 -0
- package/lib/commonjs/types.js +17 -17
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/web-system/index.js +17 -0
- package/lib/commonjs/web-system/index.js.map +1 -0
- package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js +34 -0
- package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js.map +1 -0
- package/lib/commonjs/web-system/notification/RecordingNotificationManager.js +34 -0
- package/lib/commonjs/web-system/notification/RecordingNotificationManager.js.map +1 -0
- package/lib/commonjs/web-system/notification/index.js +21 -0
- package/lib/commonjs/web-system/notification/index.js.map +1 -0
- package/lib/module/api.js +4 -0
- package/lib/module/api.js.map +1 -1
- package/lib/module/api.web.js +3 -1
- package/lib/module/api.web.js.map +1 -1
- package/lib/module/specs/NativeAudioAPIModule.js.map +1 -1
- package/lib/module/system/AudioManager.js +6 -9
- package/lib/module/system/AudioManager.js.map +1 -1
- package/lib/module/system/index.js +1 -0
- package/lib/module/system/index.js.map +1 -1
- package/lib/module/system/notification/PlaybackNotificationManager.js +131 -0
- package/lib/module/system/notification/PlaybackNotificationManager.js.map +1 -0
- package/lib/module/system/notification/RecordingNotificationManager.js +178 -0
- package/lib/module/system/notification/RecordingNotificationManager.js.map +1 -0
- package/lib/module/system/notification/SimpleNotificationManager.js +118 -0
- package/lib/module/system/notification/SimpleNotificationManager.js.map +1 -0
- package/lib/module/system/notification/index.js +7 -0
- package/lib/module/system/notification/index.js.map +1 -0
- package/lib/module/system/notification/types.js +4 -0
- package/lib/module/system/notification/types.js.map +1 -0
- package/lib/module/types.js +17 -17
- package/lib/module/types.js.map +1 -1
- package/lib/module/web-system/index.js +4 -0
- package/lib/module/web-system/index.js.map +1 -0
- package/lib/module/web-system/notification/PlaybackNotificationManager.js +30 -0
- package/lib/module/web-system/notification/PlaybackNotificationManager.js.map +1 -0
- package/lib/module/web-system/notification/RecordingNotificationManager.js +30 -0
- package/lib/module/web-system/notification/RecordingNotificationManager.js.map +1 -0
- package/lib/module/web-system/notification/index.js +5 -0
- package/lib/module/web-system/notification/index.js.map +1 -0
- package/lib/typescript/api.d.ts +2 -0
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/api.web.d.ts +3 -1
- package/lib/typescript/api.web.d.ts.map +1 -1
- package/lib/typescript/events/types.d.ts +3 -3
- package/lib/typescript/events/types.d.ts.map +1 -1
- package/lib/typescript/specs/NativeAudioAPIModule.d.ts +16 -5
- package/lib/typescript/specs/NativeAudioAPIModule.d.ts.map +1 -1
- package/lib/typescript/system/AudioManager.d.ts +4 -5
- package/lib/typescript/system/AudioManager.d.ts.map +1 -1
- package/lib/typescript/system/index.d.ts +1 -0
- package/lib/typescript/system/index.d.ts.map +1 -1
- package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts +22 -0
- package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts.map +1 -0
- package/lib/typescript/system/notification/RecordingNotificationManager.d.ts +23 -0
- package/lib/typescript/system/notification/RecordingNotificationManager.d.ts.map +1 -0
- package/lib/typescript/system/notification/SimpleNotificationManager.d.ts +20 -0
- package/lib/typescript/system/notification/SimpleNotificationManager.d.ts.map +1 -0
- package/lib/typescript/system/notification/index.d.ts +5 -0
- package/lib/typescript/system/notification/index.d.ts.map +1 -0
- package/lib/typescript/system/notification/types.d.ts +65 -0
- package/lib/typescript/system/notification/types.d.ts.map +1 -0
- package/lib/typescript/system/types.d.ts +0 -16
- package/lib/typescript/system/types.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +16 -16
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/web-system/index.d.ts +2 -0
- package/lib/typescript/web-system/index.d.ts.map +1 -0
- package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts +19 -0
- package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts.map +1 -0
- package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts +19 -0
- package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts.map +1 -0
- package/lib/typescript/web-system/notification/index.d.ts +3 -0
- package/lib/typescript/web-system/notification/index.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/api.ts +17 -0
- package/src/api.web.ts +7 -2
- package/src/events/types.ts +3 -4
- package/src/specs/NativeAudioAPIModule.ts +23 -7
- package/src/system/AudioManager.ts +10 -23
- package/src/system/index.ts +1 -0
- package/src/system/notification/PlaybackNotificationManager.ts +193 -0
- package/src/system/notification/RecordingNotificationManager.ts +242 -0
- package/src/system/notification/SimpleNotificationManager.ts +170 -0
- package/src/system/notification/index.ts +4 -0
- package/src/system/notification/types.ts +111 -0
- package/src/system/types.ts +0 -18
- package/src/types.ts +17 -17
- package/src/web-system/index.ts +1 -0
- package/src/web-system/notification/PlaybackNotificationManager.ts +60 -0
- package/src/web-system/notification/RecordingNotificationManager.ts +60 -0
- package/src/web-system/notification/index.ts +2 -0
- package/android/src/main/java/com/swmansion/audioapi/system/LockScreenManager.kt +0 -347
- package/android/src/main/java/com/swmansion/audioapi/system/MediaNotificationManager.kt +0 -273
- package/android/src/main/java/com/swmansion/audioapi/system/MediaReceiver.kt +0 -57
- package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionCallback.kt +0 -61
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
#import <audioapi/AudioAPIModuleInstaller.h>
|
|
12
12
|
#import <audioapi/ios/system/AudioEngine.h>
|
|
13
13
|
#import <audioapi/ios/system/AudioSessionManager.h>
|
|
14
|
-
#import <audioapi/ios/system/LockScreenManager.h>
|
|
15
14
|
#import <audioapi/ios/system/NotificationManager.h>
|
|
15
|
+
#import <audioapi/ios/system/notification/NotificationRegistry.h>
|
|
16
16
|
|
|
17
17
|
#import <audioapi/events/AudioEventHandlerRegistry.h>
|
|
18
18
|
|
|
@@ -50,7 +50,7 @@ RCT_EXPORT_MODULE(AudioAPIModule);
|
|
|
50
50
|
[self.audioEngine cleanup];
|
|
51
51
|
[self.notificationManager cleanup];
|
|
52
52
|
[self.audioSessionManager cleanup];
|
|
53
|
-
[self.
|
|
53
|
+
[self.notificationRegistry cleanup];
|
|
54
54
|
|
|
55
55
|
_eventHandler = nullptr;
|
|
56
56
|
|
|
@@ -66,8 +66,8 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install)
|
|
|
66
66
|
{
|
|
67
67
|
self.audioSessionManager = [[AudioSessionManager alloc] init];
|
|
68
68
|
self.audioEngine = [[AudioEngine alloc] init];
|
|
69
|
-
self.lockScreenManager = [[LockScreenManager alloc] initWithAudioAPIModule:self];
|
|
70
69
|
self.notificationManager = [[NotificationManager alloc] initWithAudioAPIModule:self];
|
|
70
|
+
self.notificationRegistry = [[NotificationRegistry alloc] initWithAudioAPIModule:self];
|
|
71
71
|
|
|
72
72
|
auto jsiRuntime = reinterpret_cast<facebook::jsi::Runtime *>(self.bridge.runtime);
|
|
73
73
|
|
|
@@ -142,21 +142,6 @@ RCT_EXPORT_METHOD(
|
|
|
142
142
|
allowHaptics:allowHaptics];
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
RCT_EXPORT_METHOD(setLockScreenInfo : (NSDictionary *)info)
|
|
146
|
-
{
|
|
147
|
-
[self.lockScreenManager setLockScreenInfo:info];
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
RCT_EXPORT_METHOD(resetLockScreenInfo)
|
|
151
|
-
{
|
|
152
|
-
[self.lockScreenManager resetLockScreenInfo];
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
RCT_EXPORT_METHOD(enableRemoteCommand : (NSString *)name enabled : (BOOL)enabled)
|
|
156
|
-
{
|
|
157
|
-
[self.lockScreenManager enableRemoteCommand:name enabled:enabled];
|
|
158
|
-
}
|
|
159
|
-
|
|
160
145
|
RCT_EXPORT_METHOD(observeAudioInterruptions : (BOOL)enabled)
|
|
161
146
|
{
|
|
162
147
|
[self.notificationManager observeAudioInterruptions:enabled];
|
|
@@ -190,6 +175,25 @@ RCT_EXPORT_METHOD(
|
|
|
190
175
|
});
|
|
191
176
|
}
|
|
192
177
|
|
|
178
|
+
RCT_EXPORT_METHOD(
|
|
179
|
+
requestNotificationPermissions : (nonnull RCTPromiseResolveBlock)
|
|
180
|
+
resolve reject : (nonnull RCTPromiseRejectBlock)reject)
|
|
181
|
+
{
|
|
182
|
+
// iOS doesn't require explicit notification permissions for media controls
|
|
183
|
+
// MPNowPlayingInfoCenter and MPRemoteCommandCenter work without permissions
|
|
184
|
+
// Return 'granted' to match the spec interface
|
|
185
|
+
resolve(@"granted");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
RCT_EXPORT_METHOD(
|
|
189
|
+
checkNotificationPermissions : (nonnull RCTPromiseResolveBlock)
|
|
190
|
+
resolve reject : (nonnull RCTPromiseRejectBlock)reject)
|
|
191
|
+
{
|
|
192
|
+
// iOS doesn't require explicit notification permissions for media controls
|
|
193
|
+
// Return 'granted' to match the spec interface
|
|
194
|
+
resolve(@"granted");
|
|
195
|
+
}
|
|
196
|
+
|
|
193
197
|
RCT_EXPORT_METHOD(
|
|
194
198
|
getDevicesInfo : (nonnull RCTPromiseResolveBlock)
|
|
195
199
|
resolve reject : (nonnull RCTPromiseRejectBlock)reject)
|
|
@@ -204,6 +208,92 @@ RCT_EXPORT_METHOD(disableSessionManagement)
|
|
|
204
208
|
[self.audioSessionManager disableSessionManagement];
|
|
205
209
|
}
|
|
206
210
|
|
|
211
|
+
// New notification system methods
|
|
212
|
+
RCT_EXPORT_METHOD(
|
|
213
|
+
registerNotification : (NSString *)type key : (NSString *)key resolve : (RCTPromiseResolveBlock)
|
|
214
|
+
resolve reject : (RCTPromiseRejectBlock)reject)
|
|
215
|
+
{
|
|
216
|
+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
217
|
+
BOOL success = [self.notificationRegistry registerNotificationType:type withKey:key];
|
|
218
|
+
|
|
219
|
+
if (success) {
|
|
220
|
+
resolve(@{@"success" : @true});
|
|
221
|
+
} else {
|
|
222
|
+
resolve(@{@"success" : @false, @"error" : @"Failed to register notification"});
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
RCT_EXPORT_METHOD(
|
|
228
|
+
showNotification : (NSString *)key options : (NSDictionary *)
|
|
229
|
+
options resolve : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject)
|
|
230
|
+
{
|
|
231
|
+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
232
|
+
BOOL success = [self.notificationRegistry showNotificationWithKey:key options:options];
|
|
233
|
+
|
|
234
|
+
if (success) {
|
|
235
|
+
resolve(@{@"success" : @true});
|
|
236
|
+
} else {
|
|
237
|
+
resolve(@{@"success" : @false, @"error" : @"Failed to show notification"});
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
RCT_EXPORT_METHOD(
|
|
243
|
+
updateNotification : (NSString *)key options : (NSDictionary *)
|
|
244
|
+
options resolve : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject)
|
|
245
|
+
{
|
|
246
|
+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
247
|
+
BOOL success = [self.notificationRegistry updateNotificationWithKey:key options:options];
|
|
248
|
+
|
|
249
|
+
if (success) {
|
|
250
|
+
resolve(@{@"success" : @true});
|
|
251
|
+
} else {
|
|
252
|
+
resolve(@{@"success" : @false, @"error" : @"Failed to update notification"});
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
RCT_EXPORT_METHOD(
|
|
258
|
+
hideNotification : (NSString *)key resolve : (RCTPromiseResolveBlock)
|
|
259
|
+
resolve reject : (RCTPromiseRejectBlock)reject)
|
|
260
|
+
{
|
|
261
|
+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
262
|
+
BOOL success = [self.notificationRegistry hideNotificationWithKey:key];
|
|
263
|
+
|
|
264
|
+
if (success) {
|
|
265
|
+
resolve(@{@"success" : @true});
|
|
266
|
+
} else {
|
|
267
|
+
resolve(@{@"success" : @false, @"error" : @"Failed to hide notification"});
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
RCT_EXPORT_METHOD(
|
|
273
|
+
unregisterNotification : (NSString *)key resolve : (RCTPromiseResolveBlock)
|
|
274
|
+
resolve reject : (RCTPromiseRejectBlock)reject)
|
|
275
|
+
{
|
|
276
|
+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
277
|
+
BOOL success = [self.notificationRegistry unregisterNotificationWithKey:key];
|
|
278
|
+
|
|
279
|
+
if (success) {
|
|
280
|
+
resolve(@{@"success" : @true});
|
|
281
|
+
} else {
|
|
282
|
+
resolve(@{@"success" : @false, @"error" : @"Failed to unregister notification"});
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
RCT_EXPORT_METHOD(
|
|
288
|
+
isNotificationActive : (NSString *)key resolve : (RCTPromiseResolveBlock)
|
|
289
|
+
resolve reject : (RCTPromiseRejectBlock)reject)
|
|
290
|
+
{
|
|
291
|
+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
292
|
+
BOOL isActive = [self.notificationRegistry isNotificationActiveWithKey:key];
|
|
293
|
+
resolve(@(isActive));
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
207
297
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
208
298
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
209
299
|
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
@@ -31,14 +31,14 @@ IOSAudioRecorder::IOSAudioRecorder(
|
|
|
31
31
|
AudioReceiverBlock receiverBlock = ^(const AudioBufferList *inputBuffer, int numFrames) {
|
|
32
32
|
if (usesFileOutput()) {
|
|
33
33
|
if (auto lock = Locker::tryLock(fileWriterMutex_)) {
|
|
34
|
-
std::
|
|
34
|
+
std::static_pointer_cast<IOSFileWriter>(fileWriter_)
|
|
35
35
|
->writeAudioData(inputBuffer, numFrames);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
if (usesCallback()) {
|
|
40
40
|
if (auto lock = Locker::tryLock(callbackMutex_)) {
|
|
41
|
-
std::
|
|
41
|
+
std::static_pointer_cast<IOSRecorderCallback>(dataCallback_)
|
|
42
42
|
->receiveAudioData(inputBuffer, numFrames);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
@@ -76,7 +76,7 @@ Result<std::string, std::string> IOSAudioRecorder::start()
|
|
|
76
76
|
return Result<std::string, std::string>::Err("Microphone permissions are not granted");
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
// TODO: recorder should probably request
|
|
79
|
+
// TODO: recorder should probably request the options if not set by user
|
|
80
80
|
// but lets handle that in another PR
|
|
81
81
|
if (![audioSessionManager isSessionActive]) {
|
|
82
82
|
return Result<std::string, std::string>::Err("Audio session is not active");
|
|
@@ -90,11 +90,12 @@ Result<std::string, std::string> IOSAudioRecorder::start()
|
|
|
90
90
|
// Engine will be started again once the native recorder starts
|
|
91
91
|
[AudioEngine.sharedInstance stopIfNecessary];
|
|
92
92
|
|
|
93
|
+
// Estimate the maximum input buffer lengths that can be expected from the sink node
|
|
93
94
|
size_t maxInputBufferLength = [nativeRecorder_ getBufferSize];
|
|
94
95
|
auto inputFormat = [nativeRecorder_ getInputFormat];
|
|
95
96
|
|
|
96
97
|
if (usesFileOutput()) {
|
|
97
|
-
auto fileResult = std::
|
|
98
|
+
auto fileResult = std::static_pointer_cast<IOSFileWriter>(fileWriter_)
|
|
98
99
|
->openFile(inputFormat, maxInputBufferLength);
|
|
99
100
|
|
|
100
101
|
if (fileResult.is_err()) {
|
|
@@ -106,7 +107,7 @@ Result<std::string, std::string> IOSAudioRecorder::start()
|
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
if (usesCallback()) {
|
|
109
|
-
auto callbackResult = std::
|
|
110
|
+
auto callbackResult = std::static_pointer_cast<IOSRecorderCallback>(dataCallback_)
|
|
110
111
|
->prepare(inputFormat, maxInputBufferLength);
|
|
111
112
|
|
|
112
113
|
if (callbackResult.is_err()) {
|
|
@@ -172,7 +173,7 @@ Result<std::string, std::string> IOSAudioRecorder::enableFileOutput(
|
|
|
172
173
|
fileWriter_ = std::make_shared<IOSFileWriter>(audioEventHandlerRegistry_, properties);
|
|
173
174
|
|
|
174
175
|
if (!isIdle()) {
|
|
175
|
-
auto result = std::
|
|
176
|
+
auto result = std::static_pointer_cast<IOSFileWriter>(fileWriter_)
|
|
176
177
|
->openFile([nativeRecorder_ getInputFormat], [nativeRecorder_ getBufferSize]);
|
|
177
178
|
|
|
178
179
|
if (result.is_err()) {
|
|
@@ -273,7 +274,7 @@ Result<NoneType, std::string> IOSAudioRecorder::setOnAudioReadyCallback(
|
|
|
273
274
|
audioEventHandlerRegistry_, sampleRate, bufferLength, channelCount, callbackId);
|
|
274
275
|
|
|
275
276
|
if (!isIdle()) {
|
|
276
|
-
auto result = std::
|
|
277
|
+
auto result = std::static_pointer_cast<IOSRecorderCallback>(dataCallback_)
|
|
277
278
|
->prepare([nativeRecorder_ getInputFormat], [nativeRecorder_ getBufferSize]);
|
|
278
279
|
|
|
279
280
|
if (result.is_err()) {
|
|
@@ -42,6 +42,7 @@ static inline uint32_t nextPowerOfTwo(uint32_t x)
|
|
|
42
42
|
return self;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// Note: this method should be called only after the session is activated
|
|
45
46
|
- (AVAudioFormat *)getInputFormat
|
|
46
47
|
{
|
|
47
48
|
AVAudioFormat *format = [AudioEngine.sharedInstance.audioEngine.inputNode inputFormatForBus:0];
|
|
@@ -95,9 +96,15 @@ static inline uint32_t nextPowerOfTwo(uint32_t x)
|
|
|
95
96
|
{
|
|
96
97
|
AudioEngine *audioEngine = [AudioEngine sharedInstance];
|
|
97
98
|
assert(audioEngine != nil);
|
|
98
|
-
[audioEngine
|
|
99
|
+
[audioEngine stopIfPossible];
|
|
99
100
|
[audioEngine detachInputNode];
|
|
100
|
-
|
|
101
|
+
|
|
102
|
+
// This makes sure that the engine releases the input properly when we no longer need it
|
|
103
|
+
// (i.e. no more misleading dot)
|
|
104
|
+
// Restart only if is not running to avoid interruptions of playback
|
|
105
|
+
if ([audioEngine getState] != AudioEngineStateRunning) {
|
|
106
|
+
[audioEngine restartAudioEngine];
|
|
107
|
+
}
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
- (void)pause
|
|
@@ -57,8 +57,6 @@ static AudioEngine *_sharedInstance = nil;
|
|
|
57
57
|
|
|
58
58
|
- (void)detachSourceNodeWithId:(NSString *)sourceNodeId
|
|
59
59
|
{
|
|
60
|
-
NSLog(@"[AudioEngine] detaching source node with ID: %@", sourceNodeId);
|
|
61
|
-
|
|
62
60
|
AVAudioSourceNode *sourceNode = [self.sourceNodes valueForKey:sourceNodeId];
|
|
63
61
|
|
|
64
62
|
if (sourceNode == nil) {
|
|
@@ -75,7 +73,6 @@ static AudioEngine *_sharedInstance = nil;
|
|
|
75
73
|
- (void)attachInputNode:(AVAudioSinkNode *)inputNode
|
|
76
74
|
{
|
|
77
75
|
self.inputNode = inputNode;
|
|
78
|
-
|
|
79
76
|
AVAudioFormat *format = [self.audioEngine.inputNode inputFormatForBus:0];
|
|
80
77
|
|
|
81
78
|
[self.audioEngine attachNode:inputNode];
|
|
@@ -105,11 +102,39 @@ static AudioEngine *_sharedInstance = nil;
|
|
|
105
102
|
|
|
106
103
|
- (void)onInterruptionEnd:(bool)shouldResume
|
|
107
104
|
{
|
|
105
|
+
NSError *error = nil;
|
|
106
|
+
|
|
108
107
|
if (self.state != AudioEngineState::AudioEngineStateInterrupted) {
|
|
108
|
+
// If engine was not interrupted, do nothing
|
|
109
|
+
// Not a real condition, but better be safe than sorry :shrug:
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Stop just in case, reset the engine and build it from scratch
|
|
114
|
+
[self stopIfNecessary];
|
|
115
|
+
[self.audioEngine reset];
|
|
116
|
+
[self rebuildAudioEngine];
|
|
117
|
+
|
|
118
|
+
// If shouldResume is false, mark the engine as paused and wait
|
|
119
|
+
// for JS-side resume command
|
|
120
|
+
// TODO: this should be notified to the user f.e. via Event Emitter
|
|
121
|
+
if (!shouldResume) {
|
|
122
|
+
self.state = AudioEngineState::AudioEngineStatePaused;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
[self.audioEngine prepare];
|
|
127
|
+
[self.audioEngine startAndReturnError:&error];
|
|
128
|
+
|
|
129
|
+
if (error != nil) {
|
|
130
|
+
NSLog(
|
|
131
|
+
@"Error while restarting the audio engine after interruption: %@",
|
|
132
|
+
[error debugDescription]);
|
|
133
|
+
self.state = AudioEngineState::AudioEngineStateIdle;
|
|
109
134
|
return;
|
|
110
135
|
}
|
|
111
136
|
|
|
112
|
-
|
|
137
|
+
self.state = AudioEngineState::AudioEngineStateRunning;
|
|
113
138
|
}
|
|
114
139
|
|
|
115
140
|
- (AudioEngineState)getState
|
|
@@ -117,6 +142,7 @@ static AudioEngine *_sharedInstance = nil;
|
|
|
117
142
|
return self.state;
|
|
118
143
|
}
|
|
119
144
|
|
|
145
|
+
/// @brief Rebuilds the audio engine by re-attaching and re-connecting all source nodes and input node.
|
|
120
146
|
- (void)rebuildAudioEngine
|
|
121
147
|
{
|
|
122
148
|
self.audioEngine = [[AVAudioEngine alloc] init];
|
|
@@ -135,6 +161,7 @@ static AudioEngine *_sharedInstance = nil;
|
|
|
135
161
|
}
|
|
136
162
|
}
|
|
137
163
|
|
|
164
|
+
// @brief Starts the audio engine if not already running.
|
|
138
165
|
- (bool)startEngine
|
|
139
166
|
{
|
|
140
167
|
NSError *error = nil;
|
|
@@ -208,6 +235,22 @@ static AudioEngine *_sharedInstance = nil;
|
|
|
208
235
|
[self stopEngine];
|
|
209
236
|
}
|
|
210
237
|
|
|
238
|
+
- (void)stopIfPossible
|
|
239
|
+
{
|
|
240
|
+
if (self.state == AudioEngineState::AudioEngineStateIdle) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
bool hasInput = self.inputNode != nil;
|
|
245
|
+
bool hasSources = [self.sourceNodes count] > 0;
|
|
246
|
+
|
|
247
|
+
if (hasInput || hasSources) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
[self stopEngine];
|
|
252
|
+
}
|
|
253
|
+
|
|
211
254
|
- (void)restartAudioEngine
|
|
212
255
|
{
|
|
213
256
|
if ([self.audioEngine isRunning]) {
|
|
@@ -225,10 +268,10 @@ static AudioEngine *_sharedInstance = nil;
|
|
|
225
268
|
NSLog(@"================ 🎧 AVAudioEngine STATE ================");
|
|
226
269
|
|
|
227
270
|
// AVAudioEngine state
|
|
228
|
-
NSLog(@"➡️ engine.isRunning: %@", self.audioEngine.isRunning ? @"
|
|
271
|
+
NSLog(@"➡️ engine.isRunning: %@", self.audioEngine.isRunning ? @"true" : @"false");
|
|
229
272
|
NSLog(
|
|
230
273
|
@"➡️ engine.isInManualRenderingMode: %@",
|
|
231
|
-
self.audioEngine.isInManualRenderingMode ? @"
|
|
274
|
+
self.audioEngine.isInManualRenderingMode ? @"true" : @"false");
|
|
232
275
|
|
|
233
276
|
// Session state
|
|
234
277
|
NSLog(@"🎚️ Session category: %@", session.category);
|
|
@@ -66,15 +66,18 @@ static AudioSessionManager *_sharedInstance = nil;
|
|
|
66
66
|
(unsigned long)self.audioSession.categoryOptions);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
if (@available(iOS 13.0, *)) {
|
|
70
|
+
if (self.audioSession.allowHapticsAndSystemSoundsDuringRecording !=
|
|
71
|
+
self.allowHapticsAndSounds) {
|
|
72
|
+
[self.audioSession setAllowHapticsAndSystemSoundsDuringRecording:self.allowHapticsAndSounds
|
|
73
|
+
error:&error];
|
|
74
|
+
|
|
75
|
+
if (error != nil) {
|
|
76
|
+
NSLog(
|
|
77
|
+
@"Error while setting allowHapticsAndSystemSoundsDuringRecording: %@",
|
|
78
|
+
[error debugDescription]);
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
78
81
|
}
|
|
79
82
|
}
|
|
80
83
|
|
|
@@ -129,11 +129,12 @@ static NSString *NotificationManagerContext = @"NotificationManagerContext";
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
bool shouldResume = interruptionOption == AVAudioSessionInterruptionOptionShouldResume;
|
|
132
|
-
[audioEngine onInterruptionEnd:shouldResume];
|
|
133
132
|
|
|
134
133
|
if (self.audioInterruptionsObserved) {
|
|
135
134
|
NSDictionary *body = @{@"type" : @"ended", @"shouldResume" : @(shouldResume)};
|
|
136
135
|
[self.audioAPIModule invokeHandlerWithEventName:@"interruption" eventBody:body];
|
|
136
|
+
} else {
|
|
137
|
+
[audioEngine onInterruptionEnd:shouldResume];
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
|
|
@@ -156,11 +157,12 @@ static NSString *NotificationManagerContext = @"NotificationManagerContext";
|
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
bool shouldResume = secondaryAudioType == AVAudioSessionSilenceSecondaryAudioHintTypeEnd;
|
|
159
|
-
[audioEngine onInterruptionEnd:shouldResume];
|
|
160
160
|
|
|
161
161
|
if (self.audioInterruptionsObserved) {
|
|
162
162
|
NSDictionary *body = @{@"type" : @"ended", @"shouldResume" : @(shouldResume)};
|
|
163
163
|
[self.audioAPIModule invokeHandlerWithEventName:@"interruption" eventBody:body];
|
|
164
|
+
} else {
|
|
165
|
+
[audioEngine onInterruptionEnd:shouldResume];
|
|
164
166
|
}
|
|
165
167
|
}
|
|
166
168
|
|
|
@@ -237,7 +239,7 @@ static NSString *NotificationManagerContext = @"NotificationManagerContext";
|
|
|
237
239
|
target:self
|
|
238
240
|
selector:@selector(checkSecondaryAudioHint)
|
|
239
241
|
userInfo:nil
|
|
240
|
-
repeats:
|
|
242
|
+
repeats:true];
|
|
241
243
|
|
|
242
244
|
[[NSRunLoop mainRunLoop] addTimer:self.hintPollingTimer forMode:NSRunLoopCommonModes];
|
|
243
245
|
}
|
|
@@ -273,11 +275,12 @@ static NSString *NotificationManagerContext = @"NotificationManagerContext";
|
|
|
273
275
|
return;
|
|
274
276
|
}
|
|
275
277
|
|
|
276
|
-
[audioEngine onInterruptionEnd:true];
|
|
277
278
|
NSDictionary *body = @{@"type" : @"ended", @"shouldResume" : @true};
|
|
278
279
|
|
|
279
280
|
if (self.audioInterruptionsObserved) {
|
|
280
281
|
[self.audioAPIModule invokeHandlerWithEventName:@"interruption" eventBody:body];
|
|
282
|
+
} else {
|
|
283
|
+
[audioEngine onInterruptionEnd:true];
|
|
281
284
|
}
|
|
282
285
|
}
|
|
283
286
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#import <Foundation/Foundation.h>
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* BaseNotification protocol
|
|
7
|
+
*
|
|
8
|
+
* Interface that all notification types must implement.
|
|
9
|
+
*/
|
|
10
|
+
@protocol BaseNotification <NSObject>
|
|
11
|
+
|
|
12
|
+
@required
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Initialize the notification.
|
|
16
|
+
* @param options Initialization options (can be nil)
|
|
17
|
+
* @return YES if successful
|
|
18
|
+
*/
|
|
19
|
+
- (BOOL)initializeWithOptions:(NSDictionary *)options;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Show the notification (sets metadata on iOS).
|
|
23
|
+
* @param options Notification options
|
|
24
|
+
* @return YES if successful
|
|
25
|
+
*/
|
|
26
|
+
- (BOOL)showWithOptions:(NSDictionary *)options;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Update notification metadata.
|
|
30
|
+
* @param options Updated information
|
|
31
|
+
* @return YES if successful
|
|
32
|
+
*/
|
|
33
|
+
- (BOOL)updateWithOptions:(NSDictionary *)options;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Hide the notification (clears metadata on iOS).
|
|
37
|
+
* @return YES if successful
|
|
38
|
+
*/
|
|
39
|
+
- (BOOL)hide;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Clean up and release resources.
|
|
43
|
+
*/
|
|
44
|
+
- (void)cleanup;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check if notification is active.
|
|
48
|
+
* @return YES if active
|
|
49
|
+
*/
|
|
50
|
+
- (BOOL)isActive;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get notification type identifier.
|
|
54
|
+
* @return Type identifier (e.g., "playback", "recording")
|
|
55
|
+
*/
|
|
56
|
+
- (NSString *)getNotificationType;
|
|
57
|
+
|
|
58
|
+
@end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#import <Foundation/Foundation.h>
|
|
4
|
+
#import <audioapi/ios/system/notification/BaseNotification.h>
|
|
5
|
+
|
|
6
|
+
@class AudioAPIModule;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* NotificationRegistry
|
|
10
|
+
*
|
|
11
|
+
* Central manager for all notification types.
|
|
12
|
+
* Manages registration, lifecycle, and routing of notification implementations.
|
|
13
|
+
*/
|
|
14
|
+
@interface NotificationRegistry : NSObject
|
|
15
|
+
|
|
16
|
+
@property (nonatomic, weak) AudioAPIModule *audioAPIModule;
|
|
17
|
+
|
|
18
|
+
- (instancetype)initWithAudioAPIModule:(AudioAPIModule *)audioAPIModule;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Register a new notification type.
|
|
22
|
+
* @param type The notification type identifier (e.g., "playback", "recording")
|
|
23
|
+
* @param key Unique key for this notification instance
|
|
24
|
+
* @return YES if registration succeeded, NO otherwise
|
|
25
|
+
*/
|
|
26
|
+
- (BOOL)registerNotificationType:(NSString *)type withKey:(NSString *)key;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Show a registered notification.
|
|
30
|
+
* @param key The notification key
|
|
31
|
+
* @param options Options for showing the notification
|
|
32
|
+
* @return YES if successful, NO otherwise
|
|
33
|
+
*/
|
|
34
|
+
- (BOOL)showNotificationWithKey:(NSString *)key options:(NSDictionary *)options;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Update a shown notification.
|
|
38
|
+
* @param key The notification key
|
|
39
|
+
* @param options Updated options
|
|
40
|
+
* @return YES if successful, NO otherwise
|
|
41
|
+
*/
|
|
42
|
+
- (BOOL)updateNotificationWithKey:(NSString *)key options:(NSDictionary *)options;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Hide a notification.
|
|
46
|
+
* @param key The notification key
|
|
47
|
+
* @return YES if successful, NO otherwise
|
|
48
|
+
*/
|
|
49
|
+
- (BOOL)hideNotificationWithKey:(NSString *)key;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Unregister and clean up a notification.
|
|
53
|
+
* @param key The notification key
|
|
54
|
+
* @return YES if successful, NO otherwise
|
|
55
|
+
*/
|
|
56
|
+
- (BOOL)unregisterNotificationWithKey:(NSString *)key;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check if a notification is active.
|
|
60
|
+
* @param key The notification key
|
|
61
|
+
* @return YES if active, NO otherwise
|
|
62
|
+
*/
|
|
63
|
+
- (BOOL)isNotificationActiveWithKey:(NSString *)key;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Clean up all notifications.
|
|
67
|
+
*/
|
|
68
|
+
- (void)cleanup;
|
|
69
|
+
|
|
70
|
+
@end
|