@siteed/expo-audio-stream 1.0.2 → 1.0.3

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.
Files changed (113) hide show
  1. package/.size-limit.json +6 -0
  2. package/build/AudioAnalysis/AudioAnalysis.types.d.ts +76 -0
  3. package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -0
  4. package/build/AudioAnalysis/AudioAnalysis.types.js +3 -0
  5. package/build/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
  6. package/build/AudioAnalysis/extractAudioAnalysis.d.ts +4 -0
  7. package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -0
  8. package/build/AudioAnalysis/extractAudioAnalysis.js +101 -0
  9. package/build/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
  10. package/build/AudioAnalysis/extractWaveform.d.ts +8 -0
  11. package/build/AudioAnalysis/extractWaveform.d.ts.map +1 -0
  12. package/build/AudioAnalysis/extractWaveform.js +14 -0
  13. package/build/AudioAnalysis/extractWaveform.js.map +1 -0
  14. package/build/AudioRecorder.provider.d.ts +14 -1
  15. package/build/AudioRecorder.provider.d.ts.map +1 -1
  16. package/build/AudioRecorder.provider.js +17 -4
  17. package/build/AudioRecorder.provider.js.map +1 -1
  18. package/build/ExpoAudioStream.types.d.ts +26 -84
  19. package/build/ExpoAudioStream.types.d.ts.map +1 -1
  20. package/build/ExpoAudioStream.types.js.map +1 -1
  21. package/build/ExpoAudioStream.web.d.ts +6 -5
  22. package/build/ExpoAudioStream.web.d.ts.map +1 -1
  23. package/build/ExpoAudioStream.web.js +9 -8
  24. package/build/ExpoAudioStream.web.js.map +1 -1
  25. package/build/ExpoAudioStreamModule.d.ts.map +1 -1
  26. package/build/ExpoAudioStreamModule.js +5 -1
  27. package/build/ExpoAudioStreamModule.js.map +1 -1
  28. package/build/{WebRecorder.d.ts → WebRecorder.web.d.ts} +7 -3
  29. package/build/WebRecorder.web.d.ts.map +1 -0
  30. package/build/{WebRecorder.js → WebRecorder.web.js} +74 -29
  31. package/build/WebRecorder.web.js.map +1 -0
  32. package/build/constants.d.ts +11 -0
  33. package/build/constants.d.ts.map +1 -0
  34. package/build/constants.js +14 -0
  35. package/build/constants.js.map +1 -0
  36. package/build/events.d.ts +6 -0
  37. package/build/events.d.ts.map +1 -0
  38. package/build/events.js +15 -0
  39. package/build/events.js.map +1 -0
  40. package/build/index.d.ts +8 -16
  41. package/build/index.d.ts.map +1 -1
  42. package/build/index.js +6 -112
  43. package/build/index.js.map +1 -1
  44. package/build/logger.d.ts +9 -0
  45. package/build/logger.d.ts.map +1 -0
  46. package/build/logger.js +17 -0
  47. package/build/logger.js.map +1 -0
  48. package/build/{useAudioRecording.d.ts → useAudioRecorder.d.ts} +6 -7
  49. package/build/useAudioRecorder.d.ts.map +1 -0
  50. package/build/{useAudioRecording.js → useAudioRecorder.js} +69 -65
  51. package/build/useAudioRecorder.js.map +1 -0
  52. package/build/utils/convertPCMToFloat32.d.ts +11 -0
  53. package/build/utils/convertPCMToFloat32.d.ts.map +1 -0
  54. package/build/utils/convertPCMToFloat32.js +41 -0
  55. package/build/utils/convertPCMToFloat32.js.map +1 -0
  56. package/build/utils/encodingToBitDepth.d.ts +5 -0
  57. package/build/utils/encodingToBitDepth.d.ts.map +1 -0
  58. package/build/utils/encodingToBitDepth.js +13 -0
  59. package/build/utils/encodingToBitDepth.js.map +1 -0
  60. package/build/utils/getWavFileInfo.d.ts +25 -0
  61. package/build/utils/getWavFileInfo.d.ts.map +1 -0
  62. package/build/utils/getWavFileInfo.js +89 -0
  63. package/build/utils/getWavFileInfo.js.map +1 -0
  64. package/build/utils/writeWavHeader.d.ts +9 -0
  65. package/build/utils/writeWavHeader.d.ts.map +1 -0
  66. package/build/utils/writeWavHeader.js +41 -0
  67. package/build/utils/writeWavHeader.js.map +1 -0
  68. package/build/workers/InlineFeaturesExtractor.web.d.ts +2 -0
  69. package/build/workers/InlineFeaturesExtractor.web.d.ts.map +1 -0
  70. package/build/workers/InlineFeaturesExtractor.web.js +303 -0
  71. package/build/workers/InlineFeaturesExtractor.web.js.map +1 -0
  72. package/build/workers/inlineAudioWebWorker.web.d.ts +2 -0
  73. package/build/workers/inlineAudioWebWorker.web.d.ts.map +1 -0
  74. package/build/workers/inlineAudioWebWorker.web.js +243 -0
  75. package/build/workers/inlineAudioWebWorker.web.js.map +1 -0
  76. package/ios/AudioStreamManager.swift +39 -2
  77. package/ios/ExpoAudioStreamModule.swift +10 -0
  78. package/package.json +7 -6
  79. package/plugin/tsconfig.json +1 -1
  80. package/publish.sh +0 -0
  81. package/src/AudioAnalysis/AudioAnalysis.types.ts +85 -0
  82. package/src/AudioAnalysis/extractAudioAnalysis.ts +136 -0
  83. package/src/AudioAnalysis/extractWaveform.ts +25 -0
  84. package/src/AudioRecorder.provider.tsx +35 -7
  85. package/src/ExpoAudioStream.types.ts +33 -94
  86. package/src/ExpoAudioStream.web.ts +17 -16
  87. package/src/ExpoAudioStreamModule.ts +6 -1
  88. package/src/{WebRecorder.ts → WebRecorder.web.ts} +85 -33
  89. package/src/constants.ts +18 -0
  90. package/src/events.ts +25 -0
  91. package/src/index.ts +8 -169
  92. package/src/logger.ts +26 -0
  93. package/src/{useAudioRecording.tsx → useAudioRecorder.tsx} +141 -136
  94. package/src/utils/convertPCMToFloat32.ts +48 -0
  95. package/src/utils/encodingToBitDepth.ts +18 -0
  96. package/src/utils/getWavFileInfo.ts +125 -0
  97. package/src/utils/writeWavHeader.ts +56 -0
  98. package/src/workers/InlineFeaturesExtractor.web.tsx +302 -0
  99. package/src/workers/inlineAudioWebWorker.web.tsx +242 -0
  100. package/build/WebRecorder.d.ts.map +0 -1
  101. package/build/WebRecorder.js.map +0 -1
  102. package/build/inlineAudioWebWorker.d.ts +0 -3
  103. package/build/inlineAudioWebWorker.d.ts.map +0 -1
  104. package/build/inlineAudioWebWorker.js +0 -340
  105. package/build/inlineAudioWebWorker.js.map +0 -1
  106. package/build/useAudioRecording.d.ts.map +0 -1
  107. package/build/useAudioRecording.js.map +0 -1
  108. package/build/utils.d.ts +0 -31
  109. package/build/utils.d.ts.map +0 -1
  110. package/build/utils.js +0 -143
  111. package/build/utils.js.map +0 -1
  112. package/src/inlineAudioWebWorker.tsx +0 -340
  113. package/src/utils.ts +0 -189
