@siteed/expo-audio-studio 2.8.0 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/CHANGELOG.md +6 -1
  2. package/app.plugin.cjs +15 -2
  3. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js +4 -0
  4. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
  5. package/build/cjs/AudioAnalysis/extractAudioAnalysis.js +205 -0
  6. package/build/cjs/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
  7. package/build/cjs/AudioAnalysis/extractAudioData.js +12 -0
  8. package/build/cjs/AudioAnalysis/extractAudioData.js.map +1 -0
  9. package/build/cjs/AudioAnalysis/extractMelSpectrogram.js +89 -0
  10. package/build/cjs/AudioAnalysis/extractMelSpectrogram.js.map +1 -0
  11. package/build/cjs/AudioAnalysis/extractPreview.js +29 -0
  12. package/build/cjs/AudioAnalysis/extractPreview.js.map +1 -0
  13. package/build/cjs/AudioAnalysis/extractWaveform.js +18 -0
  14. package/build/cjs/AudioAnalysis/extractWaveform.js.map +1 -0
  15. package/build/cjs/AudioDeviceManager.js +500 -0
  16. package/build/cjs/AudioDeviceManager.js.map +1 -0
  17. package/build/cjs/AudioRecorder.provider.js +68 -0
  18. package/build/cjs/AudioRecorder.provider.js.map +1 -0
  19. package/build/cjs/ExpoAudioStream.native.js +8 -0
  20. package/build/cjs/ExpoAudioStream.native.js.map +1 -0
  21. package/build/cjs/ExpoAudioStream.types.js +11 -0
  22. package/build/cjs/ExpoAudioStream.types.js.map +1 -0
  23. package/build/cjs/ExpoAudioStream.web.js +705 -0
  24. package/build/cjs/ExpoAudioStream.web.js.map +1 -0
  25. package/build/cjs/ExpoAudioStreamModule.js +718 -0
  26. package/build/cjs/ExpoAudioStreamModule.js.map +1 -0
  27. package/build/cjs/WebRecorder.web.js +756 -0
  28. package/build/cjs/WebRecorder.web.js.map +1 -0
  29. package/build/cjs/constants.js +17 -0
  30. package/build/cjs/constants.js.map +1 -0
  31. package/build/cjs/events.js +30 -0
  32. package/build/cjs/events.js.map +1 -0
  33. package/build/cjs/hooks/useAudioDevices.js +155 -0
  34. package/build/cjs/hooks/useAudioDevices.js.map +1 -0
  35. package/build/cjs/index.js +50 -0
  36. package/build/cjs/index.js.map +1 -0
  37. package/build/cjs/trimAudio.js +75 -0
  38. package/build/cjs/trimAudio.js.map +1 -0
  39. package/build/cjs/useAudioRecorder.js +453 -0
  40. package/build/cjs/useAudioRecorder.js.map +1 -0
  41. package/build/cjs/utils/BlobFix.js +502 -0
  42. package/build/cjs/utils/BlobFix.js.map +1 -0
  43. package/build/cjs/utils/audioProcessing.js +137 -0
  44. package/build/cjs/utils/audioProcessing.js.map +1 -0
  45. package/build/cjs/utils/concatenateBuffers.js +25 -0
  46. package/build/cjs/utils/concatenateBuffers.js.map +1 -0
  47. package/build/cjs/utils/convertPCMToFloat32.js +124 -0
  48. package/build/cjs/utils/convertPCMToFloat32.js.map +1 -0
  49. package/build/cjs/utils/crc32.js +52 -0
  50. package/build/cjs/utils/crc32.js.map +1 -0
  51. package/build/cjs/utils/encodingToBitDepth.js +17 -0
  52. package/build/cjs/utils/encodingToBitDepth.js.map +1 -0
  53. package/build/cjs/utils/getWavFileInfo.js +96 -0
  54. package/build/cjs/utils/getWavFileInfo.js.map +1 -0
  55. package/build/cjs/utils/writeWavHeader.js +88 -0
  56. package/build/cjs/utils/writeWavHeader.js.map +1 -0
  57. package/build/cjs/workers/InlineFeaturesExtractor.web.js +853 -0
  58. package/build/cjs/workers/InlineFeaturesExtractor.web.js.map +1 -0
  59. package/build/cjs/workers/inlineAudioWebWorker.web.js +184 -0
  60. package/build/cjs/workers/inlineAudioWebWorker.web.js.map +1 -0
  61. package/build/esm/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
  62. package/build/esm/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
  63. package/build/esm/AudioAnalysis/extractAudioData.js.map +1 -0
  64. package/build/esm/AudioAnalysis/extractMelSpectrogram.js.map +1 -0
  65. package/build/esm/AudioAnalysis/extractPreview.js.map +1 -0
  66. package/build/esm/AudioAnalysis/extractWaveform.js.map +1 -0
  67. package/build/esm/AudioDeviceManager.js.map +1 -0
  68. package/build/esm/AudioRecorder.provider.js.map +1 -0
  69. package/build/esm/ExpoAudioStream.native.js.map +1 -0
  70. package/build/esm/ExpoAudioStream.types.js.map +1 -0
  71. package/build/esm/ExpoAudioStream.web.js.map +1 -0
  72. package/build/esm/ExpoAudioStreamModule.js.map +1 -0
  73. package/build/esm/WebRecorder.web.js.map +1 -0
  74. package/build/esm/constants.js.map +1 -0
  75. package/build/esm/events.js.map +1 -0
  76. package/build/esm/hooks/useAudioDevices.js.map +1 -0
  77. package/build/esm/index.js.map +1 -0
  78. package/build/esm/trimAudio.js.map +1 -0
  79. package/build/esm/useAudioRecorder.js.map +1 -0
  80. package/build/esm/utils/BlobFix.js.map +1 -0
  81. package/build/esm/utils/audioProcessing.js.map +1 -0
  82. package/build/esm/utils/concatenateBuffers.js.map +1 -0
  83. package/build/esm/utils/convertPCMToFloat32.js.map +1 -0
  84. package/build/esm/utils/crc32.js.map +1 -0
  85. package/build/esm/utils/encodingToBitDepth.js.map +1 -0
  86. package/build/esm/utils/getWavFileInfo.js.map +1 -0
  87. package/build/esm/utils/writeWavHeader.js.map +1 -0
  88. package/build/esm/workers/InlineFeaturesExtractor.web.js.map +1 -0
  89. package/build/esm/workers/inlineAudioWebWorker.web.js.map +1 -0
  90. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -0
  91. package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -0
  92. package/build/types/AudioAnalysis/extractAudioData.d.ts.map +1 -0
  93. package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts.map +1 -0
  94. package/build/types/AudioAnalysis/extractPreview.d.ts.map +1 -0
  95. package/build/types/AudioAnalysis/extractWaveform.d.ts.map +1 -0
  96. package/build/types/AudioDeviceManager.d.ts.map +1 -0
  97. package/build/types/AudioRecorder.provider.d.ts.map +1 -0
  98. package/build/types/ExpoAudioStream.native.d.ts.map +1 -0
  99. package/build/types/ExpoAudioStream.types.d.ts.map +1 -0
  100. package/build/types/ExpoAudioStream.web.d.ts.map +1 -0
  101. package/build/types/ExpoAudioStreamModule.d.ts.map +1 -0
  102. package/build/types/WebRecorder.web.d.ts.map +1 -0
  103. package/build/types/constants.d.ts.map +1 -0
  104. package/build/types/events.d.ts.map +1 -0
  105. package/build/types/hooks/useAudioDevices.d.ts.map +1 -0
  106. package/build/types/index.d.ts.map +1 -0
  107. package/build/types/trimAudio.d.ts.map +1 -0
  108. package/build/types/useAudioRecorder.d.ts.map +1 -0
  109. package/build/types/utils/BlobFix.d.ts.map +1 -0
  110. package/build/types/utils/audioProcessing.d.ts.map +1 -0
  111. package/build/types/utils/concatenateBuffers.d.ts.map +1 -0
  112. package/build/types/utils/convertPCMToFloat32.d.ts.map +1 -0
  113. package/build/types/utils/crc32.d.ts.map +1 -0
  114. package/build/types/utils/encodingToBitDepth.d.ts.map +1 -0
  115. package/build/types/utils/getWavFileInfo.d.ts.map +1 -0
  116. package/build/types/utils/writeWavHeader.d.ts.map +1 -0
  117. package/build/types/workers/InlineFeaturesExtractor.web.d.ts.map +1 -0
  118. package/build/types/workers/inlineAudioWebWorker.web.d.ts.map +1 -0
  119. package/package.json +25 -7
  120. package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +0 -1
  121. package/build/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
  122. package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +0 -1
  123. package/build/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
  124. package/build/AudioAnalysis/extractAudioData.d.ts.map +0 -1
  125. package/build/AudioAnalysis/extractAudioData.js.map +0 -1
  126. package/build/AudioAnalysis/extractMelSpectrogram.d.ts.map +0 -1
  127. package/build/AudioAnalysis/extractMelSpectrogram.js.map +0 -1
  128. package/build/AudioAnalysis/extractPreview.d.ts.map +0 -1
  129. package/build/AudioAnalysis/extractPreview.js.map +0 -1
  130. package/build/AudioAnalysis/extractWaveform.d.ts.map +0 -1
  131. package/build/AudioAnalysis/extractWaveform.js.map +0 -1
  132. package/build/AudioDeviceManager.d.ts.map +0 -1
  133. package/build/AudioDeviceManager.js.map +0 -1
  134. package/build/AudioRecorder.provider.d.ts.map +0 -1
  135. package/build/AudioRecorder.provider.js.map +0 -1
  136. package/build/ExpoAudioStream.native.d.ts.map +0 -1
  137. package/build/ExpoAudioStream.native.js.map +0 -1
  138. package/build/ExpoAudioStream.types.d.ts.map +0 -1
  139. package/build/ExpoAudioStream.types.js.map +0 -1
  140. package/build/ExpoAudioStream.web.d.ts.map +0 -1
  141. package/build/ExpoAudioStream.web.js.map +0 -1
  142. package/build/ExpoAudioStreamModule.d.ts.map +0 -1
  143. package/build/ExpoAudioStreamModule.js.map +0 -1
  144. package/build/WebRecorder.web.d.ts.map +0 -1
  145. package/build/WebRecorder.web.js.map +0 -1
  146. package/build/constants.d.ts.map +0 -1
  147. package/build/constants.js.map +0 -1
  148. package/build/events.d.ts.map +0 -1
  149. package/build/events.js.map +0 -1
  150. package/build/hooks/useAudioDevices.d.ts.map +0 -1
  151. package/build/hooks/useAudioDevices.js.map +0 -1
  152. package/build/index.d.ts.map +0 -1
  153. package/build/index.js.map +0 -1
  154. package/build/trimAudio.d.ts.map +0 -1
  155. package/build/trimAudio.js.map +0 -1
  156. package/build/useAudioRecorder.d.ts.map +0 -1
  157. package/build/useAudioRecorder.js.map +0 -1
  158. package/build/utils/BlobFix.d.ts.map +0 -1
  159. package/build/utils/BlobFix.js.map +0 -1
  160. package/build/utils/audioProcessing.d.ts.map +0 -1
  161. package/build/utils/audioProcessing.js.map +0 -1
  162. package/build/utils/concatenateBuffers.d.ts.map +0 -1
  163. package/build/utils/concatenateBuffers.js.map +0 -1
  164. package/build/utils/convertPCMToFloat32.d.ts.map +0 -1
  165. package/build/utils/convertPCMToFloat32.js.map +0 -1
  166. package/build/utils/crc32.d.ts.map +0 -1
  167. package/build/utils/crc32.js.map +0 -1
  168. package/build/utils/encodingToBitDepth.d.ts.map +0 -1
  169. package/build/utils/encodingToBitDepth.js.map +0 -1
  170. package/build/utils/getWavFileInfo.d.ts.map +0 -1
  171. package/build/utils/getWavFileInfo.js.map +0 -1
  172. package/build/utils/writeWavHeader.d.ts.map +0 -1
  173. package/build/utils/writeWavHeader.js.map +0 -1
  174. package/build/workers/InlineFeaturesExtractor.web.d.ts.map +0 -1
  175. package/build/workers/InlineFeaturesExtractor.web.js.map +0 -1
  176. package/build/workers/inlineAudioWebWorker.web.d.ts.map +0 -1
  177. package/build/workers/inlineAudioWebWorker.web.js.map +0 -1
  178. /package/build/{AudioAnalysis → esm/AudioAnalysis}/AudioAnalysis.types.js +0 -0
  179. /package/build/{AudioAnalysis → esm/AudioAnalysis}/extractAudioAnalysis.js +0 -0
  180. /package/build/{AudioAnalysis → esm/AudioAnalysis}/extractAudioData.js +0 -0
  181. /package/build/{AudioAnalysis → esm/AudioAnalysis}/extractMelSpectrogram.js +0 -0
  182. /package/build/{AudioAnalysis → esm/AudioAnalysis}/extractPreview.js +0 -0
  183. /package/build/{AudioAnalysis → esm/AudioAnalysis}/extractWaveform.js +0 -0
  184. /package/build/{AudioDeviceManager.js → esm/AudioDeviceManager.js} +0 -0
  185. /package/build/{AudioRecorder.provider.js → esm/AudioRecorder.provider.js} +0 -0
  186. /package/build/{ExpoAudioStream.native.js → esm/ExpoAudioStream.native.js} +0 -0
  187. /package/build/{ExpoAudioStream.types.js → esm/ExpoAudioStream.types.js} +0 -0
  188. /package/build/{ExpoAudioStream.web.js → esm/ExpoAudioStream.web.js} +0 -0
  189. /package/build/{ExpoAudioStreamModule.js → esm/ExpoAudioStreamModule.js} +0 -0
  190. /package/build/{WebRecorder.web.js → esm/WebRecorder.web.js} +0 -0
  191. /package/build/{constants.js → esm/constants.js} +0 -0
  192. /package/build/{events.js → esm/events.js} +0 -0
  193. /package/build/{hooks → esm/hooks}/useAudioDevices.js +0 -0
  194. /package/build/{index.js → esm/index.js} +0 -0
  195. /package/build/{trimAudio.js → esm/trimAudio.js} +0 -0
  196. /package/build/{useAudioRecorder.js → esm/useAudioRecorder.js} +0 -0
  197. /package/build/{utils → esm/utils}/BlobFix.js +0 -0
  198. /package/build/{utils → esm/utils}/audioProcessing.js +0 -0
  199. /package/build/{utils → esm/utils}/concatenateBuffers.js +0 -0
  200. /package/build/{utils → esm/utils}/convertPCMToFloat32.js +0 -0
  201. /package/build/{utils → esm/utils}/crc32.js +0 -0
  202. /package/build/{utils → esm/utils}/encodingToBitDepth.js +0 -0
  203. /package/build/{utils → esm/utils}/getWavFileInfo.js +0 -0
  204. /package/build/{utils → esm/utils}/writeWavHeader.js +0 -0
  205. /package/build/{workers → esm/workers}/InlineFeaturesExtractor.web.js +0 -0
  206. /package/build/{workers → esm/workers}/inlineAudioWebWorker.web.js +0 -0
  207. /package/build/{AudioAnalysis → types/AudioAnalysis}/AudioAnalysis.types.d.ts +0 -0
  208. /package/build/{AudioAnalysis → types/AudioAnalysis}/extractAudioAnalysis.d.ts +0 -0
  209. /package/build/{AudioAnalysis → types/AudioAnalysis}/extractAudioData.d.ts +0 -0
  210. /package/build/{AudioAnalysis → types/AudioAnalysis}/extractMelSpectrogram.d.ts +0 -0
  211. /package/build/{AudioAnalysis → types/AudioAnalysis}/extractPreview.d.ts +0 -0
  212. /package/build/{AudioAnalysis → types/AudioAnalysis}/extractWaveform.d.ts +0 -0
  213. /package/build/{AudioDeviceManager.d.ts → types/AudioDeviceManager.d.ts} +0 -0
  214. /package/build/{AudioRecorder.provider.d.ts → types/AudioRecorder.provider.d.ts} +0 -0
  215. /package/build/{ExpoAudioStream.native.d.ts → types/ExpoAudioStream.native.d.ts} +0 -0
  216. /package/build/{ExpoAudioStream.types.d.ts → types/ExpoAudioStream.types.d.ts} +0 -0
  217. /package/build/{ExpoAudioStream.web.d.ts → types/ExpoAudioStream.web.d.ts} +0 -0
  218. /package/build/{ExpoAudioStreamModule.d.ts → types/ExpoAudioStreamModule.d.ts} +0 -0
  219. /package/build/{WebRecorder.web.d.ts → types/WebRecorder.web.d.ts} +0 -0
  220. /package/build/{constants.d.ts → types/constants.d.ts} +0 -0
  221. /package/build/{events.d.ts → types/events.d.ts} +0 -0
  222. /package/build/{hooks → types/hooks}/useAudioDevices.d.ts +0 -0
  223. /package/build/{index.d.ts → types/index.d.ts} +0 -0
  224. /package/build/{trimAudio.d.ts → types/trimAudio.d.ts} +0 -0
  225. /package/build/{useAudioRecorder.d.ts → types/useAudioRecorder.d.ts} +0 -0
  226. /package/build/{utils → types/utils}/BlobFix.d.ts +0 -0
  227. /package/build/{utils → types/utils}/audioProcessing.d.ts +0 -0
  228. /package/build/{utils → types/utils}/concatenateBuffers.d.ts +0 -0
  229. /package/build/{utils → types/utils}/convertPCMToFloat32.d.ts +0 -0
  230. /package/build/{utils → types/utils}/crc32.d.ts +0 -0
  231. /package/build/{utils → types/utils}/encodingToBitDepth.d.ts +0 -0
  232. /package/build/{utils → types/utils}/getWavFileInfo.d.ts +0 -0
  233. /package/build/{utils → types/utils}/writeWavHeader.d.ts +0 -0
  234. /package/build/{workers → types/workers}/InlineFeaturesExtractor.web.d.ts +0 -0
  235. /package/build/{workers → types/workers}/inlineAudioWebWorker.web.d.ts +0 -0
