@siteed/expo-audio-stream 1.0.2 → 1.0.5
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/.size-limit.json +6 -0
- package/README.md +18 -176
- package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +1 -0
- package/app.plugin.js +1 -1
- package/build/AudioAnalysis/AudioAnalysis.types.d.ts +74 -0
- package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -0
- package/build/AudioAnalysis/AudioAnalysis.types.js +3 -0
- package/build/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
- package/build/AudioAnalysis/extractAudioAnalysis.d.ts +20 -0
- package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -0
- package/build/AudioAnalysis/extractAudioAnalysis.js +88 -0
- package/build/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
- package/build/AudioAnalysis/extractWaveform.d.ts +8 -0
- package/build/AudioAnalysis/extractWaveform.d.ts.map +1 -0
- package/build/AudioAnalysis/extractWaveform.js +14 -0
- package/build/AudioAnalysis/extractWaveform.js.map +1 -0
- package/build/AudioRecorder.provider.d.ts +15 -2
- package/build/AudioRecorder.provider.d.ts.map +1 -1
- package/build/AudioRecorder.provider.js +21 -8
- package/build/AudioRecorder.provider.js.map +1 -1
- package/build/ExpoAudioStream.native.d.ts.map +1 -1
- package/build/ExpoAudioStream.native.js +2 -2
- package/build/ExpoAudioStream.native.js.map +1 -1
- package/build/ExpoAudioStream.types.d.ts +33 -89
- package/build/ExpoAudioStream.types.d.ts.map +1 -1
- package/build/ExpoAudioStream.types.js.map +1 -1
- package/build/ExpoAudioStream.web.d.ts +10 -9
- package/build/ExpoAudioStream.web.d.ts.map +1 -1
- package/build/ExpoAudioStream.web.js +44 -25
- package/build/ExpoAudioStream.web.js.map +1 -1
- package/build/ExpoAudioStreamModule.d.ts.map +1 -1
- package/build/ExpoAudioStreamModule.js +13 -8
- package/build/ExpoAudioStreamModule.js.map +1 -1
- package/build/{WebRecorder.d.ts → WebRecorder.web.d.ts} +13 -9
- package/build/WebRecorder.web.d.ts.map +1 -0
- package/build/{WebRecorder.js → WebRecorder.web.js} +118 -63
- package/build/WebRecorder.web.js.map +1 -0
- package/build/constants.d.ts +11 -0
- package/build/constants.d.ts.map +1 -0
- package/build/constants.js +14 -0
- package/build/constants.js.map +1 -0
- package/build/events.d.ts +18 -0
- package/build/events.d.ts.map +1 -0
- package/build/events.js +15 -0
- package/build/events.js.map +1 -0
- package/build/index.d.ts +9 -17
- package/build/index.d.ts.map +1 -1
- package/build/index.js +7 -113
- package/build/index.js.map +1 -1
- package/build/logger.d.ts +9 -0
- package/build/logger.d.ts.map +1 -0
- package/build/logger.js +13 -0
- package/build/logger.js.map +1 -0
- package/build/useAudioRecorder.d.ts +20 -0
- package/build/useAudioRecorder.d.ts.map +1 -0
- package/build/{useAudioRecording.js → useAudioRecorder.js} +90 -86
- package/build/useAudioRecorder.js.map +1 -0
- package/build/utils/BlobFix.d.ts +9 -0
- package/build/utils/BlobFix.d.ts.map +1 -0
- package/build/utils/BlobFix.js +494 -0
- package/build/utils/BlobFix.js.map +1 -0
- package/build/utils/concatenateBuffers.d.ts +8 -0
- package/build/utils/concatenateBuffers.d.ts.map +1 -0
- package/build/utils/concatenateBuffers.js +21 -0
- package/build/utils/concatenateBuffers.js.map +1 -0
- package/build/utils/convertPCMToFloat32.d.ts +11 -0
- package/build/utils/convertPCMToFloat32.d.ts.map +1 -0
- package/build/utils/convertPCMToFloat32.js +54 -0
- package/build/utils/convertPCMToFloat32.js.map +1 -0
- package/build/utils/encodingToBitDepth.d.ts +5 -0
- package/build/utils/encodingToBitDepth.d.ts.map +1 -0
- package/build/utils/encodingToBitDepth.js +13 -0
- package/build/utils/encodingToBitDepth.js.map +1 -0
- package/build/utils/getWavFileInfo.d.ts +26 -0
- package/build/utils/getWavFileInfo.d.ts.map +1 -0
- package/build/utils/getWavFileInfo.js +92 -0
- package/build/utils/getWavFileInfo.js.map +1 -0
- package/build/utils/writeWavHeader.d.ts +9 -0
- package/build/utils/writeWavHeader.d.ts.map +1 -0
- package/build/utils/writeWavHeader.js +41 -0
- package/build/utils/writeWavHeader.js.map +1 -0
- package/build/workers/InlineFeaturesExtractor.web.d.ts +2 -0
- package/build/workers/InlineFeaturesExtractor.web.d.ts.map +1 -0
- package/build/workers/InlineFeaturesExtractor.web.js +303 -0
- package/build/workers/InlineFeaturesExtractor.web.js.map +1 -0
- package/build/workers/inlineAudioWebWorker.web.d.ts +2 -0
- package/build/workers/inlineAudioWebWorker.web.d.ts.map +1 -0
- package/build/workers/inlineAudioWebWorker.web.js +243 -0
- package/build/workers/inlineAudioWebWorker.web.js.map +1 -0
- package/expo-module.config.json +8 -17
- package/ios/AudioStreamManager.swift +40 -2
- package/ios/ExpoAudioStreamModule.swift +11 -0
- package/ios/RecordingResult.swift +1 -0
- package/package.json +72 -64
- package/plugin/build/index.d.ts +1 -1
- package/plugin/build/index.js +7 -7
- package/plugin/src/index.ts +47 -47
- package/plugin/tsconfig.json +8 -13
- package/publish.sh +0 -0
- package/src/AudioAnalysis/AudioAnalysis.types.ts +84 -0
- package/src/AudioAnalysis/extractAudioAnalysis.ts +147 -0
- package/src/AudioAnalysis/extractWaveform.ts +25 -0
- package/src/AudioRecorder.provider.tsx +59 -31
- package/src/ExpoAudioStream.native.ts +2 -2
- package/src/ExpoAudioStream.types.ts +58 -116
- package/src/ExpoAudioStream.web.ts +233 -205
- package/src/ExpoAudioStreamModule.ts +18 -12
- package/src/WebRecorder.web.ts +433 -0
- package/src/constants.ts +18 -0
- package/src/events.ts +39 -0
- package/src/index.ts +15 -176
- package/src/logger.ts +23 -0
- package/src/useAudioRecorder.tsx +420 -0
- package/src/utils/BlobFix.ts +550 -0
- package/src/utils/concatenateBuffers.ts +24 -0
- package/src/utils/convertPCMToFloat32.ts +75 -0
- package/src/utils/encodingToBitDepth.ts +18 -0
- package/src/utils/getWavFileInfo.ts +132 -0
- package/src/utils/writeWavHeader.ts +56 -0
- package/src/workers/InlineFeaturesExtractor.web.tsx +302 -0
- package/src/workers/inlineAudioWebWorker.web.tsx +242 -0
- package/tsconfig.json +12 -7
- package/build/WebRecorder.d.ts.map +0 -1
- package/build/WebRecorder.js.map +0 -1
- package/build/inlineAudioWebWorker.d.ts +0 -3
- package/build/inlineAudioWebWorker.d.ts.map +0 -1
- package/build/inlineAudioWebWorker.js +0 -340
- package/build/inlineAudioWebWorker.js.map +0 -1
- package/build/useAudioRecording.d.ts +0 -38
- package/build/useAudioRecording.d.ts.map +0 -1
- package/build/useAudioRecording.js.map +0 -1
- package/build/utils.d.ts +0 -31
- package/build/utils.d.ts.map +0 -1
- package/build/utils.js +0 -143
- package/build/utils.js.map +0 -1
- package/src/WebRecorder.ts +0 -364
- package/src/inlineAudioWebWorker.tsx +0 -340
- package/src/useAudioRecording.tsx +0 -410
- package/src/utils.ts +0 -189
package/.size-limit.json
ADDED
package/README.md
CHANGED
|
@@ -1,186 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1 align="center">
|
|
3
|
+
@siteed/expo-audio-stream
|
|
4
|
+
<br />
|
|
5
|
+
<br />
|
|
6
|
+
<strong>@siteed/expo-audio-stream<strong> is a comprehensive library designed to facilitate real-time audio processing and streaming across iOS, Android, and web platforms.
|
|
7
|
+
<br />
|
|
8
|
+
<br />
|
|
9
|
+
<a href="https://deeeed.github.io/expo-audio-stream/playground/">
|
|
10
|
+
<img src="../../docs/demo.gif" alt="Screenshot Playground">
|
|
11
|
+
</a>
|
|
12
|
+
</h1>
|
|
13
|
+
</div>
|
|
2
14
|
|
|
3
|
-
`@siteed/expo-audio-stream` is a comprehensive library designed to facilitate real-time audio processing and streaming across iOS, Android, and web platforms.
|
|
4
15
|
|
|
5
16
|
## Features
|
|
6
17
|
|
|
7
18
|
- Real-time audio streaming across iOS, Android, and web.
|
|
8
19
|
- Configurable intervals for audio buffer receipt.
|
|
9
20
|
- Automated microphone permissions setup in managed Expo projects.
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
21
|
+
- Background audio recording on iOS.
|
|
22
|
+
- Audio features extraction during recording.
|
|
23
|
+
- Consistent WAV PCM recording format across all platforms.
|
|
13
24
|
|
|
14
|
-
##
|
|
25
|
+
## Documentation
|
|
15
26
|
|
|
16
|
-
|
|
27
|
+
For detailed documentation, please refer to the [Getting Started Guide](https://deeeed.github.io/expo-audio-stream/docs/).
|
|
17
28
|
|
|
18
|
-

|
|
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 playground ios
|
|
27
|
-
yarn playground android
|
|
28
|
-
yarn playground web
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Installation
|
|
32
|
-
|
|
33
|
-
To install `@siteed/expo-audio-stream`, add it to your project using npm or Yarn:
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
npm install @siteed/expo-audio-stream
|
|
37
|
-
# or
|
|
38
|
-
yarn add @siteed/expo-audio-stream
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
### Configuring with app.json
|
|
43
|
-
|
|
44
|
-
To ensure expo-audio-stream works correctly with Expo, you must add it as a plugin in your app.json configuration file.
|
|
45
|
-
|
|
46
|
-
```json
|
|
47
|
-
{
|
|
48
|
-
"expo": {
|
|
49
|
-
"plugins": ["@siteed/expo-audio-stream"]
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
Make sure to run `npx expo prebuild` after adding the plugin to your app.json file.
|
|
55
|
-
|
|
56
|
-
## Usage
|
|
57
|
-
|
|
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 `playground/` 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.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
#### Standalone Usage
|
|
67
|
-
|
|
68
|
-
```tsx
|
|
69
|
-
import {
|
|
70
|
-
useAudioRecorder,
|
|
71
|
-
AudioStreamResult,
|
|
72
|
-
} from '@siteed/expo-audio-stream';
|
|
73
|
-
|
|
74
|
-
export default function App() {
|
|
75
|
-
const { startRecording, stopRecording, duration, size, isRecording } = useAudioRecorder({
|
|
76
|
-
debug: true,
|
|
77
|
-
onAudioStream: (audioData: Blob) => {
|
|
78
|
-
console.log(`audio event`,audioData);
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
const handleStart = async () => {
|
|
83
|
-
const { granted } = await Audio.requestPermissionsAsync();
|
|
84
|
-
if (granted) {
|
|
85
|
-
const fileUri = await startRecording({interval: 500});
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const handleStop = async () => {
|
|
90
|
-
const result: AudioStreamResult = await stopRecording();
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const renderRecording = () => (
|
|
94
|
-
<View>
|
|
95
|
-
<Text>Duration: {duration} ms</Text>
|
|
96
|
-
<Text>Size: {size} bytes</Text>
|
|
97
|
-
<Button title="Stop Recording" onPress={handleStop} />
|
|
98
|
-
</View>
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
const renderStopped = () => (
|
|
102
|
-
<View>
|
|
103
|
-
<Button title="Start Recording" onPress={handleStart} />
|
|
104
|
-
</View>
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
return (
|
|
108
|
-
<View>
|
|
109
|
-
<Button title="Request Permission" onPress={() => Audio.requestPermissionsAsync()} />
|
|
110
|
-
{isRecording ? renderRecording() : renderStopped()}
|
|
111
|
-
</View>
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
The library also exposes an `addAudioEventListener` function that provides an `AudioEventPayload` object that you can subscribe to:
|
|
117
|
-
```tsx
|
|
118
|
-
import { addAudioEventListener } from '@siteed/expo-audio-stream';
|
|
119
|
-
|
|
120
|
-
useEffect(() => {
|
|
121
|
-
const subscribe = addAudioEventListener(async ({fileUri, deltaSize, totalSize, from, streamUuid, encoded, mimeType, buffer}) => {
|
|
122
|
-
log(`Received audio event:`, {fileUri, deltaSize, totalSize, mimeType, from, streamUuid, encodedLength: encoded?.length, bufferLength: buffer?.length})
|
|
123
|
-
});
|
|
124
|
-
return () => subscribe.remove();
|
|
125
|
-
}, []);
|
|
126
|
-
```
|
|
127
|
-
|
|
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
|
|
182
|
-
|
|
183
|
-
- on Android and IOS, audio is recorded in wav format, 16khz sample rate, 16 bit depth, 1 channel.
|
|
184
|
-
- on web, it usually records in opus but it depends on the browser configuration.
|
|
185
|
-
|
|
186
|
-
If you want to process the audio livestream directly, I recommend having another encoding step to align the audio format across platforms.
|
|
@@ -330,6 +330,7 @@ class AudioRecorderManager(
|
|
|
330
330
|
// Create result bundle
|
|
331
331
|
val result = bundleOf(
|
|
332
332
|
"fileUri" to audioFile?.toURI().toString(),
|
|
333
|
+
"filename" to audioFile?.name,
|
|
333
334
|
"durationMs" to duration,
|
|
334
335
|
"channels" to recordingConfig.channels,
|
|
335
336
|
"bitDepth" to when (recordingConfig.encoding) {
|
package/app.plugin.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports = require(
|
|
1
|
+
module.exports = require('./plugin/build')
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents various audio features extracted from an audio signal.
|
|
3
|
+
*/
|
|
4
|
+
export interface AudioFeatures {
|
|
5
|
+
energy: number;
|
|
6
|
+
mfcc: number[];
|
|
7
|
+
rms: number;
|
|
8
|
+
minAmplitude: number;
|
|
9
|
+
maxAmplitude: number;
|
|
10
|
+
zcr: number;
|
|
11
|
+
spectralCentroid: number;
|
|
12
|
+
spectralFlatness: number;
|
|
13
|
+
spectralRolloff: number;
|
|
14
|
+
spectralBandwidth: number;
|
|
15
|
+
chromagram: number[];
|
|
16
|
+
tempo: number;
|
|
17
|
+
hnr: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Options to specify which audio features to extract.
|
|
21
|
+
*/
|
|
22
|
+
export interface AudioFeaturesOptions {
|
|
23
|
+
energy?: boolean;
|
|
24
|
+
mfcc?: boolean;
|
|
25
|
+
rms?: boolean;
|
|
26
|
+
zcr?: boolean;
|
|
27
|
+
spectralCentroid?: boolean;
|
|
28
|
+
spectralFlatness?: boolean;
|
|
29
|
+
spectralRolloff?: boolean;
|
|
30
|
+
spectralBandwidth?: boolean;
|
|
31
|
+
chromagram?: boolean;
|
|
32
|
+
tempo?: boolean;
|
|
33
|
+
hnr?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Represents a single data point in the audio analysis.
|
|
37
|
+
*/
|
|
38
|
+
export interface DataPoint {
|
|
39
|
+
id: number;
|
|
40
|
+
amplitude: number;
|
|
41
|
+
activeSpeech?: boolean;
|
|
42
|
+
dB?: number;
|
|
43
|
+
silent?: boolean;
|
|
44
|
+
features?: AudioFeatures;
|
|
45
|
+
startTime?: number;
|
|
46
|
+
endTime?: number;
|
|
47
|
+
startPosition?: number;
|
|
48
|
+
endPosition?: number;
|
|
49
|
+
samples?: number;
|
|
50
|
+
speaker?: number;
|
|
51
|
+
}
|
|
52
|
+
export type AmplitudeAlgorithm = 'peak' | 'rms';
|
|
53
|
+
/**
|
|
54
|
+
* Represents the complete data from the audio analysis.
|
|
55
|
+
*/
|
|
56
|
+
export interface AudioAnalysis {
|
|
57
|
+
pointsPerSecond: number;
|
|
58
|
+
durationMs: number;
|
|
59
|
+
bitDepth: number;
|
|
60
|
+
samples: number;
|
|
61
|
+
numberOfChannels: number;
|
|
62
|
+
sampleRate: number;
|
|
63
|
+
dataPoints: DataPoint[];
|
|
64
|
+
amplitudeAlgorithm: AmplitudeAlgorithm;
|
|
65
|
+
amplitudeRange: {
|
|
66
|
+
min: number;
|
|
67
|
+
max: number;
|
|
68
|
+
};
|
|
69
|
+
speakerChanges?: {
|
|
70
|
+
timestamp: number;
|
|
71
|
+
speaker: number;
|
|
72
|
+
}[];
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=AudioAnalysis.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AudioAnalysis.types.d.ts","sourceRoot":"","sources":["../../src/AudioAnalysis/AudioAnalysis.types.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;IACxB,eAAe,EAAE,MAAM,CAAA;IACvB,iBAAiB,EAAE,MAAM,CAAA;IACzB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;CACd;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,GAAG,CAAC,EAAE,OAAO,CAAA;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,aAAa,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAEhB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAEhB,OAAO,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,KAAK,CAAA;AAE/C;;GAEG;AACH,MAAM,WAAW,aAAa;IAC1B,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,SAAS,EAAE,CAAA;IACvB,kBAAkB,EAAE,kBAAkB,CAAA;IACtC,cAAc,EAAE;QACZ,GAAG,EAAE,MAAM,CAAA;QACX,GAAG,EAAE,MAAM,CAAA;KACd,CAAA;IAED,cAAc,CAAC,EAAE;QACb,SAAS,EAAE,MAAM,CAAA;QACjB,OAAO,EAAE,MAAM,CAAA;KAClB,EAAE,CAAA;CACN"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AudioAnalysis.types.js","sourceRoot":"","sources":["../../src/AudioAnalysis/AudioAnalysis.types.ts"],"names":[],"mappings":"AAAA,sEAAsE","sourcesContent":["// packages/expo-audio-stream/src/AudioAnalysis/AudioAnalysis.types.ts\n\n/**\n * Represents various audio features extracted from an audio signal.\n */\nexport interface AudioFeatures {\n energy: number // The infinite integral of the squared signal, representing the overall energy of the audio.\n mfcc: number[] // Mel-frequency cepstral coefficients, describing the short-term power spectrum of a sound.\n rms: number // Root mean square value, indicating the amplitude of the audio signal.\n minAmplitude: number // Minimum amplitude value in the audio signal.\n maxAmplitude: number // Maximum amplitude value in the audio signal.\n zcr: number // Zero-crossing rate, indicating the rate at which the signal changes sign.\n spectralCentroid: number // The center of mass of the spectrum, indicating the brightness of the sound.\n spectralFlatness: number // Measure of the flatness of the spectrum, indicating how noise-like the signal is.\n spectralRolloff: number // The frequency below which a specified percentage (usually 85%) of the total spectral energy lies.\n spectralBandwidth: number // The width of the spectrum, indicating the range of frequencies present.\n chromagram: number[] // Chromagram, representing the 12 different pitch classes of the audio.\n tempo: number // Estimated tempo of the audio signal, measured in beats per minute (BPM).\n hnr: number // Harmonics-to-noise ratio, indicating the proportion of harmonics to noise in the audio signal.\n}\n\n/**\n * Options to specify which audio features to extract.\n */\nexport interface AudioFeaturesOptions {\n energy?: boolean\n mfcc?: boolean\n rms?: boolean\n zcr?: boolean\n spectralCentroid?: boolean\n spectralFlatness?: boolean\n spectralRolloff?: boolean\n spectralBandwidth?: boolean\n chromagram?: boolean\n tempo?: boolean\n hnr?: boolean\n}\n\n/**\n * Represents a single data point in the audio analysis.\n */\nexport interface DataPoint {\n id: number\n amplitude: number\n activeSpeech?: boolean\n dB?: number\n silent?: boolean\n features?: AudioFeatures\n startTime?: number\n endTime?: number\n // start / end position in bytes\n startPosition?: number\n endPosition?: number\n // number of audio samples for this point (samples size depends on bit depth)\n samples?: number\n // TODO: speaker detection\n speaker?: number\n}\n\nexport type AmplitudeAlgorithm = 'peak' | 'rms'\n\n/**\n * Represents the complete data from the audio analysis.\n */\nexport interface AudioAnalysis {\n pointsPerSecond: number // How many consolidated value per second\n durationMs: number // Duration of the audio in milliseconds\n bitDepth: number // Bit depth of the audio\n samples: number // Size of the audio in bytes\n numberOfChannels: number // Number of audio channels\n sampleRate: number // Sample rate of the audio\n dataPoints: DataPoint[] // Array of data points from the analysis.\n amplitudeAlgorithm: AmplitudeAlgorithm // Algorithm used to calculate amplitude values.\n amplitudeRange: {\n min: number\n max: number\n }\n // TODO: speaker detection\n speakerChanges?: {\n timestamp: number // Timestamp of the speaker change in milliseconds.\n speaker: number // Speaker identifier.\n }[]\n}\n\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AmplitudeAlgorithm, AudioAnalysis, AudioFeaturesOptions } from './AudioAnalysis.types';
|
|
2
|
+
import { WavFileInfo } from '../utils/getWavFileInfo';
|
|
3
|
+
export interface ExtractAudioAnalysisProps {
|
|
4
|
+
fileUri?: string;
|
|
5
|
+
wavMetadata?: WavFileInfo;
|
|
6
|
+
arrayBuffer?: ArrayBuffer;
|
|
7
|
+
bitDepth?: number;
|
|
8
|
+
skipWavHeader?: boolean;
|
|
9
|
+
durationMs?: number;
|
|
10
|
+
sampleRate?: number;
|
|
11
|
+
numberOfChannels?: number;
|
|
12
|
+
algorithm?: AmplitudeAlgorithm;
|
|
13
|
+
position?: number;
|
|
14
|
+
length?: number;
|
|
15
|
+
pointsPerSecond?: number;
|
|
16
|
+
features?: AudioFeaturesOptions;
|
|
17
|
+
featuresExtratorUrl?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const extractAudioAnalysis: ({ fileUri, pointsPerSecond, arrayBuffer, bitDepth, skipWavHeader, durationMs, sampleRate, numberOfChannels, algorithm, features, featuresExtratorUrl, }: ExtractAudioAnalysisProps) => Promise<AudioAnalysis>;
|
|
20
|
+
//# sourceMappingURL=extractAudioAnalysis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractAudioAnalysis.d.ts","sourceRoot":"","sources":["../../src/AudioAnalysis/extractAudioAnalysis.ts"],"names":[],"mappings":"AACA,OAAO,EACH,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACvB,MAAM,uBAAuB,CAAA;AAK9B,OAAO,EAAkB,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAKrE,MAAM,WAAW,yBAAyB;IACtC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,EAAE,kBAAkB,CAAA;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,EAAE,oBAAoB,CAAA;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC/B;AAED,eAAO,MAAM,oBAAoB,4JAY9B,yBAAyB,KAAG,OAAO,CAAC,aAAa,CAsGnD,CAAA"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import ExpoAudioStreamModule from '../ExpoAudioStreamModule';
|
|
2
|
+
import { isWeb } from '../constants';
|
|
3
|
+
import { getLogger } from '../logger';
|
|
4
|
+
import { convertPCMToFloat32 } from '../utils/convertPCMToFloat32';
|
|
5
|
+
import { getWavFileInfo } from '../utils/getWavFileInfo';
|
|
6
|
+
import { InlineFeaturesExtractor } from '../workers/InlineFeaturesExtractor.web';
|
|
7
|
+
const logger = getLogger('extractAudioAnalysis');
|
|
8
|
+
export const extractAudioAnalysis = async ({ fileUri, pointsPerSecond = 20, arrayBuffer, bitDepth, skipWavHeader = true, durationMs, sampleRate, numberOfChannels, algorithm = 'rms', features, featuresExtratorUrl, }) => {
|
|
9
|
+
if (isWeb) {
|
|
10
|
+
if (!arrayBuffer && !fileUri) {
|
|
11
|
+
throw new Error('Either arrayBuffer or fileUri must be provided');
|
|
12
|
+
}
|
|
13
|
+
if (!arrayBuffer) {
|
|
14
|
+
logger.log(`fetching fileUri`, fileUri);
|
|
15
|
+
const response = await fetch(fileUri);
|
|
16
|
+
if (!response.ok) {
|
|
17
|
+
throw new Error(`Failed to fetch fileUri: ${response.statusText}`);
|
|
18
|
+
}
|
|
19
|
+
arrayBuffer = await response.arrayBuffer();
|
|
20
|
+
logger.log(`fetched fileUri`, arrayBuffer.byteLength, arrayBuffer);
|
|
21
|
+
}
|
|
22
|
+
// Create a new copy of the ArrayBuffer to avoid detachment issues
|
|
23
|
+
const bufferCopy = arrayBuffer.slice(0);
|
|
24
|
+
logger.log(`extractAudioAnalysis skipWavHeader=${skipWavHeader} bitDepth=${bitDepth} len=${bufferCopy.byteLength}`, bufferCopy.slice(0, 100));
|
|
25
|
+
let actualBitDepth = bitDepth;
|
|
26
|
+
if (!actualBitDepth) {
|
|
27
|
+
logger.log(`extractAudioAnalysis bitDepth not provided -- getting wav file info`);
|
|
28
|
+
const fileInfo = await getWavFileInfo(bufferCopy);
|
|
29
|
+
actualBitDepth = fileInfo.bitDepth;
|
|
30
|
+
}
|
|
31
|
+
logger.log(`extractAudioAnalysis actualBitDepth=${actualBitDepth}`);
|
|
32
|
+
const { pcmValues: channelData, min, max, } = await convertPCMToFloat32({
|
|
33
|
+
buffer: arrayBuffer,
|
|
34
|
+
bitDepth: actualBitDepth,
|
|
35
|
+
skipWavHeader,
|
|
36
|
+
});
|
|
37
|
+
logger.log(`extractAudioAnalysis skipWaveHeader=${skipWavHeader} convertPCMToFloat32 length=${channelData.length} range: [ ${min} :: ${max} ]`);
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
let worker;
|
|
40
|
+
if (featuresExtratorUrl) {
|
|
41
|
+
worker = new Worker(new URL(featuresExtratorUrl, window.location.href));
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const blob = new Blob([InlineFeaturesExtractor], {
|
|
45
|
+
type: 'application/javascript',
|
|
46
|
+
});
|
|
47
|
+
const url = URL.createObjectURL(blob);
|
|
48
|
+
worker = new Worker(url);
|
|
49
|
+
}
|
|
50
|
+
worker.onmessage = (event) => {
|
|
51
|
+
resolve(event.data.result);
|
|
52
|
+
};
|
|
53
|
+
worker.onerror = (error) => {
|
|
54
|
+
reject(error);
|
|
55
|
+
};
|
|
56
|
+
worker.postMessage({
|
|
57
|
+
command: 'process',
|
|
58
|
+
channelData,
|
|
59
|
+
sampleRate,
|
|
60
|
+
pointsPerSecond,
|
|
61
|
+
algorithm,
|
|
62
|
+
bitDepth,
|
|
63
|
+
fullAudioDurationMs: durationMs,
|
|
64
|
+
numberOfChannels,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
if (!fileUri) {
|
|
70
|
+
throw new Error('fileUri is required');
|
|
71
|
+
}
|
|
72
|
+
logger.log(`extractAudioAnalysis`, {
|
|
73
|
+
fileUri,
|
|
74
|
+
pointsPerSecond,
|
|
75
|
+
algorithm,
|
|
76
|
+
});
|
|
77
|
+
const res = await ExpoAudioStreamModule.extractAudioAnalysis({
|
|
78
|
+
fileUri,
|
|
79
|
+
pointsPerSecond,
|
|
80
|
+
skipWavHeader,
|
|
81
|
+
algorithm,
|
|
82
|
+
features,
|
|
83
|
+
});
|
|
84
|
+
logger.log(`extractAudioAnalysis`, res);
|
|
85
|
+
return res;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=extractAudioAnalysis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractAudioAnalysis.js","sourceRoot":"","sources":["../../src/AudioAnalysis/extractAudioAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/expo-audio-stream/src/AudioAnalysis/extractAudioAnalysis.ts\nimport {\n AmplitudeAlgorithm,\n AudioAnalysis,\n AudioFeaturesOptions,\n} from './AudioAnalysis.types'\nimport ExpoAudioStreamModule from '../ExpoAudioStreamModule'\nimport { isWeb } from '../constants'\nimport { getLogger } from '../logger'\nimport { convertPCMToFloat32 } from '../utils/convertPCMToFloat32'\nimport { getWavFileInfo, WavFileInfo } from '../utils/getWavFileInfo'\nimport { InlineFeaturesExtractor } from '../workers/InlineFeaturesExtractor.web'\n\nconst logger = getLogger('extractAudioAnalysis')\n\nexport interface ExtractAudioAnalysisProps {\n fileUri?: string // should provide either fileUri or arrayBuffer\n wavMetadata?: WavFileInfo\n arrayBuffer?: ArrayBuffer\n bitDepth?: number\n skipWavHeader?: boolean\n durationMs?: number\n sampleRate?: number\n numberOfChannels?: number\n algorithm?: AmplitudeAlgorithm\n position?: number // Optional number of bytes to skip. Default is 0\n length?: number // Optional number of bytes to read.\n pointsPerSecond?: number // Optional number of points per second. Use to reduce the number of points and compute the number of datapoints to return.\n features?: AudioFeaturesOptions\n featuresExtratorUrl?: string\n}\n\nexport const extractAudioAnalysis = async ({\n fileUri,\n pointsPerSecond = 20,\n arrayBuffer,\n bitDepth,\n skipWavHeader = true,\n durationMs,\n sampleRate,\n numberOfChannels,\n algorithm = 'rms',\n features,\n featuresExtratorUrl,\n}: ExtractAudioAnalysisProps): Promise<AudioAnalysis> => {\n if (isWeb) {\n if (!arrayBuffer && !fileUri) {\n throw new Error('Either arrayBuffer or fileUri must be provided')\n }\n\n if (!arrayBuffer) {\n logger.log(`fetching fileUri`, fileUri)\n const response = await fetch(fileUri!)\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch fileUri: ${response.statusText}`\n )\n }\n\n arrayBuffer = await response.arrayBuffer()\n logger.log(`fetched fileUri`, arrayBuffer.byteLength, arrayBuffer)\n }\n\n // Create a new copy of the ArrayBuffer to avoid detachment issues\n const bufferCopy = arrayBuffer.slice(0)\n logger.log(\n `extractAudioAnalysis skipWavHeader=${skipWavHeader} bitDepth=${bitDepth} len=${bufferCopy.byteLength}`,\n bufferCopy.slice(0, 100)\n )\n\n let actualBitDepth = bitDepth\n if (!actualBitDepth) {\n logger.log(\n `extractAudioAnalysis bitDepth not provided -- getting wav file info`\n )\n const fileInfo = await getWavFileInfo(bufferCopy)\n actualBitDepth = fileInfo.bitDepth\n }\n logger.log(`extractAudioAnalysis actualBitDepth=${actualBitDepth}`)\n\n const {\n pcmValues: channelData,\n min,\n max,\n } = await convertPCMToFloat32({\n buffer: arrayBuffer,\n bitDepth: actualBitDepth,\n skipWavHeader,\n })\n logger.log(\n `extractAudioAnalysis skipWaveHeader=${skipWavHeader} convertPCMToFloat32 length=${channelData.length} range: [ ${min} :: ${max} ]`\n )\n\n return new Promise((resolve, reject) => {\n let worker: Worker\n if (featuresExtratorUrl) {\n worker = new Worker(\n new URL(featuresExtratorUrl, window.location.href)\n )\n } else {\n const blob = new Blob([InlineFeaturesExtractor], {\n type: 'application/javascript',\n })\n const url = URL.createObjectURL(blob)\n worker = new Worker(url)\n }\n\n worker.onmessage = (event) => {\n resolve(event.data.result)\n }\n\n worker.onerror = (error) => {\n reject(error)\n }\n\n worker.postMessage({\n command: 'process',\n channelData,\n sampleRate,\n pointsPerSecond,\n algorithm,\n bitDepth,\n fullAudioDurationMs: durationMs,\n numberOfChannels,\n })\n })\n } else {\n if (!fileUri) {\n throw new Error('fileUri is required')\n }\n logger.log(`extractAudioAnalysis`, {\n fileUri,\n pointsPerSecond,\n algorithm,\n })\n const res = await ExpoAudioStreamModule.extractAudioAnalysis({\n fileUri,\n pointsPerSecond,\n skipWavHeader,\n algorithm,\n features,\n })\n logger.log(`extractAudioAnalysis`, res)\n return res\n }\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface ExtractWaveformProps {
|
|
2
|
+
fileUri: string;
|
|
3
|
+
numberOfSamples: number;
|
|
4
|
+
offset?: number;
|
|
5
|
+
length?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare const extractWaveform: ({ fileUri, numberOfSamples, offset, length, }: ExtractWaveformProps) => Promise<unknown>;
|
|
8
|
+
//# sourceMappingURL=extractWaveform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractWaveform.d.ts","sourceRoot":"","sources":["../../src/AudioAnalysis/extractWaveform.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,oBAAoB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB;AACD,eAAO,MAAM,eAAe,kDAKzB,oBAAoB,KAAG,OAAO,CAAC,OAAO,CASxC,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import ExpoAudioStreamModule from '../ExpoAudioStreamModule';
|
|
2
|
+
import { getLogger } from '../logger';
|
|
3
|
+
const logger = getLogger('extractWaveform');
|
|
4
|
+
export const extractWaveform = async ({ fileUri, numberOfSamples, offset = 0, length, }) => {
|
|
5
|
+
const res = await ExpoAudioStreamModule.extractAudioAnalysis({
|
|
6
|
+
fileUri,
|
|
7
|
+
numberOfSamples,
|
|
8
|
+
offset,
|
|
9
|
+
length,
|
|
10
|
+
});
|
|
11
|
+
logger.log(`extractWaveform`, res);
|
|
12
|
+
return res;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=extractWaveform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractWaveform.js","sourceRoot":"","sources":["../../src/AudioAnalysis/extractWaveform.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAErC,MAAM,MAAM,GAAG,SAAS,CAAC,iBAAiB,CAAC,CAAA;AAO3C,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAAE,EAClC,OAAO,EACP,eAAe,EACf,MAAM,GAAG,CAAC,EACV,MAAM,GACa,EAAoB,EAAE;IACzC,MAAM,GAAG,GAAG,MAAM,qBAAqB,CAAC,oBAAoB,CAAC;QACzD,OAAO;QACP,eAAe;QACf,MAAM;QACN,MAAM;KACT,CAAC,CAAA;IACF,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAA;IAClC,OAAO,GAAG,CAAA;AACd,CAAC,CAAA","sourcesContent":["import ExpoAudioStreamModule from '../ExpoAudioStreamModule'\nimport { getLogger } from '../logger'\n\nconst logger = getLogger('extractWaveform')\nexport interface ExtractWaveformProps {\n fileUri: string\n numberOfSamples: number\n offset?: number\n length?: number\n}\nexport const extractWaveform = async ({\n fileUri,\n numberOfSamples,\n offset = 0,\n length,\n}: ExtractWaveformProps): Promise<unknown> => {\n const res = await ExpoAudioStreamModule.extractAudioAnalysis({\n fileUri,\n numberOfSamples,\n offset,\n length,\n })\n logger.log(`extractWaveform`, res)\n return res\n}\n"]}
|
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import {
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types';
|
|
3
|
+
import { AudioRecording, RecordingConfig, StartRecordingResult } from './ExpoAudioStream.types';
|
|
4
|
+
import { UseAudioRecorderProps } from './useAudioRecorder';
|
|
5
|
+
export interface UseAudioRecorderState {
|
|
6
|
+
startRecording: (_: RecordingConfig) => Promise<StartRecordingResult>;
|
|
7
|
+
stopRecording: () => Promise<AudioRecording | null>;
|
|
8
|
+
pauseRecording: () => void;
|
|
9
|
+
resumeRecording: () => void;
|
|
10
|
+
isRecording: boolean;
|
|
11
|
+
isPaused: boolean;
|
|
12
|
+
durationMs: number;
|
|
13
|
+
size: number;
|
|
14
|
+
analysisData?: AudioAnalysis;
|
|
15
|
+
}
|
|
3
16
|
interface AudioRecorderProviderProps {
|
|
4
17
|
children: React.ReactNode;
|
|
5
18
|
config?: UseAudioRecorderProps;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioRecorder.provider.d.ts","sourceRoot":"","sources":["../src/AudioRecorder.provider.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AudioRecorder.provider.d.ts","sourceRoot":"","sources":["../src/AudioRecorder.provider.tsx"],"names":[],"mappings":"AACA,OAAO,KAAoC,MAAM,OAAO,CAAA;AAExD,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AACnE,OAAO,EACH,cAAc,EACd,eAAe,EACf,oBAAoB,EACvB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,qBAAqB,EAAoB,MAAM,oBAAoB,CAAA;AAE5E,MAAM,WAAW,qBAAqB;IAClC,cAAc,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAA;IACrE,aAAa,EAAE,MAAM,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAA;IACnD,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,WAAW,EAAE,OAAO,CAAA;IACpB,QAAQ,EAAE,OAAO,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,CAAC,EAAE,aAAa,CAAA;CAC/B;AAuBD,UAAU,0BAA0B;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,MAAM,CAAC,EAAE,qBAAqB,CAAA;CACjC;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAUtE,CAAA;AAED,eAAO,MAAM,sBAAsB,6BAQlC,CAAA"}
|
|
@@ -1,22 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
// packages/expo-audio-stream/src/AudioRecorder.provider.tsx
|
|
2
|
+
import React, { createContext, useContext } from 'react';
|
|
3
|
+
import { useAudioRecorder } from './useAudioRecorder';
|
|
4
|
+
const initContext = {
|
|
4
5
|
isRecording: false,
|
|
5
6
|
isPaused: false,
|
|
6
7
|
durationMs: 0,
|
|
7
8
|
size: 0,
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
startRecording: async () => {
|
|
10
|
+
throw new Error('AudioRecorderProvider not found');
|
|
11
|
+
},
|
|
12
|
+
stopRecording: async () => {
|
|
13
|
+
throw new Error('AudioRecorderProvider not found');
|
|
14
|
+
},
|
|
15
|
+
pauseRecording: () => {
|
|
16
|
+
throw new Error('AudioRecorderProvider not found');
|
|
17
|
+
},
|
|
18
|
+
resumeRecording: () => {
|
|
19
|
+
throw new Error('AudioRecorderProvider not found');
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
const AudioRecorderContext = createContext(initContext);
|
|
10
23
|
export const AudioRecorderProvider = ({ children, config = {}, }) => {
|
|
11
24
|
const audioRecorder = useAudioRecorder(config);
|
|
12
25
|
return (<AudioRecorderContext.Provider value={audioRecorder}>
|
|
13
|
-
|
|
14
|
-
|
|
26
|
+
{children}
|
|
27
|
+
</AudioRecorderContext.Provider>);
|
|
15
28
|
};
|
|
16
29
|
export const useSharedAudioRecorder = () => {
|
|
17
30
|
const context = useContext(AudioRecorderContext);
|
|
18
31
|
if (!context) {
|
|
19
|
-
throw new Error(
|
|
32
|
+
throw new Error('useSharedAudioRecorder must be used within an AudioRecorderProvider');
|
|
20
33
|
}
|
|
21
34
|
return context;
|
|
22
35
|
};
|
|
@@ -1 +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,
|
|
1
|
+
{"version":3,"file":"AudioRecorder.provider.js","sourceRoot":"","sources":["../src/AudioRecorder.provider.tsx"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAQxD,OAAO,EAAyB,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAc5E,MAAM,WAAW,GAA0B;IACvC,WAAW,EAAE,KAAK;IAClB,QAAQ,EAAE,KAAK;IACf,UAAU,EAAE,CAAC;IACb,IAAI,EAAE,CAAC;IACP,cAAc,EAAE,KAAK,IAAI,EAAE;QACvB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IACtD,CAAC;IACD,aAAa,EAAE,KAAK,IAAI,EAAE;QACtB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IACtD,CAAC;IACD,cAAc,EAAE,GAAG,EAAE;QACjB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IACtD,CAAC;IACD,eAAe,EAAE,GAAG,EAAE;QAClB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IACtD,CAAC;CACJ,CAAA;AAED,MAAM,oBAAoB,GAAG,aAAa,CAAwB,WAAW,CAAC,CAAA;AAO9E,MAAM,CAAC,MAAM,qBAAqB,GAAyC,CAAC,EACxE,QAAQ,EACR,MAAM,GAAG,EAAE,GACd,EAAE,EAAE;IACD,MAAM,aAAa,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAC9C,OAAO,CACH,CAAC,oBAAoB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,CAChD;YAAA,CAAC,QAAQ,CACb;QAAA,EAAE,oBAAoB,CAAC,QAAQ,CAAC,CACnC,CAAA;AACL,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,EAAE;IACvC,MAAM,OAAO,GAAG,UAAU,CAAC,oBAAoB,CAAC,CAAA;IAChD,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACX,qEAAqE,CACxE,CAAA;IACL,CAAC;IACD,OAAO,OAAO,CAAA;AAClB,CAAC,CAAA","sourcesContent":["// packages/expo-audio-stream/src/AudioRecorder.provider.tsx\nimport React, { createContext, useContext } from 'react'\n\nimport { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types'\nimport {\n AudioRecording,\n RecordingConfig,\n StartRecordingResult,\n} from './ExpoAudioStream.types'\nimport { UseAudioRecorderProps, useAudioRecorder } from './useAudioRecorder'\n\nexport interface UseAudioRecorderState {\n startRecording: (_: RecordingConfig) => Promise<StartRecordingResult>\n stopRecording: () => Promise<AudioRecording | null>\n pauseRecording: () => void\n resumeRecording: () => void\n isRecording: boolean\n isPaused: boolean\n durationMs: number // Duration of the recording\n size: number // Size in bytes of the recorded audio\n analysisData?: AudioAnalysis // Analysis data for the recording depending on enableProcessing flag\n}\n\nconst initContext: UseAudioRecorderState = {\n isRecording: false,\n isPaused: false,\n durationMs: 0,\n size: 0,\n startRecording: async () => {\n throw new Error('AudioRecorderProvider not found')\n },\n stopRecording: async () => {\n throw new Error('AudioRecorderProvider not found')\n },\n pauseRecording: () => {\n throw new Error('AudioRecorderProvider not found')\n },\n resumeRecording: () => {\n throw new Error('AudioRecorderProvider not found')\n },\n}\n\nconst AudioRecorderContext = createContext<UseAudioRecorderState>(initContext)\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAudioStream.native.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStream.native.ts"],"names":[],"mappings":";AAKA,
|
|
1
|
+
{"version":3,"file":"ExpoAudioStream.native.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStream.native.ts"],"names":[],"mappings":";AAKA,wBAAqD"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/ExpoAudioStreamModule.ts
|
|
2
|
-
import { requireNativeModule } from
|
|
2
|
+
import { requireNativeModule } from 'expo-modules-core';
|
|
3
3
|
// It loads the native module object from the JSI or falls back to
|
|
4
4
|
// the bridge module (from NativeModulesProxy) if the remote debugger is on.
|
|
5
|
-
export default requireNativeModule(
|
|
5
|
+
export default requireNativeModule('ExpoAudioStream');
|
|
6
6
|
//# sourceMappingURL=ExpoAudioStream.native.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAudioStream.native.js","sourceRoot":"","sources":["../src/ExpoAudioStream.native.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"ExpoAudioStream.native.js","sourceRoot":"","sources":["../src/ExpoAudioStream.native.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAEvD,kEAAkE;AAClE,4EAA4E;AAC5E,eAAe,mBAAmB,CAAC,iBAAiB,CAAC,CAAA","sourcesContent":["// src/ExpoAudioStreamModule.ts\nimport { requireNativeModule } from 'expo-modules-core'\n\n// It loads the native module object from the JSI or falls back to\n// the bridge module (from NativeModulesProxy) if the remote debugger is on.\nexport default requireNativeModule('ExpoAudioStream')\n"]}
|