@siteed/expo-audio-stream 0.7.5 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @siteed/expo-audio-stream
2
2
 
3
- `@siteed/expo-audio-stream` is a comprehensive library designed to facilitate real-time audio processing and streaming across iOS, Android, and web platforms. This library leverages Expo's robust ecosystem to simplify the implementation of audio recording and streaming functionalities within React Native applications. Key features include audio streaming with configurable buffer intervals and automatic handling of microphone permissions in managed Expo projects.
3
+ `@siteed/expo-audio-stream` is a comprehensive library designed to facilitate real-time audio processing and streaming across iOS, Android, and web platforms. This library leverages Expo's robust ecosystem to simplify the implementation of audio recording and streaming functionalities within React Native applications.
4
4
 
5
5
  ## Features
6
6
 
@@ -11,6 +11,23 @@
11
11
  - Listeners for audio data events with detailed event payloads.
12
12
  - Utility functions for recording control and file management.
13
13
 
14
+ ## Example Application
15
+
16
+ The project comes with a fully functional example application that demonstrates how to use the library in a real-world scenario.
17
+
18
+ ![Example App](./docs/demo.gif)
19
+
20
+
21
+ To try it:
22
+ ```bash
23
+ git clone https://github.com/deeeed/expo-audio-stream.git
24
+ cd expo-audio-stream
25
+ yarn
26
+ yarn example ios
27
+ yarn example android
28
+ yarn example web
29
+ ```
30
+
14
31
  ## Installation
15
32
 
16
33
  To install `@siteed/expo-audio-stream`, add it to your project using npm or Yarn:
@@ -21,13 +38,10 @@ npm install @siteed/expo-audio-stream
21
38
  yarn add @siteed/expo-audio-stream
22
39
  ```
23
40
 
24
- Make sure that you have Expo set up in your project. For details on setting up Expo, refer to the Expo documentation.
25
41
 
26
42
  ### Configuring with app.json
27
43
 
28
- To ensure expo-audio-stream works correctly with Expo, you must add it as a plugin in your app.json configuration file. This step is crucial as it allows Expo to load any necessary configurations or permissions required by the library.
29
-
30
- Add the plugin to your app.json like so:
44
+ To ensure expo-audio-stream works correctly with Expo, you must add it as a plugin in your app.json configuration file.
31
45
 
32
46
  ```json
33
47
  {
@@ -41,9 +55,15 @@ Make sure to run `npx expo prebuild` after adding the plugin to your app.json fi
41
55
 
42
56
  ## Usage
43
57
 
44
- The `example/` folder contains a fully functional React Native application that demonstrates how to integrate and use the `@siteed/expo-audio-stream` library in a real-world scenario. This sample application includes features such as starting and stopping audio recordings, handling permissions, and processing live audio data.
58
+ This library provides two hooks: `useAudioRecorder` for standalone use and `useSharedAudioRecorder` for accessing shared recording state within a React context.
59
+
60
+
61
+ ### Standalone Recording
62
+
63
+ The `example/` folder contains a fully functional React Native application demonstrating how to integrate and use `useAudioRecorder` from `@siteed/expo-audio-stream`. This includes starting and stopping recordings, handling permissions, and processing live audio data.
45
64
 
46
- ### Importing the module
65
+
66
+ #### Standalone Usage
47
67
 
48
68
  ```tsx
49
69
  import {
@@ -105,17 +125,62 @@ import { addAudioEventListener } from '@siteed/expo-audio-stream';
105
125
  }, []);
106
126
  ```
107
127
 
108
- ### Recording configuration
128
+ ### Shared Recording
129
+
130
+ To facilitate state sharing across multiple components or screens, useSharedAudioRecorder can be used. It should be wrapped in a AudioRecorderProvider context provider to ensure state is managed at a higher level and shared appropriately.
131
+
132
+ #### Shared Recording Usage
133
+
134
+ ```tsx
135
+ import { AudioRecorderProvider, useSharedAudioRecorder } from '@siteed/expo-audio-stream';
136
+
137
+ export default function ParentComponent() {
138
+ return (
139
+ <AudioRecorderProvider>
140
+ <ChildComponent />
141
+ </AudioRecorderProvider>
142
+ );
143
+ }
144
+
145
+ function ChildComponent() {
146
+ const {
147
+ startRecording,
148
+ isRecording
149
+ } = useSharedAudioRecorder();
150
+
151
+ return (
152
+ <View>
153
+ <Text>{isRecording ? "Recording..." : "Ready to record"}</Text>
154
+ <Button title="Toggle Recording" onPress={startRecording} />
155
+ </View>
156
+ );
157
+ }
158
+ ```
159
+
160
+ ### Add Event Listener
161
+
162
+ You can also add an event listener to receive detailed audio event payloads, which is crucial for both standalone and shared usage scenarios.
163
+
164
+ ```tsx
165
+ import { useEffect } from 'react';
166
+ import { addAudioEventListener } from '@siteed/expo-audio-stream';
167
+
168
+ function App() {
169
+ useEffect(() => {
170
+ const subscription = addAudioEventListener(event => {
171
+ console.log("Audio event received:", event);
172
+ });
173
+
174
+ return () => subscription.remove();
175
+ }, []);
176
+
177
+ // UI code here
178
+ }
179
+ ```
180
+
181
+ ## Recording configuration
109
182
 
110
183
  - on Android and IOS, audio is recorded in wav format, 16khz sample rate, 16 bit depth, 1 channel.
111
184
  - on web, it usually records in opus but it depends on the browser configuration.
112
185
 
113
186
  If you want to process the audio livestream directly, I recommend having another encoding step to align the audio format across platforms.
114
-
115
- ### TODO
116
- this package is still in development, and there are a few things that need to be done:
117
- - Add resume (vs currently use start) support and implement pause on iOS.
118
- - Multi format support: Extend support to other audio formats beyond WAV, such as MP3 or AAC
119
- - Integrate an audio processing library for optional audio analysis, such as equalization, noise reduction, or volume normalization.
120
- - Implement a more robust error handling system to provide detailed error messages and recovery options.
121
-
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import { UseAudioRecorderProps, UseAudioRecorderState } from "./useAudioRecording";
3
+ interface AudioRecorderProviderProps {
4
+ children: React.ReactNode;
5
+ config?: UseAudioRecorderProps;
6
+ }
7
+ export declare const AudioRecorderProvider: React.FC<AudioRecorderProviderProps>;
8
+ export declare const useSharedAudioRecorder: () => UseAudioRecorderState;
9
+ export {};
10
+ //# sourceMappingURL=AudioRecorder.provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioRecorder.provider.d.ts","sourceRoot":"","sources":["../src/AudioRecorder.provider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoC,MAAM,OAAO,CAAC;AAEzD,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EAEtB,MAAM,qBAAqB,CAAC;AAU7B,UAAU,0BAA0B;IAClC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,qBAAqB,CAAC;CAChC;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAUtE,CAAC;AAEF,eAAO,MAAM,sBAAsB,6BAQlC,CAAC"}
@@ -0,0 +1,23 @@
1
+ import React, { createContext, useContext } from "react";
2
+ import { useAudioRecorder, } from "./useAudioRecording";
3
+ const AudioRecorderContext = createContext({
4
+ isRecording: false,
5
+ isPaused: false,
6
+ duration: 0,
7
+ size: 0,
8
+ // other properties filled on useAudioRecorder
9
+ });
10
+ export const AudioRecorderProvider = ({ children, config = {}, }) => {
11
+ const audioRecorder = useAudioRecorder(config);
12
+ return (<AudioRecorderContext.Provider value={audioRecorder}>
13
+ {children}
14
+ </AudioRecorderContext.Provider>);
15
+ };
16
+ export const useSharedAudioRecorder = () => {
17
+ const context = useContext(AudioRecorderContext);
18
+ if (!context) {
19
+ throw new Error("useSharedAudioRecorder must be used within an AudioRecorderProvider");
20
+ }
21
+ return context;
22
+ };
23
+ //# sourceMappingURL=AudioRecorder.provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioRecorder.provider.js","sourceRoot":"","sources":["../src/AudioRecorder.provider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAEzD,OAAO,EAGL,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,oBAAoB,GAAG,aAAa,CAAwB;IAChE,WAAW,EAAE,KAAK;IAClB,QAAQ,EAAE,KAAK;IACf,QAAQ,EAAE,CAAC;IACX,IAAI,EAAE,CAAC;IACP,8CAA8C;CACtB,CAAC,CAAC;AAO5B,MAAM,CAAC,MAAM,qBAAqB,GAAyC,CAAC,EAC1E,QAAQ,EACR,MAAM,GAAG,EAAE,GACZ,EAAE,EAAE;IACH,MAAM,aAAa,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC/C,OAAO,CACL,CAAC,oBAAoB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,CAClD;MAAA,CAAC,QAAQ,CACX;IAAA,EAAE,oBAAoB,CAAC,QAAQ,CAAC,CACjC,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,EAAE;IACzC,MAAM,OAAO,GAAG,UAAU,CAAC,oBAAoB,CAAC,CAAC;IACjD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,qEAAqE,CACtE,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC","sourcesContent":["import React, { createContext, useContext } from \"react\";\n\nimport {\n UseAudioRecorderProps,\n UseAudioRecorderState,\n useAudioRecorder,\n} from \"./useAudioRecording\";\n\nconst AudioRecorderContext = createContext<UseAudioRecorderState>({\n isRecording: false,\n isPaused: false,\n duration: 0,\n size: 0,\n // other properties filled on useAudioRecorder\n} as UseAudioRecorderState);\n\ninterface AudioRecorderProviderProps {\n children: React.ReactNode;\n config?: UseAudioRecorderProps;\n}\n\nexport const AudioRecorderProvider: React.FC<AudioRecorderProviderProps> = ({\n children,\n config = {},\n}) => {\n const audioRecorder = useAudioRecorder(config);\n return (\n <AudioRecorderContext.Provider value={audioRecorder}>\n {children}\n </AudioRecorderContext.Provider>\n );\n};\n\nexport const useSharedAudioRecorder = () => {\n const context = useContext(AudioRecorderContext);\n if (!context) {\n throw new Error(\n \"useSharedAudioRecorder must be used within an AudioRecorderProvider\",\n );\n }\n return context;\n};\n"]}
@@ -1,3 +1,4 @@
1
+ import { AudioDataEvent } from "./useAudioRecording";
1
2
  export interface AudioEventPayload {
2
3
  encoded?: string;
3
4
  buffer?: Blob;
@@ -39,5 +40,6 @@ export interface RecordingConfig {
39
40
  channels?: 1 | 2;
40
41
  encoding?: EncodingType;
41
42
  interval?: number;
43
+ onAudioStream?: (_: AudioDataEvent) => Promise<void>;
42
44
  }
43
45
  //# sourceMappingURL=ExpoAudioStream.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoAudioStream.types.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStream.types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,UAAU,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACjB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB"}
1
+ {"version":3,"file":"ExpoAudioStream.types.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStream.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,UAAU,CAAC;AACpD,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACjB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD"}
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoAudioStream.types.js","sourceRoot":"","sources":["../src/ExpoAudioStream.types.ts"],"names":[],"mappings":"","sourcesContent":["export interface AudioEventPayload {\n encoded?: string;\n buffer?: Blob;\n fileUri: string;\n lastEmittedSize: number;\n position: number;\n deltaSize: number;\n totalSize: number;\n mimeType: string;\n streamUuid: string;\n}\n\nexport interface AudioStreamResult {\n fileUri: string;\n duration: number;\n size: number;\n mimeType: string;\n channels?: number;\n bitDepth?: number;\n sampleRate?: number;\n}\n\nexport interface StartAudioStreamResult {\n fileUri: string;\n mimeType: string;\n channels?: number;\n bitDepth?: number;\n sampleRate?: number;\n}\n\nexport interface AudioStreamStatus {\n isRecording: boolean;\n isPaused: boolean;\n duration: number;\n size: number;\n interval: number;\n mimeType: string;\n}\n\nexport type EncodingType = \"pcm_16bit\" | \"pcm_8bit\";\n\nexport interface RecordingConfig {\n sampleRate?: 16000 | 44100 | 48000;\n channels?: 1 | 2; // 1 or 2 MONO or STEREO\n encoding?: EncodingType;\n interval?: number;\n}\n"]}
1
+ {"version":3,"file":"ExpoAudioStream.types.js","sourceRoot":"","sources":["../src/ExpoAudioStream.types.ts"],"names":[],"mappings":"","sourcesContent":["import { AudioDataEvent } from \"./useAudioRecording\";\n\nexport interface AudioEventPayload {\n encoded?: string;\n buffer?: Blob;\n fileUri: string;\n lastEmittedSize: number;\n position: number;\n deltaSize: number;\n totalSize: number;\n mimeType: string;\n streamUuid: string;\n}\n\nexport interface AudioStreamResult {\n fileUri: string;\n duration: number;\n size: number;\n mimeType: string;\n channels?: number;\n bitDepth?: number;\n sampleRate?: number;\n}\n\nexport interface StartAudioStreamResult {\n fileUri: string;\n mimeType: string;\n channels?: number;\n bitDepth?: number;\n sampleRate?: number;\n}\n\nexport interface AudioStreamStatus {\n isRecording: boolean;\n isPaused: boolean;\n duration: number;\n size: number;\n interval: number;\n mimeType: string;\n}\n\nexport type EncodingType = \"pcm_16bit\" | \"pcm_8bit\";\nexport interface RecordingConfig {\n sampleRate?: 16000 | 44100 | 48000;\n channels?: 1 | 2; // 1 or 2 MONO or STEREO\n encoding?: EncodingType;\n interval?: number;\n onAudioStream?: (_: AudioDataEvent) => Promise<void>;\n}\n"]}
@@ -31,8 +31,6 @@ declare class ExpoAudioStreamWeb extends EventEmitter {
31
31
  size: number;
32
32
  interval: number;
33
33
  };
