nosnia-audio-recorder 0.9.12 → 0.9.15
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/ios/NosniaAudioRecorder.mm +38 -33
- package/package.json +1 -1
|
@@ -30,7 +30,7 @@ RCT_EXPORT_MODULE(NosniaAudioRecorder)
|
|
|
30
30
|
if (self) {
|
|
31
31
|
_isRecording = NO;
|
|
32
32
|
_hasListeners = NO;
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
}
|
|
35
35
|
return self;
|
|
36
36
|
}
|
|
@@ -40,12 +40,12 @@ RCT_EXPORT_MODULE(NosniaAudioRecorder)
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
- (void)startObserving {
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
_hasListeners = YES;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
- (void)stopObserving {
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
_hasListeners = NO;
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -56,7 +56,7 @@ RCT_EXPORT_MODULE(NosniaAudioRecorder)
|
|
|
56
56
|
_progressTimer = nil;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
|
|
61
61
|
// Ensure timer is created on main thread and added to main run loop
|
|
62
62
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
@@ -77,7 +77,7 @@ RCT_EXPORT_MODULE(NosniaAudioRecorder)
|
|
|
77
77
|
// Ensure timer fires on main run loop
|
|
78
78
|
[[NSRunLoop mainRunLoop] addTimer:self->_progressTimer forMode:NSRunLoopCommonModes];
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
83
|
|
|
@@ -93,7 +93,7 @@ RCT_EXPORT_MODULE(NosniaAudioRecorder)
|
|
|
93
93
|
// Use the recorder's currentTime directly
|
|
94
94
|
double durationMs = _audioRecorder.currentTime * 1000.0;
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
durationMs, _audioRecorder.currentTime);
|
|
98
98
|
|
|
99
99
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
@@ -134,14 +134,14 @@ RCT_EXPORT_MODULE(NosniaAudioRecorder)
|
|
|
134
134
|
attributes:nil
|
|
135
135
|
error:&dirError];
|
|
136
136
|
if (!created || dirError) {
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
return nil;
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
// Verify directory is writable
|
|
143
143
|
if (![fileManager isWritableFileAtPath:recordingDir]) {
|
|
144
|
-
|
|
144
|
+
|
|
145
145
|
return nil;
|
|
146
146
|
}
|
|
147
147
|
|
|
@@ -156,18 +156,16 @@ RCT_EXPORT_MODULE(NosniaAudioRecorder)
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
RCT_EXPORT_METHOD(testEventEmitter) {
|
|
159
|
-
|
|
160
|
-
NSLog(@"[NosniaAudioRecorder] hasListeners: %d", _hasListeners);
|
|
161
|
-
NSLog(@"[NosniaAudioRecorder] bridge exists: %@", self.bridge ? @"YES" : @"NO");
|
|
159
|
+
|
|
162
160
|
|
|
163
161
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
164
|
-
|
|
162
|
+
|
|
165
163
|
[self sendEventWithName:@"onRecordingProgress"
|
|
166
164
|
body:@{
|
|
167
165
|
@"duration": @(999.0),
|
|
168
166
|
@"isRecording": @(NO)
|
|
169
167
|
}];
|
|
170
|
-
|
|
168
|
+
|
|
171
169
|
});
|
|
172
170
|
}
|
|
173
171
|
|
|
@@ -178,7 +176,7 @@ RCT_EXPORT_METHOD(debugInfo:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRe
|
|
|
178
176
|
@"isRecording": @(_isRecording),
|
|
179
177
|
@"moduleStatus": @"OK"
|
|
180
178
|
};
|
|
181
|
-
|
|
179
|
+
|
|
182
180
|
resolve(info);
|
|
183
181
|
}
|
|
184
182
|
|
|
@@ -268,23 +266,25 @@ RCT_EXPORT_METHOD(startRecording:(NSDictionary *)options
|
|
|
268
266
|
// Set audio category - use PlayAndRecord for better simulator/device compatibility
|
|
269
267
|
// This works reliably on both real devices and simulators
|
|
270
268
|
NSError *categoryError = nil;
|
|
271
|
-
AVAudioSessionCategoryOptions
|
|
269
|
+
AVAudioSessionCategoryOptions categoryOptions = AVAudioSessionCategoryOptionDefaultToSpeaker |
|
|
270
|
+
AVAudioSessionCategoryOptionAllowBluetooth |
|
|
271
|
+
AVAudioSessionCategoryOptionAllowAirPlay;
|
|
272
272
|
BOOL categorySuccess = [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord
|
|
273
|
-
withOptions:
|
|
273
|
+
withOptions:categoryOptions
|
|
274
274
|
error:&categoryError];
|
|
275
275
|
if (!categorySuccess || categoryError) {
|
|
276
276
|
NSString *msg = categoryError ? categoryError.description : @"Failed to set audio category";
|
|
277
|
-
|
|
277
|
+
|
|
278
278
|
reject(@"AUDIO_SESSION_ERROR", msg, categoryError);
|
|
279
279
|
return;
|
|
280
280
|
}
|
|
281
|
-
|
|
281
|
+
|
|
282
282
|
|
|
283
283
|
// Set audio mode to default for recording
|
|
284
284
|
NSError *modeError = nil;
|
|
285
285
|
[audioSession setMode:AVAudioSessionModeDefault error:&modeError];
|
|
286
286
|
if (modeError) {
|
|
287
|
-
|
|
287
|
+
|
|
288
288
|
}
|
|
289
289
|
|
|
290
290
|
// Activate audio session
|
|
@@ -312,7 +312,7 @@ RCT_EXPORT_METHOD(startRecording:(NSDictionary *)options
|
|
|
312
312
|
[recordingSettings setObject:channels forKey:AVNumberOfChannelsKey];
|
|
313
313
|
[recordingSettings setObject:bitrate forKey:AVEncoderBitRateKey];
|
|
314
314
|
|
|
315
|
-
|
|
315
|
+
|
|
316
316
|
|
|
317
317
|
// Validate settings dictionary created successfully
|
|
318
318
|
if (!recordingSettings || recordingSettings.count == 0) {
|
|
@@ -354,20 +354,25 @@ RCT_EXPORT_METHOD(startRecording:(NSDictionary *)options
|
|
|
354
354
|
NSError *recordStartError = nil;
|
|
355
355
|
BOOL recordStarted = NO;
|
|
356
356
|
|
|
357
|
+
// CRITICAL: Ensure audio session is active BEFORE attempting to record
|
|
358
|
+
// In Release builds, this is NOT automatically done by the system
|
|
359
|
+
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
|
|
360
|
+
NSError *preActivateError = nil;
|
|
361
|
+
BOOL preActivateSuccess = [audioSession setActive:YES error:&preActivateError];
|
|
362
|
+
|
|
363
|
+
|
|
357
364
|
// Try to prepare recorder first
|
|
358
|
-
|
|
359
|
-
NSLog(@"[NosniaAudioRecorder] Recording URL: %@", _recordingURL);
|
|
365
|
+
|
|
360
366
|
|
|
361
367
|
if (![_audioRecorder prepareToRecord]) {
|
|
362
|
-
|
|
363
|
-
NSLog(@"[NosniaAudioRecorder] Recording settings that failed: Format=%@, SampleRate=%@, Channels=%@, Bitrate=%@",
|
|
368
|
+
|
|
364
369
|
_audioRecorder.settings[AVFormatIDKey],
|
|
365
370
|
_audioRecorder.settings[AVSampleRateKey],
|
|
366
371
|
_audioRecorder.settings[AVNumberOfChannelsKey],
|
|
367
372
|
_audioRecorder.settings[AVEncoderBitRateKey]);
|
|
368
373
|
|
|
369
374
|
// Try with fallback minimal settings
|
|
370
|
-
|
|
375
|
+
|
|
371
376
|
NSDictionary *fallbackSettings = @{
|
|
372
377
|
AVFormatIDKey: @(kAudioFormatMPEG4AAC),
|
|
373
378
|
AVSampleRateKey: @(44100.0),
|
|
@@ -381,21 +386,21 @@ RCT_EXPORT_METHOD(startRecording:(NSDictionary *)options
|
|
|
381
386
|
settings:fallbackSettings
|
|
382
387
|
error:&fallbackError];
|
|
383
388
|
if (fallbackError || ![_audioRecorder prepareToRecord]) {
|
|
384
|
-
|
|
389
|
+
|
|
385
390
|
reject(@"PREPARE_ERROR", @"Failed to prepare audio recorder for recording with both standard and fallback settings", nil);
|
|
386
391
|
_audioRecorder = nil;
|
|
387
392
|
return;
|
|
388
393
|
}
|
|
389
394
|
}
|
|
390
|
-
|
|
395
|
+
|
|
391
396
|
|
|
392
|
-
|
|
397
|
+
|
|
393
398
|
recordStarted = [_audioRecorder record];
|
|
394
399
|
if (!recordStarted) {
|
|
395
|
-
|
|
400
|
+
|
|
396
401
|
|
|
397
402
|
// Try alternative: reset audio session and retry
|
|
398
|
-
|
|
403
|
+
|
|
399
404
|
NSError *resetError = nil;
|
|
400
405
|
[[AVAudioSession sharedInstance] setActive:NO error:&resetError];
|
|
401
406
|
|
|
@@ -408,14 +413,14 @@ RCT_EXPORT_METHOD(startRecording:(NSDictionary *)options
|
|
|
408
413
|
NSString *errorMsg = [NSString stringWithFormat:
|
|
409
414
|
@"Failed to start recording after retry. Recorder state: %ld. This may be a simulator audio limitation. Try on a real device.",
|
|
410
415
|
(long)_audioRecorder.recording];
|
|
411
|
-
|
|
416
|
+
|
|
412
417
|
reject(@"START_RECORDING_ERROR", errorMsg, nil);
|
|
413
418
|
_audioRecorder = nil;
|
|
414
419
|
return;
|
|
415
420
|
}
|
|
416
|
-
|
|
421
|
+
|
|
417
422
|
}
|
|
418
|
-
|
|
423
|
+
|
|
419
424
|
|
|
420
425
|
_isRecording = YES;
|
|
421
426
|
[self startProgressTimer];
|
package/package.json
CHANGED