@siteed/expo-audio-stream 1.0.1 → 1.0.2
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 +6 -6
- package/android/build.gradle +5 -0
- package/android/src/main/java/net/siteed/audiostream/AudioAnalysisData.kt +120 -0
- package/android/src/main/java/net/siteed/audiostream/AudioFileHandler.kt +34 -4
- package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +635 -0
- package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +194 -79
- package/android/src/main/java/net/siteed/audiostream/Constants.kt +1 -0
- package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +48 -2
- package/android/src/main/java/net/siteed/audiostream/FFT.kt +44 -0
- package/android/src/main/java/net/siteed/audiostream/Features.kt +56 -0
- package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +12 -0
- package/android/src/main/test/java/net/siteed/audiostream/AudioProcessorTest.kt +56 -0
- package/app.plugin.js +1 -1
- package/build/AudioRecorder.provider.js +1 -1
- package/build/AudioRecorder.provider.js.map +1 -1
- package/build/ExpoAudioStream.native.d.ts +3 -0
- package/build/ExpoAudioStream.native.d.ts.map +1 -0
- package/build/ExpoAudioStream.native.js +6 -0
- package/build/ExpoAudioStream.native.js.map +1 -0
- package/build/ExpoAudioStream.types.d.ts +79 -6
- package/build/ExpoAudioStream.types.d.ts.map +1 -1
- package/build/ExpoAudioStream.types.js.map +1 -1
- package/build/ExpoAudioStream.web.d.ts +41 -0
- package/build/ExpoAudioStream.web.d.ts.map +1 -0
- package/build/ExpoAudioStream.web.js +184 -0
- package/build/ExpoAudioStream.web.js.map +1 -0
- package/build/ExpoAudioStreamModule.d.ts +2 -2
- package/build/ExpoAudioStreamModule.d.ts.map +1 -1
- package/build/ExpoAudioStreamModule.js +12 -3
- package/build/ExpoAudioStreamModule.js.map +1 -1
- package/build/WebRecorder.d.ts +47 -0
- package/build/WebRecorder.d.ts.map +1 -0
- package/build/WebRecorder.js +243 -0
- package/build/WebRecorder.js.map +1 -0
- package/build/index.d.ts +14 -5
- package/build/index.d.ts.map +1 -1
- package/build/index.js +106 -7
- package/build/index.js.map +1 -1
- package/build/inlineAudioWebWorker.d.ts +3 -0
- package/build/inlineAudioWebWorker.d.ts.map +1 -0
- package/build/inlineAudioWebWorker.js +340 -0
- package/build/inlineAudioWebWorker.js.map +1 -0
- package/build/useAudioRecording.d.ts +24 -9
- package/build/useAudioRecording.d.ts.map +1 -1
- package/build/useAudioRecording.js +107 -29
- package/build/useAudioRecording.js.map +1 -1
- package/build/utils.d.ts +31 -0
- package/build/utils.d.ts.map +1 -0
- package/build/utils.js +143 -0
- package/build/utils.js.map +1 -0
- package/expo-module.config.json +13 -4
- package/ios/AudioAnalysisData.swift +39 -0
- package/ios/AudioProcessingHelpers.swift +59 -0
- package/ios/AudioProcessor.swift +317 -0
- package/ios/AudioStreamError.swift +7 -0
- package/ios/AudioStreamManager.swift +204 -52
- package/ios/AudioStreamManagerDelegate.swift +4 -0
- package/ios/DataPoint.swift +41 -0
- package/ios/ExpoAudioStreamModule.swift +188 -6
- package/ios/Features.swift +44 -0
- package/ios/RecordingResult.swift +19 -0
- package/ios/RecordingSettings.swift +13 -0
- package/ios/WaveformExtractor.swift +105 -0
- package/package.json +9 -9
- package/plugin/tsconfig.json +13 -8
- package/publish.sh +8 -0
- package/src/AudioRecorder.provider.tsx +1 -1
- package/src/ExpoAudioStream.native.ts +6 -0
- package/src/ExpoAudioStream.types.ts +97 -11
- package/src/ExpoAudioStream.web.ts +228 -0
- package/src/ExpoAudioStreamModule.ts +17 -3
- package/src/WebRecorder.ts +364 -0
- package/src/index.ts +166 -20
- package/src/inlineAudioWebWorker.tsx +340 -0
- package/src/useAudioRecording.tsx +410 -0
- package/src/utils.ts +189 -0
- package/build/ExpoAudioStreamModule.web.d.ts +0 -37
- package/build/ExpoAudioStreamModule.web.d.ts.map +0 -1
- package/build/ExpoAudioStreamModule.web.js +0 -156
- package/build/ExpoAudioStreamModule.web.js.map +0 -1
- package/docs/demo.gif +0 -0
- package/release-it.js +0 -18
- package/src/ExpoAudioStreamModule.web.ts +0 -181
- package/src/useAudioRecording.ts +0 -268
- package/yarn-error.log +0 -7793
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import debug from "debug";
|
|
2
|
-
import { EventEmitter } from "expo-modules-core";
|
|
3
|
-
const log = debug("expo-audio-stream:useAudioRecording");
|
|
4
|
-
class ExpoAudioStreamWeb extends EventEmitter {
|
|
5
|
-
mediaRecorder;
|
|
6
|
-
audioChunks;
|
|
7
|
-
isRecording;
|
|
8
|
-
isPaused;
|
|
9
|
-
recordingStartTime;
|
|
10
|
-
pausedTime;
|
|
11
|
-
currentDurationMs;
|
|
12
|
-
currentSize;
|
|
13
|
-
currentInterval;
|
|
14
|
-
lastEmittedSize;
|
|
15
|
-
lastEmittedTime;
|
|
16
|
-
streamUuid;
|
|
17
|
-
constructor() {
|
|
18
|
-
const mockNativeModule = {
|
|
19
|
-
addListener: (eventName) => {
|
|
20
|
-
// Not used on web
|
|
21
|
-
},
|
|
22
|
-
removeListeners: (count) => {
|
|
23
|
-
// Not used on web
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
super(mockNativeModule); // Pass the mock native module to the parent class
|
|
27
|
-
this.mediaRecorder = null;
|
|
28
|
-
this.audioChunks = [];
|
|
29
|
-
this.isRecording = false;
|
|
30
|
-
this.isPaused = false;
|
|
31
|
-
this.recordingStartTime = 0;
|
|
32
|
-
this.pausedTime = 0;
|
|
33
|
-
this.currentDurationMs = 0;
|
|
34
|
-
this.currentSize = 0;
|
|
35
|
-
this.currentInterval = 1000; // Default interval in ms
|
|
36
|
-
this.lastEmittedSize = 0;
|
|
37
|
-
this.lastEmittedTime = 0;
|
|
38
|
-
this.streamUuid = null; // Initialize UUID on first recording start
|
|
39
|
-
}
|
|
40
|
-
// Utility to handle user media stream
|
|
41
|
-
async getMediaStream() {
|
|
42
|
-
try {
|
|
43
|
-
return await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
console.error("Failed to get media stream:", error);
|
|
47
|
-
throw error;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
// Start recording with options
|
|
51
|
-
async startRecording(options = {}) {
|
|
52
|
-
if (this.isRecording) {
|
|
53
|
-
throw new Error("Recording is already in progress");
|
|
54
|
-
}
|
|
55
|
-
const stream = await this.getMediaStream();
|
|
56
|
-
this.mediaRecorder = new MediaRecorder(stream);
|
|
57
|
-
this.setupRecordingListeners();
|
|
58
|
-
this.mediaRecorder.start(options.interval || this.currentInterval);
|
|
59
|
-
this.isRecording = true;
|
|
60
|
-
this.recordingStartTime = Date.now();
|
|
61
|
-
this.pausedTime = 0;
|
|
62
|
-
this.lastEmittedSize = 0;
|
|
63
|
-
this.lastEmittedTime = 0;
|
|
64
|
-
this.streamUuid = this.generateUUID(); // Generate a UUID for the new recording session
|
|
65
|
-
const fileUri = `${this.streamUuid}.webm`;
|
|
66
|
-
const streamConfig = {
|
|
67
|
-
fileUri,
|
|
68
|
-
mimeType: "audio/webm",
|
|
69
|
-
};
|
|
70
|
-
return streamConfig;
|
|
71
|
-
}
|
|
72
|
-
// Setup listeners for the MediaRecorder
|
|
73
|
-
setupRecordingListeners() {
|
|
74
|
-
if (!this.mediaRecorder) {
|
|
75
|
-
throw new Error("No active media recorder");
|
|
76
|
-
}
|
|
77
|
-
this.mediaRecorder.ondataavailable = (event) => {
|
|
78
|
-
this.audioChunks.push(event.data);
|
|
79
|
-
this.currentSize += event.data.size; // Update the size of the recording
|
|
80
|
-
this.emitAudioEvent({ data: event.data, position: this.lastEmittedTime });
|
|
81
|
-
this.lastEmittedTime = event.timeStamp;
|
|
82
|
-
this.lastEmittedSize = this.currentSize;
|
|
83
|
-
};
|
|
84
|
-
this.mediaRecorder.onstop = () => {
|
|
85
|
-
this.isRecording = false;
|
|
86
|
-
log("Recording stopped", this.audioChunks);
|
|
87
|
-
};
|
|
88
|
-
this.mediaRecorder.onpause = () => {
|
|
89
|
-
this.isPaused = true;
|
|
90
|
-
};
|
|
91
|
-
this.mediaRecorder.onresume = () => {
|
|
92
|
-
this.isPaused = false;
|
|
93
|
-
this.recordingStartTime += Date.now() - this.pausedTime; // Adjust start time after resuming
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
emitAudioEvent({ data, position }) {
|
|
97
|
-
const fileUri = `${this.streamUuid}.webm`;
|
|
98
|
-
const audioEventPayload = {
|
|
99
|
-
fileUri,
|
|
100
|
-
mimeType: "audio/webm",
|
|
101
|
-
lastEmittedSize: this.lastEmittedSize, // Since this might be continuously streaming, adjust accordingly
|
|
102
|
-
deltaSize: data.size,
|
|
103
|
-
position,
|
|
104
|
-
totalSize: this.currentSize,
|
|
105
|
-
buffer: data,
|
|
106
|
-
streamUuid: this.streamUuid ?? "", // Generate or manage UUID for stream identification
|
|
107
|
-
};
|
|
108
|
-
this.emit("AudioData", audioEventPayload);
|
|
109
|
-
}
|
|
110
|
-
// Helper method to generate a UUID
|
|
111
|
-
generateUUID() {
|
|
112
|
-
// Implementation of UUID generation (use a library or custom method)
|
|
113
|
-
return "xxxx-xxxx-xxxx-xxxx".replace(/[x]/g, (c) => {
|
|
114
|
-
const r = (Math.random() * 16) | 0, v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
115
|
-
return v.toString(16);
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
// Stop recording
|
|
119
|
-
async stopRecording() {
|
|
120
|
-
this.mediaRecorder?.stop();
|
|
121
|
-
this.isRecording = false;
|
|
122
|
-
this.currentDurationMs = Date.now() - this.recordingStartTime;
|
|
123
|
-
const result = {
|
|
124
|
-
fileUri: `${this.streamUuid}.webm`,
|
|
125
|
-
duration: this.currentDurationMs,
|
|
126
|
-
size: this.currentSize,
|
|
127
|
-
mimeType: "audio/webm",
|
|
128
|
-
};
|
|
129
|
-
return result;
|
|
130
|
-
}
|
|
131
|
-
// Pause recording
|
|
132
|
-
async pauseRecording() {
|
|
133
|
-
if (!this.mediaRecorder) {
|
|
134
|
-
throw new Error("No active media recorder");
|
|
135
|
-
}
|
|
136
|
-
if (this.isRecording && !this.isPaused) {
|
|
137
|
-
this.mediaRecorder.pause();
|
|
138
|
-
this.pausedTime = Date.now();
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
throw new Error("Recording is not active or already paused");
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
// Get current status
|
|
145
|
-
status() {
|
|
146
|
-
return {
|
|
147
|
-
isRecording: this.isRecording,
|
|
148
|
-
isPaused: this.isPaused,
|
|
149
|
-
duration: Date.now() - this.recordingStartTime,
|
|
150
|
-
size: this.currentSize,
|
|
151
|
-
interval: this.currentInterval,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
export default new ExpoAudioStreamWeb();
|
|
156
|
-
//# sourceMappingURL=ExpoAudioStreamModule.web.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAudioStreamModule.web.js","sourceRoot":"","sources":["../src/ExpoAudioStreamModule.web.ts"],"names":[],"mappings":"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","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/docs/demo.gif
DELETED
|
Binary file
|
package/release-it.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
git: {
|
|
3
|
-
"commitMessage": "chore: release ${version}",
|
|
4
|
-
"tagName": "v${version}",
|
|
5
|
-
"requireCleanWorkingDir": false
|
|
6
|
-
},
|
|
7
|
-
npm: {
|
|
8
|
-
"publish": true
|
|
9
|
-
},
|
|
10
|
-
github: {
|
|
11
|
-
"release": true
|
|
12
|
-
},
|
|
13
|
-
plugins: {
|
|
14
|
-
"@release-it/conventional-changelog": {
|
|
15
|
-
"preset": "angular",
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import debug from "debug";
|
|
2
|
-
import { EventEmitter } from "expo-modules-core";
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
AudioEventPayload,
|
|
6
|
-
AudioStreamResult,
|
|
7
|
-
RecordingConfig,
|
|
8
|
-
StartAudioStreamResult,
|
|
9
|
-
} from "./ExpoAudioStream.types";
|
|
10
|
-
|
|
11
|
-
const log = debug("expo-audio-stream:useAudioRecording");
|
|
12
|
-
class ExpoAudioStreamWeb extends EventEmitter {
|
|
13
|
-
mediaRecorder: MediaRecorder | null;
|
|
14
|
-
audioChunks: Blob[];
|
|
15
|
-
isRecording: boolean;
|
|
16
|
-
isPaused: boolean;
|
|
17
|
-
recordingStartTime: number;
|
|
18
|
-
pausedTime: number;
|
|
19
|
-
currentDurationMs: number;
|
|
20
|
-
currentSize: number;
|
|
21
|
-
currentInterval: number;
|
|
22
|
-
lastEmittedSize: number;
|
|
23
|
-
lastEmittedTime: number;
|
|
24
|
-
streamUuid: string | null;
|
|
25
|
-
|
|
26
|
-
constructor() {
|
|
27
|
-
const mockNativeModule = {
|
|
28
|
-
addListener: (eventName: string) => {
|
|
29
|
-
// Not used on web
|
|
30
|
-
},
|
|
31
|
-
removeListeners: (count: number) => {
|
|
32
|
-
// Not used on web
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
super(mockNativeModule); // Pass the mock native module to the parent class
|
|
36
|
-
|
|
37
|
-
this.mediaRecorder = null;
|
|
38
|
-
this.audioChunks = [];
|
|
39
|
-
this.isRecording = false;
|
|
40
|
-
this.isPaused = false;
|
|
41
|
-
this.recordingStartTime = 0;
|
|
42
|
-
this.pausedTime = 0;
|
|
43
|
-
this.currentDurationMs = 0;
|
|
44
|
-
this.currentSize = 0;
|
|
45
|
-
this.currentInterval = 1000; // Default interval in ms
|
|
46
|
-
this.lastEmittedSize = 0;
|
|
47
|
-
this.lastEmittedTime = 0;
|
|
48
|
-
this.streamUuid = null; // Initialize UUID on first recording start
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Utility to handle user media stream
|
|
52
|
-
async getMediaStream() {
|
|
53
|
-
try {
|
|
54
|
-
return await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
55
|
-
} catch (error) {
|
|
56
|
-
console.error("Failed to get media stream:", error);
|
|
57
|
-
throw error;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Start recording with options
|
|
62
|
-
async startRecording(options: RecordingConfig = {}) {
|
|
63
|
-
if (this.isRecording) {
|
|
64
|
-
throw new Error("Recording is already in progress");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const stream = await this.getMediaStream();
|
|
68
|
-
this.mediaRecorder = new MediaRecorder(stream);
|
|
69
|
-
this.setupRecordingListeners();
|
|
70
|
-
this.mediaRecorder.start(options.interval || this.currentInterval);
|
|
71
|
-
this.isRecording = true;
|
|
72
|
-
this.recordingStartTime = Date.now();
|
|
73
|
-
this.pausedTime = 0;
|
|
74
|
-
this.lastEmittedSize = 0;
|
|
75
|
-
this.lastEmittedTime = 0;
|
|
76
|
-
this.streamUuid = this.generateUUID(); // Generate a UUID for the new recording session
|
|
77
|
-
const fileUri = `${this.streamUuid}.webm`;
|
|
78
|
-
const streamConfig: StartAudioStreamResult = {
|
|
79
|
-
fileUri,
|
|
80
|
-
mimeType: "audio/webm",
|
|
81
|
-
};
|
|
82
|
-
return streamConfig;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Setup listeners for the MediaRecorder
|
|
86
|
-
setupRecordingListeners() {
|
|
87
|
-
if (!this.mediaRecorder) {
|
|
88
|
-
throw new Error("No active media recorder");
|
|
89
|
-
}
|
|
90
|
-
this.mediaRecorder.ondataavailable = (event) => {
|
|
91
|
-
this.audioChunks.push(event.data);
|
|
92
|
-
this.currentSize += event.data.size; // Update the size of the recording
|
|
93
|
-
|
|
94
|
-
this.emitAudioEvent({ data: event.data, position: this.lastEmittedTime });
|
|
95
|
-
this.lastEmittedTime = event.timeStamp;
|
|
96
|
-
this.lastEmittedSize = this.currentSize;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
this.mediaRecorder.onstop = () => {
|
|
100
|
-
this.isRecording = false;
|
|
101
|
-
log("Recording stopped", this.audioChunks);
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
this.mediaRecorder.onpause = () => {
|
|
105
|
-
this.isPaused = true;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
this.mediaRecorder.onresume = () => {
|
|
109
|
-
this.isPaused = false;
|
|
110
|
-
this.recordingStartTime += Date.now() - this.pausedTime; // Adjust start time after resuming
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
emitAudioEvent({ data, position }: { data: Blob; position: number }) {
|
|
115
|
-
const fileUri = `${this.streamUuid}.webm`;
|
|
116
|
-
const audioEventPayload: AudioEventPayload = {
|
|
117
|
-
fileUri,
|
|
118
|
-
mimeType: "audio/webm",
|
|
119
|
-
lastEmittedSize: this.lastEmittedSize, // Since this might be continuously streaming, adjust accordingly
|
|
120
|
-
deltaSize: data.size,
|
|
121
|
-
position,
|
|
122
|
-
totalSize: this.currentSize,
|
|
123
|
-
buffer: data,
|
|
124
|
-
streamUuid: this.streamUuid ?? "", // Generate or manage UUID for stream identification
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
this.emit("AudioData", audioEventPayload);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Helper method to generate a UUID
|
|
131
|
-
generateUUID() {
|
|
132
|
-
// Implementation of UUID generation (use a library or custom method)
|
|
133
|
-
return "xxxx-xxxx-xxxx-xxxx".replace(/[x]/g, (c) => {
|
|
134
|
-
const r = (Math.random() * 16) | 0,
|
|
135
|
-
v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
136
|
-
return v.toString(16);
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Stop recording
|
|
141
|
-
async stopRecording(): Promise<AudioStreamResult | null> {
|
|
142
|
-
this.mediaRecorder?.stop();
|
|
143
|
-
this.isRecording = false;
|
|
144
|
-
this.currentDurationMs = Date.now() - this.recordingStartTime;
|
|
145
|
-
const result: AudioStreamResult = {
|
|
146
|
-
fileUri: `${this.streamUuid}.webm`,
|
|
147
|
-
duration: this.currentDurationMs,
|
|
148
|
-
size: this.currentSize,
|
|
149
|
-
mimeType: "audio/webm",
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
return result;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Pause recording
|
|
156
|
-
async pauseRecording() {
|
|
157
|
-
if (!this.mediaRecorder) {
|
|
158
|
-
throw new Error("No active media recorder");
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (this.isRecording && !this.isPaused) {
|
|
162
|
-
this.mediaRecorder.pause();
|
|
163
|
-
this.pausedTime = Date.now();
|
|
164
|
-
} else {
|
|
165
|
-
throw new Error("Recording is not active or already paused");
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Get current status
|
|
170
|
-
status() {
|
|
171
|
-
return {
|
|
172
|
-
isRecording: this.isRecording,
|
|
173
|
-
isPaused: this.isPaused,
|
|
174
|
-
duration: Date.now() - this.recordingStartTime,
|
|
175
|
-
size: this.currentSize,
|
|
176
|
-
interval: this.currentInterval,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export default new ExpoAudioStreamWeb();
|
package/src/useAudioRecording.ts
DELETED
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
import { Platform } from "expo-modules-core";
|
|
2
|
-
import { useCallback, useEffect, useReducer, useRef } from "react";
|
|
3
|
-
|
|
4
|
-
import { addAudioEventListener } from ".";
|
|
5
|
-
import {
|
|
6
|
-
AudioEventPayload,
|
|
7
|
-
AudioStreamResult,
|
|
8
|
-
AudioStreamStatus,
|
|
9
|
-
RecordingConfig,
|
|
10
|
-
StartAudioStreamResult,
|
|
11
|
-
} from "./ExpoAudioStream.types";
|
|
12
|
-
import ExpoAudioStreamModule from "./ExpoAudioStreamModule";
|
|
13
|
-
|
|
14
|
-
export interface AudioDataEvent {
|
|
15
|
-
data: string | Blob;
|
|
16
|
-
position: number;
|
|
17
|
-
fileUri: string;
|
|
18
|
-
eventDataSize: number;
|
|
19
|
-
totalSize: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface UseAudioRecorderProps {
|
|
23
|
-
debug?: boolean;
|
|
24
|
-
}
|
|
25
|
-
export interface UseAudioRecorderState {
|
|
26
|
-
startRecording: (_: RecordingConfig) => Promise<StartAudioStreamResult>;
|
|
27
|
-
stopRecording: () => Promise<AudioStreamResult | null>;
|
|
28
|
-
pauseRecording: () => void;
|
|
29
|
-
resumeRecording: () => void;
|
|
30
|
-
isRecording: boolean;
|
|
31
|
-
isPaused: boolean;
|
|
32
|
-
duration: number; // Duration of the recording
|
|
33
|
-
size: number; // Size in bytes of the recorded audio
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
interface RecorderState {
|
|
37
|
-
isRecording: boolean;
|
|
38
|
-
isPaused: boolean;
|
|
39
|
-
duration: number;
|
|
40
|
-
size: number;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
type RecorderAction =
|
|
44
|
-
| { type: "START" | "STOP" | "PAUSE" | "RESUME" }
|
|
45
|
-
| { type: "UPDATE_STATUS"; payload: { duration: number; size: number } };
|
|
46
|
-
|
|
47
|
-
function recorderReducer(
|
|
48
|
-
state: RecorderState,
|
|
49
|
-
action: RecorderAction,
|
|
50
|
-
): RecorderState {
|
|
51
|
-
switch (action.type) {
|
|
52
|
-
case "START":
|
|
53
|
-
return {
|
|
54
|
-
...state,
|
|
55
|
-
isRecording: true,
|
|
56
|
-
isPaused: false,
|
|
57
|
-
duration: 0,
|
|
58
|
-
size: 0,
|
|
59
|
-
};
|
|
60
|
-
case "STOP":
|
|
61
|
-
return { ...state, isRecording: false, isPaused: false };
|
|
62
|
-
case "PAUSE":
|
|
63
|
-
return { ...state, isPaused: true, isRecording: false };
|
|
64
|
-
case "RESUME":
|
|
65
|
-
return { ...state, isPaused: false, isRecording: true };
|
|
66
|
-
case "UPDATE_STATUS":
|
|
67
|
-
return {
|
|
68
|
-
...state,
|
|
69
|
-
duration: action.payload.duration,
|
|
70
|
-
size: action.payload.size,
|
|
71
|
-
};
|
|
72
|
-
default:
|
|
73
|
-
return state;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
const TAG = "[ useAudioRecorder ] ";
|
|
77
|
-
|
|
78
|
-
export function useAudioRecorder({
|
|
79
|
-
debug = false,
|
|
80
|
-
}: UseAudioRecorderProps = {}): UseAudioRecorderState {
|
|
81
|
-
const [state, dispatch] = useReducer(recorderReducer, {
|
|
82
|
-
isRecording: false,
|
|
83
|
-
isPaused: false,
|
|
84
|
-
duration: 0,
|
|
85
|
-
size: 0,
|
|
86
|
-
});
|
|
87
|
-
|
|
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
|
-
}
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
[debug],
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
const handleAudioEvent = useCallback(
|
|
106
|
-
async (eventData: AudioEventPayload) => {
|
|
107
|
-
const {
|
|
108
|
-
fileUri,
|
|
109
|
-
deltaSize,
|
|
110
|
-
totalSize,
|
|
111
|
-
lastEmittedSize,
|
|
112
|
-
position,
|
|
113
|
-
streamUuid,
|
|
114
|
-
encoded,
|
|
115
|
-
mimeType,
|
|
116
|
-
buffer,
|
|
117
|
-
} = eventData;
|
|
118
|
-
logDebug(`useAudioRecorder] Received audio event:`, {
|
|
119
|
-
fileUri,
|
|
120
|
-
deltaSize,
|
|
121
|
-
totalSize,
|
|
122
|
-
position,
|
|
123
|
-
mimeType,
|
|
124
|
-
lastEmittedSize,
|
|
125
|
-
streamUuid,
|
|
126
|
-
encodedLength: encoded?.length,
|
|
127
|
-
});
|
|
128
|
-
if (deltaSize === 0) {
|
|
129
|
-
// Ignore packet with no data
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
try {
|
|
133
|
-
// Coming from native ( ios / android ) otherwise buffer is set
|
|
134
|
-
if (Platform.OS !== "web") {
|
|
135
|
-
// Read the audio file as a base64 string for comparison
|
|
136
|
-
if (!encoded) {
|
|
137
|
-
console.error(`${TAG} Encoded audio data is missing`);
|
|
138
|
-
throw new Error("Encoded audio data is missing");
|
|
139
|
-
}
|
|
140
|
-
onAudioStreamRef.current?.({
|
|
141
|
-
data: encoded,
|
|
142
|
-
position,
|
|
143
|
-
fileUri,
|
|
144
|
-
eventDataSize: deltaSize,
|
|
145
|
-
totalSize,
|
|
146
|
-
});
|
|
147
|
-
} else if (buffer) {
|
|
148
|
-
// Coming from web
|
|
149
|
-
onAudioStreamRef.current?.({
|
|
150
|
-
data: buffer,
|
|
151
|
-
position,
|
|
152
|
-
fileUri,
|
|
153
|
-
eventDataSize: deltaSize,
|
|
154
|
-
totalSize,
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
} catch (error) {
|
|
158
|
-
console.error(`${TAG} Error processing audio event:`, error);
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
[logDebug],
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
const checkStatus = useCallback(async () => {
|
|
165
|
-
try {
|
|
166
|
-
if (!state.isRecording) {
|
|
167
|
-
logDebug(`${TAG} Not recording, exiting status check.`);
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const status: AudioStreamStatus = ExpoAudioStreamModule.status();
|
|
172
|
-
if (debug) {
|
|
173
|
-
logDebug(`${TAG} Status:`, status);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (!status.isRecording) {
|
|
177
|
-
dispatch({ type: "STOP" });
|
|
178
|
-
} else {
|
|
179
|
-
dispatch({
|
|
180
|
-
type: "UPDATE_STATUS",
|
|
181
|
-
payload: { duration: status.duration, size: status.size },
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
} catch (error) {
|
|
185
|
-
console.error(`${TAG} Error getting status:`, error);
|
|
186
|
-
}
|
|
187
|
-
}, [state.isRecording, logDebug]);
|
|
188
|
-
|
|
189
|
-
useEffect(() => {
|
|
190
|
-
let interval: number;
|
|
191
|
-
if (state.isRecording) {
|
|
192
|
-
interval = setInterval(checkStatus, 1000);
|
|
193
|
-
}
|
|
194
|
-
return () => {
|
|
195
|
-
if (interval) {
|
|
196
|
-
clearInterval(interval);
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
}, [checkStatus, state.isRecording]);
|
|
200
|
-
|
|
201
|
-
useEffect(() => {
|
|
202
|
-
logDebug(`${TAG} Registering audio event listener`);
|
|
203
|
-
const subscribe = addAudioEventListener(handleAudioEvent);
|
|
204
|
-
logDebug(`${TAG} Subscribed to audio event listener`, subscribe);
|
|
205
|
-
|
|
206
|
-
return () => {
|
|
207
|
-
logDebug(`${TAG} Removing audio event listener`);
|
|
208
|
-
subscribe.remove();
|
|
209
|
-
};
|
|
210
|
-
}, [handleAudioEvent, logDebug]);
|
|
211
|
-
|
|
212
|
-
const startRecording = useCallback(
|
|
213
|
-
async (recordingOptions: RecordingConfig) => {
|
|
214
|
-
if (debug) {
|
|
215
|
-
logDebug(`${TAG} start recoding`, recordingOptions);
|
|
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
|
-
}
|
|
225
|
-
const startResult: StartAudioStreamResult =
|
|
226
|
-
await ExpoAudioStreamModule.startRecording(options);
|
|
227
|
-
dispatch({ type: "START" });
|
|
228
|
-
|
|
229
|
-
return startResult;
|
|
230
|
-
},
|
|
231
|
-
[logDebug],
|
|
232
|
-
);
|
|
233
|
-
|
|
234
|
-
const stopRecording = useCallback(async () => {
|
|
235
|
-
logDebug(`${TAG} stoping recording`);
|
|
236
|
-
const stopResult: AudioStreamResult =
|
|
237
|
-
await ExpoAudioStreamModule.stopRecording();
|
|
238
|
-
onAudioStreamRef.current = null;
|
|
239
|
-
logDebug(`${TAG} recording stopped`, stopResult);
|
|
240
|
-
dispatch({ type: "STOP" });
|
|
241
|
-
return stopResult;
|
|
242
|
-
}, [logDebug]);
|
|
243
|
-
|
|
244
|
-
const pauseRecording = useCallback(async () => {
|
|
245
|
-
logDebug(`${TAG} pause recording`);
|
|
246
|
-
const pauseResult = await ExpoAudioStreamModule.pauseRecording();
|
|
247
|
-
dispatch({ type: "PAUSE" });
|
|
248
|
-
return pauseResult;
|
|
249
|
-
}, [logDebug]);
|
|
250
|
-
|
|
251
|
-
const resumeRecording = useCallback(async () => {
|
|
252
|
-
logDebug(`${TAG} resume recording`);
|
|
253
|
-
const resumeResult = await ExpoAudioStreamModule.resumeRecording();
|
|
254
|
-
dispatch({ type: "RESUME" });
|
|
255
|
-
return resumeResult;
|
|
256
|
-
}, [logDebug]);
|
|
257
|
-
|
|
258
|
-
return {
|
|
259
|
-
startRecording,
|
|
260
|
-
stopRecording,
|
|
261
|
-
pauseRecording,
|
|
262
|
-
resumeRecording,
|
|
263
|
-
isPaused: state.isPaused,
|
|
264
|
-
isRecording: state.isRecording,
|
|
265
|
-
duration: state.duration,
|
|
266
|
-
size: state.size,
|
|
267
|
-
};
|
|
268
|
-
}
|