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