34
- listAudioFiles(): void;
35
- clearAudioFiles(): void;
36
34
  }
37
35
  declare const _default: ExpoAudioStreamWeb;
38
36
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoAudioStreamModule.web.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStreamModule.web.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAEL,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AAGjC,cAAM,kBAAmB,SAAQ,YAAY;IAC3C,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;IACpC,WAAW,EAAE,IAAI,EAAE,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;;IA4BpB,cAAc;IAUd,cAAc,CAAC,OAAO,GAAE,eAAoB;IAwBlD,uBAAuB;IA4BvB,cAAc,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE;IAiBnE,YAAY;IAUN,aAAa,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAelD,cAAc;IAcpB,MAAM;;;;;;;IAUN,cAAc;IAId,eAAe;CAGhB;;AAED,wBAAwC"}
1
+ {"version":3,"file":"ExpoAudioStreamModule.web.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStreamModule.web.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAEL,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AAGjC,cAAM,kBAAmB,SAAQ,YAAY;IAC3C,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;IACpC,WAAW,EAAE,IAAI,EAAE,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;;IA4BpB,cAAc;IAUd,cAAc,CAAC,OAAO,GAAE,eAAoB;IAwBlD,uBAAuB;IA4BvB,cAAc,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE;IAiBnE,YAAY;IAUN,aAAa,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAelD,cAAc;IAcpB,MAAM;;;;;;;CASP;;AAED,wBAAwC"}
@@ -151,12 +151,6 @@ class ExpoAudioStreamWeb extends EventEmitter {
151
151
  interval: this.currentInterval,
152
152
  };
153
153
  }
154
- listAudioFiles() {
155
- // Not applicable on web
156
- }
157
- clearAudioFiles() {
158
- // Not applicable on web
159
- }
160
154
  }
161
155
  export default new ExpoAudioStreamWeb();