@@ -0,0 +1,853 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InlineFeaturesExtractor = void 0;
4
+ // packages/expo-audio-studio/src/workers/InlineFeaturesExtractor.web.tsx
5
+ exports.InlineFeaturesExtractor = `
6
+ // Constants
7
+ const N_FFT = 1024; // Default FFT size
8
+ const MAX_FFT_SIZE = 8192; // Maximum FFT size to prevent memory issues
9
+ const N_CHROMA = 12;
10
+
11
+ // FFT Implementation with normalized Hann window
12
+ function FFT(n) {
13
+ this.n = n;
14
+ this.cosTable = new Float32Array(n / 2);
15
+ this.sinTable = new Float32Array(n / 2);
16
+ this.hannWindow = new Float32Array(n);
17
+
18
+ // Match Android implementation with precomputed tables
19
+ const normalizationFactor = Math.sqrt(2.0 / n);
20
+ for (var i = 0; i < n / 2; i++) {
21
+ this.cosTable[i] = Math.cos(2.0 * Math.PI * i / n);
22
+ this.sinTable[i] = Math.sin(2.0 * Math.PI * i / n);
23
+ }
24
+
25
+ // Precompute normalized Hann window to match Android
26
+ for (var i = 0; i < n; i++) {
27
+ this.hannWindow[i] = normalizationFactor * 0.5 * (1 - Math.cos(2.0 * Math.PI * i / (n - 1)));
28
+ }
29
+ }
30
+
31
+ FFT.prototype.transform = function(data) {
32
+ const n = data.length;
33
+
34
+ // Validate input length is power of 2
35
+ if ((n & (n - 1)) !== 0) {
36
+ throw new Error('FFT length must be power of 2');
37
+ }
38
+
39
+ // Use iterative bit reversal instead of recursive
40
+ const bitReversedIndices = new Uint32Array(n);
41
+ for (let i = 0; i < n; i++) {
42
+ let reversed = 0;
43
+ let j = i;
44
+ let bits = Math.log2(n);
45
+ while (bits--) {
46
+ reversed = (reversed << 1) | (j & 1);
47
+ j >>= 1;
48
+ }
49
+ bitReversedIndices[i] = reversed;
50
+ }
51
+
52
+ // Apply bit reversal
53
+ for (let i = 0; i < n; i++) {
54
+ const j = bitReversedIndices[i];
55
+ if (i < j) {
56
+ const temp = data[i];
57
+ data[i] = data[j];
58
+ data[j] = temp;
59
+ }
60
+ }
61
+
62
+ // Iterative FFT computation with optimized memory usage
63
+ for (let step = 1; step < n; step <<= 1) {
64
+ const jump = step << 1;
65
+ const angleStep = Math.PI / step;
66
+
67
+ for (let group = 0; group < n; group += jump) {
68
+ for (let pair = group; pair < group + step; pair++) {
69
+ const match = pair + step;
70
+ const angle = angleStep * (pair - group);
71
+
72
+ const currentCos = Math.cos(angle);
73
+ const currentSin = Math.sin(angle);
74
+
75
+ const real = currentCos * data[match] - currentSin * data[match + 1];
76
+ const imag = currentCos * data[match + 1] + currentSin * data[match];
77
+
78
+ data[match] = data[pair] - real;
79
+ data[match + 1] = data[pair + 1] - imag;
80
+ data[pair] += real;
81
+ data[pair + 1] += imag;
82
+ }
83
+ }
84
+ }
85
+ };
86
+
87
+ // Add realInverse method
88
+ FFT.prototype.realInverse = function(powerSpectrum, output) {
89
+ const n = powerSpectrum.length;
90
+ const complexData = new Float32Array(n * 2);
91
+
92
+ // Copy power spectrum to complex format
93
+ for (let i = 0; i < n/2 + 1; i++) {
94
+ complexData[2 * i] = powerSpectrum[i];
95
+ if (2 * i + 1 < complexData.length) {
96
+ complexData[2 * i + 1] = 0;
97
+ }
98
+ }
99
+
100
+ // Conjugate for inverse FFT
101
+ for (let i = 0; i < n; i++) {
102
+ if (2 * i + 1 < complexData.length) {
103
+ complexData[2 * i + 1] = -complexData[2 * i + 1];
104
+ }
105
+ }
106
+
107
+ this.transform(complexData);
108
+
109
+ // Copy real part to output and scale
110
+ for (let i = 0; i < n; i++) {
111
+ output[i] = complexData[2 * i] / n;
112
+ }
113
+ };
114
+
115
+ // Add helper functions to match Android
116
+ function nextPowerOfTwo(n) {
117
+ let value = 1;
118
+ while (value < n) {
119
+ value *= 2;
120
+ }
121
+ return value;
122
+ }
123
+
124
+ function applyHannWindow(samples) {
125
+ const output = new Float32Array(samples.length);
126
+ for (let i = 0; i < samples.length; i++) {
127
+ const multiplier = 0.5 * (1 - Math.cos(2 * Math.PI * i / (samples.length - 1)));
128
+ output[i] = samples[i] * multiplier;
129
+ }
130
+ return output;
131
+ }
132
+
133
+ // Update spectral feature computation to match Android
134
+ function computeSpectralFeatures(segment, sampleRate, featureOptions = {}) {
135
+ try {
136
+ // Early return if no spectral features are requested
137
+ if (!featureOptions.spectralCentroid &&
138
+ !featureOptions.spectralFlatness &&
139
+ !featureOptions.spectralRollOff &&
140
+ !featureOptions.spectralBandwidth &&
141
+ !featureOptions.magnitudeSpectrum) {
142
+ return {
143
+ centroid: 0,
144
+ flatness: 0,
145
+ rollOff: 0,
146
+ bandwidth: 0,
147
+ magnitudeSpectrum: []
148
+ };
149
+ }
150
+
151
+ // Ensure we have valid data
152
+ if (!segment || segment.length === 0) {
153
+ throw new Error('Invalid segment data');
154
+ }
155
+
156
+ // Process in fixed-size chunks
157
+ const chunkSize = N_FFT;
158
+ const numChunks = Math.ceil(segment.length / chunkSize);
159
+
160
+ let results = {
161
+ centroid: 0,
162
+ flatness: 0,
163
+ rollOff: 0,
164
+ bandwidth: 0,
165
+ magnitudeSpectrum: new Float32Array(N_FFT / 2 + 1).fill(0)
166
+ };
167
+
168
+ let validChunks = 0;
169
+
170
+ // Iterate through chunks
171
+ for (let i = 0; i < numChunks; i++) {
172
+ const start = i * chunkSize;
173
+ const end = Math.min(start + chunkSize, segment.length);
174
+ const chunk = segment.slice(start, end);
175
+
176
+ if (chunk.length < N_FFT / 4) continue; // Skip very small chunks
177
+
178
+ // Process the chunk
179
+ const paddedChunk = new Float32Array(N_FFT);
180
+ paddedChunk.set(applyHannWindow(chunk));
181
+
182
+ const fft = new FFT(N_FFT);
183
+ fft.transform(paddedChunk);
184
+
185
+ // Calculate magnitude spectrum
186
+ const chunkMagnitudeSpectrum = new Float32Array(N_FFT / 2 + 1);
187
+ let hasSignal = false;
188
+
189
+ for (let j = 0; j < N_FFT / 2; j++) {
190
+ const re = paddedChunk[2 * j];
191
+ const im = paddedChunk[2 * j + 1];
192
+ const magnitude = Math.sqrt(re * re + im * im);
193
+ chunkMagnitudeSpectrum[j] = magnitude;
194
+ if (magnitude > Number.EPSILON) hasSignal = true;
195
+ }
196
+
197
+ if (!hasSignal) continue;
198
+ validChunks++;
199
+
200
+ // Accumulate results
201
+ if (featureOptions.spectralCentroid) {
202
+ const centroid = computeSpectralCentroid(chunkMagnitudeSpectrum, sampleRate);
203
+ if (!isNaN(centroid)) results.centroid += centroid;
204
+ }
205
+
206
+ if (featureOptions.spectralFlatness) {
207
+ const flatness = computeSpectralFlatness(chunkMagnitudeSpectrum);
208
+ if (!isNaN(flatness)) results.flatness += flatness;
209
+ }
210
+
211
+ if (featureOptions.spectralRollOff) {
212
+ const rolloff = computeSpectralRollOff(chunkMagnitudeSpectrum, sampleRate);
213
+ if (!isNaN(rolloff)) results.rollOff += rolloff;
214
+ }
215
+
216
+ if (featureOptions.spectralBandwidth && !isNaN(results.centroid)) {
217
+ const bandwidth = computeSpectralBandwidth(chunkMagnitudeSpectrum, sampleRate, results.centroid);
218
+ if (!isNaN(bandwidth)) results.bandwidth += bandwidth;
219
+ }
220
+
221
+ if (featureOptions.magnitudeSpectrum) {
222
+ for (let j = 0; j < results.magnitudeSpectrum.length; j++) {
223
+ results.magnitudeSpectrum[j] += chunkMagnitudeSpectrum[j];
224
+ }
225
+ }
226
+ }
227
+
228
+ // Average the accumulated results
229
+ if (validChunks > 0) {
230
+ results.centroid /= validChunks;
231
+ results.flatness /= validChunks;
232
+ results.rollOff /= validChunks;
233
+ results.bandwidth /= validChunks;
234
+
235
+ if (featureOptions.magnitudeSpectrum) {
236
+ for (let i = 0; i < results.magnitudeSpectrum.length; i++) {
237
+ results.magnitudeSpectrum[i] /= validChunks;
238
+ }
239
+ }
240
+ }
241
+
242
+ return results;
243
+ } catch (error) {
244
+ console.error('[Worker] Spectral feature computation error:', error);
245
+ return {
246
+ centroid: 0,
247
+ flatness: 0,
248
+ rollOff: 0,
249
+ bandwidth: 0,
250
+ magnitudeSpectrum: []
251
+ };
252
+ }
253
+ }
254
+
255
+ function computeSpectralCentroid(magnitudeSpectrum, sampleRate) {
256
+ const sum = magnitudeSpectrum.reduce((a, b) => a + (b || 0), 0);
257
+ if (sum <= Number.EPSILON) return 0;
258
+
259
+ const weightedSum = magnitudeSpectrum.reduce((acc, value, index) =>
260
+ acc + (index * (sampleRate / N_FFT) * (value || 0)), 0);
261
+
262
+ return weightedSum / sum;
263
+ }
264
+
265
+ function computeSpectralFlatness(powerSpectrum) {
266
+ // Add small epsilon to avoid log(0)
267
+ const epsilon = Number.EPSILON;
268
+ const validSpectrum = powerSpectrum.map(v => Math.max(v, epsilon));
269
+
270
+ const geometricMean = Math.exp(
271
+ validSpectrum
272
+ .map(v => Math.log(v))
273
+ .reduce((a, b) => a + b) / validSpectrum.length
274
+ );
275
+
276
+ const arithmeticMean =
277
+ validSpectrum.reduce((a, b) => a + b) / validSpectrum.length;
278
+
279
+ return geometricMean / arithmeticMean;
280
+ }
281
+
282
+ function computeSpectralRollOff(magnitudeSpectrum, sampleRate) {
283
+ const totalEnergy = magnitudeSpectrum.reduce((a, b) => a + b, 0);
284
+ const rollOffThreshold = totalEnergy * 0.85;
285
+ let cumulativeEnergy = 0;
286
+
287
+ for (let i = 0; i < magnitudeSpectrum.length; i++) {
288
+ cumulativeEnergy += magnitudeSpectrum[i];
289
+ if (cumulativeEnergy >= rollOffThreshold) {
290
+ return (i / magnitudeSpectrum.length) * (sampleRate / 2);
291
+ }
292
+ }
293
+
294
+ return 0;
295
+ }
296
+
297
+ function computeSpectralBandwidth(magnitudeSpectrum, sampleRate, centroid) {
298
+ const sum = magnitudeSpectrum.reduce((a, b) => a + (b || 0), 0);
299
+ if (sum <= Number.EPSILON) return 0;
300
+
301
+ const weightedSum = magnitudeSpectrum.reduce(
302
+ (acc, value, index) => {
303
+ const freq = index * sampleRate / (2 * magnitudeSpectrum.length);
304
+ return acc + (value || 0) * Math.pow(freq - centroid, 2);
305
+ }, 0
306
+ );
307
+
308
+ return Math.sqrt(weightedSum / sum);
309
+ }
310
+
311
+ function computeChroma(segmentData, sampleRate) {
312
+ // Ensure we have valid input data
313
+ if (!segmentData || segmentData.length === 0) {
314
+ return new Array(N_CHROMA).fill(0);
315
+ }
316
+
317
+ const fftLength = nextPowerOfTwo(Math.max(segmentData.length, N_FFT));
318
+ const windowed = applyHannWindow(segmentData);
319
+ const padded = new Float32Array(fftLength);
320
+ padded.set(windowed.slice(0, Math.min(windowed.length, fftLength)));
321
+
322
+ const fft = new FFT(fftLength);
323
+ try {
324
+ fft.transform(padded);
325
+ } catch (e) {
326
+ console.error('[Worker] FFT transform failed in chromagram:', e);
327
+ return new Array(N_CHROMA).fill(0);
328
+ }
329
+
330
+ const chroma = new Float32Array(N_CHROMA).fill(0);
331
+ const freqsPerBin = sampleRate / fftLength;
332
+ let totalEnergy = 0;
333
+
334
+ // First pass: compute magnitudes and total energy
335
+ for (let i = 0; i < fftLength / 2; i++) {
336
+ const freq = i * freqsPerBin;
337
+ if (freq > 20) { // Only consider frequencies above 20 Hz
338
+ const re = padded[2 * i];
339
+ const im = padded[2 * i + 1] || 0;
340
+ const magnitude = Math.sqrt(re * re + im * im);
341
+
342
+ if (magnitude > Number.EPSILON) {
343
+ // Use a more stable pitch class calculation
344
+ const midiNote = 69 + 12 * Math.log2(freq / 440.0);
345
+ const pitchClass = Math.round(midiNote) % 12;
346
+
347
+ if (pitchClass >= 0 && pitchClass < 12) {
348
+ chroma[pitchClass] += magnitude;
349
+ totalEnergy += magnitude;
350
+ }
351
+ }
352
+ }
353
+ }
354
+
355
+ // Normalize chroma values only if we have energy
356
+ if (totalEnergy > Number.EPSILON) {
357
+ for (let i = 0; i < N_CHROMA; i++) {
358
+ chroma[i] = chroma[i] / totalEnergy;
359
+ }
360
+ }
361
+
362
+ // Convert to regular array and ensure no NaN values
363
+ return Array.from(chroma, v => isNaN(v) ? 0 : v);
364
+ }
365
+
366
+ function extractHNR(segmentData) {
367
+ const frameSize = segmentData.length;
368
+ const autocorrelation = new Float32Array(frameSize);
369
+
370
+ // Compute the autocorrelation iteratively
371
+ for (let i = 0; i < frameSize; i++) {
372
+ let sum = 0;
373
+ for (let j = 0; j < frameSize - i; j++) {
374
+ sum += segmentData[j] * segmentData[j + i];
375
+ }
376
+ autocorrelation[i] = sum;
377
+ }
378
+
379
+ // Find the maximum autocorrelation value iteratively
380
+ let maxAutocorrelation = -Infinity;
381
+ for (let i = 1; i < autocorrelation.length; i++) {
382
+ if (autocorrelation[i] > maxAutocorrelation) {
383
+ maxAutocorrelation = autocorrelation[i];
384
+ }
385
+ }
386
+
387
+ // Compute the HNR
388
+ return autocorrelation[0] !== 0
389
+ ? 10 * Math.log10(maxAutocorrelation / (autocorrelation[0] - maxAutocorrelation))
390
+ : 0;
391
+ }
392
+
393
+ function estimatePitch(segment, sampleRate) {
394
+ // Early validation
395
+ if (!segment || segment.length < 2 || !sampleRate) return 0;
396
+
397
+ try {
398
+ // Apply Hann window
399
+ const windowed = applyHannWindow(segment);
400
+
401
+ // Pad for FFT
402
+ const fftLength = nextPowerOfTwo(segment.length * 2);
403
+ const padded = new Float32Array(fftLength);
404
+ padded.set(windowed);
405
+
406
+ // Perform FFT
407
+ const fft = new FFT(fftLength);
408
+ fft.transform(padded);
409
+
410
+ // Compute power spectrum
411
+ const powerSpectrum = new Float32Array(fftLength / 2 + 1);
412
+ for (let i = 0; i <= fftLength / 2; i++) {
413
+ const re = padded[2 * i];
414
+ const im = padded[2 * i + 1] || 0;
415
+ powerSpectrum[i] = re * re + im * im;
416
+ }
417
+
418
+ // Find peak frequency
419
+ let maxPower = 0;
420
+ let peakIndex = 0;
421
+ const minFreq = 50; // Minimum frequency to consider (Hz)
422
+ const maxFreq = 1000; // Maximum frequency to consider (Hz)
423
+ const minBin = Math.floor(minFreq * fftLength / sampleRate);
424
+ const maxBin = Math.ceil(maxFreq * fftLength / sampleRate);
425
+
426
+ for (let i = minBin; i <= maxBin; i++) {
427
+ if (powerSpectrum[i] > maxPower) {
428
+ maxPower = powerSpectrum[i];
429
+ peakIndex = i;
430
+ }
431
+ }
432
+
433
+ // Convert peak index to frequency
434
+ const fundamentalFreq = peakIndex * sampleRate / fftLength;
435
+
436
+ // Return 0 if the detected frequency is outside reasonable bounds
437
+ return (fundamentalFreq >= minFreq && fundamentalFreq <= maxFreq) ?
438
+ fundamentalFreq : 0;
439
+
440
+ } catch (error) {
441
+ console.error('[Worker] Pitch estimation error:', error);
442
+ return 0;
443
+ }
444
+ }
445
+
446
+ // Unique ID counter - the only state we need to maintain
447
+ let uniqueIdCounter = 0
448
+ let lastEmitTime = Date.now()
449
+
450
+ self.onmessage = function (event) {
451
+ // Extract enableLogging early so we can use it consistently
452
+ const enableLogging = event.data.enableLogging || false;
453
+
454
+ // Create consistent logger that only logs when enabled
455
+ const logger = enableLogging ? {
456
+ debug: (...args) => console.debug('[Worker]', ...args),
457
+ log: (...args) => console.log('[Worker]', ...args),
458
+ warn: (...args) => console.warn('[Worker]', ...args),
459
+ error: (...args) => console.error('[Worker]', ...args)
460
+ } : {
461
+ debug: () => {},
462
+ log: () => {},
463
+ warn: () => {},
464
+ error: () => {}
465
+ };
466
+
467
+ // Check if this is a reset command
468
+ if (event.data.command === 'resetCounter') {
469
+ const newValue = event.data.value;
470
+ logger.log('Reset counter request received with value:', newValue);
471
+
472
+ // Always respect explicit resets through the resetCounter command
473
+ uniqueIdCounter = typeof newValue === 'number' ? newValue : 0;
474
+ logger.log('Counter explicitly set to:', uniqueIdCounter);
475
+
476
+ return; // Exit early, don't process audio
477
+ }
478
+
479
+ // Regular audio processing
480
+ const {
481
+ channelData,
482
+ sampleRate,
483
+ segmentDurationMs,
484
+ algorithm,
485
+ bitDepth,
486
+ fullAudioDurationMs,
487
+ numberOfChannels,
488
+ features: _features,
489
+ intervalAnalysis = 500,
490
+ } = event.data
491
+
492
+ // Calculate subChunkStartTime safely, defaulting to 0 if fullAudioDurationMs is not a valid number
493
+ const subChunkStartTime = (typeof fullAudioDurationMs === 'number' && !isNaN(fullAudioDurationMs) && fullAudioDurationMs >= 0)
494
+ ? fullAudioDurationMs / 1000
495
+ : 0;
496
+
497
+ const features = _features || {}
498
+ const bytesPerSample = bitDepth / 8; // Calculate bytes per sample
499
+
500
+ const SILENCE_THRESHOLD = 0.01
501
+ const MIN_SILENCE_DURATION = 1.5 * sampleRate // 1.5 seconds of silence
502
+ const SPEECH_INERTIA_DURATION = 0.1 * sampleRate // Speech inertia duration in samples
503
+ const RMS_THRESHOLD = 0.01
504
+ const ZCR_THRESHOLD = 0.1
505
+
506
+ // Placeholder functions for feature extraction
507
+ const extractMFCC = (segmentData, sampleRate) => {
508
+ // Implement MFCC extraction logic here
509
+ return []
510
+ }
511
+
512
+ const extractSpectralCentroid = (segmentData, sampleRate) => {
513
+ const magnitudeSpectrum = segmentData.map((v) => v * v)
514
+ const sum = magnitudeSpectrum.reduce((a, b) => a + b, 0)
515
+ if (sum === 0) return 0
516
+
517
+ const weightedSum = magnitudeSpectrum.reduce(
518
+ (acc, value, index) => acc + index * value,
519
+ 0
520
+ )
521
+ return (
522
+ ((weightedSum / sum) * (sampleRate / 2)) / magnitudeSpectrum.length
523
+ )
524
+ }
525
+
526
+ const extractSpectralFlatness = (segmentData) => {
527
+ const magnitudeSpectrum = segmentData.map((v) => Math.abs(v))
528
+ const geometricMean = Math.exp(
529
+ magnitudeSpectrum
530
+ .map((v) => Math.log(v + Number.MIN_VALUE))
531
+ .reduce((a, b) => a + b) / magnitudeSpectrum.length
532
+ )
533
+ const arithmeticMean =
534
+ magnitudeSpectrum.reduce((a, b) => a + b) / magnitudeSpectrum.length
535
+ return arithmeticMean === 0 ? 0 : geometricMean / arithmeticMean
536
+ }
537
+
538
+ const extractSpectralRollOff = (segmentData, sampleRate) => {
539
+ const magnitudeSpectrum = segmentData.map((v) => Math.abs(v))
540
+ const totalEnergy = magnitudeSpectrum.reduce((a, b) => a + b, 0)
541
+ const rollOffThreshold = totalEnergy * 0.85
542
+ let cumulativeEnergy = 0
543
+
544
+ for (let i = 0; i < magnitudeSpectrum.length; i++) {
545
+ cumulativeEnergy += magnitudeSpectrum[i]
546
+ if (cumulativeEnergy >= rollOffThreshold) {
547
+ return (i / magnitudeSpectrum.length) * (sampleRate / 2)
548
+ }
549
+ }
550
+
551
+ return 0
552
+ }
553
+
554
+ const extractSpectralBandwidth = (segmentData, sampleRate) => {
555
+ const centroid = extractSpectralCentroid(segmentData, sampleRate)
556
+ const magnitudeSpectrum = segmentData.map((v) => Math.abs(v))
557
+ const sum = magnitudeSpectrum.reduce((a, b) => a + b, 0)
558
+ if (sum === 0) return 0
559
+
560
+ const weightedSum = magnitudeSpectrum.reduce(
561
+ (acc, value, index) => acc + value * Math.pow(index - centroid, 2),
562
+ 0
563
+ )
564
+ return Math.sqrt(weightedSum / sum)
565
+ }
566
+
567
+ const extractChromagram = (segmentData, sampleRate) => {
568
+ return [] // TODO implement
569
+ }
570
+
571
+ /**
572
+ * Creates a features object based on requested features
573
+ */
574
+ function createFeaturesObject(
575
+ features,
576
+ maxAmp,
577
+ rms,
578
+ sumSquares,
579
+ zeroCrossings,
580
+ remainingSamples,
581
+ spectralFeatures,
582
+ channelData,
583
+ startIdx,
584
+ endIdx,
585
+ sampleRate,
586
+ numberOfChannels,
587
+ bytesPerSample
588
+ ) {
589
+ // If no features are requested, return undefined
590
+ if (!Object.values(features).some(function(v) { return v; })) {
591
+ return undefined;
592
+ }
593
+
594
+ const result = {};
595
+
596
+ if (features.energy) {
597
+ result.energy = sumSquares;
598
+ }
599
+ if (features.rms) {
600
+ result.rms = rms;
601
+ }
602
+ // Always include min/max amplitude if any features are requested
603
+ result.minAmplitude = -maxAmp;
604
+ result.maxAmplitude = maxAmp;
605
+
606
+ if (features.zcr) {
607
+ result.zcr = zeroCrossings / remainingSamples;
608
+ }
609
+ if (features.spectralCentroid) {
610
+ result.spectralCentroid = spectralFeatures.centroid;
611
+ }
612
+ if (features.spectralFlatness) {
613
+ result.spectralFlatness = spectralFeatures.flatness;
614
+ }
615
+ if (features.spectralRolloff) {
616
+ result.spectralRolloff = spectralFeatures.rollOff;
617
+ }
618
+ if (features.spectralBandwidth) {
619
+ result.spectralBandwidth = spectralFeatures.bandwidth;
620
+ }
621
+ if (features.chromagram) {
622
+ result.chromagram = computeChroma(channelData.slice(startIdx, endIdx), sampleRate);
623
+ }
624
+ if (features.hnr) {
625
+ result.hnr = extractHNR(channelData.slice(startIdx, endIdx));
626
+ }
627
+ if (features.pitch) {
628
+ result.pitch = estimatePitch(channelData.slice(startIdx, endIdx), sampleRate);
629
+ }
630
+
631
+ return result;
632
+ }
633
+
634
+ function extractWaveform(
635
+ channelData,
636
+ sampleRate,
637
+ segmentDurationMs,
638
+ numberOfChannels,
639
+ bytesPerSample
640
+ ) {
641
+ const logger = enableLogging ? {
642
+ debug: (...args) => console.debug('[Worker]', ...args),
643
+ log: (...args) => console.log('[Worker]', ...args),
644
+ error: (...args) => console.error('[Worker]', ...args)
645
+ } : {
646
+ debug: () => {},
647
+ log: () => {},
648
+ error: () => {}
649
+ }
650
+
651
+ // Calculate amplitude range
652
+ let min = Infinity
653
+ let max = -Infinity
654
+ for (let i = 0; i < channelData.length; i++) {
655
+ min = Math.min(min, channelData[i])
656
+ max = Math.max(max, channelData[i])
657
+ }
658
+
659
+ const totalSamples = channelData.length
660
+ const durationMs = (totalSamples / sampleRate) * 1000
661
+
662
+ // Calculate fixed segment sizes
663
+ const samplesPerSegment = Math.floor(sampleRate * (segmentDurationMs / 1000));
664
+ const numPoints = Math.floor(totalSamples / samplesPerSegment);
665
+ const remainingSamples = totalSamples % samplesPerSegment;
666
+
667
+ const dataPoints = []
668
+
669
+ // Process full segments
670
+ for (let i = 0; i < numPoints; i++) {
671
+ const startIdx = i * samplesPerSegment
672
+ const endIdx = startIdx + samplesPerSegment
673
+
674
+ let sumSquares = 0
675
+ let maxAmp = 0
676
+ let zeroCrossings = 0
677
+
678
+ // Calculate segment features
679
+ for (let j = startIdx; j < endIdx; j++) {
680
+ const value = channelData[j]
681
+ sumSquares += value * value
682
+ maxAmp = Math.max(maxAmp, Math.abs(value))
683
+ if (j > 0 && value * channelData[j - 1] < 0) {
684
+ zeroCrossings++
685
+ }
686
+ }
687
+
688
+ const rms = Math.sqrt(sumSquares / samplesPerSegment)
689
+ const startTime = subChunkStartTime + (startIdx / sampleRate)
690
+ const endTime = subChunkStartTime + (endIdx / sampleRate)
691
+ // Calculate byte positions correctly based on numberOfChannels and bytesPerSample
692
+ const startPosition = startIdx * numberOfChannels * bytesPerSample
693
+ const endPosition = endIdx * numberOfChannels * bytesPerSample
694
+
695
+ var spectralFeatures = computeSpectralFeatures(channelData.slice(startIdx, endIdx), sampleRate, features);
696
+
697
+ // Simply use the counter, increment after assigning
698
+ const dataPoint = {
699
+ id: uniqueIdCounter++,
700
+ amplitude: maxAmp,
701
+ rms,
702
+ startTime,
703
+ endTime,
704
+ dB: 20 * Math.log10(rms + 1e-6),
705
+ silent: rms < 0.01,
706
+ startPosition,
707
+ endPosition,
708
+ samples: samplesPerSegment,
709
+ }
710
+
711
+ // Extract features if any are requested
712
+ const extractedFeatures = createFeaturesObject(
713
+ features,
714
+ maxAmp,
715
+ rms,
716
+ sumSquares,
717
+ zeroCrossings,
718
+ samplesPerSegment,
719
+ spectralFeatures,
720
+ channelData,
721
+ startIdx,
722
+ endIdx,
723
+ sampleRate,
724
+ numberOfChannels,
725
+ bytesPerSample
726
+ );
727
+
728
+ if (extractedFeatures) {
729
+ dataPoint.features = extractedFeatures;
730
+ }
731
+
732
+ dataPoints.push(dataPoint)
733
+ }
734
+
735
+ // Handle remaining samples if they exist and are enough to process
736
+ if (remainingSamples > samplesPerSegment / 4) { // Only process if we have at least 1/4 of a segment
737
+ const startIdx = numPoints * samplesPerSegment
738
+ const endIdx = totalSamples
739
+
740
+ let sumSquares = 0
741
+ let maxAmp = 0
742
+ let zeroCrossings = 0
743
+
744
+ for (let j = startIdx; j < endIdx; j++) {
745
+ const value = channelData[j]
746
+ sumSquares += value * value
747
+ maxAmp = Math.max(maxAmp, Math.abs(value))
748
+ if (j > 0 && value * channelData[j - 1] < 0) {
749
+ zeroCrossings++
750
+ }
751
+ }
752
+
753
+ const rms = Math.sqrt(sumSquares / remainingSamples)
754
+ const startTime = subChunkStartTime + (startIdx / sampleRate);
755
+ const endTime = subChunkStartTime + (endIdx / sampleRate);
756
+ // Calculate byte positions correctly based on numberOfChannels and bytesPerSample
757
+ const startPosition = startIdx * numberOfChannels * bytesPerSample
758
+ const endPosition = endIdx * numberOfChannels * bytesPerSample
759
+
760
+ var spectralFeatures = computeSpectralFeatures(channelData.slice(startIdx, endIdx), sampleRate, features);
761
+
762
+ // Simply use the counter, increment after assigning
763
+ const dataPoint = {
764
+ id: uniqueIdCounter++,
765
+ amplitude: maxAmp,
766
+ rms,
767
+ startTime,
768
+ endTime,
769
+ dB: 20 * Math.log10(rms + 1e-6),
770
+ silent: rms < 0.01,
771
+ startPosition,
772
+ endPosition,
773
+ samples: remainingSamples,
774
+ }
775
+
776
+ logger.debug('extractWaveform - dataPoint', dataPoint);
777
+ // Extract features if any are requested
778
+ const extractedFeatures = createFeaturesObject(
779
+ features,
780
+ maxAmp,
781
+ rms,
782
+ sumSquares,
783
+ zeroCrossings,
784
+ remainingSamples,
785
+ spectralFeatures,
786
+ channelData,
787
+ startIdx,
788
+ endIdx,
789
+ sampleRate,
790
+ numberOfChannels,
791
+ bytesPerSample
792
+ );
793
+
794
+ if (extractedFeatures) {
795
+ dataPoint.features = extractedFeatures;
796
+ }
797
+
798
+ dataPoints.push(dataPoint)
799
+ }
800
+
801
+ return {
802
+ durationMs,
803
+ dataPoints,
804
+ amplitudeRange: { min, max },
805
+ rmsRange: {
806
+ min: 0,
807
+ max: Math.max(Math.abs(min), Math.abs(max))
808
+ },
809
+ extractionTimeMs: Date.now() - lastEmitTime
810
+ }
811
+ }
812
+
813
+ try {
814
+ const result = extractWaveform(
815
+ channelData,
816
+ sampleRate,
817
+ segmentDurationMs,
818
+ numberOfChannels || 1, // Default to 1 channel if not provided
819
+ bytesPerSample
820
+ )
821
+
822
+ // Send complete result immediately
823
+ self.postMessage({
824
+ command: 'features',
825
+ result: {
826
+ bitDepth,
827
+ samples: channelData.length,
828
+ numberOfChannels,
829
+ sampleRate,
830
+ segmentDurationMs,
831
+ durationMs: result.durationMs,
832
+ dataPoints: result.dataPoints,
833
+ amplitudeRange: result.amplitudeRange,
834
+ rmsRange: result.rmsRange,
835
+ }
836
+ })
837
+ } catch (error) {
838
+ console.error('[Worker] Error', {
839
+ message: error.message,
840
+ stack: error.stack
841
+ });
842
+
843
+ self.postMessage({
844
+ error: {
845
+ message: error.message,
846
+ stack: error.stack,
847
+ name: error.name
848
+ }
849
+ });
850
+ }
851
+ }
852
+ `;
853
+ //# sourceMappingURL=InlineFeaturesExtractor.web.js.map