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 +46 -2
- package/android/src/main/java/com/nosniaaudiorecorder/NosniaAudioPlayerModule.kt +238 -0
- package/android/src/main/java/com/nosniaaudiorecorder/NosniaAudioRecorderPackage.kt +12 -4
- package/ios/NosniaAudioPlayer.h +7 -0
- package/ios/NosniaAudioPlayer.mm +263 -0
- package/lib/module/AudioPlayer.js +176 -0
- package/lib/module/AudioPlayer.js.map +1 -0
- package/lib/module/NativeNosniaAudioPlayer.js +5 -0
- package/lib/module/NativeNosniaAudioPlayer.js.map +1 -0
- package/lib/module/index.js +3 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/AudioPlayer.d.ts +76 -0
- package/lib/typescript/src/AudioPlayer.d.ts.map +1 -0
- package/lib/typescript/src/NativeNosniaAudioPlayer.d.ts +24 -0
- package/lib/typescript/src/NativeNosniaAudioPlayer.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +19 -7
- package/src/AudioPlayer.tsx +201 -0
- package/src/NativeNosniaAudioPlayer.ts +26 -0
- package/src/index.tsx +9 -0
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
|
|
13
|
-
NosniaAudioRecorderModule(reactContext)
|
|
14
|
-
|
|
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,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 @@
|
|
|
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":[]}
|
package/lib/module/index.js
CHANGED
package/lib/module/index.js.map
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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';
|