nosnia-audio-recorder 0.1.1 β†’ 0.2.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
@@ -8,6 +8,7 @@ A compact, high-performance audio recorder library for React Native that records
8
8
  - πŸ“± **Cross-Platform**: Works seamlessly on iOS and Android
9
9
  - 🎚️ **Configurable**: Adjust bitrate, sample rate, and channels
10
10
  - ⏸️ **Pause/Resume**: Full control over recording with pause and resume
11
+ - ⏱️ **Real-time Duration**: Get continuous recording duration updates via callback
11
12
  - πŸ”’ **Permission Handling**: Built-in permission checking and requesting
12
13
  - πŸ’Ύ **File Management**: Automatic file naming and directory management
13
14
  - πŸ“¦ **Compact**: Minimal dependencies, optimized for bundle size
@@ -52,6 +53,7 @@ console.log('Recording saved to:', filePath);
52
53
 
53
54
  ```js
54
55
  import { NosniaAudioRecorder } from 'nosnia-audio-recorder';
56
+ import { useState } from 'react';
55
57
 
56
58
  // Check and request permission
57
59
  const hasPermission = await NosniaAudioRecorder.checkPermission();
@@ -59,9 +61,16 @@ if (!hasPermission) {
59
61
  await NosniaAudioRecorder.requestPermission();
60
62
  }
61
63
 
62
- // Start recording
64
+ // Start recording with real-time duration updates
63
65
  await NosniaAudioRecorder.startRecording();
64
66
 
67
+ // Add listener for recording duration updates (every 100ms)
68
+ const removeListener = NosniaAudioRecorder.addRecordingProgressListener(
69
+ ({ duration, isRecording }) => {
70
+ console.log(`Recording ${Math.floor(duration / 1000)}s`);
71
+ }
72
+ );
73
+
65
74
  // Pause if needed
66
75
  await NosniaAudioRecorder.pauseRecording();
67
76
  await NosniaAudioRecorder.resumeRecording();
@@ -73,6 +82,9 @@ console.log(`Recording: ${status.isRecording}, Duration: ${status.duration}ms`);
73
82
  // Stop and save
74
83
  const filePath = await NosniaAudioRecorder.stopRecording();
75
84
 
85
+ // Remove listener when done
86
+ removeListener();
87
+
76
88
  // Or cancel (discard)
77
89
  await NosniaAudioRecorder.cancelRecording();
78
90
  ```
@@ -89,6 +101,8 @@ await NosniaAudioRecorder.cancelRecording();
89
101
  - `resumeRecording(): Promise<void>` - Resume recording
90
102
  - `cancelRecording(): Promise<void>` - Cancel and discard
91
103
  - `getStatus(): Promise<RecorderStatus>` - Get recorder status
104
+ - `addRecordingProgressListener(callback: RecordingProgressCallback): () => void` - Add listener for duration updates (returns cleanup function)
105
+ - `removeRecordingProgressListener(): void` - Remove duration listener
92
106
 
93
107
  ### Types
94
108
 
@@ -105,6 +119,11 @@ interface RecorderStatus {
105
119
  duration: number; // In milliseconds
106
120
  currentFilePath?: string;
107
121
  }
