nosnia-audio-recorder 0.2.0 β†’ 0.3.0

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/README.md CHANGED
@@ -9,6 +9,7 @@ A compact, high-performance audio recorder library for React Native that records
9
9
  - 🎚️ **Configurable**: Adjust bitrate, sample rate, and channels
10
10
  - ⏸️ **Pause/Resume**: Full control over recording with pause and resume
11
11
  - ⏱️ **Real-time Duration**: Get continuous recording duration updates via callback
12
+ - ▢️ **MP3 Playback**: Built-in audio player with play/pause/resume/stop/seek
12
13
  - πŸ”’ **Permission Handling**: Built-in permission checking and requesting
13
14
  - πŸ’Ύ **File Management**: Automatic file naming and directory management
14
15
  - πŸ“¦ **Compact**: Minimal dependencies, optimized for bundle size
@@ -49,10 +50,10 @@ const filePath = await NosniaAudioRecorder.stopRecording();
49
50
  console.log('Recording saved to:', filePath);
50
51
  ```
51
52
 
52
- ### Full Example
53
+ ### Full Example (Record + Playback)
53
54
 
54
55
  ```js
55
- import { NosniaAudioRecorder } from 'nosnia-audio-recorder';
56
+ import { NosniaAudioRecorder, NosniaAudioPlayer } from 'nosnia-audio-recorder';
56
57
  import { useState } from 'react';
57
58
 
58
59
  // Check and request permission
@@ -87,6 +88,16 @@ removeListener();
87
88
 
88
89
  // Or cancel (discard)
89
90
  await NosniaAudioRecorder.cancelRecording();
91
+
92
+ // Play it back
93
+ await NosniaAudioPlayer.startPlaying({ filePath });
94
+ // Optionally listen to progress
95
+ const removePlayback = NosniaAudioPlayer.addPlaybackProgressListener(({ currentTime, duration }) => {
96
+ console.log(`Progress: ${Math.floor(currentTime / 1000)} / ${Math.floor(duration / 1000)}s`);
97
+ });
98
+ // Stop playback
99
+ await NosniaAudioPlayer.stopPlaying();
100
+ removePlayback();
90
101
  ```
91
102
 
92
103
  ## API Reference
@@ -104,6 +115,20 @@ await NosniaAudioRecorder.cancelRecording();
104
115
  - `addRecordingProgressListener(callback: RecordingProgressCallback): () => void` - Add listener for duration updates (returns cleanup function)
105
116
  - `removeRecordingProgressListener(): void` - Remove duration listener
106
117
 
118
+ ### Player Methods
119
+
120
+ - `startPlaying(options: PlayOptions): Promise<void>` - Start playing an audio file
121
+ - `stopPlaying(): Promise<void>` - Stop playback and reset
122
+ - `pausePlaying(): Promise<void>` - Pause playback
123
+ - `resumePlaying(): Promise<void>` - Resume playback
124
+ - `seekToTime(time: number): Promise<void>` - Seek to time (seconds)
125
+ - `setVolume(volume: number): Promise<void>` - Set playback volume (0.0–1.0)
126
+ - `getPlayerStatus(): Promise<PlayerStatus>` - Get playback status
127
+ - `addPlaybackProgressListener(callback: PlaybackProgressCallback): () => void` - Listen to playback progress
128
+ - `addPlaybackCompleteListener(callback: () => void): () => void` - Listen for playback completion
129
+ - `removePlaybackProgressListener(): void` - Remove progress listener
130
+ - `removePlaybackCompleteListener(): void` - Remove completion listener
131
+
107
132
  ### Types
108
133
 
109
134
  ```ts
@@ -124,6 +149,25 @@ type RecordingProgressCallback = (data: {
124
149
  duration: number; // In milliseconds
125
150
  isRecording: boolean;
126
151
  }) => void;