162
156
  //# sourceMappingURL=ExpoAudioStreamModule.web.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoAudioStreamModule.web.js","sourceRoot":"","sources":["../src/ExpoAudioStreamModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AASjD,MAAM,GAAG,GAAG,KAAK,CAAC,qCAAqC,CAAC,CAAC;AACzD,MAAM,kBAAmB,SAAQ,YAAY;IAC3C,aAAa,CAAuB;IACpC,WAAW,CAAS;IACpB,WAAW,CAAU;IACrB,QAAQ,CAAU;IAClB,kBAAkB,CAAS;IAC3B,UAAU,CAAS;IACnB,iBAAiB,CAAS;IAC1B,WAAW,CAAS;IACpB,eAAe,CAAS;IACxB,eAAe,CAAS;IACxB,eAAe,CAAS;IACxB,UAAU,CAAgB;IAE1B;QACE,MAAM,gBAAgB,GAAG;YACvB,WAAW,EAAE,CAAC,SAAiB,EAAE,EAAE;gBACjC,kBAAkB;YACpB,CAAC;YACD,eAAe,EAAE,CAAC,KAAa,EAAE,EAAE;gBACjC,kBAAkB;YACpB,CAAC;SACF,CAAC;QACF,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,kDAAkD;QAE3E,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;QACtB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,yBAAyB;QACtD,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,2CAA2C;IACrE,CAAC;IAED,sCAAsC;IACtC,KAAK,CAAC,cAAc;QAClB,IAAI,CAAC;YACH,OAAO,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;YACpD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,+BAA+B;IAC/B,KAAK,CAAC,cAAc,CAAC,UAA2B,EAAE;QAChD,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACtD,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC3C,IAAI,CAAC,aAAa,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;QAC/C,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAC/B,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,eAAe,CAAC,CAAC;QACnE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACrC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,gDAAgD;QACvF,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,UAAU,OAAO,CAAC;QAC1C,MAAM,YAAY,GAA2B;YAC3C,OAAO;YACP,QAAQ,EAAE,YAAY;SACvB,CAAC;QACF,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,wCAAwC;IACxC,uBAAuB;QACrB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9C,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,eAAe,GAAG,CAAC,KAAK,EAAE,EAAE;YAC7C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAClC,IAAI,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,mCAAmC;YAExE,IAAI,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC;YAC1E,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,SAAS,CAAC;YACvC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,WAAW,CAAC;QAC1C,CAAC,CAAC;QAEF,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,GAAG,EAAE;YAC/B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YACzB,GAAG,CAAC,mBAAmB,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAC7C,CAAC,CAAC;QAEF,IAAI,CAAC,aAAa,CAAC,OAAO,GAAG,GAAG,EAAE;YAChC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACvB,CAAC,CAAC;QAEF,IAAI,CAAC,aAAa,CAAC,QAAQ,GAAG,GAAG,EAAE;YACjC,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;YACtB,IAAI,CAAC,kBAAkB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,mCAAmC;QAC9F,CAAC,CAAC;IACJ,CAAC;IAED,cAAc,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAoC;QACjE,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,UAAU,OAAO,CAAC;QAC1C,MAAM,iBAAiB,GAAsB;YAC3C,OAAO;YACP,QAAQ,EAAE,YAAY;YACtB,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,iEAAiE;YACxG,SAAS,EAAE,IAAI,CAAC,IAAI;YACpB,QAAQ;YACR,SAAS,EAAE,IAAI,CAAC,WAAW;YAC3B,MAAM,EAAE,IAAI;YACZ,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,EAAE,EAAE,oDAAoD;SACxF,CAAC;QAEF,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;IAC5C,CAAC;IAED,mCAAmC;IACnC,YAAY;QACV,qEAAqE;QACrE,OAAO,qBAAqB,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE;YACjD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAChC,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;YACtC,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,iBAAiB;IACjB,KAAK,CAAC,aAAa;QACjB,IAAI,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,kBAAkB,CAAC;QAC9D,MAAM,MAAM,GAAsB;YAChC,OAAO,EAAE,GAAG,IAAI,CAAC,UAAU,OAAO;YAClC,QAAQ,EAAE,IAAI,CAAC,iBAAiB;YAChC,IAAI,EAAE,IAAI,CAAC,WAAW;YACtB,QAAQ,EAAE,YAAY;SACvB,CAAC;QAEF,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,kBAAkB;IAClB,KAAK,CAAC,cAAc;QAClB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9C,CAAC;QAED,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACvC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;YAC3B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,qBAAqB;IACrB,MAAM;QACJ,OAAO;YACL,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,kBAAkB;YAC9C,IAAI,EAAE,IAAI,CAAC,WAAW;YACtB,QAAQ,EAAE,IAAI,CAAC,eAAe;SAC/B,CAAC;IACJ,CAAC;IAED,cAAc;QACZ,wBAAwB;IAC1B,CAAC;IAED,eAAe;QACb,wBAAwB;IAC1B,CAAC;CACF;AAED,eAAe,IAAI,kBAAkB,EAAE,CAAC","sourcesContent":["import debug from \"debug\";\nimport { EventEmitter } from \"expo-modules-core\";\n\nimport {\n AudioEventPayload,\n AudioStreamResult,\n RecordingConfig,\n StartAudioStreamResult,\n} from \"./ExpoAudioStream.types\";\n\nconst log = debug(\"expo-audio-stream:useAudioRecording\");\nclass ExpoAudioStreamWeb extends EventEmitter {\n mediaRecorder: MediaRecorder | null;\n audioChunks: Blob[];\n isRecording: boolean;\n isPaused: boolean;\n recordingStartTime: number;\n pausedTime: number;\n currentDurationMs: number;\n currentSize: number;\n currentInterval: number;\n lastEmittedSize: number;\n lastEmittedTime: number;\n streamUuid: string | null;\n\n constructor() {\n const mockNativeModule = {\n addListener: (eventName: string) => {\n // Not used on web\n },\n removeListeners: (count: number) => {\n // Not used on web\n },\n };\n super(mockNativeModule); // Pass the mock native module to the parent class\n\n this.mediaRecorder = null;\n this.audioChunks = [];\n this.isRecording = false;\n this.isPaused = false;\n this.recordingStartTime = 0;\n this.pausedTime = 0;\n this.currentDurationMs = 0;\n this.currentSize = 0;\n this.currentInterval = 1000; // Default interval in ms\n this.lastEmittedSize = 0;\n this.lastEmittedTime = 0;\n this.streamUuid = null; // Initialize UUID on first recording start\n }\n\n // Utility to handle user media stream\n async getMediaStream() {\n try {\n return await navigator.mediaDevices.getUserMedia({ audio: true });\n } catch (error) {\n console.error(\"Failed to get media stream:\", error);\n throw error;\n }\n }\n\n // Start recording with options\n async startRecording(options: RecordingConfig = {}) {\n if (this.isRecording) {\n throw new Error(\"Recording is already in progress\");\n }\n\n const stream = await this.getMediaStream();\n this.mediaRecorder = new MediaRecorder(stream);\n this.setupRecordingListeners();\n this.mediaRecorder.start(options.interval || this.currentInterval);\n this.isRecording = true;\n this.recordingStartTime = Date.now();\n this.pausedTime = 0;\n this.lastEmittedSize = 0;\n this.lastEmittedTime = 0;\n this.streamUuid = this.generateUUID(); // Generate a UUID for the new recording session\n const fileUri = `${this.streamUuid}.webm`;\n const streamConfig: StartAudioStreamResult = {\n fileUri,\n mimeType: \"audio/webm\",\n };\n return streamConfig;\n }\n\n // Setup listeners for the MediaRecorder\n setupRecordingListeners() {\n if (!this.mediaRecorder) {\n throw new Error(\"No active media recorder\");\n }\n this.mediaRecorder.ondataavailable = (event) => {\n this.audioChunks.push(event.data);\n this.currentSize += event.data.size; // Update the size of the recording\n\n this.emitAudioEvent({ data: event.data, position: this.lastEmittedTime });\n this.lastEmittedTime = event.timeStamp;\n this.lastEmittedSize = this.currentSize;\n };\n\n this.mediaRecorder.onstop = () => {\n this.isRecording = false;\n log(\"Recording stopped\", this.audioChunks);\n };\n\n this.mediaRecorder.onpause = () => {\n this.isPaused = true;\n };\n\n this.mediaRecorder.onresume = () => {\n this.isPaused = false;\n this.recordingStartTime += Date.now() - this.pausedTime; // Adjust start time after resuming\n };\n }\n\n emitAudioEvent({ data, position }: { data: Blob; position: number }) {\n const fileUri = `${this.streamUuid}.webm`;\n const audioEventPayload: AudioEventPayload = {\n fileUri,\n mimeType: \"audio/webm\",\n lastEmittedSize: this.lastEmittedSize, // Since this might be continuously streaming, adjust accordingly\n deltaSize: data.size,\n position,\n totalSize: this.currentSize,\n buffer: data,\n streamUuid: this.streamUuid ?? \"\", // Generate or manage UUID for stream identification\n };\n\n this.emit(\"AudioData\", audioEventPayload);\n }\n\n // Helper method to generate a UUID\n generateUUID() {\n // Implementation of UUID generation (use a library or custom method)\n return \"xxxx-xxxx-xxxx-xxxx\".replace(/[x]/g, (c) => {\n const r = (Math.random() * 16) | 0,\n v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n\n // Stop recording\n async stopRecording(): Promise<AudioStreamResult | null> {\n this.mediaRecorder?.stop();\n this.isRecording = false;\n this.currentDurationMs = Date.now() - this.recordingStartTime;\n const result: AudioStreamResult = {\n fileUri: `${this.streamUuid}.webm`,\n duration: this.currentDurationMs,\n size: this.currentSize,\n mimeType: \"audio/webm\",\n };\n\n return result;\n }\n\n // Pause recording\n async pauseRecording() {\n if (!this.mediaRecorder) {\n throw new Error(\"No active media recorder\");\n }\n\n if (this.isRecording && !this.isPaused) {\n this.mediaRecorder.pause();\n this.pausedTime = Date.now();\n } else {\n throw new Error(\"Recording is not active or already paused\");\n }\n }\n\n // Get current status\n status() {\n return {\n isRecording: this.isRecording,\n isPaused: this.isPaused,\n duration: Date.now() - this.recordingStartTime,\n size: this.currentSize,\n interval: this.currentInterval,\n };\n }\n\n listAudioFiles() {\n // Not applicable on web\n }\n\n clearAudioFiles() {\n // Not applicable on web\n }\n}\n\nexport default new ExpoAudioStreamWeb();\n"]}
