@siteed/expo-audio-stream 1.0.2 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/.size-limit.json +6 -0
  2. package/README.md +18 -176
  3. package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +1 -0
  4. package/app.plugin.js +1 -1
  5. package/build/AudioAnalysis/AudioAnalysis.types.d.ts +74 -0
  6. package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -0
  7. package/build/AudioAnalysis/AudioAnalysis.types.js +3 -0
  8. package/build/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
  9. package/build/AudioAnalysis/extractAudioAnalysis.d.ts +20 -0
  10. package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -0
  11. package/build/AudioAnalysis/extractAudioAnalysis.js +88 -0
  12. package/build/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
  13. package/build/AudioAnalysis/extractWaveform.d.ts +8 -0
  14. package/build/AudioAnalysis/extractWaveform.d.ts.map +1 -0
  15. package/build/AudioAnalysis/extractWaveform.js +14 -0
  16. package/build/AudioAnalysis/extractWaveform.js.map +1 -0
  17. package/build/AudioRecorder.provider.d.ts +15 -2
  18. package/build/AudioRecorder.provider.d.ts.map +1 -1
  19. package/build/AudioRecorder.provider.js +21 -8
  20. package/build/AudioRecorder.provider.js.map +1 -1
  21. package/build/ExpoAudioStream.native.d.ts.map +1 -1
  22. package/build/ExpoAudioStream.native.js +2 -2
  23. package/build/ExpoAudioStream.native.js.map +1 -1
  24. package/build/ExpoAudioStream.types.d.ts +33 -89
  25. package/build/ExpoAudioStream.types.d.ts.map +1 -1
  26. package/build/ExpoAudioStream.types.js.map +1 -1
  27. package/build/ExpoAudioStream.web.d.ts +10 -9
  28. package/build/ExpoAudioStream.web.d.ts.map +1 -1
  29. package/build/ExpoAudioStream.web.js +44 -25
  30. package/build/ExpoAudioStream.web.js.map +1 -1
  31. package/build/ExpoAudioStreamModule.d.ts.map +1 -1
  32. package/build/ExpoAudioStreamModule.js +13 -8
  33. package/build/ExpoAudioStreamModule.js.map +1 -1
  34. package/build/{WebRecorder.d.ts → WebRecorder.web.d.ts} +13 -9
  35. package/build/WebRecorder.web.d.ts.map +1 -0
  36. package/build/{WebRecorder.js → WebRecorder.web.js} +118 -63
  37. package/build/WebRecorder.web.js.map +1 -0
  38. package/build/constants.d.ts +11 -0
  39. package/build/constants.d.ts.map +1 -0
  40. package/build/constants.js +14 -0
  41. package/build/constants.js.map +1 -0
  42. package/build/events.d.ts +18 -0
  43. package/build/events.d.ts.map +1 -0
  44. package/build/events.js +15 -0
  45. package/build/events.js.map +1 -0
  46. package/build/index.d.ts +9 -17
  47. package/build/index.d.ts.map +1 -1
  48. package/build/index.js +7 -113
  49. package/build/index.js.map +1 -1
  50. package/build/logger.d.ts +9 -0
  51. package/build/logger.d.ts.map +1 -0
  52. package/build/logger.js +13 -0
  53. package/build/logger.js.map +1 -0
  54. package/build/useAudioRecorder.d.ts +20 -0
  55. package/build/useAudioRecorder.d.ts.map +1 -0
  56. package/build/{useAudioRecording.js → useAudioRecorder.js} +90 -86
  57. package/build/useAudioRecorder.js.map +1 -0
  58. package/build/utils/BlobFix.d.ts +9 -0
  59. package/build/utils/BlobFix.d.ts.map +1 -0
  60. package/build/utils/BlobFix.js +494 -0
  61. package/build/utils/BlobFix.js.map +1 -0
  62. package/build/utils/concatenateBuffers.d.ts +8 -0
  63. package/build/utils/concatenateBuffers.d.ts.map +1 -0
  64. package/build/utils/concatenateBuffers.js +21 -0
  65. package/build/utils/concatenateBuffers.js.map +1 -0
  66. package/build/utils/convertPCMToFloat32.d.ts +11 -0
  67. package/build/utils/convertPCMToFloat32.d.ts.map +1 -0
  68. package/build/utils/convertPCMToFloat32.js +54 -0
  69. package/build/utils/convertPCMToFloat32.js.map +1 -0
  70. package/build/utils/encodingToBitDepth.d.ts +5 -0
  71. package/build/utils/encodingToBitDepth.d.ts.map +1 -0
  72. package/build/utils/encodingToBitDepth.js +13 -0
  73. package/build/utils/encodingToBitDepth.js.map +1 -0
  74. package/build/utils/getWavFileInfo.d.ts +26 -0
  75. package/build/utils/getWavFileInfo.d.ts.map +1 -0
  76. package/build/utils/getWavFileInfo.js +92 -0
  77. package/build/utils/getWavFileInfo.js.map +1 -0
  78. package/build/utils/writeWavHeader.d.ts +9 -0
  79. package/build/utils/writeWavHeader.d.ts.map +1 -0
  80. package/build/utils/writeWavHeader.js +41 -0
  81. package/build/utils/writeWavHeader.js.map +1 -0
  82. package/build/workers/InlineFeaturesExtractor.web.d.ts +2 -0
  83. package/build/workers/InlineFeaturesExtractor.web.d.ts.map +1 -0
  84. package/build/workers/InlineFeaturesExtractor.web.js +303 -0
  85. package/build/workers/InlineFeaturesExtractor.web.js.map +1 -0
  86. package/build/workers/inlineAudioWebWorker.web.d.ts +2 -0
  87. package/build/workers/inlineAudioWebWorker.web.d.ts.map +1 -0
  88. package/build/workers/inlineAudioWebWorker.web.js +243 -0
  89. package/build/workers/inlineAudioWebWorker.web.js.map +1 -0
  90. package/expo-module.config.json +8 -17
  91. package/ios/AudioStreamManager.swift +40 -2
  92. package/ios/ExpoAudioStreamModule.swift +11 -0
  93. package/ios/RecordingResult.swift +1 -0
  94. package/package.json +72 -64
  95. package/plugin/build/index.d.ts +1 -1
  96. package/plugin/build/index.js +7 -7
  97. package/plugin/src/index.ts +47 -47
  98. package/plugin/tsconfig.json +8 -13
  99. package/publish.sh +0 -0
  100. package/src/AudioAnalysis/AudioAnalysis.types.ts +84 -0
  101. package/src/AudioAnalysis/extractAudioAnalysis.ts +147 -0
  102. package/src/AudioAnalysis/extractWaveform.ts +25 -0
  103. package/src/AudioRecorder.provider.tsx +59 -31
  104. package/src/ExpoAudioStream.native.ts +2 -2
  105. package/src/ExpoAudioStream.types.ts +58 -116
  106. package/src/ExpoAudioStream.web.ts +233 -205
  107. package/src/ExpoAudioStreamModule.ts +18 -12
  108. package/src/WebRecorder.web.ts +433 -0
  109. package/src/constants.ts +18 -0
  110. package/src/events.ts +39 -0
  111. package/src/index.ts +15 -176
  112. package/src/logger.ts +23 -0
  113. package/src/useAudioRecorder.tsx +420 -0
  114. package/src/utils/BlobFix.ts +550 -0
  115. package/src/utils/concatenateBuffers.ts +24 -0
  116. package/src/utils/convertPCMToFloat32.ts +75 -0
  117. package/src/utils/encodingToBitDepth.ts +18 -0
  118. package/src/utils/getWavFileInfo.ts +132 -0
  119. package/src/utils/writeWavHeader.ts +56 -0
  120. package/src/workers/InlineFeaturesExtractor.web.tsx +302 -0
  121. package/src/workers/inlineAudioWebWorker.web.tsx +242 -0
  122. package/tsconfig.json +12 -7
  123. package/build/WebRecorder.d.ts.map +0 -1
  124. package/build/WebRecorder.js.map +0 -1
  125. package/build/inlineAudioWebWorker.d.ts +0 -3
  126. package/build/inlineAudioWebWorker.d.ts.map +0 -1
  127. package/build/inlineAudioWebWorker.js +0 -340
  128. package/build/inlineAudioWebWorker.js.map +0 -1
  129. package/build/useAudioRecording.d.ts +0 -38
  130. package/build/useAudioRecording.d.ts.map +0 -1
  131. package/build/useAudioRecording.js.map +0 -1
  132. package/build/utils.d.ts +0 -31
  133. package/build/utils.d.ts.map +0 -1
  134. package/build/utils.js +0 -143
  135. package/build/utils.js.map +0 -1
  136. package/src/WebRecorder.ts +0 -364
  137. package/src/inlineAudioWebWorker.tsx +0 -340
  138. package/src/useAudioRecording.tsx +0 -410
  139. package/src/utils.ts +0 -189