152
+
153
+ interface PlayOptions {
154
+ filePath: string;
155
+ volume?: number; // 0.0 - 1.0
156
+ loop?: boolean; // repeat playback
157
+ }
158
+
159
+ interface PlayerStatus {
160
+ isPlaying: boolean;
161
+ duration: number; // In milliseconds
162
+ currentTime: number; // In milliseconds
163
+ currentFilePath?: string;
164
+ }
165
+
166
+ type PlaybackProgressCallback = (data: {
167
+ currentTime: number;
168
+ duration: number;
169
+ isPlaying: boolean;
170
+ }) => void;
127
171
  ```
128
172
 
129
173
  ## Platform Setup
@@ -0,0 +1,238 @@
1
+ package com.nosniaaudiorecorder
2
+
3
+ import android.media.MediaPlayer
4
+ import android.os.Handler
5
+ import android.os.Looper
6
+ import com.facebook.react.bridge.Promise
7
+ import com.facebook.react.bridge.ReactApplicationContext
8
+ import com.facebook.react.bridge.ReadableMap
9
+ import com.facebook.react.bridge.WritableMap
10
+ import com.facebook.react.bridge.WritableNativeMap
11
+ import com.facebook.react.bridge.Arguments
12
+ import com.facebook.react.modules.core.DeviceEventManagerModule
13
+ import com.facebook.react.module.annotations.ReactModule
14
+ import java.io.File
15
+
16
+ @ReactModule(name = NosniaAudioPlayerModule.NAME)
17
+ class NosniaAudioPlayerModule(reactContext: ReactApplicationContext) :
18
+ NativeNosniaAudioPlayerSpec(reactContext) {
19
+
20
+ private var mediaPlayer: MediaPlayer? = null
21
+ private var currentFilePath: String? = null
22
+ private var isPlaying = false
23
+
24
+ private val progressHandler = Handler(Looper.getMainLooper())
25
+ private val progressRunnable = object : Runnable {
26
+ override fun run() {
27
+ mediaPlayer?.let { player ->
28
+ if (isPlaying && player.isPlaying) {
29
+ val currentTime = player.currentPosition.toLong()
30
+ val duration = player.duration.toLong()
31
+
32
+ sendEvent("onPlaybackProgress", Arguments.createMap().apply {
33
+ putDouble("currentTime", currentTime.toDouble())
34
+ putDouble("duration", duration.toDouble())
35
+ putBoolean("isPlaying", true)
36
+ })
37
+
38
+ progressHandler.postDelayed(this, 100)
39
+ }
40
+ }
41
+ }
42
+ }
43
+
44
+ private fun sendEvent(eventName: String, params: WritableMap?) {
45
+ reactApplicationContext
46
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
47
+ .emit(eventName, params)
48
+ }
49
+
50
+ private fun startProgressUpdates() {
51
+ progressHandler.post(progressRunnable)
52
+ }
53
+
54
+ private fun stopProgressUpdates() {
55
+ progressHandler.removeCallbacks(progressRunnable)
56
+ }
57
+
58
+ override fun getName(): String {
59
+ return NAME
60
+ }
61
+
62
+ override fun startPlaying(options: ReadableMap, promise: Promise) {
63
+ try {
64
+ val filePath = options.getString("filePath")
65
+ if (filePath == null) {
66
+ promise.reject("INVALID_PATH", "File path is required")
67
+ return
68
+ }
69
+
70
+ val volume = if (options.hasKey("volume")) {
71
+ options.getDouble("volume").toFloat()
72
+ } else {
73
+ 1.0f
74
+ }
75
+
76
+ val loop = if (options.hasKey("loop")) {
77
+ options.getBoolean("loop")
78
+ } else {
79
+ false
80
+ }
81
+
82
+ // Check if file exists
83
+ val file = File(filePath)
84
+ if (!file.exists()) {
85
+ promise.reject("FILE_NOT_FOUND", "Audio file not found: $filePath")
86
+ return
87
+ }
88
+
89
+ // Release existing player if any
90
+ mediaPlayer?.release()
91
+
92
+ mediaPlayer = MediaPlayer().apply {
93
+ setDataSource(filePath)
94
+ setVolume(volume, volume)
95
+ isLooping = loop
96
+
97
+ setOnCompletionListener {
98
+ isPlaying = false
99
+ stopProgressUpdates()
100
+ sendEvent("onPlaybackComplete", Arguments.createMap())
101
+ }
102
+
103
+ setOnErrorListener { _, what, extra ->
104
+ isPlaying = false
105
+ stopProgressUpdates()
106
+ true
107
+ }
108
+
109
+ prepare()
110
+ start()
111
+ }
112
+
113
+ isPlaying = true
114
+ currentFilePath = filePath
115
+ startProgressUpdates()
116
+ promise.resolve(null)
117
+ } catch (e: Exception) {
118
+ mediaPlayer?.release()
119
+ mediaPlayer = null
120
+ isPlaying = false
121
+ promise.reject("START_PLAYING_ERROR", e.message, e)
122
+ }
123
+ }
124
+
125
+ override fun stopPlaying(promise: Promise) {
126
+ try {
127
+ stopProgressUpdates()
128
+
129
+ mediaPlayer?.apply {
130
+ if (isPlaying) {
131
+ stop()
132
+ }
133
+ release()
134
+ }
135
+ mediaPlayer = null
136
+ isPlaying = false
137
+ currentFilePath = null
138
+
139
+ promise.resolve(null)
140
+ } catch (e: Exception) {
141
+ promise.reject("STOP_PLAYING_ERROR", e.message, e)
142
+ }
143
+ }
144
+
145
+ override fun pausePlaying(promise: Promise) {
146
+ try {
147
+ if (!isPlaying || mediaPlayer == null) {
148
+ promise.reject("NOT_PLAYING", "No playback in progress")
149
+ return
150
+ }
151
+
152
+ mediaPlayer?.pause()
153
+ isPlaying = false
154
+ promise.resolve(null)
155
+ } catch (e: Exception) {
156
+ promise.reject("PAUSE_ERROR", e.message, e)
157
+ }
158
+ }
159
+
160
+ override fun resumePlaying(promise: Promise) {
161
+ try {
162
+ if (isPlaying || mediaPlayer == null) {
163
+ promise.reject("NOT_PAUSED", "Playback is not paused")
164
+ return
165
+ }
166
+
167
+ mediaPlayer?.start()
168
+ isPlaying = true
169
+ promise.resolve(null)
170
+ } catch (e: Exception) {
171
+ promise.reject("RESUME_ERROR", e.message, e)
172
+ }
173
+ }
174
+
175
+ override fun seekToTime(time: Double, promise: Promise) {
176
+ try {
177
+ if (mediaPlayer == null) {
178
+ promise.reject("NO_PLAYER", "No audio player initialized")
179
+ return
180
+ }
181
+
182
+ val timeMs = (time * 1000).toInt()
183
+ mediaPlayer?.seekTo(timeMs)
184
+ promise.resolve(null)
185
+ } catch (e: Exception) {
186
+ promise.reject("SEEK_ERROR", e.message, e)
187
+ }
188
+ }
189
+
190
+ override fun setVolume(volume: Double, promise: Promise) {
191
+ try {
192
+ if (mediaPlayer == null) {
193
+ promise.reject("NO_PLAYER", "No audio player initialized")
194
+ return
195
+ }
196
+
197
+ val volumeFloat = volume.toFloat()
198
+ mediaPlayer?.setVolume(volumeFloat, volumeFloat)
199
+ promise.resolve(null)
200
+ } catch (e: Exception) {
201
+ promise.reject("SET_VOLUME_ERROR", e.message, e)
202
+ }
203
+ }
204
+
205
+ override fun getPlayerStatus(promise: Promise) {
206
+ try {
207
+ val status = WritableNativeMap().apply {
208
+ putBoolean("isPlaying", isPlaying)
209
+
210
+ if (mediaPlayer != null) {
211
+ putDouble("duration", mediaPlayer!!.duration.toDouble())
212
+ putDouble("currentTime", mediaPlayer!!.currentPosition.toDouble())
213
+ } else {
214
+ putDouble("duration", 0.0)
215
+ putDouble("currentTime", 0.0)
216
+ }
217
+
218
+ if (currentFilePath != null) {
219
+ putString("currentFilePath", currentFilePath)
220
+ }
221
+ }
222
+ promise.resolve(status)
223
+ } catch (e: Exception) {
224
+ promise.reject("STATUS_ERROR", e.message, e)
225
+ }
226
+ }
227
+
228
+ override fun onCatalystInstanceDestroy() {
229
+ super.onCatalystInstanceDestroy()
230
+ stopProgressUpdates()
231
+ mediaPlayer?.release()
232
+ mediaPlayer = null
233
+ }
234
+
235
+ companion object {
236
+ const val NAME = "NosniaAudioPlayer"
237
+ }
238
+ }
@@ -9,10 +9,10 @@ import java.util.HashMap
9
9
 
10
10
  class NosniaAudioRecorderPackage : BaseReactPackage() {
11
11
  override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
12
- return if (name == NosniaAudioRecorderModule.NAME) {
13
- NosniaAudioRecorderModule(reactContext)
14
- } else {
15
- null
12
+ return when (name) {
13
+ NosniaAudioRecorderModule.NAME -> NosniaAudioRecorderModule(reactContext)
14
+ NosniaAudioPlayerModule.NAME -> NosniaAudioPlayerModule(reactContext)
15
+ else -> null
16
16
  }
17
17
  }
18
18
 
@@ -27,6 +27,14 @@ class NosniaAudioRecorderPackage : BaseReactPackage() {
27
27
  false, // isCxxModule
28
28
  true // isTurboModule
29
29
  )
30
+ moduleInfos[NosniaAudioPlayerModule.NAME] = ReactModuleInfo(
31
+ NosniaAudioPlayerModule.NAME,
32
+ NosniaAudioPlayerModule.NAME,
33
+ false, // canOverrideExistingModule
34
+ false, // needsEagerInit
35
+ false, // isCxxModule
36
+ true // isTurboModule
37
+ )
30
38
  moduleInfos
31
39
  }
32
40
  }
@@ -0,0 +1,7 @@
1
+ #import <NosniaAudioPlayerSpec/NosniaAudioPlayerSpec.h>
2
+ #import <React/RCTBridgeModule.h>
3
+ #import <React/RCTEventEmitter.h>
4
+
5
+ @interface NosniaAudioPlayer : RCTEventEmitter <NativeNosniaAudioPlayerSpec>
6
+
7
+ @end
@@ -0,0 +1,263 @@
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
+ + (NSString *)moduleName {
260
+ return @"NosniaAudioPlayer";
261
+ }
262
+
263
+ @end
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+
3
+ import { NativeEventEmitter, NativeModules } from 'react-native';
4
+ import NativeModule from "./NativeNosniaAudioPlayer.js";
5
+ class AudioPlayer {
6
+ static instance = null;
7
+ progressListener = null;
8
+ completeListener = null;
9
+ constructor() {
10
+ this.eventEmitter = new NativeEventEmitter(NativeModules.NosniaAudioPlayer);
11
+ }
12
+ static getInstance() {
13
+ if (!AudioPlayer.instance) {
14
+ AudioPlayer.instance = new AudioPlayer();
15
+ }
16
+ return AudioPlayer.instance;
17
+ }
18
+
19
+ /**
20
+ * Add a listener for playback progress updates
21
+ * @param callback Function called with playback progress (every 100ms during playback)
22
+ * @returns Function to remove the listener
23
+ */
24
+ addPlaybackProgressListener(callback) {
25
+ if (this.progressListener) {
26
+ this.progressListener.remove();
27
+ }
28
+ this.progressListener = this.eventEmitter.addListener('onPlaybackProgress', data => callback(data));
29
+ return () => {
30
+ if (this.progressListener) {
31
+ this.progressListener.remove();
32
+ this.progressListener = null;
33
+ }
34
+ };
35
+ }
36
+
37
+ /**
38
+ * Remove the playback progress listener
39
+ */
40
+ removePlaybackProgressListener() {
41
+ if (this.progressListener) {
42
+ this.progressListener.remove();
43
+ this.progressListener = null;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Add a listener for playback completion
49
+ * @param callback Function called when playback completes
50
+ * @returns Function to remove the listener
51
+ */
52
+ addPlaybackCompleteListener(callback) {
53
+ if (this.completeListener) {
54
+ this.completeListener.remove();
55
+ }
56
+ this.completeListener = this.eventEmitter.addListener('onPlaybackComplete', callback);
57
+ return () => {
58
+ if (this.completeListener) {
59
+ this.completeListener.remove();
60
+ this.completeListener = null;
61
+ }
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Remove the playback complete listener
67
+ */
68
+ removePlaybackCompleteListener() {
69
+ if (this.completeListener) {
70
+ this.completeListener.remove();
71
+ this.completeListener = null;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Start playing audio from a file path
77
+ * @param options Configuration options (filePath, volume, loop)
78
+ * @returns Promise that resolves when playback starts
79
+ */
80
+ async startPlaying(options) {
81
+ try {
82
+ const config = {
83
+ volume: 1.0,
84
+ loop: false,
85
+ ...options
86
+ };
87
+ return await NativeModule.startPlaying(config);
88
+ } catch (error) {
89
+ console.error('Error starting playback:', error);
90
+ throw error;
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Stop playing and reset player
96
+ * @returns Promise that resolves when playback stops
97
+ */
98
+ async stopPlaying() {
99
+ try {
100
+ return await NativeModule.stopPlaying();
101
+ } catch (error) {
102
+ console.error('Error stopping playback:', error);
103
+ throw error;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Pause the current playback
109
+ * @returns Promise that resolves when playback is paused
110
+ */
111
+ async pausePlaying() {
112
+ try {
113
+ return await NativeModule.pausePlaying();
114
+ } catch (error) {
115
+ console.error('Error pausing playback:', error);
116
+ throw error;
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Resume a paused playback
122
+ * @returns Promise that resolves when playback resumes
123
+ */
124
+ async resumePlaying() {
125
+ try {
126
+ return await NativeModule.resumePlaying();
127
+ } catch (error) {
128
+ console.error('Error resuming playback:', error);
129
+ throw error;
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Seek to a specific time in the audio
135
+ * @param time Time in seconds
136
+ * @returns Promise that resolves when seek completes
137
+ */
138
+ async seekToTime(time) {
139
+ try {
140
+ return await NativeModule.seekToTime(time);
141
+ } catch (error) {
142
+ console.error('Error seeking:', error);
143
+ throw error;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Set the playback volume
149
+ * @param volume Volume level (0.0 to 1.0)
150
+ * @returns Promise that resolves when volume is set
151
+ */
152
+ async setVolume(volume) {
153
+ try {
154
+ const clampedVolume = Math.max(0, Math.min(1, volume));
155
+ return await NativeModule.setVolume(clampedVolume);
156
+ } catch (error) {
157
+ console.error('Error setting volume:', error);
158
+ throw error;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Get the current status of the player
164
+ * @returns Promise that resolves to the current player status
165
+ */
166
+ async getStatus() {
167
+ try {
168
+ return await NativeModule.getPlayerStatus();
169
+ } catch (error) {
170
+ console.error('Error getting player status:', error);
171
+ throw error;
172
+ }
173
+ }
174
+ }
175
+ export const NosniaAudioPlayer = AudioPlayer.getInstance();
176
+ //# sourceMappingURL=AudioPlayer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NativeEventEmitter","NativeModules","NativeModule","AudioPlayer","instance","progressListener","completeListener","constructor","eventEmitter","NosniaAudioPlayer","getInstance","addPlaybackProgressListener","callback","remove","addListener","data","removePlaybackProgressListener","addPlaybackCompleteListener","removePlaybackCompleteListener","startPlaying","options","config","volume","loop","error","console","stopPlaying","pausePlaying","resumePlaying","seekToTime","time","setVolume","clampedVolume","Math","max","min","getStatus","getPlayerStatus"],"sourceRoot":"..\\..\\src","sources":["AudioPlayer.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ import { TurboModuleRegistry } from 'react-native';
4
+ export default TurboModuleRegistry.getEnforcing('NosniaAudioPlayer');
5
+ //# sourceMappingURL=NativeNosniaAudioPlayer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"..\\..\\src","sources":["NativeNosniaAudioPlayer.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAyBpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,mBAAmB,CAAC","ignoreList":[]}
@@ -163,4 +163,7 @@ class AudioRecorder {
163
163
  }
164
164
  }
165
165
  export const NosniaAudioRecorder = AudioRecorder.getInstance();
166
+
167
+ // Export AudioPlayer
168
+ export { NosniaAudioPlayer } from "./AudioPlayer.js";
166
169
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["NativeEventEmitter","NativeModules","NativeModule","AudioRecorder","instance","progressListener","constructor","eventEmitter","NosniaAudioRecorder","getInstance","addRecordingProgressListener","callback","remove","addListener","data","removeRecordingProgressListener","requestPermission","requestAudioPermission","error","console","checkPermission","checkAudioPermission","startRecording","options","hasPermission","Error","config","bitrate","channels","sampleRate","stopRecording","pauseRecording","resumeRecording","cancelRecording","getStatus","getRecorderStatus"],"sourceRoot":"..\\..\\src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["NativeEventEmitter","NativeModules","NativeModule","AudioRecorder","instance","progressListener","constructor","eventEmitter","NosniaAudioRecorder","getInstance","addRecordingProgressListener","callback","remove","addListener","data","removeRecordingProgressListener","requestPermission","requestAudioPermission","error","console","checkPermission","checkAudioPermission","startRecording","options","hasPermission","Error","config","bitrate","channels","sampleRate","stopRecording","pauseRecording","resumeRecording","cancelRecording","getStatus","getRecorderStatus","NosniaAudioPlayer"],"sourceRoot":"..\\..\\src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,76 @@
1
+ import { type PlayOptions, type PlayerStatus } from './NativeNosniaAudioPlayer';
2
+ export type { PlayOptions, PlayerStatus };
3
+ export type PlaybackProgressCallback = (data: {
4
+ currentTime: number;
5
+ duration: number;
6
+ isPlaying: boolean;
7
+ }) => void;
8
+ export type PlaybackCompleteCallback = () => void;
9
+ declare class AudioPlayer {
10
+ private static instance;
11
+ private eventEmitter;
12
+ private progressListener;
13
+ private completeListener;
14
+ private constructor();
15
+ static getInstance(): AudioPlayer;
16
+ /**
17
+ * Add a listener for playback progress updates
18
+ * @param callback Function called with playback progress (every 100ms during playback)
19
+ * @returns Function to remove the listener
20
+ */
21
+ addPlaybackProgressListener(callback: PlaybackProgressCallback): () => void;
22
+ /**
23
+ * Remove the playback progress listener
24
+ */
25
+ removePlaybackProgressListener(): void;
26
+ /**
27
+ * Add a listener for playback completion
28
+ * @param callback Function called when playback completes
29
+ * @returns Function to remove the listener
30
+ */
31
+ addPlaybackCompleteListener(callback: PlaybackCompleteCallback): () => void;
32
+ /**
33
+ * Remove the playback complete listener
34
+ */
35
+ removePlaybackCompleteListener(): void;
36
+ /**
37
+ * Start playing audio from a file path
38
+ * @param options Configuration options (filePath, volume, loop)
39
+ * @returns Promise that resolves when playback starts
40
+ */
41
+ startPlaying(options: PlayOptions): Promise<void>;
42
+ /**
43
+ * Stop playing and reset player
44
+ * @returns Promise that resolves when playback stops
45
+ */
46
+ stopPlaying(): Promise<void>;
47
+ /**
48
+ * Pause the current playback
49
+ * @returns Promise that resolves when playback is paused
50
+ */
51
+ pausePlaying(): Promise<void>;
52
+ /**
53
+ * Resume a paused playback
54
+ * @returns Promise that resolves when playback resumes
55
+ */
56
+ resumePlaying(): Promise<void>;
57
+ /**
58
+ * Seek to a specific time in the audio
59
+ * @param time Time in seconds
60
+ * @returns Promise that resolves when seek completes
61
+ */
62
+ seekToTime(time: number): Promise<void>;
63
+ /**
64
+ * Set the playback volume
65
+ * @param volume Volume level (0.0 to 1.0)
66
+ * @returns Promise that resolves when volume is set
67
+ */
68
+ setVolume(volume: number): Promise<void>;
69
+ /**
70
+ * Get the current status of the player
71
+ * @returns Promise that resolves to the current player status
72
+ */
73
+ getStatus(): Promise<PlayerStatus>;
74
+ }
75
+ export declare const NosniaAudioPlayer: AudioPlayer;
76
+ //# sourceMappingURL=AudioPlayer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioPlayer.d.ts","sourceRoot":"","sources":["../../../src/AudioPlayer.tsx"],"names":[],"mappings":"AACA,OAAqB,EACnB,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC;AAE1C,MAAM,MAAM,wBAAwB,GAAG,CAAC,IAAI,EAAE;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB,KAAK,IAAI,CAAC;AAEX,MAAM,MAAM,wBAAwB,GAAG,MAAM,IAAI,CAAC;AAElD,cAAM,WAAW;IACf,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA4B;IACnD,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,gBAAgB,CAAa;IAErC,OAAO;IAIP,MAAM,CAAC,WAAW,IAAI,WAAW;IAOjC;;;;OAIG;IACH,2BAA2B,CAAC,QAAQ,EAAE,wBAAwB,GAAG,MAAM,IAAI;IAkB3E;;OAEG;IACH,8BAA8B,IAAI,IAAI;IAOtC;;;;OAIG;IACH,2BAA2B,CAAC,QAAQ,EAAE,wBAAwB,GAAG,MAAM,IAAI;IAkB3E;;OAEG;IACH,8BAA8B,IAAI,IAAI;IAOtC;;;;OAIG;IACG,YAAY,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAcvD;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IASlC;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IASnC;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IASpC;;;;OAIG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS7C;;;;OAIG;IACG,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU9C;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC;CAQzC;AAED,eAAO,MAAM,iBAAiB,aAA4B,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { type TurboModule } from 'react-native';
2
+ export interface PlayerStatus {
3
+ isPlaying: boolean;
4
+ duration: number;
5
+ currentTime: number;
6
+ currentFilePath?: string;
7
+ }
8
+ export interface PlayOptions {
9
+ filePath: string;
10
+ volume?: number;
11
+ loop?: boolean;
12
+ }
13
+ export interface Spec extends TurboModule {
14
+ startPlaying(options: PlayOptions): Promise<void>;
15
+ stopPlaying(): Promise<void>;
16
+ pausePlaying(): Promise<void>;
17
+ resumePlaying(): Promise<void>;
18
+ seekToTime(time: number): Promise<void>;
19
+ setVolume(volume: number): Promise<void>;
20
+ getPlayerStatus(): Promise<PlayerStatus>;
21
+ }
22
+ declare const _default: Spec;
23
+ export default _default;
24
+ //# sourceMappingURL=NativeNosniaAudioPlayer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeNosniaAudioPlayer.d.ts","sourceRoot":"","sources":["../../../src/NativeNosniaAudioPlayer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,YAAY,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,eAAe,IAAI,OAAO,CAAC,YAAY,CAAC,CAAC;CAC1C;;AAED,wBAA2E"}
@@ -63,4 +63,5 @@ declare class AudioRecorder {
63
63
  getStatus(): Promise<RecorderStatus>;
64
64
  }
65
65
  export declare const NosniaAudioRecorder: AudioRecorder;
66
+ export { NosniaAudioPlayer, type PlayOptions, type PlayerStatus, type PlaybackProgressCallback, type PlaybackCompleteCallback, } from './AudioPlayer';
66
67
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAqB,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,6BAA6B,CAAC;AAErC,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC;AAEhD,MAAM,MAAM,yBAAyB,GAAG,CAAC,IAAI,EAAE;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;CACtB,KAAK,IAAI,CAAC;AAEX,cAAM,aAAa;IACjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA8B;IACrD,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,gBAAgB,CAAa;IAErC,OAAO;IAMP,MAAM,CAAC,WAAW,IAAI,aAAa;IAOnC;;;;OAIG;IACH,4BAA4B,CAC1B,QAAQ,EAAE,yBAAyB,GAClC,MAAM,IAAI;IAmBb;;OAEG;IACH,+BAA+B,IAAI,IAAI;IAOvC;;;OAGG;IACG,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC;IAS3C;;;OAGG;IACG,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IASzC;;;;OAIG;IACG,cAAc,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB9D;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAStC;;;OAGG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IASrC;;;OAGG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAStC;;;OAGG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAStC;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,cAAc,CAAC;CAQ3C;AAED,eAAO,MAAM,mBAAmB,eAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAqB,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,6BAA6B,CAAC;AAErC,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC;AAEhD,MAAM,MAAM,yBAAyB,GAAG,CAAC,IAAI,EAAE;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;CACtB,KAAK,IAAI,CAAC;AAEX,cAAM,aAAa;IACjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA8B;IACrD,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,gBAAgB,CAAa;IAErC,OAAO;IAMP,MAAM,CAAC,WAAW,IAAI,aAAa;IAOnC;;;;OAIG;IACH,4BAA4B,CAC1B,QAAQ,EAAE,yBAAyB,GAClC,MAAM,IAAI;IAmBb;;OAEG;IACH,+BAA+B,IAAI,IAAI;IAOvC;;;OAGG;IACG,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC;IAS3C;;;OAGG;IACG,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IASzC;;;;OAIG;IACG,cAAc,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB9D;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAStC;;;OAGG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IASrC;;;OAGG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAStC;;;OAGG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAStC;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,cAAc,CAAC;CAQ3C;AAED,eAAO,MAAM,mBAAmB,eAA8B,CAAC;AAG/D,OAAO,EACL,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,GAC9B,MAAM,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nosnia-audio-recorder",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "This is a modern audio recorder which actually works cross platform",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -111,12 +111,24 @@
111
111
  ]
112
112
  },
113
113
  "codegenConfig": {
114
- "name": "NosniaAudioRecorderSpec",
115
- "type": "modules",
116
- "jsSrcsDir": "src",
117
- "android": {
118
- "javaPackageName": "com.nosniaaudiorecorder"
119
- }
114
+ "libraries": [
115
+ {
116
+ "name": "NosniaAudioRecorderSpec",
117
+ "type": "modules",
118
+ "jsSrcsDir": "src",
119
+ "android": {
120
+ "javaPackageName": "com.nosniaaudiorecorder"
121
+ }
122
+ },
123
+ {
124
+ "name": "NosniaAudioPlayerSpec",
125
+ "type": "modules",
126
+ "jsSrcsDir": "src",
127
+ "android": {
128
+ "javaPackageName": "com.nosniaaudiorecorder"
129
+ }
130
+ }
131
+ ]
120
132
  },
121
133
  "prettier": {
122
134
  "quoteProps": "consistent",
@@ -0,0 +1,201 @@
1
+ import { NativeEventEmitter, NativeModules } from 'react-native';
2
+ import NativeModule, {
3
+ type PlayOptions,
4
+ type PlayerStatus,
5
+ } from './NativeNosniaAudioPlayer';
6
+
7
+ export type { PlayOptions, PlayerStatus };
8
+
9
+ export type PlaybackProgressCallback = (data: {
10
+ currentTime: number;
11
+ duration: number;
12
+ isPlaying: boolean;
13
+ }) => void;
14
+
15
+ export type PlaybackCompleteCallback = () => void;
16
+
17
+ class AudioPlayer {
18
+ private static instance: AudioPlayer | null = null;
19
+ private eventEmitter: NativeEventEmitter;
20
+ private progressListener: any = null;
21
+ private completeListener: any = null;
22
+
23
+ private constructor() {
24
+ this.eventEmitter = new NativeEventEmitter(NativeModules.NosniaAudioPlayer);
25
+ }
26
+
27
+ static getInstance(): AudioPlayer {
28
+ if (!AudioPlayer.instance) {
29
+ AudioPlayer.instance = new AudioPlayer();
30
+ }
31
+ return AudioPlayer.instance;
32
+ }
33
+
34
+ /**
35
+ * Add a listener for playback progress updates
36
+ * @param callback Function called with playback progress (every 100ms during playback)
37
+ * @returns Function to remove the listener
38
+ */
39
+ addPlaybackProgressListener(callback: PlaybackProgressCallback): () => void {
40
+ if (this.progressListener) {
41
+ this.progressListener.remove();
42
+ }
43
+
44
+ this.progressListener = this.eventEmitter.addListener(
45
+ 'onPlaybackProgress',
46
+ (data: any) => callback(data)
47
+ );
48
+
49
+ return () => {
50
+ if (this.progressListener) {
51
+ this.progressListener.remove();
52
+ this.progressListener = null;
53
+ }
54
+ };
55
+ }
56
+
57
+ /**
58
+ * Remove the playback progress listener
59
+ */
60
+ removePlaybackProgressListener(): void {
61
+ if (this.progressListener) {
62
+ this.progressListener.remove();
63
+ this.progressListener = null;
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Add a listener for playback completion
69
+ * @param callback Function called when playback completes
70
+ * @returns Function to remove the listener
71
+ */
72
+ addPlaybackCompleteListener(callback: PlaybackCompleteCallback): () => void {
73
+ if (this.completeListener) {
74
+ this.completeListener.remove();
75
+ }
76
+
77
+ this.completeListener = this.eventEmitter.addListener(
78
+ 'onPlaybackComplete',
79
+ callback
80
+ );
81
+
82
+ return () => {
83
+ if (this.completeListener) {
84
+ this.completeListener.remove();
85
+ this.completeListener = null;
86
+ }
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Remove the playback complete listener
92
+ */
93
+ removePlaybackCompleteListener(): void {
94
+ if (this.completeListener) {
95
+ this.completeListener.remove();
96
+ this.completeListener = null;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Start playing audio from a file path
102
+ * @param options Configuration options (filePath, volume, loop)
103
+ * @returns Promise that resolves when playback starts
104
+ */
105
+ async startPlaying(options: PlayOptions): Promise<void> {
106
+ try {
107
+ const config: PlayOptions = {
108
+ volume: 1.0,
109
+ loop: false,
110
+ ...options,
111
+ };
112
+ return await NativeModule.startPlaying(config);
113
+ } catch (error) {
114
+ console.error('Error starting playback:', error);
115
+ throw error;
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Stop playing and reset player
121
+ * @returns Promise that resolves when playback stops
122
+ */
123
+ async stopPlaying(): Promise<void> {
124
+ try {
125
+ return await NativeModule.stopPlaying();
126
+ } catch (error) {
127
+ console.error('Error stopping playback:', error);
128
+ throw error;
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Pause the current playback
134
+ * @returns Promise that resolves when playback is paused
135
+ */
136
+ async pausePlaying(): Promise<void> {
137
+ try {
138
+ return await NativeModule.pausePlaying();
139
+ } catch (error) {
140
+ console.error('Error pausing playback:', error);
141
+ throw error;
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Resume a paused playback
147
+ * @returns Promise that resolves when playback resumes
148
+ */
149
+ async resumePlaying(): Promise<void> {
150
+ try {
151
+ return await NativeModule.resumePlaying();
152
+ } catch (error) {
153
+ console.error('Error resuming playback:', error);
154
+ throw error;
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Seek to a specific time in the audio
160
+ * @param time Time in seconds
161
+ * @returns Promise that resolves when seek completes
162
+ */
163
+ async seekToTime(time: number): Promise<void> {
164
+ try {
165
+ return await NativeModule.seekToTime(time);
166
+ } catch (error) {
167
+ console.error('Error seeking:', error);
168
+ throw error;
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Set the playback volume
174
+ * @param volume Volume level (0.0 to 1.0)
175
+ * @returns Promise that resolves when volume is set
176
+ */
177
+ async setVolume(volume: number): Promise<void> {
178
+ try {
179
+ const clampedVolume = Math.max(0, Math.min(1, volume));
180
+ return await NativeModule.setVolume(clampedVolume);
181
+ } catch (error) {
182
+ console.error('Error setting volume:', error);
183
+ throw error;
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Get the current status of the player
189
+ * @returns Promise that resolves to the current player status
190
+ */
191
+ async getStatus(): Promise<PlayerStatus> {
192
+ try {
193
+ return await NativeModule.getPlayerStatus();
194
+ } catch (error) {
195
+ console.error('Error getting player status:', error);
196
+ throw error;
197
+ }
198
+ }
199
+ }
200
+
201
+ export const NosniaAudioPlayer = AudioPlayer.getInstance();
@@ -0,0 +1,26 @@
1
+ import { TurboModuleRegistry, type TurboModule } from 'react-native';
2
+
3
+ export interface PlayerStatus {
4
+ isPlaying: boolean;
5
+ duration: number;
6
+ currentTime: number;
7
+ currentFilePath?: string;
8
+ }
9
+
10
+ export interface PlayOptions {
11
+ filePath: string;
12
+ volume?: number; // 0.0 to 1.0
13
+ loop?: boolean;
14
+ }
15
+
16
+ export interface Spec extends TurboModule {
17
+ startPlaying(options: PlayOptions): Promise<void>;
18
+ stopPlaying(): Promise<void>;
19
+ pausePlaying(): Promise<void>;
20
+ resumePlaying(): Promise<void>;
21
+ seekToTime(time: number): Promise<void>;
22
+ setVolume(volume: number): Promise<void>;
23
+ getPlayerStatus(): Promise<PlayerStatus>;
24
+ }
25
+
26
+ export default TurboModuleRegistry.getEnforcing<Spec>('NosniaAudioPlayer');
package/src/index.tsx CHANGED
@@ -186,3 +186,12 @@ class AudioRecorder {
186
186
  }
187
187
 
188
188
  export const NosniaAudioRecorder = AudioRecorder.getInstance();
189
+
190
+ // Export AudioPlayer
191
+ export {
192
+ NosniaAudioPlayer,
193
+ type PlayOptions,
194
+ type PlayerStatus,
195
+ type PlaybackProgressCallback,
196
+ type PlaybackCompleteCallback,
197
+ } from './AudioPlayer';