1
+ {"version":3,"file":"ExpoAudioStreamModule.web.js","sourceRoot":"","sources":["../src/ExpoAudioStreamModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AASjD,MAAM,GAAG,GAAG,KAAK,CAAC,qCAAqC,CAAC,CAAC;AACzD,MAAM,kBAAmB,SAAQ,YAAY;IAC3C,aAAa,CAAuB;IACpC,WAAW,CAAS;IACpB,WAAW,CAAU;IACrB,QAAQ,CAAU;IAClB,kBAAkB,CAAS;IAC3B,UAAU,CAAS;IACnB,iBAAiB,CAAS;IAC1B,WAAW,CAAS;IACpB,eAAe,CAAS;IACxB,eAAe,CAAS;IACxB,eAAe,CAAS;IACxB,UAAU,CAAgB;IAE1B;QACE,MAAM,gBAAgB,GAAG;YACvB,WAAW,EAAE,CAAC,SAAiB,EAAE,EAAE;gBACjC,kBAAkB;YACpB,CAAC;YACD,eAAe,EAAE,CAAC,KAAa,EAAE,EAAE;gBACjC,kBAAkB;YACpB,CAAC;SACF,CAAC;QACF,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,kDAAkD;QAE3E,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;QACtB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,yBAAyB;QACtD,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,2CAA2C;IACrE,CAAC;IAED,sCAAsC;IACtC,KAAK,CAAC,cAAc;QAClB,IAAI,CAAC;YACH,OAAO,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;YACpD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,+BAA+B;IAC/B,KAAK,CAAC,cAAc,CAAC,UAA2B,EAAE;QAChD,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACtD,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC3C,IAAI,CAAC,aAAa,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;QAC/C,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAC/B,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,eAAe,CAAC,CAAC;QACnE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACrC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,gDAAgD;QACvF,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,UAAU,OAAO,CAAC;QAC1C,MAAM,YAAY,GAA2B;YAC3C,OAAO;YACP,QAAQ,EAAE,YAAY;SACvB,CAAC;QACF,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,wCAAwC;IACxC,uBAAuB;QACrB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9C,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,eAAe,GAAG,CAAC,KAAK,EAAE,EAAE;YAC7C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAClC,IAAI,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,mCAAmC;YAExE,IAAI,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC;YAC1E,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,SAAS,CAAC;YACvC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,WAAW,CAAC;QAC1C,CAAC,CAAC;QAEF,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,GAAG,EAAE;YAC/B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YACzB,GAAG,CAAC,mBAAmB,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAC7C,CAAC,CAAC;QAEF,IAAI,CAAC,aAAa,CAAC,OAAO,GAAG,GAAG,EAAE;YAChC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACvB,CAAC,CAAC;QAEF,IAAI,CAAC,aAAa,CAAC,QAAQ,GAAG,GAAG,EAAE;YACjC,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;YACtB,IAAI,CAAC,kBAAkB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,mCAAmC;QAC9F,CAAC,CAAC;IACJ,CAAC;IAED,cAAc,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAoC;QACjE,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,UAAU,OAAO,CAAC;QAC1C,MAAM,iBAAiB,GAAsB;YAC3C,OAAO;YACP,QAAQ,EAAE,YAAY;YACtB,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,iEAAiE;YACxG,SAAS,EAAE,IAAI,CAAC,IAAI;YACpB,QAAQ;YACR,SAAS,EAAE,IAAI,CAAC,WAAW;YAC3B,MAAM,EAAE,IAAI;YACZ,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,EAAE,EAAE,oDAAoD;SACxF,CAAC;QAEF,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;IAC5C,CAAC;IAED,mCAAmC;IACnC,YAAY;QACV,qEAAqE;QACrE,OAAO,qBAAqB,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE;YACjD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAChC,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;YACtC,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,iBAAiB;IACjB,KAAK,CAAC,aAAa;QACjB,IAAI,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,kBAAkB,CAAC;QAC9D,MAAM,MAAM,GAAsB;YAChC,OAAO,EAAE,GAAG,IAAI,CAAC,UAAU,OAAO;YAClC,QAAQ,EAAE,IAAI,CAAC,iBAAiB;YAChC,IAAI,EAAE,IAAI,CAAC,WAAW;YACtB,QAAQ,EAAE,YAAY;SACvB,CAAC;QAEF,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,kBAAkB;IAClB,KAAK,CAAC,cAAc;QAClB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9C,CAAC;QAED,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACvC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;YAC3B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,qBAAqB;IACrB,MAAM;QACJ,OAAO;YACL,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,kBAAkB;YAC9C,IAAI,EAAE,IAAI,CAAC,WAAW;YACtB,QAAQ,EAAE,IAAI,CAAC,eAAe;SAC/B,CAAC;IACJ,CAAC;CACF;AAED,eAAe,IAAI,kBAAkB,EAAE,CAAC","sourcesContent":["import debug from \"debug\";\nimport { EventEmitter } from \"expo-modules-core\";\n\nimport {\n AudioEventPayload,\n AudioStreamResult,\n RecordingConfig,\n StartAudioStreamResult,\n} from \"./ExpoAudioStream.types\";\n\nconst log = debug(\"expo-audio-stream:useAudioRecording\");\nclass ExpoAudioStreamWeb extends EventEmitter {\n mediaRecorder: MediaRecorder | null;\n audioChunks: Blob[];\n isRecording: boolean;\n isPaused: boolean;\n recordingStartTime: number;\n pausedTime: number;\n currentDurationMs: number;\n currentSize: number;\n currentInterval: number;\n lastEmittedSize: number;\n lastEmittedTime: number;\n streamUuid: string | null;\n\n constructor() {\n const mockNativeModule = {\n addListener: (eventName: string) => {\n // Not used on web\n },\n removeListeners: (count: number) => {\n // Not used on web\n },\n };\n super(mockNativeModule); // Pass the mock native module to the parent class\n\n this.mediaRecorder = null;\n this.audioChunks = [];\n this.isRecording = false;\n this.isPaused = false;\n this.recordingStartTime = 0;\n this.pausedTime = 0;\n this.currentDurationMs = 0;\n this.currentSize = 0;\n this.currentInterval = 1000; // Default interval in ms\n this.lastEmittedSize = 0;\n this.lastEmittedTime = 0;\n this.streamUuid = null; // Initialize UUID on first recording start\n }\n\n // Utility to handle user media stream\n async getMediaStream() {\n try {\n return await navigator.mediaDevices.getUserMedia({ audio: true });\n } catch (error) {\n console.error(\"Failed to get media stream:\", error);\n throw error;\n }\n }\n\n // Start recording with options\n async startRecording(options: RecordingConfig = {}) {\n if (this.isRecording) {\n throw new Error(\"Recording is already in progress\");\n }\n\n const stream = await this.getMediaStream();\n this.mediaRecorder = new MediaRecorder(stream);\n this.setupRecordingListeners();\n this.mediaRecorder.start(options.interval || this.currentInterval);\n this.isRecording = true;\n this.recordingStartTime = Date.now();\n this.pausedTime = 0;\n this.lastEmittedSize = 0;\n this.lastEmittedTime = 0;\n this.streamUuid = this.generateUUID(); // Generate a UUID for the new recording session\n const fileUri = `${this.streamUuid}.webm`;\n const streamConfig: StartAudioStreamResult = {\n fileUri,\n mimeType: \"audio/webm\",\n };\n return streamConfig;\n }\n\n // Setup listeners for the MediaRecorder\n setupRecordingListeners() {\n if (!this.mediaRecorder) {\n throw new Error(\"No active media recorder\");\n }\n this.mediaRecorder.ondataavailable = (event) => {\n this.audioChunks.push(event.data);\n this.currentSize += event.data.size; // Update the size of the recording\n\n this.emitAudioEvent({ data: event.data, position: this.lastEmittedTime });\n this.lastEmittedTime = event.timeStamp;\n this.lastEmittedSize = this.currentSize;\n };\n\n this.mediaRecorder.onstop = () => {\n this.isRecording = false;\n log(\"Recording stopped\", this.audioChunks);\n };\n\n this.mediaRecorder.onpause = () => {\n this.isPaused = true;\n };\n\n this.mediaRecorder.onresume = () => {\n this.isPaused = false;\n this.recordingStartTime += Date.now() - this.pausedTime; // Adjust start time after resuming\n };\n }\n\n emitAudioEvent({ data, position }: { data: Blob; position: number }) {\n const fileUri = `${this.streamUuid}.webm`;\n const audioEventPayload: AudioEventPayload = {\n fileUri,\n mimeType: \"audio/webm\",\n lastEmittedSize: this.lastEmittedSize, // Since this might be continuously streaming, adjust accordingly\n deltaSize: data.size,\n position,\n totalSize: this.currentSize,\n buffer: data,\n streamUuid: this.streamUuid ?? \"\", // Generate or manage UUID for stream identification\n };\n\n this.emit(\"AudioData\", audioEventPayload);\n }\n\n // Helper method to generate a UUID\n generateUUID() {\n // Implementation of UUID generation (use a library or custom method)\n return \"xxxx-xxxx-xxxx-xxxx\".replace(/[x]/g, (c) => {\n const r = (Math.random() * 16) | 0,\n v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n\n // Stop recording\n async stopRecording(): Promise<AudioStreamResult | null> {\n this.mediaRecorder?.stop();\n this.isRecording = false;\n this.currentDurationMs = Date.now() - this.recordingStartTime;\n const result: AudioStreamResult = {\n fileUri: `${this.streamUuid}.webm`,\n duration: this.currentDurationMs,\n size: this.currentSize,\n mimeType: \"audio/webm\",\n };\n\n return result;\n }\n\n // Pause recording\n async pauseRecording() {\n if (!this.mediaRecorder) {\n throw new Error(\"No active media recorder\");\n }\n\n if (this.isRecording && !this.isPaused) {\n this.mediaRecorder.pause();\n this.pausedTime = Date.now();\n } else {\n throw new Error(\"Recording is not active or already paused\");\n }\n }\n\n // Get current status\n status() {\n return {\n isRecording: this.isRecording,\n isPaused: this.isPaused,\n duration: Date.now() - this.recordingStartTime,\n size: this.currentSize,\n interval: this.currentInterval,\n };\n }\n}\n\nexport default new ExpoAudioStreamWeb();\n"]}
package/build/index.d.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  import { type Subscription } from "expo-modules-core";
2
+ import { AudioRecorderProvider, useSharedAudioRecorder } from "./AudioRecorder.provider";
2
3
  import { AudioEventPayload } from "./ExpoAudioStream.types";
3
- import { useAudioRecorder, UseAudioRecorderState, AudioDataEvent } from "./useAudioRecording";
4
- export declare function listAudioFiles(): Promise<string[]>;
5
- export declare function clearAudioFiles(): Promise<void>;
4
+ import { AudioDataEvent, UseAudioRecorderState, useAudioRecorder } from "./useAudioRecording";
6
5
  export declare function test(): void;
7
6
  export declare function addAudioEventListener(listener: (event: AudioEventPayload) => Promise<void>): Subscription;
8
- export type { AudioEventPayload, UseAudioRecorderState, AudioDataEvent };
9
- export { useAudioRecorder };
7
+ export { AudioRecorderProvider, useAudioRecorder, useSharedAudioRecorder };
8
+ export type { AudioDataEvent, AudioEventPayload, UseAudioRecorderState };
10
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,cAAc,EACf,MAAM,qBAAqB,CAAC;AAM7B,wBAAgB,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAElD;AAED,wBAAgB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED,wBAAgB,IAAI,IAAI,IAAI,CAE3B;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,GACpD,YAAY,CAGd;AAED,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,cAAc,EAAE,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,qBAAqB,CAAC;AAM7B,wBAAgB,IAAI,IAAI,IAAI,CAE3B;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,GACpD,YAAY,CAGd;AAED,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,CAAC;AAC3E,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,CAAC"}
package/build/index.js CHANGED
@@ -1,13 +1,10 @@
1
1
  import { EventEmitter, NativeModulesProxy, } from "expo-modules-core";
2
+ // Import the native module. On web, it will be resolved to ExpoAudioStream.web.ts
3
+ // and on native platforms to ExpoAudioStream.ts
4
+ import { AudioRecorderProvider, useSharedAudioRecorder, } from "./AudioRecorder.provider";
2
5
  import ExpoAudioStreamModule from "./ExpoAudioStreamModule";
3
6
  import { useAudioRecorder, } from "./useAudioRecording";
4
7
  const emitter = new EventEmitter(ExpoAudioStreamModule ?? NativeModulesProxy.ExpoAudioStream);
5
- export function listAudioFiles() {
6
- return ExpoAudioStreamModule.listAudioFiles();
7
- }
8
- export function clearAudioFiles() {
9
- return ExpoAudioStreamModule.clearAudioFiles();
10
- }
11
8
  export function test() {
12
9
  return ExpoAudioStreamModule.test();
13
10
  }
@@ -15,5 +12,5 @@ export function addAudioEventListener(listener) {
15
12
  console.log(`addAudioEventListener`, listener);
16
13
  return emitter.addListener("AudioData", listener);
17
14
  }
18
- export { useAudioRecorder };
15
+ export { AudioRecorderProvider, useAudioRecorder, useSharedAudioRecorder };
19
16
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,kBAAkB,GAEnB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,qBAAqB,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EACL,gBAAgB,GAGjB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,OAAO,GAAG,IAAI,YAAY,CAC9B,qBAAqB,IAAI,kBAAkB,CAAC,eAAe,CAC5D,CAAC;AAEF,MAAM,UAAU,cAAc;IAC5B,OAAO,qBAAqB,CAAC,cAAc,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,qBAAqB,CAAC,eAAe,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,IAAI;IAClB,OAAO,qBAAqB,CAAC,IAAI,EAAE,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,QAAqD;IAErD,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAE,QAAQ,CAAC,CAAC;IAC/C,OAAO,OAAO,CAAC,WAAW,CAAoB,WAAW,EAAE,QAAQ,CAAC,CAAC;AACvE,CAAC;AAGD,OAAO,EAAE,gBAAgB,EAAE,CAAC","sourcesContent":["import {\n EventEmitter,\n NativeModulesProxy,\n type Subscription,\n} from \"expo-modules-core\";\n\n// Import the native module. On web, it will be resolved to ExpoAudioStream.web.ts\n// and on native platforms to ExpoAudioStream.ts\nimport { AudioEventPayload } from \"./ExpoAudioStream.types\";\nimport ExpoAudioStreamModule from \"./ExpoAudioStreamModule\";\nimport {\n useAudioRecorder,\n UseAudioRecorderState,\n AudioDataEvent,\n} from \"./useAudioRecording\";\n\nconst emitter = new EventEmitter(\n ExpoAudioStreamModule ?? NativeModulesProxy.ExpoAudioStream,\n);\n\nexport function listAudioFiles(): Promise<string[]> {\n return ExpoAudioStreamModule.listAudioFiles();\n}\n\nexport function clearAudioFiles(): Promise<void> {\n return ExpoAudioStreamModule.clearAudioFiles();\n}\n\nexport function test(): void {\n return ExpoAudioStreamModule.test();\n}\n\nexport function addAudioEventListener(\n listener: (event: AudioEventPayload) => Promise<void>,\n): Subscription {\n console.log(`addAudioEventListener`, listener);\n return emitter.addListener<AudioEventPayload>(\"AudioData\", listener);\n}\n\nexport type { AudioEventPayload, UseAudioRecorderState, AudioDataEvent };\nexport { useAudioRecorder };\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,kBAAkB,GAEnB,MAAM,mBAAmB,CAAC;AAE3B,kFAAkF;AAClF,gDAAgD;AAChD,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAElC,OAAO,qBAAqB,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAGL,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,OAAO,GAAG,IAAI,YAAY,CAC9B,qBAAqB,IAAI,kBAAkB,CAAC,eAAe,CAC5D,CAAC;AAEF,MAAM,UAAU,IAAI;IAClB,OAAO,qBAAqB,CAAC,IAAI,EAAE,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,QAAqD;IAErD,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAE,QAAQ,CAAC,CAAC;IAC/C,OAAO,OAAO,CAAC,WAAW,CAAoB,WAAW,EAAE,QAAQ,CAAC,CAAC;AACvE,CAAC;AAED,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,CAAC","sourcesContent":["import {\n EventEmitter,\n NativeModulesProxy,\n type Subscription,\n} from \"expo-modules-core\";\n\n// Import the native module. On web, it will be resolved to ExpoAudioStream.web.ts\n// and on native platforms to ExpoAudioStream.ts\nimport {\n AudioRecorderProvider,\n useSharedAudioRecorder,\n} from \"./AudioRecorder.provider\";\nimport { AudioEventPayload } from \"./ExpoAudioStream.types\";\nimport ExpoAudioStreamModule from \"./ExpoAudioStreamModule\";\nimport {\n AudioDataEvent,\n UseAudioRecorderState,\n useAudioRecorder,\n} from \"./useAudioRecording\";\n\nconst emitter = new EventEmitter(\n ExpoAudioStreamModule ?? NativeModulesProxy.ExpoAudioStream,\n);\n\nexport function test(): void {\n return ExpoAudioStreamModule.test();\n}\n\nexport function addAudioEventListener(\n listener: (event: AudioEventPayload) => Promise<void>,\n): Subscription {\n console.log(`addAudioEventListener`, listener);\n return emitter.addListener<AudioEventPayload>(\"AudioData\", listener);\n}\n\nexport { AudioRecorderProvider, useAudioRecorder, useSharedAudioRecorder };\nexport type { AudioDataEvent, AudioEventPayload, UseAudioRecorderState };\n"]}
@@ -6,6 +6,9 @@ export interface AudioDataEvent {
6
6
  eventDataSize: number;
7
7
  totalSize: number;
8
8
  }
