nosnia-audio-recorder 0.9.14 → 0.9.17

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.
@@ -1,254 +1,254 @@
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
- @end
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
+ @end