@@ -0,0 +1,550 @@
1
+ /*
2
+ * There is a bug where `navigator.mediaDevices.getUserMedia` + `MediaRecorder`
3
+ * creates WEBM files without duration metadata. See:
4
+ * - https://bugs.chromium.org/p/chromium/issues/detail?id=642012
5
+ * - https://stackoverflow.com/a/39971175/13989043
6
+ *
7
+ * This file contains a function that fixes the duration metadata of a WEBM file.
8
+ * - Answer found: https://stackoverflow.com/a/75218309/13989043
9
+ * - Code adapted from: https://github.com/mat-sz/webm-fix-duration
10
+ * (forked from https://github.com/yusitnikov/fix-webm-duration)
11
+ */
12
+
13
+ /*
14
+ * This is the list of possible WEBM file sections by their IDs.
15
+ * Possible types: Container, Binary, Uint, Int, String, Float, Date
16
+ */
17
+ interface Section {
18
+ name: string;
19
+ type: string;
20
+ }
21
+
22
+ const sections: Record<number, Section> = {
23
+ 0xa45dfa3: { name: "EBML", type: "Container" },
24
+ 0x286: { name: "EBMLVersion", type: "Uint" },
25
+ 0x2f7: { name: "EBMLReadVersion", type: "Uint" },
26
+ 0x2f2: { name: "EBMLMaxIDLength", type: "Uint" },
27
+ 0x2f3: { name: "EBMLMaxSizeLength", type: "Uint" },
28
+ 0x282: { name: "DocType", type: "String" },
29
+ 0x287: { name: "DocTypeVersion", type: "Uint" },
30
+ 0x285: { name: "DocTypeReadVersion", type: "Uint" },
31
+ 0x6c: { name: "Void", type: "Binary" },
32
+ 0x3f: { name: "CRC-32", type: "Binary" },
33
+ 0xb538667: { name: "SignatureSlot", type: "Container" },
34
+ 0x3e8a: { name: "SignatureAlgo", type: "Uint" },
35
+ 0x3e9a: { name: "SignatureHash", type: "Uint" },
36
+ 0x3ea5: { name: "SignaturePublicKey", type: "Binary" },
37
+ 0x3eb5: { name: "Signature", type: "Binary" },
38
+ 0x3e5b: { name: "SignatureElements", type: "Container" },
39
+ 0x3e7b: { name: "SignatureElementList", type: "Container" },
40
+ 0x2532: { name: "SignedElement", type: "Binary" },
41
+ 0x8538067: { name: "Segment", type: "Container" },
42
+ 0x14d9b74: { name: "SeekHead", type: "Container" },
43
+ 0xdbb: { name: "Seek", type: "Container" },
44
+ 0x13ab: { name: "SeekID", type: "Binary" },
45
+ 0x13ac: { name: "SeekPosition", type: "Uint" },
46
+ 0x549a966: { name: "Info", type: "Container" },
47
+ 0x33a4: { name: "SegmentUID", type: "Binary" },
48
+ 0x3384: { name: "SegmentFilename", type: "String" },
49
+ 0x1cb923: { name: "PrevUID", type: "Binary" },
50
+ 0x1c83ab: { name: "PrevFilename", type: "String" },
51
+ 0x1eb923: { name: "NextUID", type: "Binary" },
52
+ 0x1e83bb: { name: "NextFilename", type: "String" },
53
+ 0x444: { name: "SegmentFamily", type: "Binary" },
54
+ 0x2924: { name: "ChapterTranslate", type: "Container" },
55
+ 0x29fc: { name: "ChapterTranslateEditionUID", type: "Uint" },
56
+ 0x29bf: { name: "ChapterTranslateCodec", type: "Uint" },
57
+ 0x29a5: { name: "ChapterTranslateID", type: "Binary" },
58
+ 0xad7b1: { name: "TimecodeScale", type: "Uint" },
59
+ 0x489: { name: "Duration", type: "Float" },
60
+ 0x461: { name: "DateUTC", type: "Date" },
61
+ 0x3ba9: { name: "Title", type: "String" },
62
+ 0xd80: { name: "MuxingApp", type: "String" },
63
+ 0x1741: { name: "WritingApp", type: "String" },
64
+ // 0xf43b675: { name: 'Cluster', type: 'Container' },
65
+ 0x67: { name: "Timecode", type: "Uint" },
66
+ 0x1854: { name: "SilentTracks", type: "Container" },
67
+ 0x18d7: { name: "SilentTrackNumber", type: "Uint" },
68
+ 0x27: { name: "Position", type: "Uint" },
69
+ 0x2b: { name: "PrevSize", type: "Uint" },
70
+ 0x23: { name: "SimpleBlock", type: "Binary" },
71
+ 0x20: { name: "BlockGroup", type: "Container" },
72
+ 0x21: { name: "Block", type: "Binary" },
73
+ 0x22: { name: "BlockVirtual", type: "Binary" },
74
+ 0x35a1: { name: "BlockAdditions", type: "Container" },
75
+ 0x26: { name: "BlockMore", type: "Container" },
76
+ 0x6e: { name: "BlockAddID", type: "Uint" },
77
+ 0x25: { name: "BlockAdditional", type: "Binary" },
78
+ 0x1b: { name: "BlockDuration", type: "Uint" },
79
+ 0x7a: { name: "ReferencePriority", type: "Uint" },
80
+ 0x7b: { name: "ReferenceBlock", type: "Int" },
81
+ 0x7d: { name: "ReferenceVirtual", type: "Int" },
82
+ 0x24: { name: "CodecState", type: "Binary" },
83
+ 0x35a2: { name: "DiscardPadding", type: "Int" },
84
+ 0xe: { name: "Slices", type: "Container" },
85
+ 0x68: { name: "TimeSlice", type: "Container" },
86
+ 0x4c: { name: "LaceNumber", type: "Uint" },
87
+ 0x4d: { name: "FrameNumber", type: "Uint" },
88
+ 0x4b: { name: "BlockAdditionID", type: "Uint" },
89
+ 0x4e: { name: "Delay", type: "Uint" },
90
+ 0x4f: { name: "SliceDuration", type: "Uint" },
91
+ 0x48: { name: "ReferenceFrame", type: "Container" },
92
+ 0x49: { name: "ReferenceOffset", type: "Uint" },
93
+ 0x4a: { name: "ReferenceTimeCode", type: "Uint" },
94
+ 0x2f: { name: "EncryptedBlock", type: "Binary" },
95
+ 0x654ae6b: { name: "Tracks", type: "Container" },
96
+ 0x2e: { name: "TrackEntry", type: "Container" },
97
+ 0x57: { name: "TrackNumber", type: "Uint" },
98
+ 0x33c5: { name: "TrackUID", type: "Uint" },
99
+ 0x3: { name: "TrackType", type: "Uint" },
100
+ 0x39: { name: "FlagEnabled", type: "Uint" },
101
+ 0x8: { name: "FlagDefault", type: "Uint" },
102
+ 0x15aa: { name: "FlagForced", type: "Uint" },
103
+ 0x1c: { name: "FlagLacing", type: "Uint" },
104
+ 0x2de7: { name: "MinCache", type: "Uint" },
105
+ 0x2df8: { name: "MaxCache", type: "Uint" },
106
+ 0x3e383: { name: "DefaultDuration", type: "Uint" },
107
+ 0x34e7a: { name: "DefaultDecodedFieldDuration", type: "Uint" },
108
+ 0x3314f: { name: "TrackTimecodeScale", type: "Float" },
109
+ 0x137f: { name: "TrackOffset", type: "Int" },
110
+ 0x15ee: { name: "MaxBlockAdditionID", type: "Uint" },
111
+ 0x136e: { name: "Name", type: "String" },
112
+ 0x2b59c: { name: "Language", type: "String" },
113
+ 0x6: { name: "CodecID", type: "String" },
114
+ 0x23a2: { name: "CodecPrivate", type: "Binary" },
115
+ 0x58688: { name: "CodecName", type: "String" },
116
+ 0x3446: { name: "AttachmentLink", type: "Uint" },
117
+ 0x1a9697: { name: "CodecSettings", type: "String" },
118
+ 0x1b4040: { name: "CodecInfoURL", type: "String" },
119
+ 0x6b240: { name: "CodecDownloadURL", type: "String" },
120
+ 0x2a: { name: "CodecDecodeAll", type: "Uint" },
121
+ 0x2fab: { name: "TrackOverlay", type: "Uint" },
122
+ 0x16aa: { name: "CodecDelay", type: "Uint" },
123
+ 0x16bb: { name: "SeekPreRoll", type: "Uint" },
124
+ 0x2624: { name: "TrackTranslate", type: "Container" },
125
+ 0x26fc: { name: "TrackTranslateEditionUID", type: "Uint" },
126
+ 0x26bf: { name: "TrackTranslateCodec", type: "Uint" },
127
+ 0x26a5: { name: "TrackTranslateTrackID", type: "Binary" },
128
+ 0x60: { name: "Video", type: "Container" },
129
+ 0x1a: { name: "FlagInterlaced", type: "Uint" },
130
+ 0x13b8: { name: "StereoMode", type: "Uint" },
131
+ 0x13c0: { name: "AlphaMode", type: "Uint" },
132
+ 0x13b9: { name: "OldStereoMode", type: "Uint" },
133
+ 0x30: { name: "PixelWidth", type: "Uint" },
134
+ 0x3a: { name: "PixelHeight", type: "Uint" },
135
+ 0x14aa: { name: "PixelCropBottom", type: "Uint" },
136
+ 0x14bb: { name: "PixelCropTop", type: "Uint" },
137
+ 0x14cc: { name: "PixelCropLeft", type: "Uint" },
138
+ 0x14dd: { name: "PixelCropRight", type: "Uint" },
139
+ 0x14b0: { name: "DisplayWidth", type: "Uint" },
140
+ 0x14ba: { name: "DisplayHeight", type: "Uint" },
141
+ 0x14b2: { name: "DisplayUnit", type: "Uint" },
142
+ 0x14b3: { name: "AspectRatioType", type: "Uint" },
143
+ 0xeb524: { name: "ColourSpace", type: "Binary" },
144
+ 0xfb523: { name: "GammaValue", type: "Float" },
145
+ 0x383e3: { name: "FrameRate", type: "Float" },
146
+ 0x61: { name: "Audio", type: "Container" },
147
+ 0x35: { name: "SamplingFrequency", type: "Float" },
148
+ 0x38b5: { name: "OutputSamplingFrequency", type: "Float" },
149
+ 0x1f: { name: "Channels", type: "Uint" },
150
+ 0x3d7b: { name: "ChannelPositions", type: "Binary" },
151
+ 0x2264: { name: "BitDepth", type: "Uint" },
152
+ 0x62: { name: "TrackOperation", type: "Container" },
153
+ 0x63: { name: "TrackCombinePlanes", type: "Container" },
154
+ 0x64: { name: "TrackPlane", type: "Container" },
155
+ 0x65: { name: "TrackPlaneUID", type: "Uint" },
156
+ 0x66: { name: "TrackPlaneType", type: "Uint" },
157
+ 0x69: { name: "TrackJoinBlocks", type: "Container" },
158
+ 0x6d: { name: "TrackJoinUID", type: "Uint" },
159
+ 0x40: { name: "TrickTrackUID", type: "Uint" },
160
+ 0x41: { name: "TrickTrackSegmentUID", type: "Binary" },
161
+ 0x46: { name: "TrickTrackFlag", type: "Uint" },
162
+ 0x47: { name: "TrickMasterTrackUID", type: "Uint" },
163
+ 0x44: { name: "TrickMasterTrackSegmentUID", type: "Binary" },
164
+ 0x2d80: { name: "ContentEncodings", type: "Container" },
165
+ 0x2240: { name: "ContentEncoding", type: "Container" },
166
+ 0x1031: { name: "ContentEncodingOrder", type: "Uint" },
167
+ 0x1032: { name: "ContentEncodingScope", type: "Uint" },
168
+ 0x1033: { name: "ContentEncodingType", type: "Uint" },
169
+ 0x1034: { name: "ContentCompression", type: "Container" },
170
+ 0x254: { name: "ContentCompAlgo", type: "Uint" },
171
+ 0x255: { name: "ContentCompSettings", type: "Binary" },
172
+ 0x1035: { name: "ContentEncryption", type: "Container" },
173
+ 0x7e1: { name: "ContentEncAlgo", type: "Uint" },
174
+ 0x7e2: { name: "ContentEncKeyID", type: "Binary" },
175
+ 0x7e3: { name: "ContentSignature", type: "Binary" },
176
+ 0x7e4: { name: "ContentSigKeyID", type: "Binary" },
177
+ 0x7e5: { name: "ContentSigAlgo", type: "Uint" },
178
+ 0x7e6: { name: "ContentSigHashAlgo", type: "Uint" },
179
+ 0xc53bb6b: { name: "Cues", type: "Container" },
180
+ 0x3b: { name: "CuePoint", type: "Container" },
181
+ 0x33: { name: "CueTime", type: "Uint" },
182
+ 0x37: { name: "CueTrackPositions", type: "Container" },
183
+ 0x77: { name: "CueTrack", type: "Uint" },
184
+ 0x71: { name: "CueClusterPosition", type: "Uint" },
185
+ 0x70: { name: "CueRelativePosition", type: "Uint" },
186
+ 0x32: { name: "CueDuration", type: "Uint" },
187
+ 0x1378: { name: "CueBlockNumber", type: "Uint" },
188
+ 0x6a: { name: "CueCodecState", type: "Uint" },
189
+ 0x5b: { name: "CueReference", type: "Container" },
190
+ 0x16: { name: "CueRefTime", type: "Uint" },
191
+ 0x17: { name: "CueRefCluster", type: "Uint" },
192
+ 0x135f: { name: "CueRefNumber", type: "Uint" },
193
+ 0x6b: { name: "CueRefCodecState", type: "Uint" },
194
+ 0x941a469: { name: "Attachments", type: "Container" },
195
+ 0x21a7: { name: "AttachedFile", type: "Container" },
196
+ 0x67e: { name: "FileDescription", type: "String" },
197
+ 0x66e: { name: "FileName", type: "String" },
198
+ 0x660: { name: "FileMimeType", type: "String" },
199
+ 0x65c: { name: "FileData", type: "Binary" },
200
+ 0x6ae: { name: "FileUID", type: "Uint" },
201
+ 0x675: { name: "FileReferral", type: "Binary" },
202
+ 0x661: { name: "FileUsedStartTime", type: "Uint" },
203
+ 0x662: { name: "FileUsedEndTime", type: "Uint" },
204
+ 0x43a770: { name: "Chapters", type: "Container" },
205
+ 0x5b9: { name: "EditionEntry", type: "Container" },
206
+ 0x5bc: { name: "EditionUID", type: "Uint" },
207
+ 0x5bd: { name: "EditionFlagHidden", type: "Uint" },
208
+ 0x5db: { name: "EditionFlagDefault", type: "Uint" },
209
+ 0x5dd: { name: "EditionFlagOrdered", type: "Uint" },
210
+ 0x36: { name: "ChapterAtom", type: "Container" },
211
+ 0x33c4: { name: "ChapterUID", type: "Uint" },
212
+ 0x1654: { name: "ChapterStringUID", type: "String" },
213
+ 0x11: { name: "ChapterTimeStart", type: "Uint" },
214
+ 0x12: { name: "ChapterTimeEnd", type: "Uint" },
215
+ 0x18: { name: "ChapterFlagHidden", type: "Uint" },
216
+ 0x598: { name: "ChapterFlagEnabled", type: "Uint" },
217
+ 0x2e67: { name: "ChapterSegmentUID", type: "Binary" },
218
+ 0x2ebc: { name: "ChapterSegmentEditionUID", type: "Uint" },
219
+ 0x23c3: { name: "ChapterPhysicalEquiv", type: "Uint" },
220
+ 0xf: { name: "ChapterTrack", type: "Container" },
221
+ 0x9: { name: "ChapterTrackNumber", type: "Uint" },
222
+ 0x0: { name: "ChapterDisplay", type: "Container" },
223
+ 0x5: { name: "ChapString", type: "String" },
224
+ 0x37c: { name: "ChapLanguage", type: "String" },
225
+ 0x37e: { name: "ChapCountry", type: "String" },
226
+ 0x2944: { name: "ChapProcess", type: "Container" },
227
+ 0x2955: { name: "ChapProcessCodecID", type: "Uint" },
228
+ 0x50d: { name: "ChapProcessPrivate", type: "Binary" },
229
+ 0x2911: { name: "ChapProcessCommand", type: "Container" },
230
+ 0x2922: { name: "ChapProcessTime", type: "Uint" },
231
+ 0x2933: { name: "ChapProcessData", type: "Binary" },
232
+ 0x254c367: { name: "Tags", type: "Container" },
233
+ 0x3373: { name: "Tag", type: "Container" },
234
+ 0x23c0: { name: "Targets", type: "Container" },
235
+ 0x28ca: { name: "TargetTypeValue", type: "Uint" },
236
+ 0x23ca: { name: "TargetType", type: "String" },
237
+ 0x23c5: { name: "TagTrackUID", type: "Uint" },
238
+ 0x23c9: { name: "TagEditionUID", type: "Uint" },
239
+ 0x23c4: { name: "TagChapterUID", type: "Uint" },
240
+ 0x23c6: { name: "TagAttachmentUID", type: "Uint" },
241
+ 0x27c8: { name: "SimpleTag", type: "Container" },
242
+ 0x5a3: { name: "TagName", type: "String" },
243
+ 0x47a: { name: "TagLanguage", type: "String" },
244
+ 0x484: { name: "TagDefault", type: "Uint" },
245
+ 0x487: { name: "TagString", type: "String" },
246
+ 0x485: { name: "TagBinary", type: "Binary" },
247
+ };
248
+
249
+ class WebmBase<T> {
250
+ source?: Uint8Array;
251
+ data?: T;
252
+
253
+ constructor(private name = "Unknown", private type = "Unknown") {}
254
+
255
+ updateBySource() {}
256
+
257
+ setSource(source: Uint8Array) {
258
+ this.source = source;
259
+ this.updateBySource();
260
+ }
261
+
262
+ updateByData() {}
263
+
264
+ setData(data: T) {
265
+ this.data = data;
266
+ this.updateByData();
267
+ }
268
+ }
269
+
270
+ class WebmUint extends WebmBase<string> {
271
+ constructor(name: string, type: string) {
272
+ super(name, type || "Uint");
273
+ }
274
+
275
+ updateBySource() {
276
+ // use hex representation of a number instead of number value
277
+ this.data = "";
278
+ for (let i = 0; i < this.source!.length; i++) {
279
+ const hex = this.source![i].toString(16);
280
+ this.data += padHex(hex);
281
+ }
282
+ }
283
+
284
+ updateByData() {
285
+ const length = this.data!.length / 2;
286
+ this.source = new Uint8Array(length);
287
+ for (let i = 0; i < length; i++) {
288
+ const hex = this.data!.substr(i * 2, 2);
289
+ this.source[i] = parseInt(hex, 16);
290
+ }
291
+ }
292
+
293
+ getValue() {
294
+ return parseInt(this.data!, 16);
295
+ }
296
+
297
+ setValue(value: number) {
298
+ this.setData(padHex(value.toString(16)));
299
+ }
300
+ }
301
+
302
+ function padHex(hex: string) {
303
+ return hex.length % 2 === 1 ? "0" + hex : hex;
304
+ }
305
+
306
+ class WebmFloat extends WebmBase<number> {
307
+ constructor(name: string, type: string) {
308
+ super(name, type || "Float");
309
+ }
310
+
311
+ getFloatArrayType() {
312
+ return this.source && this.source.length === 4
313
+ ? Float32Array
314
+ : Float64Array;
315
+ }
316
+ updateBySource() {
317
+ const byteArray = this.source!.reverse();
318
+ const floatArrayType = this.getFloatArrayType();
319
+ const floatArray = new floatArrayType(byteArray.buffer);
320
+ this.data! = floatArray[0];
321
+ }
322
+ updateByData() {
323
+ const floatArrayType = this.getFloatArrayType();
324
+ const floatArray = new floatArrayType([this.data!]);
325
+ const byteArray = new Uint8Array(floatArray.buffer);
326
+ this.source = byteArray.reverse();
327
+ }
328
+ getValue() {
329
+ return this.data;
330
+ }
331
+ setValue(value: number) {
332
+ this.setData(value);
333
+ }
334
+ }
335
+
336
+ interface ContainerData {
337
+ id: number;
338
+ idHex?: string;
339
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
340
+ data: WebmBase<any>;
341
+ }
342
+
343
+ class WebmContainer extends WebmBase<ContainerData[]> {
344
+ offset: number = 0;
345
+ data: ContainerData[] = [];
346
+
347
+ constructor(name: string, type: string) {
348
+ super(name, type || "Container");
349
+ }
350
+
351
+ readByte() {
352
+ return this.source![this.offset++];
353
+ }
354
+ readUint() {
355
+ const firstByte = this.readByte();
356
+ const bytes = 8 - firstByte.toString(2).length;
357
+ let value = firstByte - (1 << (7 - bytes));
358
+ for (let i = 0; i < bytes; i++) {
359
+ // don't use bit operators to support x86
360
+ value *= 256;
361
+ value += this.readByte();
362
+ }
363
+ return value;
364
+ }
365
+ updateBySource() {
366
+ let end: number | undefined = undefined;
367
+ this.data = [];
368
+ for (
369
+ this.offset = 0;
370
+ this.offset < this.source!.length;
371
+ this.offset = end
372
+ ) {
373
+ const id = this.readUint();
374
+ const len = this.readUint();
375
+ end = Math.min(this.offset + len, this.source!.length);
376
+ const data = this.source!.slice(this.offset, end);
377
+
378
+ const info = sections[id] || { name: "Unknown", type: "Unknown" };
379
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
380
+ let ctr: any = WebmBase;
381
+ switch (info.type) {
382
+ case "Container":
383
+ ctr = WebmContainer;
384
+ break;
385
+ case "Uint":
386
+ ctr = WebmUint;
387
+ break;
388
+ case "Float":
389
+ ctr = WebmFloat;
390
+ break;
391
+ }
392
+ const section = new ctr(info.name, info.type);
393
+ section.setSource(data);
394
+ this.data.push({
395
+ id: id,
396
+ idHex: id.toString(16),
397
+ data: section,
398
+ });
399
+ }
400
+ }
401
+ writeUint(x: number, draft = false) {
402
+ let flag = 0x80;
403
+ let bytes = 1;
404
+ // eslint-disable-next-line no-empty
405
+ for (; x >= flag && bytes < 8; bytes++, flag *= 0x80) {}
406
+
407
+ if (!draft) {
408
+ let value = flag + x;
409
+ for (let i = bytes - 1; i >= 0; i--) {
410
+ // don't use bit operators to support x86
411
+ const c = value % 256;
412
+ this.source![this.offset! + i] = c;
413
+ value = (value - c) / 256;
414
+ }
415
+ }
416
+
417
+ this.offset += bytes;
418
+ }
419
+
420
+ writeSections(draft = false) {
421
+ this.offset = 0;
422
+ for (let i = 0; i < this.data.length; i++) {
423
+ const section = this.data[i],
424
+ content = section.data.source,
425
+ contentLength = content!.length;
426
+ this.writeUint(section.id, draft);
427
+ this.writeUint(contentLength, draft);
428
+ if (!draft) {
429
+ this.source!.set(content!, this.offset);
430
+ }
431
+ this.offset += contentLength;
432
+ }
433
+ return this.offset;
434
+ }
435
+
436
+ updateByData() {
437
+ // run without accessing this.source to determine total length - need to know it to create Uint8Array
438
+ const length = this.writeSections(true);
439
+ this.source = new Uint8Array(length);
440
+ // now really write data
441
+ this.writeSections();
442
+ }
443
+
444
+ getSectionById(id: number) {
445
+ for (let i = 0; i < this.data.length; i++) {
446
+ const section = this.data[i];
447
+ if (section.id === id) {
448
+ return section.data;
449
+ }
450
+ }
451
+
452
+ return undefined;
453
+ }
454
+ }
455
+
456
+ class WebmFile extends WebmContainer {
457
+ constructor(source: Uint8Array) {
458
+ super("File", "File");
459
+ this.setSource(source);
460
+ }
461
+
462
+ fixDuration(duration: number) {
463
+ const segmentSection = this.getSectionById(0x8538067) as WebmContainer;
464
+ if (!segmentSection) {
465
+ return false;
466
+ }
467
+
468
+ const infoSection = segmentSection.getSectionById(
469
+ 0x549a966,
470
+ ) as WebmContainer;
471
+ if (!infoSection) {
472
+ return false;
473
+ }
474
+
475
+ const timeScaleSection = infoSection.getSectionById(
476
+ 0xad7b1,
477
+ ) as WebmFloat;
478
+ if (!timeScaleSection) {
479
+ return false;
480
+ }
481
+
482
+ let durationSection = infoSection.getSectionById(0x489) as WebmFloat;
483
+ if (durationSection) {
484
+ if (durationSection.getValue()! <= 0) {
485
+ durationSection.setValue(duration);
486
+ } else {
487
+ return false;
488
+ }
489
+ } else {
490
+ // append Duration section
491
+ durationSection = new WebmFloat("Duration", "Float");
492
+ durationSection.setValue(duration);
493
+ infoSection.data.push({
494
+ id: 0x489,
495
+ data: durationSection,
496
+ });
497
+ }
498
+
499
+ // set default time scale to 1 millisecond (1000000 nanoseconds)
500
+ timeScaleSection.setValue(1000000);
501
+ infoSection.updateByData();
502
+ segmentSection.updateByData();
503
+ this.updateByData();
504
+
505
+ return true;
506
+ }
507
+
508
+ toBlob(type = "video/webm") {
509
+ return new Blob([this.source!.buffer], { type });
510
+ }
511
+ }
512
+
513
+ /**
514
+ * Fixes duration on MediaRecorder output.
515
+ * @param blob Input Blob with incorrect duration.
516
+ * @param duration Correct duration (in milliseconds).
517
+ * @param type Output blob mimetype (default: video/webm).
518
+ * @returns
519
+ */
520
+ export const webmFixDuration = (
521
+ blob: Blob,
522
+ duration: number,
523
+ type = "video/webm",
524
+ ): Promise<Blob> => {
525
+ return new Promise((resolve, reject) => {
526
+ try {
527
+ const reader = new FileReader();
528
+
529
+ reader.addEventListener("loadend", () => {
530
+ try {
531
+ const result = reader.result as ArrayBuffer;
532
+ const file = new WebmFile(new Uint8Array(result));
533
+ if (file.fixDuration(duration)) {
534
+ resolve(file.toBlob(type));
535
+ } else {
536
+ resolve(blob);
537
+ }
538
+ } catch (ex) {
539
+ reject(ex);
540
+ }
541
+ });
542
+
543
+ reader.addEventListener("error", () => reject());
544
+
545
+ reader.readAsArrayBuffer(blob);
546
+ } catch (ex) {
547
+ reject(ex);
548
+ }
549
+ });
550
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Concatenates an array of ArrayBuffers into a single ArrayBuffer.
3
+ *
4
+ * @param buffers - An array of ArrayBuffers to be concatenated.
5
+ * @returns A single ArrayBuffer containing the concatenated data.
6
+ */
7
+ export const concatenateBuffers = (buffers: ArrayBuffer[]): ArrayBuffer => {
8
+ // Filter out any undefined or null buffers
9
+ const validBuffers = buffers.filter((buffer) => buffer)
10
+ const totalLength = validBuffers.reduce(
11
+ (sum, buffer) => sum + buffer.byteLength,
12
+ 0
13
+ )
14
+ // Create a new Uint8Array to hold the concatenated result
15
+ const result = new Uint8Array(totalLength)
16
+ // Offset to keep track of the current position in the result array
17
+ let offset = 0
18
+
19
+ for (const buffer of validBuffers) {
20
+ result.set(new Uint8Array(buffer), offset)
21
+ offset += buffer.byteLength
22
+ }
23
+ return result.buffer
24
+ }
@@ -0,0 +1,75 @@
1
+ import { getWavFileInfo, WavFileInfo } from './getWavFileInfo'
2
+ import { getLogger } from '../logger'
3
+
4
+ export const WAV_HEADER_SIZE = 44
5
+
6
+ const logger = getLogger('convertPCMToFloat32')
7
+
8
+ const convertSample = (
9
+ dataView: DataView,
10
+ offset: number,
11
+ bitDepth: number
12
+ ): number => {
13
+ switch (bitDepth) {
14
+ case 8:
15
+ return (dataView.getUint8(offset) - 128) / 128
16
+ case 16:
17
+ return dataView.getInt16(offset, true) / 32768
18
+ case 24:
19
+ return (
20
+ ((dataView.getUint8(offset) |
21
+ (dataView.getUint8(offset + 1) << 8) |
22
+ (dataView.getUint8(offset + 2) << 16)) /
23
+ 8388608) *
24
+ 2 -
25
+ 1
26
+ )
27
+ case 32:
28
+ return dataView.getFloat32(offset, true)
29
+ default:
30
+ throw new Error(`Unsupported bit depth: ${bitDepth}`)
31
+ }
32
+ }
33
+
34
+ export const convertPCMToFloat32 = async ({
35
+ bitDepth,
36
+ buffer,
37
+ skipWavHeader = false,
38
+ }: {
39
+ buffer: ArrayBuffer
40
+ bitDepth: number
41
+ skipWavHeader?: boolean
42
+ }): Promise<{ pcmValues: Float32Array; min: number; max: number }> => {
43
+ try {
44
+ logger.debug(
45
+ `Converting PCM to Float32: bitDepth: ${bitDepth}, buffer.byteLength: ${buffer.byteLength}`
46
+ )
47
+ const dataView = new DataView(buffer)
48
+ let headerOffset = 0
49
+ if (skipWavHeader) {
50
+ const wavFileInfo: WavFileInfo = await getWavFileInfo(buffer)
51
+ headerOffset = wavFileInfo.dataChunkOffset
52
+ }
53
+
54
+ const dataLength = buffer.byteLength - headerOffset
55
+ const sampleLength = Math.floor(dataLength / (bitDepth / 8))
56
+ const float32Array = new Float32Array(sampleLength)
57
+ let min = Infinity
58
+ let max = -Infinity
59
+
60
+ for (let i = 0; i < sampleLength; i++) {
61
+ const offset = headerOffset + i * (bitDepth / 8)
62
+ const value = convertSample(dataView, offset, bitDepth)
63
+
64
+ if (value < min) min = value
65
+ if (value > max) max = value
66
+
67
+ float32Array[i] = value
68
+ }
69
+
70
+ return { pcmValues: float32Array, min, max }
71
+ } catch (error: unknown) {
72
+ logger.error(`Error converting PCM to Float32`, error)
73
+ return { pcmValues: new Float32Array(), min: 0, max: 0 }
74
+ }
75
+ }
@@ -0,0 +1,18 @@
1
+ import { BitDepth, EncodingType } from '../ExpoAudioStream.types'
2
+
3
+ export const encodingToBitDepth = ({
4
+ encoding,
5
+ }: {
6
+ encoding: EncodingType
7
+ }): BitDepth => {
8
+ switch (encoding) {
9
+ case 'pcm_32bit':
10
+ return 32
11
+ case 'pcm_16bit':
12
+ return 16
13
+ case 'pcm_8bit':
14
+ return 8
15
+ default:
16
+ throw new Error(`Unsupported encoding type: ${encoding}`)
17
+ }
18
+ }