122
+
123
+ type RecordingProgressCallback = (data: {
124
+ duration: number; // In milliseconds
125
+ isRecording: boolean;
126
+ }) => void;
108
127
  ```
109
128
 
110
129
  ## Platform Setup
@@ -5,12 +5,16 @@ import android.content.Context
5
5
  import android.media.MediaRecorder
6
6
  import android.os.Build
7
7
  import android.os.Environment
8
+ import android.os.Handler
9
+ import android.os.Looper
8
10
  import androidx.core.content.ContextCompat
9
11
  import com.facebook.react.bridge.Promise
10
12
  import com.facebook.react.bridge.ReactApplicationContext
11
13
  import com.facebook.react.bridge.ReadableMap
12
14
  import com.facebook.react.bridge.WritableMap
13
15
  import com.facebook.react.bridge.WritableNativeMap
16
+ import com.facebook.react.bridge.Arguments
17
+ import com.facebook.react.modules.core.DeviceEventManagerModule
14
18
  import com.facebook.react.module.annotations.ReactModule
15
19
  import java.io.File
16
20
  import java.text.SimpleDateFormat
@@ -25,6 +29,42 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
25
29
  private var currentFilePath: String? = null
26
30
  private var isRecording = false
27
31
  private var isPaused = false
32
+ private var startTime: Long = 0
33
+ private var pausedDuration: Long = 0
34
+ private var pauseStartTime: Long = 0
35
+
36
+ private val progressHandler = Handler(Looper.getMainLooper())
37
+ private val progressRunnable = object : Runnable {
38
+ override fun run() {
39
+ if (isRecording && !isPaused) {
40
+ val currentTime = System.currentTimeMillis()
41
+ val duration = currentTime - startTime - pausedDuration
42
+
43
+ sendEvent("onRecordingProgress", Arguments.createMap().apply {
44
+ putDouble("duration", duration.toDouble())
45
+ putBoolean("isRecording", true)
46
+ })
47
+
48
+ progressHandler.postDelayed(this, 100) // Update every 100ms
49
+ }
50
+ }
51
+ }
52
+
53
+ private fun sendEvent(eventName: String, params: WritableMap?) {
54
+ reactApplicationContext
55
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
56
+ .emit(eventName, params)
57
+ }
58
+
59
+ private fun startProgressUpdates() {
60
+ startTime = System.currentTimeMillis()
61
+ pausedDuration = 0
62
+ progressHandler.post(progressRunnable)
63
+ }
64
+
65
+ private fun stopProgressUpdates() {
66
+ progressHandler.removeCallbacks(progressRunnable)
67
+ }
28
68
 
29
69
  override fun getName(): String {
30
70
  return NAME
@@ -69,6 +109,7 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
69
109
 
70
110
  isRecording = true
71
111
  isPaused = false
112
+ startProgressUpdates()
72
113
  promise.resolve(null)
73
114
  } catch (e: Exception) {
74
115
  mediaRecorder?.release()
@@ -85,6 +126,7 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
85
126
  return
86
127
  }
87
128
 
129
+ stopProgressUpdates()
88
130
  mediaRecorder?.apply {
89
131
  stop()
90
132
  release()
@@ -97,6 +139,7 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
97
139
  currentFilePath = null
98
140
  promise.resolve(filePath)
99
141
  } catch (e: Exception) {
142
+ stopProgressUpdates()
100
143
  mediaRecorder?.release()
101
144
  mediaRecorder = null
102
145
  isRecording = false
@@ -114,6 +157,7 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
114
157
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
115
158
  mediaRecorder?.pause()
116
159
  isPaused = true
160
+ pauseStartTime = System.currentTimeMillis()
117
161
  promise.resolve(null)
118
162
  } else {
119
163
  promise.reject("NOT_SUPPORTED", "Pause is not supported on this API level")
@@ -133,6 +177,7 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
133
177
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
134
178
  mediaRecorder?.resume()
135
179
  isPaused = false
180
+ pausedDuration += System.currentTimeMillis() - pauseStartTime
136
181
  promise.resolve(null)
137
182
  } else {
138
183
  promise.reject("NOT_SUPPORTED", "Resume is not supported on this API level")
@@ -144,6 +189,8 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
144
189
 
145
190
  override fun cancelRecording(promise: Promise) {
146
191
  try {
192
+ stopProgressUpdates()
193
+
147
194
  mediaRecorder?.apply {
148
195
  try {
149
196
  stop()
@@ -169,9 +216,20 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
169
216
 
170
217
  override fun getRecorderStatus(promise: Promise) {
171
218
  try {
219
+ val currentTime = System.currentTimeMillis()
220
+ val duration = if (isRecording) {
221
+ if (isPaused) {
222
+ pauseStartTime - startTime - pausedDuration
223
+ } else {
224
+ currentTime - startTime - pausedDuration
225
+ }
226
+ } else {
227
+ 0
228
+ }
229
+
172
230
  val status = WritableNativeMap().apply {
173
231
  putBoolean("isRecording", isRecording)
174
- putDouble("duration", mediaRecorder?.currentPosition?.toDouble() ?: 0.0)
232
+ putDouble("duration", duration.toDouble())
175
233
  if (currentFilePath != null) {
176
234
  putString("currentFilePath", currentFilePath)
177
235
  }
@@ -1,6 +1,7 @@
1
1
  #import <NosniaAudioRecorderSpec/NosniaAudioRecorderSpec.h>
2
2
  #import <React/RCTBridgeModule.h>
3
+ #import <React/RCTEventEmitter.h>
3
4
 
4
- @interface NosniaAudioRecorder : NSObject <NativeNosniaAudioRecorderSpec>
5
+ @interface NosniaAudioRecorder : RCTEventEmitter <NativeNosniaAudioRecorderSpec>
5
6
 
6
7
  @end
@@ -1,27 +1,72 @@
1
1
  #import "NosniaAudioRecorder.h"
2
2
  #import <AVFoundation/AVFoundation.h>
3
3
  #import <React/RCTBridgeModule.h>
4
+ #import <React/RCTEventEmitter.h>
4
5
 
5
6
  @interface NosniaAudioRecorder () <AVAudioRecorderDelegate>
6
7
  @property (nonatomic, strong) AVAudioRecorder *audioRecorder;
7
8
  @property (nonatomic, strong) NSURL *recordingURL;
8
9
  @property (nonatomic, assign) BOOL isRecording;
10
+ @property (nonatomic, strong) NSTimer *progressTimer;
9
11
  @end
10
12
 
11
13
  @implementation NosniaAudioRecorder {
12
14
  AVAudioRecorder *_audioRecorder;
13
15
  NSURL *_recordingURL;
14
16
  BOOL _isRecording;
17
+ NSTimer *_progressTimer;
18
+ BOOL _hasListeners;
15
19
  }
16
20
 
21
+ RCT_EXPORT_MODULE(NosniaAudioRecorder)
22
+
17
23
  - (instancetype)init {
18
24
  self = [super init];
19
25
  if (self) {
20
26
  _isRecording = NO;
27
+ _hasListeners = NO;
21
28
  }
22
29
  return self;
23
30
  }
24
31
 
32
+ - (NSArray<NSString *> *)supportedEvents {
33
+ return @[@"onRecordingProgress"];
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->_audioRecorder && self->_isRecording) {
53
+ NSTimeInterval currentTime = self->_audioRecorder.currentTime;
54
+ [self sendEventWithName:@"onRecordingProgress"
55
+ body:@{
56
+ @"duration": @(currentTime * 1000),
57
+ @"isRecording": @(self->_audioRecorder.isRecording)
58
+ }];
59
+ }
60
+ }];
61
+ }
62
+
63
+ - (void)stopProgressTimer {
64
+ if (_progressTimer) {
65
+ [_progressTimer invalidate];
66
+ _progressTimer = nil;
67
+ }
68
+ }
69
+
25
70
  - (NSString *)getRecordingDirectory {
26
71
  NSArray *paths = NSSearchPathForDirectoriesInDomains(
27
72
  NSDocumentDirectory,
@@ -104,6 +149,7 @@
104
149
  }
105
150
 
106
151
  _isRecording = YES;
152
+ [self startProgressTimer];
107
153
  resolve(nil);
108
154
  } @catch (NSException *exception) {
109
155
  reject(@"START_RECORDING_ERROR", exception.reason, nil);
@@ -118,6 +164,7 @@
118
164
  return;
119
165
  }
120
166
 
167
+ [self stopProgressTimer];
121
168
  [_audioRecorder stop];
122
169
  NSString *filePath = [_recordingURL path];
123
170
  _audioRecorder = nil;
@@ -162,6 +209,8 @@
162
209
  - (void)cancelRecording:(RCTPromiseResolveBlock)resolve
163
210
  reject:(RCTPromiseRejectBlock)reject {
164
211
  @try {
212
+ [self stopProgressTimer];
213
+
165
214
  if (_audioRecorder) {
166
215
  [_audioRecorder stop];
167
216
  _audioRecorder = nil;
@@ -224,11 +273,13 @@
224
273
  - (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder
225
274
  successfully:(BOOL)flag {
226
275
  _isRecording = NO;
276
+ [self stopProgressTimer];
227
277
  }
228
278
 
229
279
  - (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder
230
280
  error:(NSError *)error {
231
281
  _isRecording = NO;
282
+ [self stopProgressTimer];
232
283
  }
233
284
 
234
285
  - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
@@ -1,9 +1,13 @@
1
1
  "use strict";
2
2
 
3
+ import { NativeEventEmitter, NativeModules } from 'react-native';
3
4
  import NativeModule from "./NativeNosniaAudioRecorder.js";
4
5
  class AudioRecorder {
5
6
  static instance = null;
6
- constructor() {}
7
+ progressListener = null;
8
+ constructor() {
9
+ this.eventEmitter = new NativeEventEmitter(NativeModules.NosniaAudioRecorder);
10
+ }
7
11
  static getInstance() {
8
12
  if (!AudioRecorder.instance) {
9
13
  AudioRecorder.instance = new AudioRecorder();
@@ -11,6 +15,35 @@ class AudioRecorder {
11
15
  return AudioRecorder.instance;
12
16
  }
13
17
 
18
+ /**
19
+ * Add a listener for recording progress updates
20
+ * @param callback Function called with duration updates (every 100ms during recording)
21
+ * @returns Function to remove the listener
22
+ */
23
+ addRecordingProgressListener(callback) {
24
+ // Remove existing listener if any
25
+ if (this.progressListener) {
26
+ this.progressListener.remove();
27
+ }
28
+ this.progressListener = this.eventEmitter.addListener('onRecordingProgress', 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 recording progress listener
39
+ */
40
+ removeRecordingProgressListener() {
41
+ if (this.progressListener) {
42
+ this.progressListener.remove();
43
+ this.progressListener = null;
44
+ }
45
+ }
46
+
14
47
  /**
15
48
  * Request audio recording permission from the user
16
49
  * @returns Promise that resolves to true if permission is granted
@@ -1 +1 @@
1
- {"version":3,"names":["NativeModule","AudioRecorder","instance","constructor","getInstance","requestPermission","requestAudioPermission","error","console","checkPermission","checkAudioPermission","startRecording","options","hasPermission","Error","config","bitrate","channels","sampleRate","stopRecording","pauseRecording","resumeRecording","cancelRecording","getStatus","getRecorderStatus","NosniaAudioRecorder"],"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"],"sourceRoot":"..\\..\\src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -1,9 +1,25 @@
1
1
  import { type RecorderOptions, type RecorderStatus } from './NativeNosniaAudioRecorder';
2
2
  export type { RecorderOptions, RecorderStatus };
3
+ export type RecordingProgressCallback = (data: {
4
+ duration: number;
5
+ isRecording: boolean;
6
+ }) => void;
3
7
  declare class AudioRecorder {
4
8
  private static instance;
9
+ private eventEmitter;
10
+ private progressListener;
5
11
  private constructor();
6
12
  static getInstance(): AudioRecorder;
13
+ /**
14
+ * Add a listener for recording progress updates
15
+ * @param callback Function called with duration updates (every 100ms during recording)
16
+ * @returns Function to remove the listener
17
+ */
18
+ addRecordingProgressListener(callback: RecordingProgressCallback): () => void;
19
+ /**
20
+ * Remove the recording progress listener
21
+ */
22
+ removeRecordingProgressListener(): void;
7
23
  /**
8
24
  * Request audio recording permission from the user
9
25
  * @returns Promise that resolves to true if permission is granted
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAqB,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,6BAA6B,CAAC;AAErC,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC;AAEhD,cAAM,aAAa;IACjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA8B;IAErD,OAAO;IAEP,MAAM,CAAC,WAAW,IAAI,aAAa;IAOnC;;;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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nosnia-audio-recorder",
3
- "version": "0.1.1",
3
+ "version": "0.2.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",
package/src/index.tsx CHANGED
@@ -1,3 +1,4 @@
1
+ import { NativeEventEmitter, NativeModules } from 'react-native';
1
2
  import NativeModule, {
2
3
  type RecorderOptions,
3
4
  type RecorderStatus,
@@ -5,10 +6,21 @@ import NativeModule, {
5
6
 
6
7
  export type { RecorderOptions, RecorderStatus };
7
8
 
9
+ export type RecordingProgressCallback = (data: {
10
+ duration: number;
11
+ isRecording: boolean;
12
+ }) => void;
13
+
8
14
  class AudioRecorder {
9
15
  private static instance: AudioRecorder | null = null;
16
+ private eventEmitter: NativeEventEmitter;
17
+ private progressListener: any = null;
10
18
 
11
- private constructor() {}
19
+ private constructor() {
20
+ this.eventEmitter = new NativeEventEmitter(
21
+ NativeModules.NosniaAudioRecorder
22
+ );
23
+ }
12
24
 
13
25
  static getInstance(): AudioRecorder {
14
26
  if (!AudioRecorder.instance) {
@@ -17,6 +29,42 @@ class AudioRecorder {
17
29
  return AudioRecorder.instance;
18
30
  }
19
31
 
32
+ /**
33
+ * Add a listener for recording progress updates
34
+ * @param callback Function called with duration updates (every 100ms during recording)
35
+ * @returns Function to remove the listener
36
+ */
37
+ addRecordingProgressListener(
38
+ callback: RecordingProgressCallback
39
+ ): () => void {
40
+ // Remove existing listener if any
41
+ if (this.progressListener) {
42
+ this.progressListener.remove();
43
+ }
44
+
45
+ this.progressListener = this.eventEmitter.addListener(
46
+ 'onRecordingProgress',
47
+ (data: any) => callback(data)
48
+ );
49
+
50
+ return () => {
51
+ if (this.progressListener) {
52
+ this.progressListener.remove();
53
+ this.progressListener = null;
54
+ }
55
+ };
56
+ }
57
+
58
+ /**
59
+ * Remove the recording progress listener
60
+ */
61
+ removeRecordingProgressListener(): void {
62
+ if (this.progressListener) {
63
+ this.progressListener.remove();
64
+ this.progressListener = null;
65
+ }
66
+ }
67
+
20
68
  /**
21
69
  * Request audio recording permission from the user
22
70
  * @returns Promise that resolves to true if permission is granted