@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/build/utils.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { EncodingType } from \"./ExpoAudioStream.types\";\n\nexport const WAV_HEADER_SIZE = 44;\nexport const convertPCMToFloat32 = ({\n bitDepth,\n buffer,\n skipWavHeader = false,\n}: {\n buffer: ArrayBuffer;\n bitDepth: number;\n skipWavHeader?: boolean;\n}): { pcmValues: Float32Array; min: number; max: number } => {\n const dataView = new DataView(buffer);\n const headerOffset = skipWavHeader ? WAV_HEADER_SIZE : 0;\n const dataLength = buffer.byteLength - headerOffset;\n const sampleLength = dataLength / (bitDepth / 8);\n const float32Array = new Float32Array(sampleLength);\n let min = Infinity;\n let max = -Infinity;\n\n for (let i = 0; i < sampleLength; i++) {\n let value = 0;\n const offset = headerOffset + i * (bitDepth / 8);\n switch (bitDepth) {\n case 8:\n value = dataView.getUint8(offset) / 128;\n break;\n case 16:\n value = dataView.getInt16(offset, true) / 32768;\n break;\n case 24:\n value =\n (dataView.getUint8(offset) +\n (dataView.getUint8(offset + 1) << 8) +\n (dataView.getUint8(offset + 2) << 16)) /\n 8388608;\n break;\n case 32:\n value = dataView.getFloat32(offset, true);\n break;\n default:\n throw new Error(`Unsupported bit depth: ${bitDepth}`);\n }\n if (value < min) min = value;\n if (value > max) max = value;\n float32Array[i] = value;\n }\n\n return { pcmValues: float32Array, min, max };\n};\n\ninterface WavHeaderOptions {\n buffer: ArrayBuffer;\n sampleRate: number;\n numChannels: number;\n bitDepth: number;\n}\n\nexport const writeWavHeader = ({\n buffer,\n sampleRate,\n numChannels,\n bitDepth,\n}: WavHeaderOptions): ArrayBuffer => {\n const bytesPerSample = bitDepth / 8;\n const numSamples = buffer.byteLength / (numChannels * bytesPerSample);\n const view = new DataView(buffer);\n const blockAlign = numChannels * bytesPerSample;\n const byteRate = sampleRate * blockAlign;\n\n // Function to write a string to the DataView\n const writeString = (view: DataView, offset: number, string: string) => {\n for (let i = 0; i < string.length; i++) {\n view.setUint8(offset + i, string.charCodeAt(i));\n }\n };\n\n // Check if the buffer already has a WAV header by looking for \"RIFF\" at the start\n const existingHeader = view.getUint32(0, false) === 0x52494646; // \"RIFF\" in ASCII\n\n if (!existingHeader) {\n // Write the WAV header\n writeString(view, 0, \"RIFF\"); // ChunkID\n view.setUint32(4, 36 + numSamples * blockAlign, true); // ChunkSize\n writeString(view, 8, \"WAVE\"); // Format\n writeString(view, 12, \"fmt \"); // Subchunk1ID\n view.setUint32(16, 16, true); // Subchunk1Size (16 for PCM)\n view.setUint16(20, bitDepth === 32 ? 3 : 1, true); // AudioFormat (3 for float, 1 for PCM)\n view.setUint16(22, numChannels, true); // NumChannels\n view.setUint32(24, sampleRate, true); // SampleRate\n view.setUint32(28, byteRate, true); // ByteRate\n view.setUint16(32, blockAlign, true); // BlockAlign\n view.setUint16(34, bitDepth, true); // BitsPerSample\n writeString(view, 36, \"data\"); // Subchunk2ID\n view.setUint32(40, numSamples * blockAlign, true); // Subchunk2Size\n } else {\n // Update the existing WAV header if necessary\n view.setUint32(4, 36 + numSamples * blockAlign, true); // Update ChunkSize\n view.setUint32(24, sampleRate, true); // Update SampleRate\n view.setUint32(28, byteRate, true); // Update ByteRate\n view.setUint16(32, blockAlign, true); // Update BlockAlign\n view.setUint32(40, numSamples * blockAlign, true); // Update Subchunk2Size\n }\n\n return buffer;\n};\n\nexport interface WavFileInfo {\n sampleRate: number;\n numChannels: number;\n bitDepth: number;\n size: number; // in bytes\n durationMs: number; // in seconds\n}\n\nexport const getWavFileInfo = async (\n arrayBuffer: ArrayBuffer,\n): Promise<WavFileInfo> => {\n const view = new DataView(arrayBuffer);\n\n // Check if the file is a valid RIFF/WAVE file\n const riffHeader = view.getUint32(0, false); // \"RIFF\"\n const waveHeader = view.getUint32(8, false); // \"WAVE\"\n if (riffHeader !== 0x52494646 || waveHeader !== 0x57415645) {\n throw new Error(\"Invalid WAV file\");\n }\n\n // Locate the \"fmt \" chunk\n let fmtChunkOffset = 12;\n let sampleRate = 0;\n let numChannels = 0;\n let bitDepth = 0;\n let dataChunkSize = 0;\n let audioFormat = 0;\n\n while (fmtChunkOffset < view.byteLength) {\n const chunkId = view.getUint32(fmtChunkOffset, false);\n const chunkSize = view.getUint32(fmtChunkOffset + 4, true);\n if (chunkId === 0x666d7420) {\n // \"fmt \"\n audioFormat = view.getUint16(fmtChunkOffset + 8, true);\n if (audioFormat !== 1 && audioFormat !== 3) {\n throw new Error(\"Unsupported WAV file format\");\n }\n numChannels = view.getUint16(fmtChunkOffset + 10, true);\n sampleRate = view.getUint32(fmtChunkOffset + 12, true);\n bitDepth = view.getUint16(fmtChunkOffset + 22, true);\n } else if (chunkId === 0x64617461) {\n // \"data\"\n dataChunkSize = chunkSize;\n break;\n }\n fmtChunkOffset += 8 + chunkSize;\n }\n\n if (!sampleRate || !numChannels || !bitDepth || !dataChunkSize) {\n throw new Error(\"Incomplete WAV file information\");\n }\n\n // Calculate duration\n const bytesPerSample = bitDepth / 8;\n const numSamples = dataChunkSize / (numChannels * bytesPerSample);\n const durationMs = (numSamples / sampleRate) * 1000;\n\n return {\n sampleRate,\n numChannels,\n bitDepth,\n size: arrayBuffer.byteLength,\n durationMs,\n };\n};\n\nexport const encodingToBitDepth = ({\n encoding,\n}: {\n encoding: EncodingType;\n}): number => {\n switch (encoding) {\n case \"pcm_32bit\":\n return 32;\n case \"pcm_16bit\":\n return 16;\n case \"pcm_8bit\":\n return 8;\n default:\n throw new Error(`Unsupported encoding type: ${encoding}`);\n }\n};\n"]}
|
package/src/WebRecorder.ts
DELETED
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
// src/WebRecorder.ts
|
|
2
|
-
import { AudioAnalysisData, RecordingConfig } from "./ExpoAudioStream.types";
|
|
3
|
-
import {
|
|
4
|
-
EmitAudioAnalysisFunction,
|
|
5
|
-
EmitAudioEventFunction,
|
|
6
|
-
} from "./ExpoAudioStream.web";
|
|
7
|
-
import { encodingToBitDepth } from "./utils";
|
|
8
|
-
interface AudioWorkletEvent {
|
|
9
|
-
data: {
|
|
10
|
-
command: string;
|
|
11
|
-
recordedData?: ArrayBuffer;
|
|
12
|
-
sampleRate?: number;
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
interface AudioFeaturesEvent {
|
|
17
|
-
data: {
|
|
18
|
-
command: string;
|
|
19
|
-
result: AudioAnalysisData;
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const DEFAULT_WEB_BITDEPTH = 32;
|
|
24
|
-
const DEFAULT_WEB_POINTS_PER_SECOND = 10;
|
|
25
|
-
const DEFAULT_WEB_INTERVAL = 500;
|
|
26
|
-
const DEFAULT_WEB_NUMBER_OF_CHANNELS = 1;
|
|
27
|
-
|
|
28
|
-
// const log = debug("expo-audio-stream:WebRecorder");
|
|
29
|
-
const log = console.log;
|
|
30
|
-
export class WebRecorder {
|
|
31
|
-
private audioContext: AudioContext;
|
|
32
|
-
private audioWorkletNode!: AudioWorkletNode;
|
|
33
|
-
private featureExtractorWorker: Worker;
|
|
34
|
-
private source: MediaStreamAudioSourceNode;
|
|
35
|
-
private audioWorkletUrl: string;
|
|
36
|
-
private emitAudioEventCallback: EmitAudioEventFunction;
|
|
37
|
-
private emitAudioAnalysisCallback: EmitAudioAnalysisFunction;
|
|
38
|
-
private config: RecordingConfig;
|
|
39
|
-
private position: number; // Track the cumulative position
|
|
40
|
-
private numberOfChannels: number; // Number of audio channels
|
|
41
|
-
private bitDepth: number; // Bit depth of the audio
|
|
42
|
-
private exportBitDepth: number; // Bit depth of the audio
|
|
43
|
-
private buffers: ArrayBuffer[]; // Array to store the buffers
|
|
44
|
-
private audioAnalysisData: AudioAnalysisData; // Keep updating the full audio analysis data with latest events
|
|
45
|
-
|
|
46
|
-
constructor({
|
|
47
|
-
audioContext,
|
|
48
|
-
source,
|
|
49
|
-
recordingConfig,
|
|
50
|
-
featuresExtratorUrl,
|
|
51
|
-
audioWorkletUrl,
|
|
52
|
-
emitAudioEventCallback,
|
|
53
|
-
emitAudioAnalysisCallback,
|
|
54
|
-
}: {
|
|
55
|
-
audioContext: AudioContext;
|
|
56
|
-
source: MediaStreamAudioSourceNode;
|
|
57
|
-
recordingConfig: RecordingConfig;
|
|
58
|
-
featuresExtratorUrl: string;
|
|
59
|
-
audioWorkletUrl: string;
|
|
60
|
-
emitAudioEventCallback: EmitAudioEventFunction;
|
|
61
|
-
emitAudioAnalysisCallback: EmitAudioAnalysisFunction;
|
|
62
|
-
}) {
|
|
63
|
-
this.audioContext = audioContext;
|
|
64
|
-
this.source = source;
|
|
65
|
-
this.audioWorkletUrl = audioWorkletUrl;
|
|
66
|
-
this.emitAudioEventCallback = emitAudioEventCallback;
|
|
67
|
-
this.emitAudioAnalysisCallback = emitAudioAnalysisCallback;
|
|
68
|
-
this.config = recordingConfig;
|
|
69
|
-
this.position = 0;
|
|
70
|
-
this.buffers = []; // Initialize the buffers array
|
|
71
|
-
|
|
72
|
-
const audioContextFormat = this.checkAudioContextFormat({
|
|
73
|
-
sampleRate: this.audioContext.sampleRate,
|
|
74
|
-
});
|
|
75
|
-
log("Initialized WebRecorder with config:", {
|
|
76
|
-
sampleRate: audioContextFormat.sampleRate,
|
|
77
|
-
bitDepth: audioContextFormat.bitDepth,
|
|
78
|
-
numberOfChannels: audioContextFormat.numberOfChannels,
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
this.bitDepth = audioContextFormat.bitDepth;
|
|
82
|
-
this.numberOfChannels =
|
|
83
|
-
audioContextFormat.numberOfChannels || DEFAULT_WEB_NUMBER_OF_CHANNELS; // Default to 1 if not available
|
|
84
|
-
this.exportBitDepth =
|
|
85
|
-
encodingToBitDepth({
|
|
86
|
-
encoding: recordingConfig.encoding ?? "pcm_32bit",
|
|
87
|
-
}) ||
|
|
88
|
-
audioContextFormat.bitDepth ||
|
|
89
|
-
DEFAULT_WEB_BITDEPTH;
|
|
90
|
-
|
|
91
|
-
this.audioAnalysisData = {
|
|
92
|
-
amplitudeRange: { min: 0, max: 0 },
|
|
93
|
-
dataPoints: [],
|
|
94
|
-
durationMs: 0,
|
|
95
|
-
samples: 0,
|
|
96
|
-
bitDepth: this.bitDepth,
|
|
97
|
-
numberOfChannels: this.numberOfChannels,
|
|
98
|
-
sampleRate: this.config.sampleRate || this.audioContext.sampleRate,
|
|
99
|
-
pointsPerSecond:
|
|
100
|
-
this.config.pointsPerSecond || DEFAULT_WEB_POINTS_PER_SECOND,
|
|
101
|
-
speakerChanges: [],
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
// Initialize the feature extractor worker
|
|
105
|
-
//TODO: create audio feature extractor from a Blob instead of url since we cannot include the url directly in the library
|
|
106
|
-
// We keep the url during dev and use the blob in production.
|
|
107
|
-
this.featureExtractorWorker = new Worker(
|
|
108
|
-
new URL(featuresExtratorUrl, window.location.href),
|
|
109
|
-
);
|
|
110
|
-
this.featureExtractorWorker.onmessage =
|
|
111
|
-
this.handleFeatureExtractorMessage.bind(this);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
async init() {
|
|
115
|
-
try {
|
|
116
|
-
// TODO: Use the inline processor script for the audio worklet if the script is not available
|
|
117
|
-
// const blob = new Blob([InlineProcessorScrippt], {
|
|
118
|
-
// type: "application/javascript",
|
|
119
|
-
// });
|
|
120
|
-
// const url = URL.createObjectURL(blob);
|
|
121
|
-
// await this.audioContext.audioWorklet.addModule(url);
|
|
122
|
-
await this.audioContext.audioWorklet.addModule(this.audioWorkletUrl);
|
|
123
|
-
|
|
124
|
-
this.audioWorkletNode = new AudioWorkletNode(
|
|
125
|
-
this.audioContext,
|
|
126
|
-
"recorder-processor",
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
this.audioWorkletNode.port.onmessage = async (
|
|
130
|
-
event: AudioWorkletEvent,
|
|
131
|
-
) => {
|
|
132
|
-
const command = event.data.command;
|
|
133
|
-
if (command !== "newData") {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
// Handle the audio blob (e.g., send it to the server or process it further)
|
|
137
|
-
log("Received audio blob from processor", event);
|
|
138
|
-
const pcmBuffer = event.data.recordedData;
|
|
139
|
-
|
|
140
|
-
if (!pcmBuffer) {
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
this.buffers.push(pcmBuffer); // Store the buffer
|
|
145
|
-
const sampleRate =
|
|
146
|
-
event.data.sampleRate ?? this.audioContext.sampleRate;
|
|
147
|
-
const otherSampleRate = this.audioContext.sampleRate;
|
|
148
|
-
|
|
149
|
-
// Pass the intermediary buffer to the feature extractor worker
|
|
150
|
-
const pcmBufferCopy = pcmBuffer.slice(0);
|
|
151
|
-
const channelData = new Float32Array(pcmBufferCopy);
|
|
152
|
-
|
|
153
|
-
const duration = channelData.length / sampleRate; // Calculate duration of the current buffer
|
|
154
|
-
const otherDuration =
|
|
155
|
-
pcmBuffer.byteLength /
|
|
156
|
-
(otherSampleRate * (this.exportBitDepth / this.numberOfChannels)); // Calculate duration of the current buffer
|
|
157
|
-
log(
|
|
158
|
-
`sampleRate=${sampleRate} Duration: ${duration} -- otherSampleRate=${otherSampleRate} Other duration: ${otherDuration}`,
|
|
159
|
-
);
|
|
160
|
-
|
|
161
|
-
this.emitAudioEventCallback({
|
|
162
|
-
data: pcmBuffer,
|
|
163
|
-
position: this.position,
|
|
164
|
-
});
|
|
165
|
-
this.position += duration; // Update position
|
|
166
|
-
|
|
167
|
-
this.featureExtractorWorker.postMessage(
|
|
168
|
-
{
|
|
169
|
-
command: "process",
|
|
170
|
-
channelData,
|
|
171
|
-
sampleRate: this.audioContext.sampleRate,
|
|
172
|
-
pointsPerSecond:
|
|
173
|
-
this.config.pointsPerSecond || DEFAULT_WEB_POINTS_PER_SECOND,
|
|
174
|
-
algorithm: this.config.algorithm || "rms",
|
|
175
|
-
bitDepth: this.bitDepth,
|
|
176
|
-
fullAudioDurationMs: this.position * 1000,
|
|
177
|
-
numberOfChannels: this.numberOfChannels,
|
|
178
|
-
features: this.config.features,
|
|
179
|
-
},
|
|
180
|
-
[],
|
|
181
|
-
);
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
log(
|
|
185
|
-
`WebRecorder initialized -- recordSampleRate=${this.audioContext.sampleRate}`,
|
|
186
|
-
this.config,
|
|
187
|
-
);
|
|
188
|
-
this.audioWorkletNode.port.postMessage({
|
|
189
|
-
command: "init",
|
|
190
|
-
recordSampleRate: this.audioContext.sampleRate, // Pass the original sample rate
|
|
191
|
-
exportSampleRate:
|
|
192
|
-
this.config.sampleRate ?? this.audioContext.sampleRate,
|
|
193
|
-
bitDepth: this.bitDepth,
|
|
194
|
-
exportBitDepth: this.exportBitDepth,
|
|
195
|
-
channels: this.numberOfChannels,
|
|
196
|
-
interval: this.config.interval ?? DEFAULT_WEB_INTERVAL,
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
// Connect the source to the AudioWorkletNode and start recording
|
|
200
|
-
this.source.connect(this.audioWorkletNode);
|
|
201
|
-
this.audioWorkletNode.connect(this.audioContext.destination);
|
|
202
|
-
} catch (error) {
|
|
203
|
-
console.error("Failed to initialize WebRecorder", error);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
handleFeatureExtractorMessage(event: AudioFeaturesEvent) {
|
|
208
|
-
if (event.data.command === "features") {
|
|
209
|
-
const segmentResult = event.data.result;
|
|
210
|
-
|
|
211
|
-
// Merge the segment result with the full audio analysis data
|
|
212
|
-
this.audioAnalysisData.dataPoints.push(...segmentResult.dataPoints);
|
|
213
|
-
this.audioAnalysisData.speakerChanges?.push(
|
|
214
|
-
...(segmentResult.speakerChanges ?? []),
|
|
215
|
-
);
|
|
216
|
-
this.audioAnalysisData.durationMs = segmentResult.durationMs;
|
|
217
|
-
if (segmentResult.amplitudeRange) {
|
|
218
|
-
this.audioAnalysisData.amplitudeRange = {
|
|
219
|
-
min: Math.min(
|
|
220
|
-
this.audioAnalysisData.amplitudeRange.min,
|
|
221
|
-
segmentResult.amplitudeRange.min,
|
|
222
|
-
),
|
|
223
|
-
max: Math.max(
|
|
224
|
-
this.audioAnalysisData.amplitudeRange.max,
|
|
225
|
-
segmentResult.amplitudeRange.max,
|
|
226
|
-
),
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
// Handle the extracted features (e.g., emit an event or log them)
|
|
230
|
-
log("features event segmentResult", segmentResult);
|
|
231
|
-
log("features event audioAnalysisData", this.audioAnalysisData);
|
|
232
|
-
this.emitAudioAnalysisCallback(segmentResult);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
start() {
|
|
237
|
-
this.source.connect(this.audioWorkletNode);
|
|
238
|
-
this.audioWorkletNode.connect(this.audioContext.destination);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
stop() {
|
|
242
|
-
return new Promise((resolve, reject) => {
|
|
243
|
-
try {
|
|
244
|
-
if (this.audioWorkletNode) {
|
|
245
|
-
// this.source.disconnect(this.audioWorkletNode);
|
|
246
|
-
// this.audioWorkletNode.disconnect(this.audioContext.destination);
|
|
247
|
-
this.audioWorkletNode.port.postMessage({ command: "stop" });
|
|
248
|
-
|
|
249
|
-
// Set a timeout to reject the promise if no message is received within 5 seconds
|
|
250
|
-
const timeout = setTimeout(() => {
|
|
251
|
-
this.audioWorkletNode.port.removeEventListener(
|
|
252
|
-
"message",
|
|
253
|
-
onMessage,
|
|
254
|
-
);
|
|
255
|
-
reject(
|
|
256
|
-
new Error("Timeout error, audioWorkletNode didn't complete."),
|
|
257
|
-
);
|
|
258
|
-
}, 5000);
|
|
259
|
-
|
|
260
|
-
// Listen for the recordedData message to confirm stopping
|
|
261
|
-
const onMessage = async (event: AudioWorkletEvent) => {
|
|
262
|
-
const command = event.data.command;
|
|
263
|
-
if (command === "recordedData") {
|
|
264
|
-
clearTimeout(timeout); // Clear the timeout
|
|
265
|
-
|
|
266
|
-
const rawPCMDataFull = event.data.recordedData?.slice(
|
|
267
|
-
0,
|
|
268
|
-
) as ArrayBuffer;
|
|
269
|
-
|
|
270
|
-
// Compute duration of the recorded data
|
|
271
|
-
const duration =
|
|
272
|
-
rawPCMDataFull.byteLength /
|
|
273
|
-
(this.audioContext.sampleRate *
|
|
274
|
-
(this.exportBitDepth / this.numberOfChannels));
|
|
275
|
-
log(
|
|
276
|
-
`Received recorded data -- Duration: ${duration} vs ${rawPCMDataFull.byteLength / this.audioContext.sampleRate} seconds`,
|
|
277
|
-
);
|
|
278
|
-
log(
|
|
279
|
-
`recordedData.length=${rawPCMDataFull.byteLength} vs transmittedData.length=${this.buffers[0].byteLength}`,
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
// Remove the event listener after receiving the final data
|
|
283
|
-
this.audioWorkletNode.port.removeEventListener(
|
|
284
|
-
"message",
|
|
285
|
-
onMessage,
|
|
286
|
-
);
|
|
287
|
-
resolve(this.buffers); // Resolve the promise with the collected buffers
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
this.audioWorkletNode.port.addEventListener("message", onMessage);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Stop all media stream tracks to stop the browser recording
|
|
294
|
-
this.stopMediaStreamTracks();
|
|
295
|
-
} catch (error) {
|
|
296
|
-
reject(error);
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
pause() {
|
|
302
|
-
this.source.disconnect(this.audioWorkletNode); // Disconnect the source from the AudioWorkletNode
|
|
303
|
-
this.audioWorkletNode.disconnect(this.audioContext.destination); // Disconnect the AudioWorkletNode from the destination
|
|
304
|
-
this.audioWorkletNode.port.postMessage({ command: "pause" });
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
stopMediaStreamTracks() {
|
|
308
|
-
// Stop all audio tracks to stop the recording icon
|
|
309
|
-
const tracks = this.source.mediaStream.getTracks();
|
|
310
|
-
tracks.forEach((track) => track.stop());
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
async playRecordedData({
|
|
314
|
-
recordedData,
|
|
315
|
-
}: {
|
|
316
|
-
recordedData: ArrayBuffer;
|
|
317
|
-
mimeType?: string;
|
|
318
|
-
}) {
|
|
319
|
-
try {
|
|
320
|
-
const blob = new Blob([recordedData]);
|
|
321
|
-
const url = URL.createObjectURL(blob);
|
|
322
|
-
const response = await fetch(url);
|
|
323
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
324
|
-
|
|
325
|
-
// Decode the audio data
|
|
326
|
-
const audioBuffer = await this.audioContext.decodeAudioData(arrayBuffer);
|
|
327
|
-
|
|
328
|
-
// Create a buffer source node and play the audio
|
|
329
|
-
const bufferSource = this.audioContext.createBufferSource();
|
|
330
|
-
bufferSource.buffer = audioBuffer;
|
|
331
|
-
bufferSource.connect(this.audioContext.destination);
|
|
332
|
-
bufferSource.start();
|
|
333
|
-
log("Playing recorded data", recordedData);
|
|
334
|
-
} catch (error) {
|
|
335
|
-
console.error(`Failed to play recorded data:`, error);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
private checkAudioContextFormat({ sampleRate }: { sampleRate: number }) {
|
|
340
|
-
// Create a silent AudioBuffer
|
|
341
|
-
const frameCount = sampleRate * 1.0; // 1 second buffer
|
|
342
|
-
const audioBuffer = this.audioContext.createBuffer(
|
|
343
|
-
1,
|
|
344
|
-
frameCount,
|
|
345
|
-
sampleRate,
|
|
346
|
-
);
|
|
347
|
-
|
|
348
|
-
// Check the format
|
|
349
|
-
const channelData = audioBuffer.getChannelData(0);
|
|
350
|
-
const bitDepth = channelData.BYTES_PER_ELEMENT * 8; // 4 bytes per element means 32-bit
|
|
351
|
-
|
|
352
|
-
return {
|
|
353
|
-
sampleRate: audioBuffer.sampleRate,
|
|
354
|
-
bitDepth,
|
|
355
|
-
numberOfChannels: audioBuffer.numberOfChannels,
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
resume() {
|
|
360
|
-
this.source.connect(this.audioWorkletNode);
|
|
361
|
-
this.audioWorkletNode.connect(this.audioContext.destination);
|
|
362
|
-
this.audioWorkletNode.port.postMessage({ command: "resume" });
|
|
363
|
-
}
|
|
364
|
-
}
|