nosnia-audio-recorder 0.3.9 → 0.4.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.
- package/NosniaAudioRecorder.podspec +4 -2
- package/android/src/main/java/com/nosniaaudiorecorder/NosniaAudioPlayerModule.kt +238 -238
- package/ios/NosniaAudioPlayer.h +7 -7
- package/ios/NosniaAudioPlayer.mm +259 -264
- package/ios/NosniaAudioRecorder.mm +0 -5
- package/ios/Podfile +16 -0
- package/ios/Pods/Pods.xcodeproj/project.pbxproj +207 -0
- package/ios/Pods/Pods.xcodeproj/xcuserdata/nosnia.xcuserdatad/xcschemes/xcschememanagement.plist +10 -0
- package/lib/module/AudioPlayer.js.map +1 -1
- package/lib/module/NativeNosniaAudioPlayer.js.map +1 -1
- package/lib/module/NativeNosniaAudioRecorder.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/package.json +182 -182
package/ios/NosniaAudioPlayer.mm
CHANGED
|
@@ -1,264 +1,259 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
3
|
-
|
|
4
|
-
#import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@
|
|
10
|
-
@property (nonatomic, strong)
|
|
11
|
-
@
|
|
12
|
-
|
|
13
|
-
@
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
BOOL
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
status[@"duration"] = @(
|
|
223
|
-
status[@"currentTime"] = @(
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
@end
|
|
263
|
-
|
|
264
|
-
#endif /* __NOSNIA_AUDIO_PLAYER_MM__ */
|
|
1
|
+
#import "NosniaAudioPlayer.h"
|
|
2
|
+
#import <AVFoundation/AVFoundation.h>
|
|
3
|
+
#import <React/RCTBridgeModule.h>
|
|
4
|
+
#import <React/RCTEventEmitter.h>
|
|
5
|
+
|
|
6
|
+
@interface NosniaAudioPlayer () <AVAudioPlayerDelegate>
|
|
7
|
+
@property (nonatomic, strong) AVAudioPlayer *audioPlayer;
|
|
8
|
+
@property (nonatomic, assign) BOOL isPlaying;
|
|
9
|
+
@property (nonatomic, strong) NSTimer *progressTimer;
|
|
10
|
+
@property (nonatomic, strong) NSString *currentFilePath;
|
|
11
|
+
@end
|
|
12
|
+
|
|
13
|
+
@implementation NosniaAudioPlayer {
|
|
14
|
+
AVAudioPlayer *_audioPlayer;
|
|
15
|
+
BOOL _isPlaying;
|
|
16
|
+
NSTimer *_progressTimer;
|
|
17
|
+
NSString *_currentFilePath;
|
|
18
|
+
BOOL _hasListeners;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
RCT_EXPORT_MODULE(NosniaAudioPlayer)
|
|
22
|
+
|
|
23
|
+
- (instancetype)init {
|
|
24
|
+
self = [super init];
|
|
25
|
+
if (self) {
|
|
26
|
+
_isPlaying = NO;
|
|
27
|
+
_hasListeners = NO;
|
|
28
|
+
}
|
|
29
|
+
return self;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
- (NSArray<NSString *> *)supportedEvents {
|
|
33
|
+
return @[@"onPlaybackProgress", @"onPlaybackComplete"];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
- (void)startObserving {
|
|
37
|
+
_hasListeners = YES;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
- (void)stopObserving {
|
|
41
|
+
_hasListeners = NO;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
- (void)startProgressTimer {
|
|
45
|
+
if (_progressTimer) {
|
|
46
|
+
[_progressTimer invalidate];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
_progressTimer = [NSTimer scheduledTimerWithTimeInterval:0.1
|
|
50
|
+
repeats:YES
|
|
51
|
+
block:^(NSTimer * _Nonnull timer) {
|
|
52
|
+
if (self->_hasListeners && self->_audioPlayer && self->_isPlaying) {
|
|
53
|
+
NSTimeInterval currentTime = self->_audioPlayer.currentTime;
|
|
54
|
+
NSTimeInterval duration = self->_audioPlayer.duration;
|
|
55
|
+
[self sendEventWithName:@"onPlaybackProgress"
|
|
56
|
+
body:@{
|
|
57
|
+
@"currentTime": @(currentTime * 1000),
|
|
58
|
+
@"duration": @(duration * 1000),
|
|
59
|
+
@"isPlaying": @(self->_audioPlayer.isPlaying)
|
|
60
|
+
}];
|
|
61
|
+
}
|
|
62
|
+
}];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
- (void)stopProgressTimer {
|
|
66
|
+
if (_progressTimer) {
|
|
67
|
+
[_progressTimer invalidate];
|
|
68
|
+
_progressTimer = nil;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
- (void)startPlaying:(NSDictionary *)options
|
|
73
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
74
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
75
|
+
@try {
|
|
76
|
+
NSString *filePath = options[@"filePath"];
|
|
77
|
+
if (!filePath) {
|
|
78
|
+
reject(@"INVALID_PATH", @"File path is required", nil);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
NSNumber *volume = options[@"volume"] ?: @(1.0);
|
|
83
|
+
NSNumber *loop = options[@"loop"] ?: @(NO);
|
|
84
|
+
|
|
85
|
+
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
|
|
86
|
+
NSError *error = nil;
|
|
87
|
+
|
|
88
|
+
// Setup audio session for playback
|
|
89
|
+
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
|
|
90
|
+
[audioSession setCategory:AVAudioSessionCategoryPlayback error:&error];
|
|
91
|
+
if (error) {
|
|
92
|
+
reject(@"AUDIO_SESSION_ERROR", error.description, error);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
[audioSession setActive:YES error:&error];
|
|
96
|
+
if (error) {
|
|
97
|
+
reject(@"AUDIO_SESSION_ERROR", error.description, error);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error];
|
|
102
|
+
if (error) {
|
|
103
|
+
reject(@"INIT_PLAYER_ERROR", error.description, error);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
_audioPlayer.delegate = self;
|
|
108
|
+
_audioPlayer.volume = [volume floatValue];
|
|
109
|
+
_audioPlayer.numberOfLoops = [loop boolValue] ? -1 : 0;
|
|
110
|
+
|
|
111
|
+
if (![_audioPlayer prepareToPlay]) {
|
|
112
|
+
reject(@"PREPARE_ERROR", @"Failed to prepare audio player", nil);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (![_audioPlayer play]) {
|
|
117
|
+
reject(@"START_PLAYING_ERROR", @"Failed to start playing", nil);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
_isPlaying = YES;
|
|
122
|
+
_currentFilePath = filePath;
|
|
123
|
+
[self startProgressTimer];
|
|
124
|
+
resolve(nil);
|
|
125
|
+
} @catch (NSException *exception) {
|
|
126
|
+
reject(@"START_PLAYING_ERROR", exception.reason, nil);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
- (void)stopPlaying:(RCTPromiseResolveBlock)resolve
|
|
131
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
132
|
+
@try {
|
|
133
|
+
[self stopProgressTimer];
|
|
134
|
+
|
|
135
|
+
if (_audioPlayer) {
|
|
136
|
+
[_audioPlayer stop];
|
|
137
|
+
_audioPlayer = nil;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
_isPlaying = NO;
|
|
141
|
+
_currentFilePath = nil;
|
|
142
|
+
resolve(nil);
|
|
143
|
+
} @catch (NSException *exception) {
|
|
144
|
+
reject(@"STOP_PLAYING_ERROR", exception.reason, nil);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
- (void)pausePlaying:(RCTPromiseResolveBlock)resolve
|
|
149
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
150
|
+
@try {
|
|
151
|
+
if (!_isPlaying || !_audioPlayer) {
|
|
152
|
+
reject(@"NOT_PLAYING", @"No playback in progress", nil);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
[_audioPlayer pause];
|
|
157
|
+
_isPlaying = NO;
|
|
158
|
+
resolve(nil);
|
|
159
|
+
} @catch (NSException *exception) {
|
|
160
|
+
reject(@"PAUSE_ERROR", exception.reason, nil);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
- (void)resumePlaying:(RCTPromiseResolveBlock)resolve
|
|
165
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
166
|
+
@try {
|
|
167
|
+
if (!_audioPlayer || _isPlaying) {
|
|
168
|
+
reject(@"NOT_PAUSED", @"Playback is not paused", nil);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
[_audioPlayer play];
|
|
173
|
+
_isPlaying = YES;
|
|
174
|
+
resolve(nil);
|
|
175
|
+
} @catch (NSException *exception) {
|
|
176
|
+
reject(@"RESUME_ERROR", exception.reason, nil);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
- (void)seekToTime:(double)time
|
|
181
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
182
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
183
|
+
@try {
|
|
184
|
+
if (!_audioPlayer) {
|
|
185
|
+
reject(@"NO_PLAYER", @"No audio player initialized", nil);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
_audioPlayer.currentTime = time;
|
|
190
|
+
resolve(nil);
|
|
191
|
+
} @catch (NSException *exception) {
|
|
192
|
+
reject(@"SEEK_ERROR", exception.reason, nil);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
- (void)setVolume:(double)volume
|
|
197
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
198
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
199
|
+
@try {
|
|
200
|
+
if (!_audioPlayer) {
|
|
201
|
+
reject(@"NO_PLAYER", @"No audio player initialized", nil);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
_audioPlayer.volume = volume;
|
|
206
|
+
resolve(nil);
|
|
207
|
+
} @catch (NSException *exception) {
|
|
208
|
+
reject(@"SET_VOLUME_ERROR", exception.reason, nil);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
- (void)getPlayerStatus:(RCTPromiseResolveBlock)resolve
|
|
213
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
214
|
+
@try {
|
|
215
|
+
NSMutableDictionary *status = [NSMutableDictionary dictionary];
|
|
216
|
+
status[@"isPlaying"] = @(_isPlaying);
|
|
217
|
+
|
|
218
|
+
if (_audioPlayer) {
|
|
219
|
+
status[@"duration"] = @(_audioPlayer.duration * 1000);
|
|
220
|
+
status[@"currentTime"] = @(_audioPlayer.currentTime * 1000);
|
|
221
|
+
} else {
|
|
222
|
+
status[@"duration"] = @(0);
|
|
223
|
+
status[@"currentTime"] = @(0);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (_currentFilePath) {
|
|
227
|
+
status[@"currentFilePath"] = _currentFilePath;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
resolve(status);
|
|
231
|
+
} @catch (NSException *exception) {
|
|
232
|
+
reject(@"STATUS_ERROR", exception.reason, nil);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
#pragma mark - AVAudioPlayerDelegate
|
|
237
|
+
|
|
238
|
+
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player
|
|
239
|
+
successfully:(BOOL)flag {
|
|
240
|
+
_isPlaying = NO;
|
|
241
|
+
[self stopProgressTimer];
|
|
242
|
+
|
|
243
|
+
if (_hasListeners) {
|
|
244
|
+
[self sendEventWithName:@"onPlaybackComplete" body:@{}];
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player
|
|
249
|
+
error:(NSError *)error {
|
|
250
|
+
_isPlaying = NO;
|
|
251
|
+
[self stopProgressTimer];
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
255
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
256
|
+
return std::make_shared<facebook::react::NativeNosniaAudioPlayerSpecJSI>(params);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
@end
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
#ifndef __NOSNIA_AUDIO_RECORDER_MM__
|
|
2
|
-
#define __NOSNIA_AUDIO_RECORDER_MM__
|
|
3
|
-
|
|
4
1
|
#import "NosniaAudioRecorder.h"
|
|
5
2
|
#import <AVFoundation/AVFoundation.h>
|
|
6
3
|
#import <React/RCTBridgeModule.h>
|
|
@@ -499,5 +496,3 @@ RCT_EXPORT_MODULE(NosniaAudioRecorder)
|
|
|
499
496
|
}
|
|
500
497
|
|
|
501
498
|
@end
|
|
502
|
-
|
|
503
|
-
#endif /* __NOSNIA_AUDIO_RECORDER_MM__ */
|
package/ios/Podfile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
post_install do |installer|
|
|
2
|
+
# Keep existing RN post install if present
|
|
3
|
+
begin
|
|
4
|
+
react_native_post_install(installer)
|
|
5
|
+
rescue NameError
|
|
6
|
+
# react_native_post_install not defined in some templates — ignore
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Allow non-modular includes in framework modules to avoid
|
|
10
|
+
# "non-modular-include-in-framework-module" compile errors coming from RN pods.
|
|
11
|
+
installer.pods_project.targets.each do |target|
|
|
12
|
+
target.build_configurations.each do |config|
|
|
13
|
+
config.build_settings['CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES'] = 'YES'
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|