9
+ export interface UseAudioRecorderProps {
10
+ debug?: boolean;
11
+ }
9
12
  export interface UseAudioRecorderState {
10
13
  startRecording: (_: RecordingConfig) => Promise<StartAudioStreamResult>;
11
14
  stopRecording: () => Promise<AudioStreamResult | null>;
@@ -16,8 +19,5 @@ export interface UseAudioRecorderState {
16
19
  duration: number;
17
20
  size: number;
18
21
  }
19
- export declare function useAudioRecorder({ onAudioStream, debug, }: {
20
- onAudioStream?: (_: AudioDataEvent) => Promise<void>;
21
- debug?: boolean;
22
- }): UseAudioRecorderState;
22
+ export declare function useAudioRecorder({ debug, }?: UseAudioRecorderProps): UseAudioRecorderState;
23
23
  //# sourceMappingURL=useAudioRecording.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useAudioRecording.d.ts","sourceRoot":"","sources":["../src/useAudioRecording.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,iBAAiB,EAEjB,eAAe,EACf,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AAGjC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACxE,aAAa,EAAE,MAAM,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IACvD,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AA4CD,wBAAgB,gBAAgB,CAAC,EAC/B,aAAa,EACb,KAAa,GACd,EAAE;IACD,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,qBAAqB,CAiMxB"}
1
+ {"version":3,"file":"useAudioRecording.d.ts","sourceRoot":"","sources":["../src/useAudioRecording.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,iBAAiB,EAEjB,eAAe,EACf,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AAGjC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AACD,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACxE,aAAa,EAAE,MAAM,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IACvD,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AA4CD,wBAAgB,gBAAgB,CAAC,EAC/B,KAAa,GACd,GAAE,qBAA0B,GAAG,qBAAqB,CA4LpD"}
@@ -1,5 +1,5 @@
1
1
  import { Platform } from "expo-modules-core";
2
- import { useCallback, useEffect, useReducer } from "react";
2
+ import { useCallback, useEffect, useReducer, useRef } from "react";
3
3
  import { addAudioEventListener } from ".";
4
4
  import ExpoAudioStreamModule from "./ExpoAudioStreamModule";
5
5
  function recorderReducer(state, action) {
@@ -29,24 +29,24 @@ function recorderReducer(state, action) {
29
29
  }
30
30
  }
31
31
  const TAG = "[ useAudioRecorder ] ";
32
- export function useAudioRecorder({ onAudioStream, debug = false, }) {
32
+ export function useAudioRecorder({ debug = false, } = {}) {
33
33
  const [state, dispatch] = useReducer(recorderReducer, {
34
34
  isRecording: false,
35
35
  isPaused: false,
36
36
  duration: 0,
37
37
  size: 0,
38
38
  });
39
- console.log(`[useAudioRecorder] RENDERING state`, state);
40
- const logDebug = (message, data) => {
39
+ const onAudioStreamRef = useRef(null);
40
+ const logDebug = useCallback((message, data) => {
41
41
  if (debug) {
42
42
  if (data) {
43
- console.log(`[useAudioRecorder] ${message}`, data);
43
+ console.log(`${TAG} ${message}`, data);
44
44
  }
45
45
  else {
46
- console.log(`[useAudioRecorder] ${message}`);
46
+ console.log(`${TAG} ${message}`);
47
47
  }
48
48
  }
49
- };
49
+ }, [debug]);
50
50
  const handleAudioEvent = useCallback(async (eventData) => {
51
51
  const { fileUri, deltaSize, totalSize, lastEmittedSize, position, streamUuid, encoded, mimeType, buffer, } = eventData;
52
52
  logDebug(`useAudioRecorder] Received audio event:`, {
@@ -63,41 +63,25 @@ export function useAudioRecorder({ onAudioStream, debug = false, }) {
63
63
  // Ignore packet with no data
64
64
  return;
65
65
  }
66
- // Add more detailed handling here
67
66
  try {
68
67
  // Coming from native ( ios / android ) otherwise buffer is set
69
68
  if (Platform.OS !== "web") {
70
69
  // Read the audio file as a base64 string for comparison
71
70
  if (!encoded) {
72
- console.error("[useAudioRecorder] Encoded audio data is missing");
71
+ console.error(`${TAG} Encoded audio data is missing`);
73
72
  throw new Error("Encoded audio data is missing");
74
73
  }
75
- await onAudioStream?.({
74
+ onAudioStreamRef.current?.({
76
75
  data: encoded,
77
76
  position,
78
77
  fileUri,
79
78
  eventDataSize: deltaSize,
80
79
  totalSize,
81
80
  });
82
- // Below code is optional, used to compare encoded data to audio on file system
83
- // Fetch the audio data from the fileUri
84
- // const options = {
85
- // encoding: FileSystem.EncodingType.Base64,
86
- // position: lastEmittedSize,
87
- // length: deltaSize,
88
- // };
89
- // const base64Content = await FileSystem.readAsStringAsync(fileUri, options);
90
- // const binaryData = atob(base64Content);
91
- // const content = new Uint8Array(binaryData.length);
92
- // for (let i = 0; i < binaryData.length; i++) {
93
- // content[i] = binaryData.charCodeAt(i);
94
- // }
95
- // const audioBlob = new Blob([content], { type: 'application/octet-stream' }); // Create a Blob from the byte array
96
- // console.debug(`Read audio file (len: ${content.length}) vs ${deltaSize}`)
97
81
  }
98
82
  else if (buffer) {
99
83
  // Coming from web
100
- await onAudioStream?.({
84
+ onAudioStreamRef.current?.({
101
85
  data: buffer,
102
86
  position,
103
87
  fileUri,
@@ -109,7 +93,7 @@ export function useAudioRecorder({ onAudioStream, debug = false, }) {
109
93
  catch (error) {
110
94
  console.error(`${TAG} Error processing audio event:`, error);
111
95
  }
112
- }, [logDebug, onAudioStream]);
96
+ }, [logDebug]);
113
97
  const checkStatus = useCallback(async () => {
114
98
  try {
115
99
  if (!state.isRecording) {
@@ -118,7 +102,7 @@ export function useAudioRecorder({ onAudioStream, debug = false, }) {
118
102
  }
119
103
  const status = ExpoAudioStreamModule.status();
120
104
  if (debug) {
121
- logDebug("[useAudioRecorder] Status:", status);
105
+ logDebug(`${TAG} Status:`, status);
122
106
  }
123
107
  if (!status.isRecording) {
124
108
  dispatch({ type: "STOP" });
@@ -131,7 +115,7 @@ export function useAudioRecorder({ onAudioStream, debug = false, }) {
131
115
  }
132
116
  }
133
117
  catch (error) {
134
- console.error(`[useAudioRecorder] Error getting status:`, error);
118
+ console.error(`${TAG} Error getting status:`, error);
135
119
  }
136
120
  }, [state.isRecording, logDebug]);
137
121
  useEffect(() => {
@@ -146,28 +130,35 @@ export function useAudioRecorder({ onAudioStream, debug = false, }) {
146
130
  };
147
131
  }, [checkStatus, state.isRecording]);
148
132
  useEffect(() => {
149
- if (!onAudioStream) {
150
- return;
151
- }
152
- logDebug(`${TAG} Registering audio event listener`, onAudioStream);
133
+ logDebug(`${TAG} Registering audio event listener`);
153
134
  const subscribe = addAudioEventListener(handleAudioEvent);
154
135
  logDebug(`${TAG} Subscribed to audio event listener`, subscribe);
155
136
  return () => {
156
137
  logDebug(`${TAG} Removing audio event listener`);
157
138
  subscribe.remove();
158
139
  };
159
- }, [onAudioStream, handleAudioEvent, logDebug]);
140
+ }, [handleAudioEvent, logDebug]);
160
141
  const startRecording = useCallback(async (recordingOptions) => {
161
142
  if (debug) {
162
143
  logDebug(`${TAG} start recoding`, recordingOptions);
163
144
  }
164
- const startResult = await ExpoAudioStreamModule.startRecording(recordingOptions);
145
+ // remove onAudioStream from recordingOptions
146
+ const { onAudioStream, ...options } = recordingOptions;
147
+ if (typeof onAudioStream === "function") {
148
+ onAudioStreamRef.current = onAudioStream;
149
+ }
150
+ else {
151
+ console.warn(`${TAG} onAudioStream is not a function`, onAudioStream);
152
+ onAudioStreamRef.current = null;
153
+ }
154
+ const startResult = await ExpoAudioStreamModule.startRecording(options);
165
155
  dispatch({ type: "START" });
166
156
  return startResult;
167
157
  }, [logDebug]);
168
158
  const stopRecording = useCallback(async () => {
169
159
  logDebug(`${TAG} stoping recording`);
170
160
  const stopResult = await ExpoAudioStreamModule.stopRecording();
161
+ onAudioStreamRef.current = null;
171
162
  logDebug(`${TAG} recording stopped`, stopResult);
172
163
  dispatch({ type: "STOP" });
173
164
  return stopResult;
@@ -1 +1 @@
1
- {"version":3,"file":"useAudioRecording.js","sourceRoot":"","sources":["../src/useAudioRecording.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from \"expo-modules-core\";\nimport { useCallback, useEffect, useReducer } from \"react\";\n\nimport { addAudioEventListener } from \".\";\nimport {\n AudioEventPayload,\n AudioStreamResult,\n AudioStreamStatus,\n RecordingConfig,\n StartAudioStreamResult,\n} from \"./ExpoAudioStream.types\";\nimport ExpoAudioStreamModule from \"./ExpoAudioStreamModule\";\n\nexport interface AudioDataEvent {\n data: string | Blob;\n position: number;\n fileUri: string;\n eventDataSize: number;\n totalSize: number;\n}\nexport interface UseAudioRecorderState {\n startRecording: (_: RecordingConfig) => Promise<StartAudioStreamResult>;\n stopRecording: () => Promise<AudioStreamResult | null>;\n pauseRecording: () => void;\n resumeRecording: () => void;\n isRecording: boolean;\n isPaused: boolean;\n duration: number; // Duration of the recording\n size: number; // Size in bytes of the recorded audio\n}\n\ninterface RecorderState {\n isRecording: boolean;\n isPaused: boolean;\n duration: number;\n size: number;\n}\n\ntype RecorderAction =\n | { type: \"START\" | \"STOP\" | \"PAUSE\" | \"RESUME\" }\n | { type: \"UPDATE_STATUS\"; payload: { duration: number; size: number } };\n\nfunction recorderReducer(\n state: RecorderState,\n action: RecorderAction,\n): RecorderState {\n switch (action.type) {\n case \"START\":\n return {\n ...state,\n isRecording: true,\n isPaused: false,\n duration: 0,\n size: 0,\n };\n case \"STOP\":\n return { ...state, isRecording: false, isPaused: false };\n case \"PAUSE\":\n return { ...state, isPaused: true, isRecording: false };\n case \"RESUME\":\n return { ...state, isPaused: false, isRecording: true };\n case \"UPDATE_STATUS\":\n return {\n ...state,\n duration: action.payload.duration,\n size: action.payload.size,\n };\n default:\n return state;\n }\n}\nconst TAG = \"[ useAudioRecorder ] \";\n\nexport function useAudioRecorder({\n onAudioStream,\n debug = false,\n}: {\n onAudioStream?: (_: AudioDataEvent) => Promise<void>;\n debug?: boolean;\n}): UseAudioRecorderState {\n const [state, dispatch] = useReducer(recorderReducer, {\n isRecording: false,\n isPaused: false,\n duration: 0,\n size: 0,\n });\n\n console.log(`[useAudioRecorder] RENDERING state`, state);\n const logDebug = (message: string, data?: any) => {\n if (debug) {\n if (data) {\n console.log(`[useAudioRecorder] ${message}`, data);\n } else {\n console.log(`[useAudioRecorder] ${message}`);\n }\n }\n };\n\n const handleAudioEvent = useCallback(\n async (eventData: AudioEventPayload) => {\n const {\n fileUri,\n deltaSize,\n totalSize,\n lastEmittedSize,\n position,\n streamUuid,\n encoded,\n mimeType,\n buffer,\n } = eventData;\n logDebug(`useAudioRecorder] Received audio event:`, {\n fileUri,\n deltaSize,\n totalSize,\n position,\n mimeType,\n lastEmittedSize,\n streamUuid,\n encodedLength: encoded?.length,\n });\n if (deltaSize === 0) {\n // Ignore packet with no data\n return;\n }\n // Add more detailed handling here\n try {\n // Coming from native ( ios / android ) otherwise buffer is set\n if (Platform.OS !== \"web\") {\n // Read the audio file as a base64 string for comparison\n if (!encoded) {\n console.error(\"[useAudioRecorder] Encoded audio data is missing\");\n throw new Error(\"Encoded audio data is missing\");\n }\n await onAudioStream?.({\n data: encoded,\n position,\n fileUri,\n eventDataSize: deltaSize,\n totalSize,\n });\n\n // Below code is optional, used to compare encoded data to audio on file system\n // Fetch the audio data from the fileUri\n // const options = {\n // encoding: FileSystem.EncodingType.Base64,\n // position: lastEmittedSize,\n // length: deltaSize,\n // };\n // const base64Content = await FileSystem.readAsStringAsync(fileUri, options);\n // const binaryData = atob(base64Content);\n // const content = new Uint8Array(binaryData.length);\n // for (let i = 0; i < binaryData.length; i++) {\n // content[i] = binaryData.charCodeAt(i);\n // }\n // const audioBlob = new Blob([content], { type: 'application/octet-stream' }); // Create a Blob from the byte array\n // console.debug(`Read audio file (len: ${content.length}) vs ${deltaSize}`)\n } else if (buffer) {\n // Coming from web\n await onAudioStream?.({\n data: buffer,\n position,\n fileUri,\n eventDataSize: deltaSize,\n totalSize,\n });\n }\n } catch (error) {\n console.error(`${TAG} Error processing audio event:`, error);\n }\n },\n [logDebug, onAudioStream],\n );\n\n const checkStatus = useCallback(async () => {\n try {\n if (!state.isRecording) {\n logDebug(`${TAG} Not recording, exiting status check.`);\n return;\n }\n\n const status: AudioStreamStatus = ExpoAudioStreamModule.status();\n if (debug) {\n logDebug(\"[useAudioRecorder] Status:\", status);\n }\n\n if (!status.isRecording) {\n dispatch({ type: \"STOP\" });\n } else {\n dispatch({\n type: \"UPDATE_STATUS\",\n payload: { duration: status.duration, size: status.size },\n });\n }\n } catch (error) {\n console.error(`[useAudioRecorder] Error getting status:`, error);\n }\n }, [state.isRecording, logDebug]);\n\n useEffect(() => {\n let interval: number;\n if (state.isRecording) {\n interval = setInterval(checkStatus, 1000);\n }\n return () => {\n if (interval) {\n clearInterval(interval);\n }\n };\n }, [checkStatus, state.isRecording]);\n\n useEffect(() => {\n if (!onAudioStream) {\n return;\n }\n logDebug(`${TAG} Registering audio event listener`, onAudioStream);\n const subscribe = addAudioEventListener(handleAudioEvent);\n logDebug(`${TAG} Subscribed to audio event listener`, subscribe);\n\n return () => {\n logDebug(`${TAG} Removing audio event listener`);\n subscribe.remove();\n };\n }, [onAudioStream, handleAudioEvent, logDebug]);\n\n const startRecording = useCallback(\n async (recordingOptions: RecordingConfig) => {\n if (debug) {\n logDebug(`${TAG} start recoding`, recordingOptions);\n }\n const startResult: StartAudioStreamResult =\n await ExpoAudioStreamModule.startRecording(recordingOptions);\n dispatch({ type: \"START\" });\n\n return startResult;\n },\n [logDebug],\n );\n\n const stopRecording = useCallback(async () => {\n logDebug(`${TAG} stoping recording`);\n const stopResult: AudioStreamResult =\n await ExpoAudioStreamModule.stopRecording();\n logDebug(`${TAG} recording stopped`, stopResult);\n dispatch({ type: \"STOP\" });\n return stopResult;\n }, [logDebug]);\n\n const pauseRecording = useCallback(async () => {\n logDebug(`${TAG} pause recording`);\n const pauseResult = await ExpoAudioStreamModule.pauseRecording();\n dispatch({ type: \"PAUSE\" });\n return pauseResult;\n }, [logDebug]);\n\n const resumeRecording = useCallback(async () => {\n logDebug(`${TAG} resume recording`);\n const resumeResult = await ExpoAudioStreamModule.resumeRecording();\n dispatch({ type: \"RESUME\" });\n return resumeResult;\n }, [logDebug]);\n\n return {\n startRecording,\n stopRecording,\n pauseRecording,\n resumeRecording,\n isPaused: state.isPaused,\n isRecording: state.isRecording,\n duration: state.duration,\n size: state.size,\n };\n}\n"]}
1
+ {"version":3,"file":"useAudioRecording.js","sourceRoot":"","sources":["../src/useAudioRecording.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from \"expo-modules-core\";\nimport { useCallback, useEffect, useReducer, useRef } from \"react\";\n\nimport { addAudioEventListener } from \".\";\nimport {\n AudioEventPayload,\n AudioStreamResult,\n AudioStreamStatus,\n RecordingConfig,\n StartAudioStreamResult,\n} from \"./ExpoAudioStream.types\";\nimport ExpoAudioStreamModule from \"./ExpoAudioStreamModule\";\n\nexport interface AudioDataEvent {\n data: string | Blob;\n position: number;\n fileUri: string;\n eventDataSize: number;\n totalSize: number;\n}\n\nexport interface UseAudioRecorderProps {\n debug?: boolean;\n}\nexport interface UseAudioRecorderState {\n startRecording: (_: RecordingConfig) => Promise<StartAudioStreamResult>;\n stopRecording: () => Promise<AudioStreamResult | null>;\n pauseRecording: () => void;\n resumeRecording: () => void;\n isRecording: boolean;\n isPaused: boolean;\n duration: number; // Duration of the recording\n size: number; // Size in bytes of the recorded audio\n}\n\ninterface RecorderState {\n isRecording: boolean;\n isPaused: boolean;\n duration: number;\n size: number;\n}\n\ntype RecorderAction =\n | { type: \"START\" | \"STOP\" | \"PAUSE\" | \"RESUME\" }\n | { type: \"UPDATE_STATUS\"; payload: { duration: number; size: number } };\n\nfunction recorderReducer(\n state: RecorderState,\n action: RecorderAction,\n): RecorderState {\n switch (action.type) {\n case \"START\":\n return {\n ...state,\n isRecording: true,\n isPaused: false,\n duration: 0,\n size: 0,\n };\n case \"STOP\":\n return { ...state, isRecording: false, isPaused: false };\n case \"PAUSE\":\n return { ...state, isPaused: true, isRecording: false };\n case \"RESUME\":\n return { ...state, isPaused: false, isRecording: true };\n case \"UPDATE_STATUS\":\n return {\n ...state,\n duration: action.payload.duration,\n size: action.payload.size,\n };\n default:\n return state;\n }\n}\nconst TAG = \"[ useAudioRecorder ] \";\n\nexport function useAudioRecorder({\n debug = false,\n}: UseAudioRecorderProps = {}): UseAudioRecorderState {\n const [state, dispatch] = useReducer(recorderReducer, {\n isRecording: false,\n isPaused: false,\n duration: 0,\n size: 0,\n });\n\n const onAudioStreamRef = useRef<\n ((_: AudioDataEvent) => Promise<void>) | null\n >(null);\n\n const logDebug = useCallback(\n (message: string, data?: any) => {\n if (debug) {\n if (data) {\n console.log(`${TAG} ${message}`, data);\n } else {\n console.log(`${TAG} ${message}`);\n }\n }\n },\n [debug],\n );\n\n const handleAudioEvent = useCallback(\n async (eventData: AudioEventPayload) => {\n const {\n fileUri,\n deltaSize,\n totalSize,\n lastEmittedSize,\n position,\n streamUuid,\n encoded,\n mimeType,\n buffer,\n } = eventData;\n logDebug(`useAudioRecorder] Received audio event:`, {\n fileUri,\n deltaSize,\n totalSize,\n position,\n mimeType,\n lastEmittedSize,\n streamUuid,\n encodedLength: encoded?.length,\n });\n if (deltaSize === 0) {\n // Ignore packet with no data\n return;\n }\n try {\n // Coming from native ( ios / android ) otherwise buffer is set\n if (Platform.OS !== \"web\") {\n // Read the audio file as a base64 string for comparison\n if (!encoded) {\n console.error(`${TAG} Encoded audio data is missing`);\n throw new Error(\"Encoded audio data is missing\");\n }\n onAudioStreamRef.current?.({\n data: encoded,\n position,\n fileUri,\n eventDataSize: deltaSize,\n totalSize,\n });\n } else if (buffer) {\n // Coming from web\n onAudioStreamRef.current?.({\n data: buffer,\n position,\n fileUri,\n eventDataSize: deltaSize,\n totalSize,\n });\n }\n } catch (error) {\n console.error(`${TAG} Error processing audio event:`, error);\n }\n },\n [logDebug],\n );\n\n const checkStatus = useCallback(async () => {\n try {\n if (!state.isRecording) {\n logDebug(`${TAG} Not recording, exiting status check.`);\n return;\n }\n\n const status: AudioStreamStatus = ExpoAudioStreamModule.status();\n if (debug) {\n logDebug(`${TAG} Status:`, status);\n }\n\n if (!status.isRecording) {\n dispatch({ type: \"STOP\" });\n } else {\n dispatch({\n type: \"UPDATE_STATUS\",\n payload: { duration: status.duration, size: status.size },\n });\n }\n } catch (error) {\n console.error(`${TAG} Error getting status:`, error);\n }\n }, [state.isRecording, logDebug]);\n\n useEffect(() => {\n let interval: number;\n if (state.isRecording) {\n interval = setInterval(checkStatus, 1000);\n }\n return () => {\n if (interval) {\n clearInterval(interval);\n }\n };\n }, [checkStatus, state.isRecording]);\n\n useEffect(() => {\n logDebug(`${TAG} Registering audio event listener`);\n const subscribe = addAudioEventListener(handleAudioEvent);\n logDebug(`${TAG} Subscribed to audio event listener`, subscribe);\n\n return () => {\n logDebug(`${TAG} Removing audio event listener`);\n subscribe.remove();\n };\n }, [handleAudioEvent, logDebug]);\n\n const startRecording = useCallback(\n async (recordingOptions: RecordingConfig) => {\n if (debug) {\n logDebug(`${TAG} start recoding`, recordingOptions);\n }\n // remove onAudioStream from recordingOptions\n const { onAudioStream, ...options } = recordingOptions;\n if (typeof onAudioStream === \"function\") {\n onAudioStreamRef.current = onAudioStream;\n } else {\n console.warn(`${TAG} onAudioStream is not a function`, onAudioStream);\n onAudioStreamRef.current = null;\n }\n const startResult: StartAudioStreamResult =\n await ExpoAudioStreamModule.startRecording(options);\n dispatch({ type: \"START\" });\n\n return startResult;\n },\n [logDebug],\n );\n\n const stopRecording = useCallback(async () => {\n logDebug(`${TAG} stoping recording`);\n const stopResult: AudioStreamResult =\n await ExpoAudioStreamModule.stopRecording();\n onAudioStreamRef.current = null;\n logDebug(`${TAG} recording stopped`, stopResult);\n dispatch({ type: \"STOP\" });\n return stopResult;\n }, [logDebug]);\n\n const pauseRecording = useCallback(async () => {\n logDebug(`${TAG} pause recording`);\n const pauseResult = await ExpoAudioStreamModule.pauseRecording();\n dispatch({ type: \"PAUSE\" });\n return pauseResult;\n }, [logDebug]);\n\n const resumeRecording = useCallback(async () => {\n logDebug(`${TAG} resume recording`);\n const resumeResult = await ExpoAudioStreamModule.resumeRecording();\n dispatch({ type: \"RESUME\" });\n return resumeResult;\n }, [logDebug]);\n\n return {\n startRecording,\n stopRecording,\n pauseRecording,\n resumeRecording,\n isPaused: state.isPaused,\n isRecording: state.isRecording,\n duration: state.duration,\n size: state.size,\n };\n}\n"]}
package/docs/demo.gif ADDED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siteed/expo-audio-stream",
3
- "version": "0.7.5",
3
+ "version": "1.0.1",
4
4
  "description": "stream audio crossplatform",
5
5
  "license": "MIT",
6
6
  "main": "build/index.js",
@@ -49,7 +49,6 @@
49
49
  "eslint-plugin-prettier": "^5.1.3",
50
50
  "eslint-plugin-promise": "^6.1.1",
51
51
  "eslint-plugin-react": "^7.34.1",
52
- "expo-file-system": "^16.0.9",
53
52
  "expo-module-scripts": "^3.4.2",
54
53
  "expo-modules-core": "^1.11.12",
55
54
  "prettier": "^3.2.5",
@@ -57,7 +56,6 @@
57
56
  },
58
57
  "peerDependencies": {
59
58
  "expo": "*",
60
- "expo-file-system": "*",
61
59
  "react": "*",
62
60
  "react-native": "*"
63
61
  },
@@ -0,0 +1,42 @@
1
+ import React, { createContext, useContext } from "react";
2
+
3
+ import {
4
+ UseAudioRecorderProps,
5
+ UseAudioRecorderState,
6
+ useAudioRecorder,
7
+ } from "./useAudioRecording";
8
+
9
+ const AudioRecorderContext = createContext<UseAudioRecorderState>({
10
+ isRecording: false,
11
+ isPaused: false,
12
+ duration: 0,
13
+ size: 0,
14
+ // other properties filled on useAudioRecorder
15
+ } as UseAudioRecorderState);
16
+
17
+ interface AudioRecorderProviderProps {
18
+ children: React.ReactNode;
19
+ config?: UseAudioRecorderProps;
20
+ }
21
+
22
+ export const AudioRecorderProvider: React.FC<AudioRecorderProviderProps> = ({
23
+ children,
24
+ config = {},
25
+ }) => {
26
+ const audioRecorder = useAudioRecorder(config);
27
+ return (
28
+ <AudioRecorderContext.Provider value={audioRecorder}>
29
+ {children}
30
+ </AudioRecorderContext.Provider>
31
+ );
32
+ };
33
+
34
+ export const useSharedAudioRecorder = () => {
35
+ const context = useContext(AudioRecorderContext);
36
+ if (!context) {
37
+ throw new Error(
38
+ "useSharedAudioRecorder must be used within an AudioRecorderProvider",
39
+ );
40
+ }
41
+ return context;
42
+ };
@@ -1,3 +1,5 @@
1
+ import { AudioDataEvent } from "./useAudioRecording";
2
+
1
3
  export interface AudioEventPayload {
2
4
  encoded?: string;
3
5
  buffer?: Blob;
@@ -38,10 +40,10 @@ export interface AudioStreamStatus {
38
40
  }
39
41
 
40
42
  export type EncodingType = "pcm_16bit" | "pcm_8bit";
41
-
42
43
  export interface RecordingConfig {
43
44
  sampleRate?: 16000 | 44100 | 48000;
44
45
  channels?: 1 | 2; // 1 or 2 MONO or STEREO
45
46
  encoding?: EncodingType;
46
47
  interval?: number;
48
+ onAudioStream?: (_: AudioDataEvent) => Promise<void>;
47
49
  }
@@ -176,14 +176,6 @@ class ExpoAudioStreamWeb extends EventEmitter {
176
176
  interval: this.currentInterval,
177
177
  };
178
178
  }
179
-
180
- listAudioFiles() {
181
- // Not applicable on web
182
- }
183
-
184
- clearAudioFiles() {
185
- // Not applicable on web
186
- }
187
179
  }
188
180
 
189
181
  export default new ExpoAudioStreamWeb();
package/src/index.ts CHANGED
@@ -6,26 +6,22 @@ import {
6
6
 
7
7
  // Import the native module. On web, it will be resolved to ExpoAudioStream.web.ts
8
8
  // and on native platforms to ExpoAudioStream.ts
9
+ import {
10
+ AudioRecorderProvider,
11
+ useSharedAudioRecorder,
12
+ } from "./AudioRecorder.provider";
9
13
  import { AudioEventPayload } from "./ExpoAudioStream.types";
10
14
  import ExpoAudioStreamModule from "./ExpoAudioStreamModule";
11
15
  import {
12
- useAudioRecorder,
13
- UseAudioRecorderState,
14
16
  AudioDataEvent,
17
+ UseAudioRecorderState,
18
+ useAudioRecorder,
15
19
  } from "./useAudioRecording";
16
20
 
17
21
  const emitter = new EventEmitter(
18
22
  ExpoAudioStreamModule ?? NativeModulesProxy.ExpoAudioStream,
19
23
  );
20
24
 
21
- export function listAudioFiles(): Promise<string[]> {
22
- return ExpoAudioStreamModule.listAudioFiles();
23
- }
24
-
25
- export function clearAudioFiles(): Promise<void> {
26
- return ExpoAudioStreamModule.clearAudioFiles();
27
- }
28
-
29
25
  export function test(): void {
30
26
  return ExpoAudioStreamModule.test();
31
27
  }
@@ -37,5 +33,5 @@ export function addAudioEventListener(
37
33
  return emitter.addListener<AudioEventPayload>("AudioData", listener);
38
34
  }
39
35
 
40
- export type { AudioEventPayload, UseAudioRecorderState, AudioDataEvent };
41
- export { useAudioRecorder };
36
+ export { AudioRecorderProvider, useAudioRecorder, useSharedAudioRecorder };
37
+ export type { AudioDataEvent, AudioEventPayload, UseAudioRecorderState };
@@ -1,5 +1,5 @@
1
1
  import { Platform } from "expo-modules-core";
2
- import { useCallback, useEffect, useReducer } from "react";
2
+ import { useCallback, useEffect, useReducer, useRef } from "react";
3
3
 
4
4
  import { addAudioEventListener } from ".";
5
5
  import {
@@ -18,6 +18,10 @@ export interface AudioDataEvent {
18
18
  eventDataSize: number;
19
19
  totalSize: number;
20
20
  }
21
+
22
+ export interface UseAudioRecorderProps {
23
+ debug?: boolean;
24
+ }
21
25
  export interface UseAudioRecorderState {
22
26
  startRecording: (_: RecordingConfig) => Promise<StartAudioStreamResult>;
23
27
  stopRecording: () => Promise<AudioStreamResult | null>;
@@ -72,12 +76,8 @@ function recorderReducer(
72
76
  const TAG = "[ useAudioRecorder ] ";
73
77
 
74
78
  export function useAudioRecorder({
75
- onAudioStream,
76
79
  debug = false,
77
- }: {
78
- onAudioStream?: (_: AudioDataEvent) => Promise<void>;
79
- debug?: boolean;
80
- }): UseAudioRecorderState {
80
+ }: UseAudioRecorderProps = {}): UseAudioRecorderState {
81
81
  const [state, dispatch] = useReducer(recorderReducer, {
82
82
  isRecording: false,
83
83
  isPaused: false,
@@ -85,16 +85,22 @@ export function useAudioRecorder({
85
85
  size: 0,
86
86
  });
87
87
 
88
- console.log(`[useAudioRecorder] RENDERING state`, state);
89
- const logDebug = (message: string, data?: any) => {
90
- if (debug) {
91
- if (data) {
92
- console.log(`[useAudioRecorder] ${message}`, data);
93
- } else {
94
- console.log(`[useAudioRecorder] ${message}`);
88
+ const onAudioStreamRef = useRef<
89
+ ((_: AudioDataEvent) => Promise<void>) | null
90
+ >(null);
91
+
92
+ const logDebug = useCallback(
93
+ (message: string, data?: any) => {
94
+ if (debug) {
95
+ if (data) {
96
+ console.log(`${TAG} ${message}`, data);
97
+ } else {
98
+ console.log(`${TAG} ${message}`);
99
+ }
95
100
  }
96
- }
97
- };
101
+ },
102
+ [debug],
103
+ );
98
104
 
99
105
  const handleAudioEvent = useCallback(
100
106
  async (eventData: AudioEventPayload) => {
@@ -123,41 +129,24 @@ export function useAudioRecorder({
123
129
  // Ignore packet with no data
124
130
  return;
125
131
  }
126
- // Add more detailed handling here
127
132
  try {
128
133
  // Coming from native ( ios / android ) otherwise buffer is set
129
134
  if (Platform.OS !== "web") {
130
135
  // Read the audio file as a base64 string for comparison
131
136
  if (!encoded) {
132
- console.error("[useAudioRecorder] Encoded audio data is missing");
137
+ console.error(`${TAG} Encoded audio data is missing`);
133
138
  throw new Error("Encoded audio data is missing");
134
139
  }
135
- await onAudioStream?.({
140
+ onAudioStreamRef.current?.({
136
141
  data: encoded,
137
142
  position,
138
143
  fileUri,
139
144
  eventDataSize: deltaSize,
140
145
  totalSize,
141
146
  });
142
-
143
- // Below code is optional, used to compare encoded data to audio on file system
144
- // Fetch the audio data from the fileUri
145
- // const options = {
146
- // encoding: FileSystem.EncodingType.Base64,
147
- // position: lastEmittedSize,
148
- // length: deltaSize,
149
- // };
150
- // const base64Content = await FileSystem.readAsStringAsync(fileUri, options);
151
- // const binaryData = atob(base64Content);
152
- // const content = new Uint8Array(binaryData.length);
153
- // for (let i = 0; i < binaryData.length; i++) {
154
- // content[i] = binaryData.charCodeAt(i);
155
- // }
156
- // const audioBlob = new Blob([content], { type: 'application/octet-stream' }); // Create a Blob from the byte array
157
- // console.debug(`Read audio file (len: ${content.length}) vs ${deltaSize}`)
158
147
  } else if (buffer) {
159
148
  // Coming from web
160
- await onAudioStream?.({
149
+ onAudioStreamRef.current?.({
161
150
  data: buffer,
162
151
  position,
163
152
  fileUri,
@@ -169,7 +158,7 @@ export function useAudioRecorder({
169
158
  console.error(`${TAG} Error processing audio event:`, error);
170
159
  }
171
160
  },
172
- [logDebug, onAudioStream],
161
+ [logDebug],
173
162
  );
174
163
 
175
164
  const checkStatus = useCallback(async () => {
@@ -181,7 +170,7 @@ export function useAudioRecorder({
181
170
 
182
171
  const status: AudioStreamStatus = ExpoAudioStreamModule.status();
183
172
  if (debug) {
184
- logDebug("[useAudioRecorder] Status:", status);
173
+ logDebug(`${TAG} Status:`, status);
185
174
  }
186
175
 
187
176
  if (!status.isRecording) {
@@ -193,7 +182,7 @@ export function useAudioRecorder({
193
182
  });
194
183
  }
195
184
  } catch (error) {
196
- console.error(`[useAudioRecorder] Error getting status:`, error);
185
+ console.error(`${TAG} Error getting status:`, error);
197
186
  }
198
187
  }, [state.isRecording, logDebug]);
199
188
 
@@ -210,10 +199,7 @@ export function useAudioRecorder({
210
199
  }, [checkStatus, state.isRecording]);
211
200
 
212
201
  useEffect(() => {
213
- if (!onAudioStream) {
214
- return;
215
- }
216
- logDebug(`${TAG} Registering audio event listener`, onAudioStream);
202
+ logDebug(`${TAG} Registering audio event listener`);
217
203
  const subscribe = addAudioEventListener(handleAudioEvent);
218
204
  logDebug(`${TAG} Subscribed to audio event listener`, subscribe);
219
205
 
@@ -221,15 +207,23 @@ export function useAudioRecorder({
221
207
  logDebug(`${TAG} Removing audio event listener`);
222
208
  subscribe.remove();
223
209
  };
224
- }, [onAudioStream, handleAudioEvent, logDebug]);
210
+ }, [handleAudioEvent, logDebug]);
225
211
 
226
212
  const startRecording = useCallback(
227
213
  async (recordingOptions: RecordingConfig) => {
228
214
  if (debug) {
229
215
  logDebug(`${TAG} start recoding`, recordingOptions);
230
216
  }
217
+ // remove onAudioStream from recordingOptions
218
+ const { onAudioStream, ...options } = recordingOptions;
219
+ if (typeof onAudioStream === "function") {
220
+ onAudioStreamRef.current = onAudioStream;
221
+ } else {
222
+ console.warn(`${TAG} onAudioStream is not a function`, onAudioStream);
223
+ onAudioStreamRef.current = null;
224
+ }
231
225
  const startResult: StartAudioStreamResult =
232
- await ExpoAudioStreamModule.startRecording(recordingOptions);
226
+ await ExpoAudioStreamModule.startRecording(options);
233
227
  dispatch({ type: "START" });
234
228
 
235
229
  return startResult;
@@ -241,6 +235,7 @@ export function useAudioRecorder({
241
235
  logDebug(`${TAG} stoping recording`);
242
236
  const stopResult: AudioStreamResult =
243
237
  await ExpoAudioStreamModule.stopRecording();
238
+ onAudioStreamRef.current = null;
244
239
  logDebug(`${TAG} recording stopped`, stopResult);
245
240
  dispatch({ type: "STOP" });
246
241
  return stopResult;