@@ -0,0 +1,242 @@
1
+ export const InlineAudioWebWorker = `
2
+ const DEFAULT_BIT_DEPTH = 32;
3
+ const DEFAULT_SAMPLE_RATE = 44100;
4
+
5
+ class RecorderProcessor extends AudioWorkletProcessor {
6
+ constructor() {
7
+ super();
8
+ this.recordedBuffers = []; // Float32Array
9
+ this.newRecBuffer = []; // Float32Array
10
+ this.resampledBuffer = []; // Float32Array
11
+ this.exportIntervalSamples = 0;
12
+ this.samplesSinceLastExport = 0;
13
+ this.recordSampleRate = DEFAULT_SAMPLE_RATE; // To be overwritten
14
+ this.exportSampleRate = DEFAULT_SAMPLE_RATE; // To be overwritten
15
+ this.recordBitDepth = DEFAULT_BIT_DEPTH; // Default to 32-bit depth
16
+ this.exportBitDepth = DEFAULT_BIT_DEPTH; // To be overwritten
17
+ this.numberOfChannels = 1; // Default to 1 channel (mono)
18
+ this.isRecording = true;
19
+ this.port.onmessage = this.handleMessage.bind(this);
20
+ }
21
+
22
+ handleMessage(event) {
23
+ switch (event.data.command) {
24
+ case "init":
25
+ this.recordSampleRate = event.data.recordSampleRate;
26
+ this.exportSampleRate =
27
+ event.data.exportSampleRate || event.data.recordSampleRate;
28
+ this.exportIntervalSamples =
29
+ this.recordSampleRate * (event.data.interval / 1000);
30
+ if (event.data.numberOfChannels) {
31
+ this.numberOfChannels = event.data.numberOfChannels;
32
+ }
33
+ if (event.data.recordBitDepth) {
34
+ this.recordBitDepth = event.data.recordBitDepth;
35
+ }
36
+ this.exportBitDepth =
37
+ event.data.exportBitDepth || this.recordBitDepth || DEFAULT_BIT_DEPTH;
38
+ break;
39
+ case "stop":
40
+ this.isRecording = false;
41
+ this.getAllRecordedData()
42
+ .then((fullRecordedData) => {
43
+ this.port.postMessage({
44
+ command: "recordedData",
45
+ recordedData: fullRecordedData,
46
+ bitDepth: this.exportBitDepth,
47
+ sampleRate: this.exportSampleRate,
48
+ });
49
+ return fullRecordedData;
50
+ })
51
+ .catch((error) => {
52
+ console.error("Error extracting recorded data:", error);
53
+ });
54
+ break;
55
+ }
56
+ }
57
+
58
+ process(inputs, outputs, parameters) {
59
+ if (!this.isRecording) return true;
60
+ const input = inputs[0];
61
+ if (input.length > 0) {
62
+ const newBuffer = new Float32Array(input[0]);
63
+ this.newRecBuffer.push(newBuffer);
64
+ this.recordedBuffers.push(newBuffer);
65
+ this.samplesSinceLastExport += newBuffer.length;
66
+
67
+ if (this.samplesSinceLastExport >= this.exportIntervalSamples) {
68
+ this.exportNewData();
69
+ this.samplesSinceLastExport = 0;
70
+ }
71
+ }
72
+ return true;
73
+ }
74
+
75
+ mergeBuffers(bufferArray, recLength) {
76
+ const result = new Float32Array(recLength);
77
+ let offset = 0;
78
+ for (let i = 0; i < bufferArray.length; i++) {
79
+ result.set(bufferArray[i], offset);
80
+ offset += bufferArray[i].length;
81
+ }
82
+ return result;
83
+ }
84
+
85
+ floatTo16BitPCM(input) {
86
+ const output = new Int16Array(input.length);
87
+ for (let i = 0; i < input.length; i++) {
88
+ const s = Math.max(-1, Math.min(1, input[i]));
89
+ output[i] = s < 0 ? s * 0x8000 : s * 0x7fff;
90
+ }
91
+ console.debug(
92
+ "Float to 16-bit PCM conversion complete. Output byte length:",
93
+ output.byteLength,
94
+ );
95
+ return output;
96
+ }
97
+
98
+ floatTo32BitPCM(input) {
99
+ const output = new Int32Array(input.length);
100
+ for (let i = 0; i < input.length; i++) {
101
+ const s = Math.max(-1, Math.min(1, input[i]));
102
+ output[i] = s < 0 ? s * 0x80000000 : s * 0x7fffffff;
103
+ }
104
+ console.debug(
105
+ "Float to 32-bit PCM conversion complete. Output byte length:",
106
+ output.byteLength,
107
+ );
108
+ return output;
109
+ }
110
+
111
+ resample(samples, targetSampleRate) {
112
+ if (this.recordSampleRate === targetSampleRate) {
113
+ return samples;
114
+ }
115
+ const resampledBuffer = new Float32Array(
116
+ (samples.length * targetSampleRate) / this.recordSampleRate,
117
+ );
118
+ const ratio = this.recordSampleRate / targetSampleRate;
119
+ let offset = 0;
120
+ for (let i = 0; i < resampledBuffer.length; i++) {
121
+ const nextOffset = Math.floor((i + 1) * ratio);
122
+ let accum = 0;
123
+ let count = 0;
124
+ for (let j = offset; j < nextOffset && j < samples.length; j++) {
125
+ accum += samples[j];
126
+ count++;
127
+ }
128
+ resampledBuffer[i] = accum / count;
129
+ offset = nextOffset;
130
+ }
131
+ return resampledBuffer;
132
+ }
133
+
134
+ async resampleBuffer(buffer, targetSampleRate) {
135
+ if (typeof OfflineAudioContext === "undefined") {
136
+ return this.resample(buffer, targetSampleRate);
137
+ }
138
+
139
+ if (this.recordSampleRate === targetSampleRate) {
140
+ return buffer;
141
+ }
142
+ const offlineContext = new OfflineAudioContext(
143
+ this.numberOfChannels,
144
+ buffer.length,
145
+ this.recordSampleRate,
146
+ );
147
+ const sourceBuffer = offlineContext.createBuffer(
148
+ this.numberOfChannels,
149
+ buffer.length,
150
+ this.recordSampleRate,
151
+ );
152
+ sourceBuffer.copyToChannel(buffer, 0);
153
+
154
+ const bufferSource = offlineContext.createBufferSource();
155
+ bufferSource.buffer = sourceBuffer;
156
+ bufferSource.connect(offlineContext.destination);
157
+ bufferSource.start();
158
+
159
+ const renderedBuffer = await offlineContext.startRendering();
160
+
161
+ const resampledBuffer = new Float32Array(renderedBuffer.length);
162
+ renderedBuffer.copyFromChannel(resampledBuffer, 0);
163
+
164
+ return resampledBuffer;
165
+ }
166
+
167
+ async exportNewData() {
168
+ // Calculate the total length of the new recorded buffers
169
+ const length = this.newRecBuffer.reduce(
170
+ (acc, buffer) => acc + buffer.length,
171
+ 0,
172
+ );
173
+
174
+ // Merge all new recorded buffers into a single buffer
175
+ const mergedBuffer = this.mergeBuffers(this.newRecBuffer, length);
176
+
177
+ const resampledBuffer = await this.resampleBuffer(
178
+ mergedBuffer,
179
+ this.exportSampleRate,
180
+ );
181
+
182
+ let finalBuffer = resampledBuffer; // Float32Array
183
+ if (this.recordBitDepth !== this.exportBitDepth) {
184
+ if (this.exportBitDepth === 16) {
185
+ finalBuffer = this.floatTo16BitPCM(resampledBuffer);
186
+ } else if (this.exportBitDepth === 32) {
187
+ finalBuffer = this.floatTo32BitPCM(resampledBuffer);
188
+ }
189
+ }
190
+
191
+ const originalSize = mergedBuffer.byteLength;
192
+ const resampledSize = resampledBuffer.byteLength;
193
+ const finalSize = finalBuffer.byteLength;
194
+
195
+ // Clear the new recorded buffers after they have been processed
196
+ this.newRecBuffer.length = 0;
197
+
198
+ // Post the message to the main thread
199
+ // The first argument is the message data, containing the encoded WAV buffer
200
+ // The second argument is the transfer list, which transfers ownership of the ArrayBuffer
201
+ // to the main thread, avoiding the need to copy the buffer and improving performance
202
+ // this.port.postMessage({ recordedData: encodedWav.buffer, sampleRate: this.recordSampleRate }, [encodedWav.buffer]);
203
+ this.port.postMessage({
204
+ command: "newData",
205
+ recordedData: finalBuffer.buffer,
206
+ sampleRate: this.exportSampleRate,
207
+ bitDepth: this.exportBitDepth,
208
+ });
209
+ }
210
+
211
+ async getAllRecordedData() {
212
+ const length = this.recordedBuffers.reduce(
213
+ (acc, buffer) => acc + buffer.length,
214
+ 0,
215
+ );
216
+ const mergedBuffer = this.mergeBuffers(this.recordedBuffers, length);
217
+ const resampledBuffer = await this.resampleBuffer(
218
+ mergedBuffer,
219
+ this.exportSampleRate,
220
+ );
221
+ // Convert to the desired bit depth if necessary
222
+ let finalBuffer = resampledBuffer;
223
+ if (this.recordBitDepth !== this.exportBitDepth) {
224
+ if (this.exportBitDepth === 16) {
225
+ finalBuffer = this.floatTo16BitPCM(resampledBuffer);
226
+ } else if (this.exportBitDepth === 32) {
227
+ finalBuffer = this.floatTo32BitPCM(resampledBuffer);
228
+ }
229
+ }
230
+
231
+ const originalSize = mergedBuffer.byteLength;
232
+ const resampledSize = resampledBuffer.byteLength;
233
+ const finalSize = finalBuffer.byteLength;
234
+
235
+ this.recordedBuffers.length = 0; // Clear the buffers after extraction
236
+
237
+ return finalBuffer.buffer;
238
+ }
239
+ }
240
+
241
+ registerProcessor("recorder-processor", RecorderProcessor);
242
+ `;
@@ -1 +0,0 @@
1
- {"version":3,"file":"WebRecorder.d.ts","sourceRoot":"","sources":["../src/WebRecorder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC7E,OAAO,EACL,yBAAyB,EACzB,sBAAsB,EACvB,MAAM,uBAAuB,CAAC;AAU/B,UAAU,kBAAkB;IAC1B,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,iBAAiB,CAAC;KAC3B,CAAC;CACH;AASD,qBAAa,WAAW;IACtB,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,gBAAgB,CAAoB;IAC5C,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,sBAAsB,CAAyB;IACvD,OAAO,CAAC,yBAAyB,CAA4B;IAC7D,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,iBAAiB,CAAoB;gBAEjC,EACV,YAAY,EACZ,MAAM,EACN,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,sBAAsB,EACtB,yBAAyB,GAC1B,EAAE;QACD,YAAY,EAAE,YAAY,CAAC;QAC3B,MAAM,EAAE,0BAA0B,CAAC;QACnC,eAAe,EAAE,eAAe,CAAC;QACjC,mBAAmB,EAAE,MAAM,CAAC;QAC5B,eAAe,EAAE,MAAM,CAAC;QACxB,sBAAsB,EAAE,sBAAsB,CAAC;QAC/C,yBAAyB,EAAE,yBAAyB,CAAC;KACtD;IAoDK,IAAI;IA6FV,6BAA6B,CAAC,KAAK,EAAE,kBAAkB;IA6BvD,KAAK;IAKL,IAAI;IA4DJ,KAAK;IAML,qBAAqB;IAMf,gBAAgB,CAAC,EACrB,YAAY,GACb,EAAE;QACD,YAAY,EAAE,WAAW,CAAC;QAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB;IAqBD,OAAO,CAAC,uBAAuB;IAoB/B,MAAM;CAKP"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"WebRecorder.js","sourceRoot":"","sources":["../src/WebRecorder.ts"],"names":[],"mappings":"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","sourcesContent":["// src/WebRecorder.ts\nimport { AudioAnalysisData, RecordingConfig } from \"./ExpoAudioStream.types\";\nimport {\n EmitAudioAnalysisFunction,\n EmitAudioEventFunction,\n} from \"./ExpoAudioStream.web\";\nimport { encodingToBitDepth } from \"./utils\";\ninterface AudioWorkletEvent {\n data: {\n command: string;\n recordedData?: ArrayBuffer;\n sampleRate?: number;\n };\n}\n\ninterface AudioFeaturesEvent {\n data: {\n command: string;\n result: AudioAnalysisData;\n };\n}\n\nconst DEFAULT_WEB_BITDEPTH = 32;\nconst DEFAULT_WEB_POINTS_PER_SECOND = 10;\nconst DEFAULT_WEB_INTERVAL = 500;\nconst DEFAULT_WEB_NUMBER_OF_CHANNELS = 1;\n\n// const log = debug(\"expo-audio-stream:WebRecorder\");\nconst log = console.log;\nexport class WebRecorder {\n private audioContext: AudioContext;\n private audioWorkletNode!: AudioWorkletNode;\n private featureExtractorWorker: Worker;\n private source: MediaStreamAudioSourceNode;\n private audioWorkletUrl: string;\n private emitAudioEventCallback: EmitAudioEventFunction;\n private emitAudioAnalysisCallback: EmitAudioAnalysisFunction;\n private config: RecordingConfig;\n private position: number; // Track the cumulative position\n private numberOfChannels: number; // Number of audio channels\n private bitDepth: number; // Bit depth of the audio\n private exportBitDepth: number; // Bit depth of the audio\n private buffers: ArrayBuffer[]; // Array to store the buffers\n private audioAnalysisData: AudioAnalysisData; // Keep updating the full audio analysis data with latest events\n\n constructor({\n audioContext,\n source,\n recordingConfig,\n featuresExtratorUrl,\n audioWorkletUrl,\n emitAudioEventCallback,\n emitAudioAnalysisCallback,\n }: {\n audioContext: AudioContext;\n source: MediaStreamAudioSourceNode;\n recordingConfig: RecordingConfig;\n featuresExtratorUrl: string;\n audioWorkletUrl: string;\n emitAudioEventCallback: EmitAudioEventFunction;\n emitAudioAnalysisCallback: EmitAudioAnalysisFunction;\n }) {\n this.audioContext = audioContext;\n this.source = source;\n this.audioWorkletUrl = audioWorkletUrl;\n this.emitAudioEventCallback = emitAudioEventCallback;\n this.emitAudioAnalysisCallback = emitAudioAnalysisCallback;\n this.config = recordingConfig;\n this.position = 0;\n this.buffers = []; // Initialize the buffers array\n\n const audioContextFormat = this.checkAudioContextFormat({\n sampleRate: this.audioContext.sampleRate,\n });\n log(\"Initialized WebRecorder with config:\", {\n sampleRate: audioContextFormat.sampleRate,\n bitDepth: audioContextFormat.bitDepth,\n numberOfChannels: audioContextFormat.numberOfChannels,\n });\n\n this.bitDepth = audioContextFormat.bitDepth;\n this.numberOfChannels =\n audioContextFormat.numberOfChannels || DEFAULT_WEB_NUMBER_OF_CHANNELS; // Default to 1 if not available\n this.exportBitDepth =\n encodingToBitDepth({\n encoding: recordingConfig.encoding ?? \"pcm_32bit\",\n }) ||\n audioContextFormat.bitDepth ||\n DEFAULT_WEB_BITDEPTH;\n\n this.audioAnalysisData = {\n amplitudeRange: { min: 0, max: 0 },\n dataPoints: [],\n durationMs: 0,\n samples: 0,\n bitDepth: this.bitDepth,\n numberOfChannels: this.numberOfChannels,\n sampleRate: this.config.sampleRate || this.audioContext.sampleRate,\n pointsPerSecond:\n this.config.pointsPerSecond || DEFAULT_WEB_POINTS_PER_SECOND,\n speakerChanges: [],\n };\n\n // Initialize the feature extractor worker\n //TODO: create audio feature extractor from a Blob instead of url since we cannot include the url directly in the library\n // We keep the url during dev and use the blob in production.\n this.featureExtractorWorker = new Worker(\n new URL(featuresExtratorUrl, window.location.href),\n );\n this.featureExtractorWorker.onmessage =\n this.handleFeatureExtractorMessage.bind(this);\n }\n\n async init() {\n try {\n // TODO: Use the inline processor script for the audio worklet if the script is not available\n // const blob = new Blob([InlineProcessorScrippt], {\n // type: \"application/javascript\",\n // });\n // const url = URL.createObjectURL(blob);\n // await this.audioContext.audioWorklet.addModule(url);\n await this.audioContext.audioWorklet.addModule(this.audioWorkletUrl);\n\n this.audioWorkletNode = new AudioWorkletNode(\n this.audioContext,\n \"recorder-processor\",\n );\n\n this.audioWorkletNode.port.onmessage = async (\n event: AudioWorkletEvent,\n ) => {\n const command = event.data.command;\n if (command !== \"newData\") {\n return;\n }\n // Handle the audio blob (e.g., send it to the server or process it further)\n log(\"Received audio blob from processor\", event);\n const pcmBuffer = event.data.recordedData;\n\n if (!pcmBuffer) {\n return;\n }\n\n this.buffers.push(pcmBuffer); // Store the buffer\n const sampleRate =\n event.data.sampleRate ?? this.audioContext.sampleRate;\n const otherSampleRate = this.audioContext.sampleRate;\n\n // Pass the intermediary buffer to the feature extractor worker\n const pcmBufferCopy = pcmBuffer.slice(0);\n const channelData = new Float32Array(pcmBufferCopy);\n\n const duration = channelData.length / sampleRate; // Calculate duration of the current buffer\n const otherDuration =\n pcmBuffer.byteLength /\n (otherSampleRate * (this.exportBitDepth / this.numberOfChannels)); // Calculate duration of the current buffer\n log(\n `sampleRate=${sampleRate} Duration: ${duration} -- otherSampleRate=${otherSampleRate} Other duration: ${otherDuration}`,\n );\n\n this.emitAudioEventCallback({\n data: pcmBuffer,\n position: this.position,\n });\n this.position += duration; // Update position\n\n this.featureExtractorWorker.postMessage(\n {\n command: \"process\",\n channelData,\n sampleRate: this.audioContext.sampleRate,\n pointsPerSecond:\n this.config.pointsPerSecond || DEFAULT_WEB_POINTS_PER_SECOND,\n algorithm: this.config.algorithm || \"rms\",\n bitDepth: this.bitDepth,\n fullAudioDurationMs: this.position * 1000,\n numberOfChannels: this.numberOfChannels,\n features: this.config.features,\n },\n [],\n );\n };\n\n log(\n `WebRecorder initialized -- recordSampleRate=${this.audioContext.sampleRate}`,\n this.config,\n );\n this.audioWorkletNode.port.postMessage({\n command: \"init\",\n recordSampleRate: this.audioContext.sampleRate, // Pass the original sample rate\n exportSampleRate:\n this.config.sampleRate ?? this.audioContext.sampleRate,\n bitDepth: this.bitDepth,\n exportBitDepth: this.exportBitDepth,\n channels: this.numberOfChannels,\n interval: this.config.interval ?? DEFAULT_WEB_INTERVAL,\n });\n\n // Connect the source to the AudioWorkletNode and start recording\n this.source.connect(this.audioWorkletNode);\n this.audioWorkletNode.connect(this.audioContext.destination);\n } catch (error) {\n console.error(\"Failed to initialize WebRecorder\", error);\n }\n }\n\n handleFeatureExtractorMessage(event: AudioFeaturesEvent) {\n if (event.data.command === \"features\") {\n const segmentResult = event.data.result;\n\n // Merge the segment result with the full audio analysis data\n this.audioAnalysisData.dataPoints.push(...segmentResult.dataPoints);\n this.audioAnalysisData.speakerChanges?.push(\n ...(segmentResult.speakerChanges ?? []),\n );\n this.audioAnalysisData.durationMs = segmentResult.durationMs;\n if (segmentResult.amplitudeRange) {\n this.audioAnalysisData.amplitudeRange = {\n min: Math.min(\n this.audioAnalysisData.amplitudeRange.min,\n segmentResult.amplitudeRange.min,\n ),\n max: Math.max(\n this.audioAnalysisData.amplitudeRange.max,\n segmentResult.amplitudeRange.max,\n ),\n };\n }\n // Handle the extracted features (e.g., emit an event or log them)\n log(\"features event segmentResult\", segmentResult);\n log(\"features event audioAnalysisData\", this.audioAnalysisData);\n this.emitAudioAnalysisCallback(segmentResult);\n }\n }\n\n start() {\n this.source.connect(this.audioWorkletNode);\n this.audioWorkletNode.connect(this.audioContext.destination);\n }\n\n stop() {\n return new Promise((resolve, reject) => {\n try {\n if (this.audioWorkletNode) {\n // this.source.disconnect(this.audioWorkletNode);\n // this.audioWorkletNode.disconnect(this.audioContext.destination);\n this.audioWorkletNode.port.postMessage({ command: \"stop\" });\n\n // Set a timeout to reject the promise if no message is received within 5 seconds\n const timeout = setTimeout(() => {\n this.audioWorkletNode.port.removeEventListener(\n \"message\",\n onMessage,\n );\n reject(\n new Error(\"Timeout error, audioWorkletNode didn't complete.\"),\n );\n }, 5000);\n\n // Listen for the recordedData message to confirm stopping\n const onMessage = async (event: AudioWorkletEvent) => {\n const command = event.data.command;\n if (command === \"recordedData\") {\n clearTimeout(timeout); // Clear the timeout\n\n const rawPCMDataFull = event.data.recordedData?.slice(\n 0,\n ) as ArrayBuffer;\n\n // Compute duration of the recorded data\n const duration =\n rawPCMDataFull.byteLength /\n (this.audioContext.sampleRate *\n (this.exportBitDepth / this.numberOfChannels));\n log(\n `Received recorded data -- Duration: ${duration} vs ${rawPCMDataFull.byteLength / this.audioContext.sampleRate} seconds`,\n );\n log(\n `recordedData.length=${rawPCMDataFull.byteLength} vs transmittedData.length=${this.buffers[0].byteLength}`,\n );\n\n // Remove the event listener after receiving the final data\n this.audioWorkletNode.port.removeEventListener(\n \"message\",\n onMessage,\n );\n resolve(this.buffers); // Resolve the promise with the collected buffers\n }\n };\n this.audioWorkletNode.port.addEventListener(\"message\", onMessage);\n }\n\n // Stop all media stream tracks to stop the browser recording\n this.stopMediaStreamTracks();\n } catch (error) {\n reject(error);\n }\n });\n }\n\n pause() {\n this.source.disconnect(this.audioWorkletNode); // Disconnect the source from the AudioWorkletNode\n this.audioWorkletNode.disconnect(this.audioContext.destination); // Disconnect the AudioWorkletNode from the destination\n this.audioWorkletNode.port.postMessage({ command: \"pause\" });\n }\n\n stopMediaStreamTracks() {\n // Stop all audio tracks to stop the recording icon\n const tracks = this.source.mediaStream.getTracks();\n tracks.forEach((track) => track.stop());\n }\n\n async playRecordedData({\n recordedData,\n }: {\n recordedData: ArrayBuffer;\n mimeType?: string;\n }) {\n try {\n const blob = new Blob([recordedData]);\n const url = URL.createObjectURL(blob);\n const response = await fetch(url);\n const arrayBuffer = await response.arrayBuffer();\n\n // Decode the audio data\n const audioBuffer = await this.audioContext.decodeAudioData(arrayBuffer);\n\n // Create a buffer source node and play the audio\n const bufferSource = this.audioContext.createBufferSource();\n bufferSource.buffer = audioBuffer;\n bufferSource.connect(this.audioContext.destination);\n bufferSource.start();\n log(\"Playing recorded data\", recordedData);\n } catch (error) {\n console.error(`Failed to play recorded data:`, error);\n }\n }\n\n private checkAudioContextFormat({ sampleRate }: { sampleRate: number }) {\n // Create a silent AudioBuffer\n const frameCount = sampleRate * 1.0; // 1 second buffer\n const audioBuffer = this.audioContext.createBuffer(\n 1,\n frameCount,\n sampleRate,\n );\n\n // Check the format\n const channelData = audioBuffer.getChannelData(0);\n const bitDepth = channelData.BYTES_PER_ELEMENT * 8; // 4 bytes per element means 32-bit\n\n return {\n sampleRate: audioBuffer.sampleRate,\n bitDepth,\n numberOfChannels: audioBuffer.numberOfChannels,\n };\n }\n\n resume() {\n this.source.connect(this.audioWorkletNode);\n this.audioWorkletNode.connect(this.audioContext.destination);\n this.audioWorkletNode.port.postMessage({ command: \"resume\" });\n }\n}\n"]}
@@ -1,3 +0,0 @@
1
- export declare const SimpleInlineProcessorScript = "\nclass RecorderProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.recordedBuffers = []; // Float32Array\n this.newRecBuffer = []; // Float32Array\n this.exportIntervalSamples = 0;\n this.samplesSinceLastExport = 0;\n this.recordSampleRate = 44100; // To be overwritten\n this.exportSampleRate = 44100; // To be overwritten\n this.channels = 1; // Default to 1 channel (mono)\n this.bitDepth = 32; // Default to 32-bit depth\n this.isRecording = true;\n this.port.onmessage = this.handleMessage.bind(this);\n }\n\n handleMessage(event) {\n switch (event.data.command) {\n case 'init':\n this.recordSampleRate = event.data.recordSampleRate;\n this.exportSampleRate = event.data.exportSampleRate || event.data.recordSampleRate;\n this.exportIntervalSamples = this.recordSampleRate * (event.data.interval / 1000);\n break;\n case 'stop':\n this.isRecording = false;\n const fullRecordedData = this.getAllRecordedData();\n this.port.postMessage({ command: 'recordedData', recordedData: fullRecordedData });\n break;\n }\n }\n\n process(inputs, outputs, parameters) {\n if (!this.isRecording) return true;\n const input = inputs[0];\n if (input.length > 0) {\n const newBuffer = new Float32Array(input[0]);\n this.newRecBuffer.push(newBuffer);\n this.recordedBuffers.push(newBuffer);\n this.samplesSinceLastExport += newBuffer.length;\n\n if (this.samplesSinceLastExport >= this.exportIntervalSamples) {\n this.exportNewData();\n this.samplesSinceLastExport = 0;\n }\n }\n return true;\n }\n\n mergeBuffers(bufferArray, recLength) {\n const result = new Float32Array(recLength);\n let offset = 0;\n for (let i = 0; i < bufferArray.length; i++) {\n result.set(bufferArray[i], offset);\n offset += bufferArray[i].length;\n }\n return result;\n }\n\n floatTo16BitPCM(output, offset, input) {\n for (let i = 0; i < input.length; i++, offset += 2) {\n const s = Math.max(-1, Math.min(1, input[i]));\n output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);\n }\n console.debug('Float to 16-bit PCM conversion complete. Output byte length:', offset);\n }\n\n floatTo32BitPCM(output, offset, input) {\n for (let i = 0; i < input.length; i++, offset += 4) {\n output.setFloat32(offset, input[i], true);\n }\n console.debug('Float to 32-bit PCM (no conversion) complete. Output byte length:', offset);\n }\n\n writeString(view, offset, string) {\n for (let i = 0; i < string.length; i++) {\n view.setUint8(offset + i, string.charCodeAt(i));\n }\n }\n\n encodeWAV(samples, includeHeader = true) {\n const sampleCount = samples.length;\n const buffer = new ArrayBuffer((includeHeader ? 44 : 0) + sampleCount * 4);\n const view = new DataView(buffer);\n\n if (includeHeader) {\n this.writeString(view, 0, 'RIFF');\n view.setUint32(4, 36 + sampleCount * 4, true); // File size - 8 bytes\n this.writeString(view, 8, 'WAVE');\n this.writeString(view, 12, 'fmt ');\n view.setUint32(16, 16, true); // PCM format\n view.setUint16(20, 3, true); // Format code 3 for float\n view.setUint16(22, 1, true); // Mono channel\n view.setUint32(24, this.recordSampleRate, true); // Sample rate\n view.setUint32(28, this.recordSampleRate * 4, true); // Byte rate\n view.setUint16(32, 4, true); // Block align (4 bytes for 32-bit float)\n view.setUint16(34, 32, true); // Bits per sample (32-bit float)\n this.writeString(view, 36, 'data');\n view.setUint32(40, sampleCount * 4, true); // Data chunk size\n }\n\n console.debug('Writing PCM samples to DataView. Offset:', includeHeader ? 44 : 0, 'Samples length:', sampleCount);\n this.floatTo32BitPCM(view, includeHeader ? 44 : 0, samples);\n // this.floatTo16BitPCM(view, includeHeader ? 44 : 0, samples);\n\n console.debug('Encoded WAV DataView:', view);\n console.debug('Encoded WAV length:', view.byteLength);\n\n return view;\n }\n\n\n exportNewData() {\n // Calculate the total length of the new recorded buffers\n const length = this.newRecBuffer.reduce((acc, buffer) => acc + buffer.length, 0);\n\n // Merge all new recorded buffers into a single buffer\n const mergedBuffer = this.mergeBuffers(this.newRecBuffer, length);\n\n // Encode the merged buffer into a WAV format\n const encodedWav = this.encodeWAV(mergedBuffer, false);\n\n // Clear the new recorded buffers after they have been processed\n this.newRecBuffer.length = 0;\n\n // Post the message to the main thread\n // The first argument is the message data, containing the encoded WAV buffer\n // The second argument is the transfer list, which transfers ownership of the ArrayBuffer\n // to the main thread, avoiding the need to copy the buffer and improving performance\n this.port.postMessage({ recordedData: encodedWav.buffer }, [encodedWav.buffer]);\n }\n\n getAllRecordedData() {\n const length = this.recordedBuffers.reduce((acc, buffer) => acc + buffer.length, 0);\n const mergedBuffer = this.mergeBuffers(this.recordedBuffers, length);\n\n\n // Calculate the duration based on the sample count and sample rate\n const sampleCount = mergedBuffer.length;\n const mergedBufferDuration = sampleCount / this.recordSampleRate;\n\n const encodedWav = this.encodeWAV(mergedBuffer, true);\n\n // Calculate and log the duration for encodedWav.buffer based on sample count\n const encodedWavBufferDuration = sampleCount / this.recordSampleRate;\n\n this.recordedBuffers.length = 0; // Clear the buffers after extraction\n\n // Returning both for testing, comment one of the returns based on your test\n console.debug('mergedBuffer:', mergedBuffer);\n console.debug('encodedWav.buffer:', encodedWav.buffer);\n\n // Uncomment the appropriate return for testing\n // return mergedBuffer; // This works when played\n return encodedWav.buffer; // This doesn't work when played\n }\n}\n\nregisterProcessor('recorder-processor', RecorderProcessor);\n";
2
- export declare const InlineProcessorScrippt = "\nclass RecorderProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.recLength = 0;\n this.recBuffer = [];\n this.headerSent = false;\n this.newRecBuffer = [];\n this.exportIntervalSamples = 0;\n this.samplesSinceLastExport = 0;\n this.recordSampleRate = 44100; // To be overwrited\n this.exportSampleRate = 44100; // To be overwrited\n this.isRecording = true;\n\n this.port.onmessage = this.handleMessage.bind(this);\n }\n\n handleMessage(event) {\n switch (event.data.command) {\n case 'init':\n this.recordSampleRate = event.data.recordSampleRate;\n this.exportSampleRate = event.data.exportSampleRate || event.data.recordSampleRate;\n this.exportIntervalSamples = this.recordSampleRate * (event.data.interval / 1000);\n break;\n case 'stop':\n this.isRecording = false;\n break;\n case 'getRecordedData':\n const recordedData = this.getRecordedData();\n this.port.postMessage({ command: 'recordedData', recordedData });\n break;\n }\n }\n\n process(inputs) {\n if (!this.isRecording) {\n return true; // Exit early if not recording\n }\n\n if (inputs.length === 0 || inputs[0].length === 0) {\n console.warn('RecorderProcessor -- No input received.');\n return true; // Exit early if no input\n }\n\n const buffer = inputs[0];\n if (!buffer) {\n console.error('Input buffer is null.');\n return true; // Exit early if buffer is null\n }\n\n try {\n this.record(buffer);\n\n // this.samplesSinceLastExport += buffer.length;\n // if (this.samplesSinceLastExport >= this.exportIntervalSamples) {\n // this.exportBuffer();\n // this.samplesSinceLastExport = 0;\n // }\n } catch (error) {\n console.error('Error during processing:', error);\n }\n return true;\n }\n\n record(inputBuffer) {\n this.recBuffer.push(inputBuffer);\n this.newRecBuffer.push(inputBuffer);\n this.recLength += inputBuffer.length;\n }\n\n exportBuffer() {\n const mergedBuffers = this.mergeBuffers(this.newRecBuffer, this.newRecBuffer.reduce((len, buf) => len + buf.length, 0));\n console.log('Merged buffer length:', mergedBuffers.length); // Debug log\n\n const downsampledBuffer = this.downsampleBuffer(mergedBuffers, this.exportSampleRate);\n console.log('Downsampled buffer length:', downsampledBuffer.length); // Debug log\n\n const encodedWav = downsampledBuffer;\n // const encodedWav = this.encodeWAV(downsampledBuffer);\n // console.log('Encoded WAV length:', encodedWav.byteLength); // Debug log\n\n this.port.postMessage({ encodedWav, sampleRate: this.exportSampleRate });\n this.newRecBuffer = []; // Clear the new data buffer after export\n this.headerSent = true; // Indicate that the header has been sent\n }\n\n downsampleBuffer(buffer, exportSampleRate) {\n console.log('Original buffer length:', buffer.length); // Debug log\n console.log('Record sample rate:', this.recordSampleRate); // Debug log\n console.log('Export sample rate:', exportSampleRate); // Debug log\n if (exportSampleRate === this.recordSampleRate) {\n return buffer;\n }\n\n const sampleRateRatio = this.recordSampleRate / exportSampleRate;\n const newLength = Math.round(buffer.length / sampleRateRatio);\n console.log('Sample rate ratio:', sampleRateRatio); // Debug log\n console.log('New length after downsampling:', newLength); // Debug log\n \n if (newLength <= 0) {\n console.error('New length is zero or negative, returning empty buffer.'); // Debug log\n return new Float32Array(0);\n }\n const result = new Float32Array(newLength);\n let offsetResult = 0;\n let offsetBuffer = 0;\n while (offsetResult < result.length) {\n const nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);\n let accum = 0, count = 0;\n for (let i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) {\n accum += buffer[i];\n count++;\n }\n result[offsetResult] = accum / count;\n offsetResult++;\n offsetBuffer = nextOffsetBuffer;\n }\n return result;\n }\n\n mergeBuffers(bufferArray, recLength) {\n const result = new Float32Array(recLength);\n let offset = 0;\n for (let i = 0; i < bufferArray.length; i++) {\n result.set(bufferArray[i], offset);\n offset += bufferArray[i].length;\n }\n return result;\n }\n\n floatTo16BitPCM(output, offset, input) {\n for (let i = 0; i < input.length; i++, offset += 2) {\n const s = Math.max(-1, Math.min(1, input[i]));\n output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);\n }\n }\n\n writeString(view, offset, string) {\n for (let i = 0; i < string.length; i++) {\n view.setUint8(offset + i, string.charCodeAt(i));\n }\n }\n\n getRecordedData() {\n const length = this.recBuffer.reduce((acc, buffer) => acc + buffer.length, 0);\n const result = new Float32Array(length);\n let offset = 0;\n for (const buffer of this.recBuffer) {\n result.set(buffer, offset);\n offset += buffer.length;\n }\n this.recBuffer.length = 0; // Clear the buffers after extraction\n\n return result;\n }\n\n encodeWAV(samples) {\n const buffer = new ArrayBuffer(44 + samples.length * 2);\n const view = new DataView(buffer);\n this.writeString(view, 0, 'RIFF');\n view.setUint32(4, 32 + samples.length * 2, true);\n this.writeString(view, 8, 'WAVE');\n this.writeString(view, 12, 'fmt ');\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, 1, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, sampleRate * 2, true);\n view.setUint16(32, 2, true);\n view.setUint16(34, 16, true);\n this.writeString(view, 36, 'data');\n view.setUint32(40, samples.length * 2, true);\n this.floatTo16BitPCM(view, 44, samples);\n return view;\n }\n}\n\nregisterProcessor('recorder-processor', RecorderProcessor);\n";
3
- //# sourceMappingURL=inlineAudioWebWorker.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"inlineAudioWebWorker.d.ts","sourceRoot":"","sources":["../src/inlineAudioWebWorker.tsx"],"names":[],"mappings":"AAAA,eAAO,MAAM,2BAA2B,62MA8JvC,CAAC;AAGF,eAAO,MAAM,sBAAsB,89LAkLlC,CAAC"}
@@ -1,340 +0,0 @@
1
- export const SimpleInlineProcessorScript = `
2
- class RecorderProcessor extends AudioWorkletProcessor {
3
- constructor() {
4
- super();
5
- this.recordedBuffers = []; // Float32Array
6
- this.newRecBuffer = []; // Float32Array
7
- this.exportIntervalSamples = 0;
8
- this.samplesSinceLastExport = 0;
9
- this.recordSampleRate = 44100; // To be overwritten
10
- this.exportSampleRate = 44100; // To be overwritten
11
- this.channels = 1; // Default to 1 channel (mono)
12
- this.bitDepth = 32; // Default to 32-bit depth
13
- this.isRecording = true;
14
- this.port.onmessage = this.handleMessage.bind(this);
15
- }
16
-
17
- handleMessage(event) {
18
- switch (event.data.command) {
19
- case 'init':
20
- this.recordSampleRate = event.data.recordSampleRate;
21
- this.exportSampleRate = event.data.exportSampleRate || event.data.recordSampleRate;
22
- this.exportIntervalSamples = this.recordSampleRate * (event.data.interval / 1000);
23
- break;
24
- case 'stop':
25
- this.isRecording = false;
26
- const fullRecordedData = this.getAllRecordedData();
27
- this.port.postMessage({ command: 'recordedData', recordedData: fullRecordedData });
28
- break;
29
- }
30
- }
31
-
32
- process(inputs, outputs, parameters) {
33
- if (!this.isRecording) return true;
34
- const input = inputs[0];
35
- if (input.length > 0) {
36
- const newBuffer = new Float32Array(input[0]);
37
- this.newRecBuffer.push(newBuffer);
38
- this.recordedBuffers.push(newBuffer);
39
- this.samplesSinceLastExport += newBuffer.length;
40
-
41
- if (this.samplesSinceLastExport >= this.exportIntervalSamples) {
42
- this.exportNewData();
43
- this.samplesSinceLastExport = 0;
44
- }
45
- }
46
- return true;
47
- }
48
-
49
- mergeBuffers(bufferArray, recLength) {
50
- const result = new Float32Array(recLength);
51
- let offset = 0;
52
- for (let i = 0; i < bufferArray.length; i++) {
53
- result.set(bufferArray[i], offset);
54
- offset += bufferArray[i].length;
55
- }
56
- return result;
57
- }
58
-
59
- floatTo16BitPCM(output, offset, input) {
60
- for (let i = 0; i < input.length; i++, offset += 2) {
61
- const s = Math.max(-1, Math.min(1, input[i]));
62
- output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
63
- }
64
- console.debug('Float to 16-bit PCM conversion complete. Output byte length:', offset);
65
- }
66
-
67
- floatTo32BitPCM(output, offset, input) {
68
- for (let i = 0; i < input.length; i++, offset += 4) {
69
- output.setFloat32(offset, input[i], true);
70
- }
71
- console.debug('Float to 32-bit PCM (no conversion) complete. Output byte length:', offset);
72
- }
73
-
74
- writeString(view, offset, string) {
75
- for (let i = 0; i < string.length; i++) {
76
- view.setUint8(offset + i, string.charCodeAt(i));
77
- }
78
- }
79
-
80
- encodeWAV(samples, includeHeader = true) {
81
- const sampleCount = samples.length;
82
- const buffer = new ArrayBuffer((includeHeader ? 44 : 0) + sampleCount * 4);
83
- const view = new DataView(buffer);
84
-
85
- if (includeHeader) {
86
- this.writeString(view, 0, 'RIFF');
87
- view.setUint32(4, 36 + sampleCount * 4, true); // File size - 8 bytes
88
- this.writeString(view, 8, 'WAVE');
89
- this.writeString(view, 12, 'fmt ');
90
- view.setUint32(16, 16, true); // PCM format
91
- view.setUint16(20, 3, true); // Format code 3 for float
92
- view.setUint16(22, 1, true); // Mono channel
93
- view.setUint32(24, this.recordSampleRate, true); // Sample rate
94
- view.setUint32(28, this.recordSampleRate * 4, true); // Byte rate
95
- view.setUint16(32, 4, true); // Block align (4 bytes for 32-bit float)
96
- view.setUint16(34, 32, true); // Bits per sample (32-bit float)
97
- this.writeString(view, 36, 'data');
98
- view.setUint32(40, sampleCount * 4, true); // Data chunk size
99
- }
100
-
101
- console.debug('Writing PCM samples to DataView. Offset:', includeHeader ? 44 : 0, 'Samples length:', sampleCount);
102
- this.floatTo32BitPCM(view, includeHeader ? 44 : 0, samples);
103
- // this.floatTo16BitPCM(view, includeHeader ? 44 : 0, samples);
104
-
105
- console.debug('Encoded WAV DataView:', view);
106
- console.debug('Encoded WAV length:', view.byteLength);
107
-
108
- return view;
109
- }
110
-
111
-
112
- exportNewData() {
113
- // Calculate the total length of the new recorded buffers
114
- const length = this.newRecBuffer.reduce((acc, buffer) => acc + buffer.length, 0);
115
-
116
- // Merge all new recorded buffers into a single buffer
117
- const mergedBuffer = this.mergeBuffers(this.newRecBuffer, length);
118
-
119
- // Encode the merged buffer into a WAV format
120
- const encodedWav = this.encodeWAV(mergedBuffer, false);
121
-
122
- // Clear the new recorded buffers after they have been processed
123
- this.newRecBuffer.length = 0;
124
-
125
- // Post the message to the main thread
126
- // The first argument is the message data, containing the encoded WAV buffer
127
- // The second argument is the transfer list, which transfers ownership of the ArrayBuffer
128
- // to the main thread, avoiding the need to copy the buffer and improving performance
129
- this.port.postMessage({ recordedData: encodedWav.buffer }, [encodedWav.buffer]);
130
- }
131
-
132
- getAllRecordedData() {
133
- const length = this.recordedBuffers.reduce((acc, buffer) => acc + buffer.length, 0);
134
- const mergedBuffer = this.mergeBuffers(this.recordedBuffers, length);
135
-
136
-
137
- // Calculate the duration based on the sample count and sample rate
138
- const sampleCount = mergedBuffer.length;
139
- const mergedBufferDuration = sampleCount / this.recordSampleRate;
140
-
141
- const encodedWav = this.encodeWAV(mergedBuffer, true);
142
-
143
- // Calculate and log the duration for encodedWav.buffer based on sample count
144
- const encodedWavBufferDuration = sampleCount / this.recordSampleRate;
145
-
146
- this.recordedBuffers.length = 0; // Clear the buffers after extraction
147
-
148
- // Returning both for testing, comment one of the returns based on your test
149
- console.debug('mergedBuffer:', mergedBuffer);
150
- console.debug('encodedWav.buffer:', encodedWav.buffer);
151
-
152
- // Uncomment the appropriate return for testing
153
- // return mergedBuffer; // This works when played
154
- return encodedWav.buffer; // This doesn't work when played
155
- }
156
- }
157
-
158
- registerProcessor('recorder-processor', RecorderProcessor);
159
- `;
160
- // Because we use expo and needs to include the worker script in the shared library, better inline it in the module.
161
- export const InlineProcessorScrippt = `
162
- class RecorderProcessor extends AudioWorkletProcessor {
163
- constructor() {
164
- super();
165
- this.recLength = 0;
166
- this.recBuffer = [];
167
- this.headerSent = false;
168
- this.newRecBuffer = [];
169
- this.exportIntervalSamples = 0;
170
- this.samplesSinceLastExport = 0;
171
- this.recordSampleRate = 44100; // To be overwrited
172
- this.exportSampleRate = 44100; // To be overwrited
173
- this.isRecording = true;
174
-
175
- this.port.onmessage = this.handleMessage.bind(this);
176
- }
177
-
178
- handleMessage(event) {
179
- switch (event.data.command) {
180
- case 'init':
181
- this.recordSampleRate = event.data.recordSampleRate;
182
- this.exportSampleRate = event.data.exportSampleRate || event.data.recordSampleRate;
183
- this.exportIntervalSamples = this.recordSampleRate * (event.data.interval / 1000);
184
- break;
185
- case 'stop':
186
- this.isRecording = false;
187
- break;
188
- case 'getRecordedData':
189
- const recordedData = this.getRecordedData();
190
- this.port.postMessage({ command: 'recordedData', recordedData });
191
- break;
192
- }
193
- }
194
-
195
- process(inputs) {
196
- if (!this.isRecording) {
197
- return true; // Exit early if not recording
198
- }
199
-
200
- if (inputs.length === 0 || inputs[0].length === 0) {
201
- console.warn('RecorderProcessor -- No input received.');
202
- return true; // Exit early if no input
203
- }
204
-
205
- const buffer = inputs[0];
206
- if (!buffer) {
207
- console.error('Input buffer is null.');
208
- return true; // Exit early if buffer is null
209
- }
210
-
211
- try {
212
- this.record(buffer);
213
-
214
- // this.samplesSinceLastExport += buffer.length;
215
- // if (this.samplesSinceLastExport >= this.exportIntervalSamples) {
216
- // this.exportBuffer();
217
- // this.samplesSinceLastExport = 0;
218
- // }
219
- } catch (error) {
220
- console.error('Error during processing:', error);
221
- }
222
- return true;
223
- }
224
-
225
- record(inputBuffer) {
226
- this.recBuffer.push(inputBuffer);
227
- this.newRecBuffer.push(inputBuffer);
228
- this.recLength += inputBuffer.length;
229
- }
230
-
231
- exportBuffer() {
232
- const mergedBuffers = this.mergeBuffers(this.newRecBuffer, this.newRecBuffer.reduce((len, buf) => len + buf.length, 0));
233
- console.log('Merged buffer length:', mergedBuffers.length); // Debug log
234
-
235
- const downsampledBuffer = this.downsampleBuffer(mergedBuffers, this.exportSampleRate);
236
- console.log('Downsampled buffer length:', downsampledBuffer.length); // Debug log
237
-
238
- const encodedWav = downsampledBuffer;
239
- // const encodedWav = this.encodeWAV(downsampledBuffer);
240
- // console.log('Encoded WAV length:', encodedWav.byteLength); // Debug log
241
-
242
- this.port.postMessage({ encodedWav, sampleRate: this.exportSampleRate });
243
- this.newRecBuffer = []; // Clear the new data buffer after export
244
- this.headerSent = true; // Indicate that the header has been sent
245
- }
246
-
247
- downsampleBuffer(buffer, exportSampleRate) {
248
- console.log('Original buffer length:', buffer.length); // Debug log
249
- console.log('Record sample rate:', this.recordSampleRate); // Debug log
250
- console.log('Export sample rate:', exportSampleRate); // Debug log
251
- if (exportSampleRate === this.recordSampleRate) {
252
- return buffer;
253
- }
254
-
255
- const sampleRateRatio = this.recordSampleRate / exportSampleRate;
256
- const newLength = Math.round(buffer.length / sampleRateRatio);
257
- console.log('Sample rate ratio:', sampleRateRatio); // Debug log
258
- console.log('New length after downsampling:', newLength); // Debug log
259
-
260
- if (newLength <= 0) {
261
- console.error('New length is zero or negative, returning empty buffer.'); // Debug log
262
- return new Float32Array(0);
263
- }
264
- const result = new Float32Array(newLength);
265
- let offsetResult = 0;
266
- let offsetBuffer = 0;
267
- while (offsetResult < result.length) {
268
- const nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);
269
- let accum = 0, count = 0;
270
- for (let i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) {
271
- accum += buffer[i];
272
- count++;
273
- }
274
- result[offsetResult] = accum / count;
275
- offsetResult++;
276
- offsetBuffer = nextOffsetBuffer;
277
- }
278
- return result;
279
- }
280
-
281
- mergeBuffers(bufferArray, recLength) {
282
- const result = new Float32Array(recLength);
283
- let offset = 0;
284
- for (let i = 0; i < bufferArray.length; i++) {
285
- result.set(bufferArray[i], offset);
286
- offset += bufferArray[i].length;
287
- }
288
- return result;
289
- }
290
-
291
- floatTo16BitPCM(output, offset, input) {
292
- for (let i = 0; i < input.length; i++, offset += 2) {
293
- const s = Math.max(-1, Math.min(1, input[i]));
294
- output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
295
- }
296
- }
297
-
298
- writeString(view, offset, string) {
299
- for (let i = 0; i < string.length; i++) {
300
- view.setUint8(offset + i, string.charCodeAt(i));
301
- }
302
- }
303
-
304
- getRecordedData() {
305
- const length = this.recBuffer.reduce((acc, buffer) => acc + buffer.length, 0);
306
- const result = new Float32Array(length);
307
- let offset = 0;
308
- for (const buffer of this.recBuffer) {
309
- result.set(buffer, offset);
310
- offset += buffer.length;
311
- }
312
- this.recBuffer.length = 0; // Clear the buffers after extraction
313
-
314
- return result;
315
- }
316
-
317
- encodeWAV(samples) {
318
- const buffer = new ArrayBuffer(44 + samples.length * 2);
319
- const view = new DataView(buffer);
320
- this.writeString(view, 0, 'RIFF');
321
- view.setUint32(4, 32 + samples.length * 2, true);
322
- this.writeString(view, 8, 'WAVE');
323
- this.writeString(view, 12, 'fmt ');
324
- view.setUint32(16, 16, true);
325
- view.setUint16(20, 1, true);
326
- view.setUint16(22, 1, true);
327
- view.setUint32(24, sampleRate, true);
328
- view.setUint32(28, sampleRate * 2, true);
329
- view.setUint16(32, 2, true);
330
- view.setUint16(34, 16, true);
331
- this.writeString(view, 36, 'data');
332
- view.setUint32(40, samples.length * 2, true);
333
- this.floatTo16BitPCM(view, 44, samples);
334
- return view;
335
- }
336
- }
337
-
338
- registerProcessor('recorder-processor', RecorderProcessor);
339
- `;
340
- //# sourceMappingURL=inlineAudioWebWorker.js.map