@siteed/expo-audio-studio 2.1.1 → 2.3.0

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 (38) hide show
  1. package/CHANGELOG.md +14 -1
  2. package/android/src/main/AndroidManifest.xml +1 -1
  3. package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +8 -4
  4. package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +23 -16
  5. package/build/AudioAnalysis/extractAudioAnalysis.d.ts +6 -0
  6. package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -1
  7. package/build/AudioAnalysis/extractAudioAnalysis.js +1 -8
  8. package/build/AudioAnalysis/extractAudioAnalysis.js.map +1 -1
  9. package/build/AudioAnalysis/extractMelSpectrogram.js.map +1 -1
  10. package/build/ExpoAudioStreamModule.js +1 -1
  11. package/build/ExpoAudioStreamModule.js.map +1 -1
  12. package/build/utils/crc32.d.ts +7 -0
  13. package/build/utils/crc32.d.ts.map +1 -0
  14. package/build/utils/crc32.js +12 -0
  15. package/build/utils/crc32.js.map +1 -0
  16. package/build/utils/crc32.native.d.ts +2 -0
  17. package/build/utils/crc32.native.d.ts.map +1 -0
  18. package/build/utils/crc32.native.js +5 -0
  19. package/build/utils/crc32.native.js.map +1 -0
  20. package/build/utils/crc32.web.d.ts +4 -0
  21. package/build/utils/crc32.web.d.ts.map +1 -0
  22. package/build/utils/crc32.web.js +3 -0
  23. package/build/utils/crc32.web.js.map +1 -0
  24. package/ios/AudioProcessor.swift +7 -4
  25. package/ios/AudioStreamManager.swift +25 -11
  26. package/ios/DecodingConfig.swift +19 -7
  27. package/ios/ExpoAudioStream.podspec +1 -1
  28. package/ios/ExpoAudioStreamModule.swift +6 -4
  29. package/package.json +6 -3
  30. package/plugin/build/index.js +2 -3
  31. package/plugin/src/index.ts +2 -3
  32. package/src/AudioAnalysis/extractAudioAnalysis.ts +1 -2
  33. package/src/AudioAnalysis/extractMelSpectrogram.ts +2 -2
  34. package/src/ExpoAudioStreamModule.ts +1 -1
  35. package/src/types/crc-32.d.ts +9 -0
  36. package/src/utils/crc32.native.ts +4 -0
  37. package/src/utils/crc32.ts +21 -0
  38. package/src/utils/crc32.web.ts +3 -0
package/CHANGELOG.md CHANGED
@@ -8,6 +8,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  ## [Unreleased]
9
9
 
10
10
 
11
+ ## [2.3.0] - 2025-03-29
12
+ ### Changed
13
+ - fix: always generate a new UUID unless filename is provided (#182) ([f98a9a5](https://github.com/deeeed/expo-audio-stream/commit/f98a9a52393829e6c4a79aee3575fbfcc9416c19))
14
+ - refactor(audio-studio): introduce constants for silence threshold and WAV header size (#188) ([e8aa329](https://github.com/deeeed/expo-audio-stream/commit/e8aa3298bd6ba029d38898360b7df26b3fd5485f))
15
+ - docs: enhance installation and API reference documentation for phone call handling (#187) ([fcaece1](https://github.com/deeeed/expo-audio-stream/commit/fcaece18cf046d970b9659f3f12a19deb096bceb))
16
+ ## [2.2.0] - 2025-03-28
17
+ ### Changed
18
+ - refactor(audio-studio): implement platform-specific CRC32 handling ([b61a3d7](https://github.com/deeeed/expo-audio-stream/commit/b61a3d743914e66888ec6cc4cb8e010ff1992698))
19
+ - chore: update Expo dependencies and remove invalid design-system version ([16e5007](https://github.com/deeeed/expo-audio-stream/commit/16e50077690b55977c22fbcb08be75834146ff47))
20
+ - fix: linting issues ([741589d](https://github.com/deeeed/expo-audio-stream/commit/741589d60485a2d049e7adf529d3fd2b999fa098))
21
+ - chore(expo-audio-studio): release @siteed/expo-audio-studio@2.1.1 ([1b17ac6](https://github.com/deeeed/expo-audio-stream/commit/1b17ac6e103f2ca50f29668b3ddaaf57a4b4b7d3))
11
22
  ## [2.1.1] - 2025-03-04
12
23
  ### Changed
13
24
  - feat: Rename`@siteed/expo-audio-stream` to `@siteed/expo-audio-studio` (#160) ([1b99191](https://github.com/deeeed/expo-audio-stream/commit/1b9919143413a900aefed94c20fc9a8b0e6050d3))
@@ -171,7 +182,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
171
182
  - Feature: Audio features extraction during recording.
172
183
  - Feature: Consistent WAV PCM recording format across all platforms.
173
184
 
174
- [unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.1.1...HEAD
185
+ [unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.3.0...HEAD
186
+ [2.3.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.2.0...@siteed/expo-audio-studio@2.3.0
187
+ [2.2.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.1.1...@siteed/expo-audio-studio@2.2.0
175
188
  [2.1.1]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.1.0...@siteed/expo-audio-studio@2.1.1
176
189
  [2.1.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@2.0.1...@siteed/expo-audio-stream@2.1.0
177
190
  [2.0.1]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@2.0.0...@siteed/expo-audio-stream@2.0.1
@@ -1,6 +1,6 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
1
2
  <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
3
  <!-- Permissions will be merged into the app's manifest -->
3
- <uses-permission android:name="android.permission.READ_PHONE_STATE" />
4
4
  <uses-permission android:name="android.permission.RECORD_AUDIO"/>
5
5
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
6
6
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>
@@ -37,7 +37,8 @@ class AudioRecorderManager(
37
37
  private val filesDir: File,
38
38
  private val permissionUtils: PermissionUtils,
39
39
  private val audioDataEncoder: AudioDataEncoder,
40
- private val eventSender: EventSender
40
+ private val eventSender: EventSender,
41
+ private val enablePhoneStateHandling: Boolean = true
41
42
  ) {
42
43
  private var audioRecord: AudioRecord? = null
43
44
  private var bufferSizeInBytes = 0
@@ -348,8 +349,10 @@ class AudioRecorderManager(
348
349
  @RequiresApi(Build.VERSION_CODES.R)
349
350
  fun startRecording(options: Map<String, Any?>, promise: Promise) {
350
351
  try {
351
- // Initialize phone state listener
352
- initializePhoneStateListener()
352
+ // Initialize phone state listener only if enabled
353
+ if (enablePhoneStateHandling) {
354
+ initializePhoneStateListener()
355
+ }
353
356
 
354
357
  // Request audio focus
355
358
  if (!requestAudioFocus()) {
@@ -479,7 +482,8 @@ class AudioRecorderManager(
479
482
  return false
480
483
  }
481
484
 
482
- if (!permissionUtils.checkPhoneStatePermission()) {
485
+ // Only check phone state permission if enabled
486
+ if (enablePhoneStateHandling && !permissionUtils.checkPhoneStatePermission()) {
483
487
  Log.w(Constants.TAG, "READ_PHONE_STATE permission not granted, phone call interruption handling will be disabled")
484
488
  // Don't reject here, just log warning as this is optional
485
489
  }
@@ -16,6 +16,7 @@ import java.util.zip.CRC32
16
16
  class ExpoAudioStreamModule : Module(), EventSender {
17
17
  private lateinit var audioRecorderManager: AudioRecorderManager
18
18
  private lateinit var audioProcessor: AudioProcessor
19
+ private var enablePhoneStateHandling: Boolean = true // Default to true for backward compatibility
19
20
 
20
21
  private val audioFileHandler by lazy {
21
22
  AudioFileHandler(appContext.reactContext?.filesDir ?: throw IllegalStateException("React context not available"))
@@ -174,10 +175,14 @@ class ExpoAudioStreamModule : Module(), EventSender {
174
175
  AsyncFunction("requestPermissionsAsync") { promise: Promise ->
175
176
  try {
176
177
  val permissions = mutableListOf(
177
- Manifest.permission.RECORD_AUDIO,
178
- Manifest.permission.READ_PHONE_STATE
178
+ Manifest.permission.RECORD_AUDIO
179
179
  )
180
180
 
181
+ // Only add phone state permission if enabled
182
+ if (enablePhoneStateHandling) {
183
+ permissions.add(Manifest.permission.READ_PHONE_STATE)
184
+ }
185
+
181
186
  // Add foreground service permission for Android 14+
182
187
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
183
188
  Log.d(Constants.TAG, "Adding FOREGROUND_SERVICE_MICROPHONE permission request")
@@ -198,10 +203,14 @@ class ExpoAudioStreamModule : Module(), EventSender {
198
203
 
199
204
  AsyncFunction("getPermissionsAsync") { promise: Promise ->
200
205
  val permissions = mutableListOf(
201
- Manifest.permission.RECORD_AUDIO,
202
- Manifest.permission.READ_PHONE_STATE
206
+ Manifest.permission.RECORD_AUDIO
203
207
  )
204
208
 
209
+ // Only add phone state permission if enabled
210
+ if (enablePhoneStateHandling) {
211
+ permissions.add(Manifest.permission.READ_PHONE_STATE)
212
+ }
213
+
205
214
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
206
215
  permissions.add(Manifest.permission.FOREGROUND_SERVICE_MICROPHONE)
207
216
  }
@@ -714,20 +723,18 @@ class ExpoAudioStreamModule : Module(), EventSender {
714
723
  }
715
724
 
716
725
  private fun initializeManager() {
717
- val androidContext =
718
- appContext.reactContext ?: throw IllegalStateException("Android context not available")
719
- val permissionUtils = PermissionUtils(androidContext)
720
- val audioEncoder = AudioDataEncoder()
721
- audioRecorderManager =
722
- AudioRecorderManager(androidContext, androidContext.filesDir, permissionUtils, audioEncoder, this)
723
- audioRecorderManager = AudioRecorderManager.initialize(
724
- androidContext,
725
- androidContext.filesDir,
726
+ val filesDir = appContext.reactContext?.filesDir ?: throw IllegalStateException("React context not available")
727
+ val permissionUtils = PermissionUtils(appContext.reactContext!!)
728
+ val audioDataEncoder = AudioDataEncoder()
729
+ audioRecorderManager = AudioRecorderManager(
730
+ appContext.reactContext!!,
731
+ filesDir,
726
732
  permissionUtils,
727
- audioEncoder,
728
- this
733
+ audioDataEncoder,
734
+ this,
735
+ enablePhoneStateHandling
729
736
  )
730
- audioProcessor = AudioProcessor(androidContext.filesDir)
737
+ audioProcessor = AudioProcessor(filesDir)
731
738
  }
732
739
 
733
740
 
@@ -1,3 +1,9 @@
1
+ /**
2
+ * This module provides functions for extracting and analyzing audio data.
3
+ * - `extractAudioAnalysis`: For detailed analysis with customizable ranges and decoding options.
4
+ * - `extractWavAudioAnalysis`: For analyzing WAV files without decoding, preserving original PCM values.
5
+ * - `extractPreview`: For generating quick previews of audio waveforms, optimized for UI rendering.
6
+ */
1
7
  import { ConsoleLike } from '../ExpoAudioStream.types';
2
8
  import { AudioAnalysis, AudioFeaturesOptions, DecodingConfig } from './AudioAnalysis.types';
3
9
  import { WavFileInfo } from '../utils/getWavFileInfo';
@@ -1 +1 @@
1
- {"version":3,"file":"extractAudioAnalysis.d.ts","sourceRoot":"","sources":["../../src/AudioAnalysis/extractAudioAnalysis.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAGtD,OAAO,EACH,aAAa,EACb,oBAAoB,EAEpB,cAAc,EACjB,MAAM,uBAAuB,CAAA;AAG9B,OAAO,EAAkB,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAerE,MAAM,WAAW,4BAA4B;IACzC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,EAAE,oBAAoB,CAAA;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,eAAe,CAAC,EAAE,cAAc,CAAA;CACnC;AAGD,UAAU,kBAAkB;IACxB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,EAAE,oBAAoB,CAAA;IAC/B,eAAe,CAAC,EAAE,cAAc,CAAA;IAChC,MAAM,CAAC,EAAE,WAAW,CAAA;CACvB;AAGD,UAAU,gBAAiB,SAAQ,kBAAkB;IACjD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,KAAK,CAAA;IAChB,MAAM,CAAC,EAAE,KAAK,CAAA;CACjB;AAGD,UAAU,gBAAiB,SAAQ,kBAAkB;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,KAAK,CAAA;IACnB,SAAS,CAAC,EAAE,KAAK,CAAA;CACpB;AAED;;;;;GAKG;AACH,MAAM,MAAM,yBAAyB,GAAG,gBAAgB,GAAG,gBAAgB,CAAA;AAE3E;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACtC,KAAK,EAAE,yBAAyB,GACjC,OAAO,CAAC,aAAa,CAAC,CAkHxB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,yIAY/B,4BAA4B,KAAG,QAAQ,aAAa,CAkGtD,CAAA"}
1
+ {"version":3,"file":"extractAudioAnalysis.d.ts","sourceRoot":"","sources":["../../src/AudioAnalysis/extractAudioAnalysis.ts"],"names":[],"mappings":"AACA;;;;;GAKG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAGtD,OAAO,EACH,aAAa,EACb,oBAAoB,EAEpB,cAAc,EACjB,MAAM,uBAAuB,CAAA;AAI9B,OAAO,EAAkB,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAerE,MAAM,WAAW,4BAA4B;IACzC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,EAAE,oBAAoB,CAAA;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,eAAe,CAAC,EAAE,cAAc,CAAA;CACnC;AAGD,UAAU,kBAAkB;IACxB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,EAAE,oBAAoB,CAAA;IAC/B,eAAe,CAAC,EAAE,cAAc,CAAA;IAChC,MAAM,CAAC,EAAE,WAAW,CAAA;CACvB;AAGD,UAAU,gBAAiB,SAAQ,kBAAkB;IACjD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,KAAK,CAAA;IAChB,MAAM,CAAC,EAAE,KAAK,CAAA;CACjB;AAGD,UAAU,gBAAiB,SAAQ,kBAAkB;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,KAAK,CAAA;IACnB,SAAS,CAAC,EAAE,KAAK,CAAA;CACpB;AAED;;;;;GAKG;AACH,MAAM,MAAM,yBAAyB,GAAG,gBAAgB,GAAG,gBAAgB,CAAA;AAE3E;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACtC,KAAK,EAAE,yBAAyB,GACjC,OAAO,CAAC,aAAa,CAAC,CAkHxB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,yIAY/B,4BAA4B,KAAG,QAAQ,aAAa,CAkGtD,CAAA"}
@@ -1,15 +1,8 @@
1
- // packages/expo-audio-stream/src/AudioAnalysis/extractAudioAnalysis.ts
2
- /**
3
- * This module provides functions for extracting and analyzing audio data.
4
- * - `extractAudioAnalysis`: For detailed analysis with customizable ranges and decoding options.
5
- * - `extractWavAudioAnalysis`: For analyzing WAV files without decoding, preserving original PCM values.
6
- * - `extractPreview`: For generating quick previews of audio waveforms, optimized for UI rendering.
7
- */
8
- import crc32 from 'crc-32';
9
1
  import ExpoAudioStreamModule from '../ExpoAudioStreamModule';
10
2
  import { isWeb } from '../constants';
11
3
  import { processAudioBuffer } from '../utils/audioProcessing';
12
4
  import { convertPCMToFloat32 } from '../utils/convertPCMToFloat32';
5
+ import crc32 from '../utils/crc32';
13
6
  import { getWavFileInfo } from '../utils/getWavFileInfo';
14
7
  import { InlineFeaturesExtractor } from '../workers/InlineFeaturesExtractor.web';
15
8
  function calculateCRC32ForDataPoint(data) {
@@ -1 +1 @@
1
- {"version":3,"file":"extractAudioAnalysis.js","sourceRoot":"","sources":["../../src/AudioAnalysis/extractAudioAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/expo-audio-stream/src/AudioAnalysis/extractAudioAnalysis.ts\n/**\n * This module provides functions for extracting and analyzing audio data.\n * - `extractAudioAnalysis`: For detailed analysis with customizable ranges and decoding options.\n * - `extractWavAudioAnalysis`: For analyzing WAV files without decoding, preserving original PCM values.\n * - `extractPreview`: For generating quick previews of audio waveforms, optimized for UI rendering.\n */\nimport crc32 from 'crc-32'\n\nimport { ConsoleLike } from '../ExpoAudioStream.types'\nimport ExpoAudioStreamModule from '../ExpoAudioStreamModule'\nimport { isWeb } from '../constants'\nimport {\n AudioAnalysis,\n AudioFeaturesOptions,\n DataPoint,\n DecodingConfig,\n} from './AudioAnalysis.types'\nimport { processAudioBuffer } from '../utils/audioProcessing'\nimport { convertPCMToFloat32 } from '../utils/convertPCMToFloat32'\nimport { getWavFileInfo, WavFileInfo } from '../utils/getWavFileInfo'\nimport { InlineFeaturesExtractor } from '../workers/InlineFeaturesExtractor.web'\n\nfunction calculateCRC32ForDataPoint(data: Float32Array): number {\n // Convert float array to byte array for CRC32\n const byteArray = new Uint8Array(data.length * 4)\n const dataView = new DataView(byteArray.buffer)\n\n for (let i = 0; i < data.length; i++) {\n dataView.setFloat32(i * 4, data[i], true)\n }\n\n return crc32.buf(byteArray)\n}\n\nexport interface ExtractWavAudioAnalysisProps {\n fileUri?: string // should provide either fileUri or arrayBuffer\n wavMetadata?: WavFileInfo\n arrayBuffer?: ArrayBuffer\n bitDepth?: number\n durationMs?: number\n sampleRate?: number\n numberOfChannels?: number\n position?: number // Optional number of bytes to skip. Default is 0\n length?: number // Optional number of bytes to read.\n segmentDurationMs?: number // Optional number of points per second. Use to reduce the number of points and compute the number of datapoints to return.\n features?: AudioFeaturesOptions\n featuresExtratorUrl?: string\n logger?: ConsoleLike\n decodingOptions?: DecodingConfig\n}\n\n// Define base options interface with common properties\ninterface BaseExtractOptions {\n fileUri?: string\n arrayBuffer?: ArrayBuffer\n /**\n * Duration of each analysis segment in milliseconds. Defaults to 100ms if not specified.\n */\n segmentDurationMs?: number\n features?: AudioFeaturesOptions\n decodingOptions?: DecodingConfig\n logger?: ConsoleLike\n}\n\n// Time-based range options\ninterface TimeRangeOptions extends BaseExtractOptions {\n startTimeMs?: number\n endTimeMs?: number\n position?: never\n length?: never\n}\n\n// Byte-based range options\ninterface ByteRangeOptions extends BaseExtractOptions {\n position?: number\n length?: number\n startTimeMs?: never\n endTimeMs?: never\n}\n\n/**\n * Options for extracting audio analysis.\n * - For time-based analysis, provide `startTimeMs` and `endTimeMs`.\n * - For byte-based analysis, provide `position` and `length`.\n * - Do not mix time and byte ranges.\n */\nexport type ExtractAudioAnalysisProps = TimeRangeOptions | ByteRangeOptions\n\n/**\n * Extracts detailed audio analysis from the specified audio file or buffer.\n * Supports either time-based or byte-based ranges for flexibility in analysis.\n *\n * @param props - The options for extraction, including file URI, ranges, and decoding settings.\n * @returns A promise that resolves to the audio analysis data.\n * @throws {Error} If both time and byte ranges are provided or if required parameters are missing.\n */\nexport async function extractAudioAnalysis(\n props: ExtractAudioAnalysisProps\n): Promise<AudioAnalysis> {\n const {\n fileUri,\n arrayBuffer,\n decodingOptions,\n logger,\n segmentDurationMs = 100,\n features,\n } = props\n\n if (isWeb) {\n try {\n // Create AudioContext here\n const audioContext = new (window.AudioContext ||\n (window as any).webkitAudioContext)({\n sampleRate: decodingOptions?.targetSampleRate ?? 16000,\n })\n\n try {\n const processedBuffer = await processAudioBuffer({\n arrayBuffer,\n fileUri,\n targetSampleRate:\n decodingOptions?.targetSampleRate ?? 16000,\n targetChannels: decodingOptions?.targetChannels ?? 1,\n normalizeAudio: decodingOptions?.normalizeAudio ?? false,\n startTimeMs:\n 'startTimeMs' in props ? props.startTimeMs : undefined,\n endTimeMs:\n 'endTimeMs' in props ? props.endTimeMs : undefined,\n position: 'position' in props ? props.position : undefined,\n length: 'length' in props ? props.length : undefined,\n audioContext, // Pass the context we created\n logger,\n })\n\n const channelData = processedBuffer.buffer.getChannelData(0)\n\n // Create and initialize the worker\n const blob = new Blob([InlineFeaturesExtractor], {\n type: 'application/javascript',\n })\n const workerUrl = URL.createObjectURL(blob)\n const worker = new Worker(workerUrl)\n\n return new Promise((resolve, reject) => {\n worker.onmessage = (event) => {\n if (event.data.error) {\n reject(new Error(event.data.error))\n return\n }\n\n const result: AudioAnalysis = event.data.result\n // Calculate CRC32 after worker completes if requested\n if (features?.crc32) {\n const samplesPerSegment = Math.floor(\n (processedBuffer.sampleRate *\n segmentDurationMs) /\n 1000\n )\n\n result.dataPoints = result.dataPoints.map(\n (point: DataPoint, index: number) => {\n const startSample =\n index * samplesPerSegment\n const segmentData = channelData.slice(\n startSample,\n startSample + samplesPerSegment\n )\n\n return {\n ...point,\n features: {\n ...point.features,\n crc32: calculateCRC32ForDataPoint(\n segmentData\n ),\n },\n }\n }\n )\n }\n\n URL.revokeObjectURL(workerUrl)\n worker.terminate()\n resolve(result)\n }\n\n worker.onerror = (error) => {\n URL.revokeObjectURL(workerUrl)\n worker.terminate()\n reject(error)\n }\n\n worker.postMessage({\n channelData,\n sampleRate: processedBuffer.sampleRate,\n segmentDurationMs,\n bitDepth: decodingOptions?.targetBitDepth ?? 32,\n numberOfChannels: processedBuffer.channels,\n // enableLogging: !!logger,\n features,\n })\n })\n } finally {\n await audioContext.close()\n }\n } catch (error) {\n logger?.error('Failed to process audio:', error)\n throw error\n }\n } else {\n return await ExpoAudioStreamModule.extractAudioAnalysis(props)\n }\n}\n\n/**\n * Analyzes WAV files without decoding, preserving original PCM values.\n * Use this function when you need to ensure the analysis matches other software by avoiding any transformations.\n *\n * @param props - The options for WAV analysis, including file URI and range.\n * @returns A promise that resolves to the audio analysis data.\n */\nexport const extractRawWavAnalysis = async ({\n fileUri,\n segmentDurationMs = 100, // Default to 100ms\n arrayBuffer,\n bitDepth,\n durationMs,\n sampleRate,\n numberOfChannels,\n features,\n logger,\n position = 0,\n length,\n}: ExtractWavAudioAnalysisProps): Promise<AudioAnalysis> => {\n if (isWeb) {\n if (!arrayBuffer && !fileUri) {\n throw new Error('Either arrayBuffer or fileUri must be provided')\n }\n\n if (!arrayBuffer) {\n logger?.log(`fetching fileUri`, fileUri)\n const response = await fetch(fileUri!)\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch fileUri: ${response.statusText}`\n )\n }\n\n arrayBuffer = await response.arrayBuffer()\n logger?.log(`fetched fileUri`, arrayBuffer.byteLength, arrayBuffer)\n }\n\n // Create a new copy of the ArrayBuffer to avoid detachment issues\n const bufferCopy = arrayBuffer.slice(0)\n logger?.log(\n `extractAudioAnalysis bitDepth=${bitDepth} len=${bufferCopy.byteLength}`,\n bufferCopy.slice(0, 100)\n )\n\n let actualBitDepth = bitDepth\n if (!actualBitDepth) {\n logger?.log(\n `extractAudioAnalysis bitDepth not provided -- getting wav file info`\n )\n const fileInfo = await getWavFileInfo(bufferCopy)\n actualBitDepth = fileInfo.bitDepth\n }\n logger?.log(`extractAudioAnalysis actualBitDepth=${actualBitDepth}`)\n\n const {\n pcmValues: channelData,\n min,\n max,\n } = await convertPCMToFloat32({\n buffer: arrayBuffer,\n bitDepth: actualBitDepth,\n })\n logger?.log(\n `extractAudioAnalysis convertPCMToFloat32 length=${channelData.length} range: [ ${min} :: ${max} ]`\n )\n\n // Apply position and length constraints to channelData if specified\n const startIndex = position\n const endIndex = length ? startIndex + length : channelData.length\n const constrainedChannelData = channelData.slice(startIndex, endIndex)\n\n return new Promise((resolve, reject) => {\n const blob = new Blob([InlineFeaturesExtractor], {\n type: 'application/javascript',\n })\n const url = URL.createObjectURL(blob)\n const worker = new Worker(url)\n\n worker.onmessage = (event) => {\n resolve(event.data.result)\n }\n\n worker.onerror = (error) => {\n reject(error)\n }\n\n worker.postMessage({\n command: 'process',\n channelData: constrainedChannelData,\n sampleRate,\n segmentDurationMs,\n logger,\n bitDepth,\n fullAudioDurationMs: durationMs,\n numberOfChannels,\n })\n })\n } else {\n if (!fileUri) {\n throw new Error('fileUri is required')\n }\n logger?.log(`extractAudioAnalysis`, {\n fileUri,\n segmentDurationMs,\n })\n const res = await ExpoAudioStreamModule.extractAudioAnalysis({\n fileUri,\n segmentDurationMs,\n features,\n position,\n length,\n })\n logger?.log(`extractAudioAnalysis`, res)\n return res\n }\n}\n"]}
1
+ {"version":3,"file":"extractAudioAnalysis.js","sourceRoot":"","sources":["../../src/AudioAnalysis/extractAudioAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/expo-audio-stream/src/AudioAnalysis/extractAudioAnalysis.ts\n/**\n * This module provides functions for extracting and analyzing audio data.\n * - `extractAudioAnalysis`: For detailed analysis with customizable ranges and decoding options.\n * - `extractWavAudioAnalysis`: For analyzing WAV files without decoding, preserving original PCM values.\n * - `extractPreview`: For generating quick previews of audio waveforms, optimized for UI rendering.\n */\nimport { ConsoleLike } from '../ExpoAudioStream.types'\nimport ExpoAudioStreamModule from '../ExpoAudioStreamModule'\nimport { isWeb } from '../constants'\nimport {\n AudioAnalysis,\n AudioFeaturesOptions,\n DataPoint,\n DecodingConfig,\n} from './AudioAnalysis.types'\nimport { processAudioBuffer } from '../utils/audioProcessing'\nimport { convertPCMToFloat32 } from '../utils/convertPCMToFloat32'\nimport crc32 from '../utils/crc32'\nimport { getWavFileInfo, WavFileInfo } from '../utils/getWavFileInfo'\nimport { InlineFeaturesExtractor } from '../workers/InlineFeaturesExtractor.web'\n\nfunction calculateCRC32ForDataPoint(data: Float32Array): number {\n // Convert float array to byte array for CRC32\n const byteArray = new Uint8Array(data.length * 4)\n const dataView = new DataView(byteArray.buffer)\n\n for (let i = 0; i < data.length; i++) {\n dataView.setFloat32(i * 4, data[i], true)\n }\n\n return crc32.buf(byteArray)\n}\n\nexport interface ExtractWavAudioAnalysisProps {\n fileUri?: string // should provide either fileUri or arrayBuffer\n wavMetadata?: WavFileInfo\n arrayBuffer?: ArrayBuffer\n bitDepth?: number\n durationMs?: number\n sampleRate?: number\n numberOfChannels?: number\n position?: number // Optional number of bytes to skip. Default is 0\n length?: number // Optional number of bytes to read.\n segmentDurationMs?: number // Optional number of points per second. Use to reduce the number of points and compute the number of datapoints to return.\n features?: AudioFeaturesOptions\n featuresExtratorUrl?: string\n logger?: ConsoleLike\n decodingOptions?: DecodingConfig\n}\n\n// Define base options interface with common properties\ninterface BaseExtractOptions {\n fileUri?: string\n arrayBuffer?: ArrayBuffer\n /**\n * Duration of each analysis segment in milliseconds. Defaults to 100ms if not specified.\n */\n segmentDurationMs?: number\n features?: AudioFeaturesOptions\n decodingOptions?: DecodingConfig\n logger?: ConsoleLike\n}\n\n// Time-based range options\ninterface TimeRangeOptions extends BaseExtractOptions {\n startTimeMs?: number\n endTimeMs?: number\n position?: never\n length?: never\n}\n\n// Byte-based range options\ninterface ByteRangeOptions extends BaseExtractOptions {\n position?: number\n length?: number\n startTimeMs?: never\n endTimeMs?: never\n}\n\n/**\n * Options for extracting audio analysis.\n * - For time-based analysis, provide `startTimeMs` and `endTimeMs`.\n * - For byte-based analysis, provide `position` and `length`.\n * - Do not mix time and byte ranges.\n */\nexport type ExtractAudioAnalysisProps = TimeRangeOptions | ByteRangeOptions\n\n/**\n * Extracts detailed audio analysis from the specified audio file or buffer.\n * Supports either time-based or byte-based ranges for flexibility in analysis.\n *\n * @param props - The options for extraction, including file URI, ranges, and decoding settings.\n * @returns A promise that resolves to the audio analysis data.\n * @throws {Error} If both time and byte ranges are provided or if required parameters are missing.\n */\nexport async function extractAudioAnalysis(\n props: ExtractAudioAnalysisProps\n): Promise<AudioAnalysis> {\n const {\n fileUri,\n arrayBuffer,\n decodingOptions,\n logger,\n segmentDurationMs = 100,\n features,\n } = props\n\n if (isWeb) {\n try {\n // Create AudioContext here\n const audioContext = new (window.AudioContext ||\n (window as any).webkitAudioContext)({\n sampleRate: decodingOptions?.targetSampleRate ?? 16000,\n })\n\n try {\n const processedBuffer = await processAudioBuffer({\n arrayBuffer,\n fileUri,\n targetSampleRate:\n decodingOptions?.targetSampleRate ?? 16000,\n targetChannels: decodingOptions?.targetChannels ?? 1,\n normalizeAudio: decodingOptions?.normalizeAudio ?? false,\n startTimeMs:\n 'startTimeMs' in props ? props.startTimeMs : undefined,\n endTimeMs:\n 'endTimeMs' in props ? props.endTimeMs : undefined,\n position: 'position' in props ? props.position : undefined,\n length: 'length' in props ? props.length : undefined,\n audioContext, // Pass the context we created\n logger,\n })\n\n const channelData = processedBuffer.buffer.getChannelData(0)\n\n // Create and initialize the worker\n const blob = new Blob([InlineFeaturesExtractor], {\n type: 'application/javascript',\n })\n const workerUrl = URL.createObjectURL(blob)\n const worker = new Worker(workerUrl)\n\n return new Promise((resolve, reject) => {\n worker.onmessage = (event) => {\n if (event.data.error) {\n reject(new Error(event.data.error))\n return\n }\n\n const result: AudioAnalysis = event.data.result\n // Calculate CRC32 after worker completes if requested\n if (features?.crc32) {\n const samplesPerSegment = Math.floor(\n (processedBuffer.sampleRate *\n segmentDurationMs) /\n 1000\n )\n\n result.dataPoints = result.dataPoints.map(\n (point: DataPoint, index: number) => {\n const startSample =\n index * samplesPerSegment\n const segmentData = channelData.slice(\n startSample,\n startSample + samplesPerSegment\n )\n\n return {\n ...point,\n features: {\n ...point.features,\n crc32: calculateCRC32ForDataPoint(\n segmentData\n ),\n },\n }\n }\n )\n }\n\n URL.revokeObjectURL(workerUrl)\n worker.terminate()\n resolve(result)\n }\n\n worker.onerror = (error) => {\n URL.revokeObjectURL(workerUrl)\n worker.terminate()\n reject(error)\n }\n\n worker.postMessage({\n channelData,\n sampleRate: processedBuffer.sampleRate,\n segmentDurationMs,\n bitDepth: decodingOptions?.targetBitDepth ?? 32,\n numberOfChannels: processedBuffer.channels,\n // enableLogging: !!logger,\n features,\n })\n })\n } finally {\n await audioContext.close()\n }\n } catch (error) {\n logger?.error('Failed to process audio:', error)\n throw error\n }\n } else {\n return await ExpoAudioStreamModule.extractAudioAnalysis(props)\n }\n}\n\n/**\n * Analyzes WAV files without decoding, preserving original PCM values.\n * Use this function when you need to ensure the analysis matches other software by avoiding any transformations.\n *\n * @param props - The options for WAV analysis, including file URI and range.\n * @returns A promise that resolves to the audio analysis data.\n */\nexport const extractRawWavAnalysis = async ({\n fileUri,\n segmentDurationMs = 100, // Default to 100ms\n arrayBuffer,\n bitDepth,\n durationMs,\n sampleRate,\n numberOfChannels,\n features,\n logger,\n position = 0,\n length,\n}: ExtractWavAudioAnalysisProps): Promise<AudioAnalysis> => {\n if (isWeb) {\n if (!arrayBuffer && !fileUri) {\n throw new Error('Either arrayBuffer or fileUri must be provided')\n }\n\n if (!arrayBuffer) {\n logger?.log(`fetching fileUri`, fileUri)\n const response = await fetch(fileUri!)\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch fileUri: ${response.statusText}`\n )\n }\n\n arrayBuffer = await response.arrayBuffer()\n logger?.log(`fetched fileUri`, arrayBuffer.byteLength, arrayBuffer)\n }\n\n // Create a new copy of the ArrayBuffer to avoid detachment issues\n const bufferCopy = arrayBuffer.slice(0)\n logger?.log(\n `extractAudioAnalysis bitDepth=${bitDepth} len=${bufferCopy.byteLength}`,\n bufferCopy.slice(0, 100)\n )\n\n let actualBitDepth = bitDepth\n if (!actualBitDepth) {\n logger?.log(\n `extractAudioAnalysis bitDepth not provided -- getting wav file info`\n )\n const fileInfo = await getWavFileInfo(bufferCopy)\n actualBitDepth = fileInfo.bitDepth\n }\n logger?.log(`extractAudioAnalysis actualBitDepth=${actualBitDepth}`)\n\n const {\n pcmValues: channelData,\n min,\n max,\n } = await convertPCMToFloat32({\n buffer: arrayBuffer,\n bitDepth: actualBitDepth,\n })\n logger?.log(\n `extractAudioAnalysis convertPCMToFloat32 length=${channelData.length} range: [ ${min} :: ${max} ]`\n )\n\n // Apply position and length constraints to channelData if specified\n const startIndex = position\n const endIndex = length ? startIndex + length : channelData.length\n const constrainedChannelData = channelData.slice(startIndex, endIndex)\n\n return new Promise((resolve, reject) => {\n const blob = new Blob([InlineFeaturesExtractor], {\n type: 'application/javascript',\n })\n const url = URL.createObjectURL(blob)\n const worker = new Worker(url)\n\n worker.onmessage = (event) => {\n resolve(event.data.result)\n }\n\n worker.onerror = (error) => {\n reject(error)\n }\n\n worker.postMessage({\n command: 'process',\n channelData: constrainedChannelData,\n sampleRate,\n segmentDurationMs,\n logger,\n bitDepth,\n fullAudioDurationMs: durationMs,\n numberOfChannels,\n })\n })\n } else {\n if (!fileUri) {\n throw new Error('fileUri is required')\n }\n logger?.log(`extractAudioAnalysis`, {\n fileUri,\n segmentDurationMs,\n })\n const res = await ExpoAudioStreamModule.extractAudioAnalysis({\n fileUri,\n segmentDurationMs,\n features,\n position,\n length,\n })\n logger?.log(`extractAudioAnalysis`, res)\n return res\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"extractMelSpectrogram.js","sourceRoot":"","sources":["../../src/AudioAnalysis/extractMelSpectrogram.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @experimental This feature is experimental and currently only available on Android.\n * The API may change in future versions. The web implementation is a placeholder.\n */\n\nimport { ExpoAudioStreamModule } from '..'\nimport { isWeb } from '../constants'\nimport {\n ExtractMelSpectrogramOptions,\n MelSpectrogram,\n} from './AudioAnalysis.types'\nimport {\n processAudioBuffer,\n ProcessedAudioData,\n} from '../utils/audioProcessing'\n\n/**\n * Extracts a mel spectrogram from audio data\n * \n * @experimental This feature is experimental and currently only available on Android.\n * The iOS implementation will throw an \"UNSUPPORTED_PLATFORM\" error.\n * The web implementation is a placeholder that returns dummy data.\n */\nexport async function extractMelSpectrogram(\n options: ExtractMelSpectrogramOptions\n): Promise<MelSpectrogram> {\n const {\n fileUri,\n arrayBuffer,\n windowSizeMs,\n hopLengthMs,\n nMels,\n fMin = 0,\n fMax,\n windowType = 'hann',\n normalize = false,\n logScale = true,\n decodingOptions,\n startTimeMs,\n endTimeMs,\n logger,\n } = options\n\n if (isWeb) {\n // Create audio context\n const audioContext = new (window.AudioContext ||\n (window as any).webkitAudioContext)()\n\n try {\n // Process audio data using the existing utility\n const processedAudio: ProcessedAudioData = await processAudioBuffer(\n {\n arrayBuffer,\n fileUri,\n targetSampleRate:\n decodingOptions?.targetSampleRate || 16000,\n targetChannels: decodingOptions?.targetChannels || 1,\n normalizeAudio: decodingOptions?.normalizeAudio ?? false,\n startTimeMs,\n endTimeMs,\n audioContext,\n logger: options.logger,\n }\n )\n\n // Calculate window and hop size in samples\n const sampleRate = processedAudio.sampleRate\n const windowSize = Math.floor((windowSizeMs * sampleRate) / 1000)\n const hopLength = Math.floor((hopLengthMs * sampleRate) / 1000)\n const maxFreq = fMax || sampleRate / 2\n\n // Extract the mel spectrogram from the processed audio\n const spectrogram = computeMelSpectrogram(\n processedAudio.channelData,\n sampleRate,\n nMels,\n windowSize,\n hopLength,\n fMin,\n maxFreq,\n windowType,\n normalize,\n logScale\n )\n\n const timeSteps = spectrogram.length\n\n return {\n spectrogram,\n sampleRate,\n nMels,\n timeSteps,\n durationMs: processedAudio.durationMs,\n }\n } catch (error) {\n logger?.error('Error extracting mel spectrogram:', error)\n throw error\n } finally {\n // Close the audio context\n await audioContext.close()\n }\n }\n return ExpoAudioStreamModule.extractMelSpectrogram(options)\n}\n\n/**\n * Computes a mel spectrogram from audio data\n * \n * @experimental This is a placeholder implementation that returns dummy data.\n * The actual implementation will be added in a future version.\n */\nfunction computeMelSpectrogram(\n audioData: Float32Array,\n sampleRate: number,\n nMels: number,\n windowSize: number,\n hopLength: number,\n fMin: number,\n fMax: number,\n windowType: 'hann' | 'hamming',\n normalize: boolean,\n logScale: boolean\n): number[][] {\n // Placeholder for the actual implementation\n // This would include:\n // 1. Windowing the audio data using the specified window type\n // 2. Computing the STFT (Short-Time Fourier Transform)\n // 3. Converting to power spectrogram\n // 4. Applying mel filterbanks\n // 5. Taking the logarithm if logScale is true\n // 6. Normalizing if normalize is true\n\n // For now, return a dummy implementation\n const numFrames =\n Math.floor((audioData.length - windowSize) / hopLength) + 1\n const spectrogram: number[][] = []\n\n // Create dummy mel spectrogram data\n for (let i = 0; i < numFrames; i++) {\n spectrogram.push(Array(nMels).fill(0))\n }\n\n return spectrogram\n}\n"]}
1
+ {"version":3,"file":"extractMelSpectrogram.js","sourceRoot":"","sources":["../../src/AudioAnalysis/extractMelSpectrogram.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @experimental This feature is experimental and currently only available on Android.\n * The API may change in future versions. The web implementation is a placeholder.\n */\n\nimport { ExpoAudioStreamModule } from '..'\nimport { isWeb } from '../constants'\nimport {\n ExtractMelSpectrogramOptions,\n MelSpectrogram,\n} from './AudioAnalysis.types'\nimport {\n processAudioBuffer,\n ProcessedAudioData,\n} from '../utils/audioProcessing'\n\n/**\n * Extracts a mel spectrogram from audio data\n *\n * @experimental This feature is experimental and currently only available on Android.\n * The iOS implementation will throw an \"UNSUPPORTED_PLATFORM\" error.\n * The web implementation is a placeholder that returns dummy data.\n */\nexport async function extractMelSpectrogram(\n options: ExtractMelSpectrogramOptions\n): Promise<MelSpectrogram> {\n const {\n fileUri,\n arrayBuffer,\n windowSizeMs,\n hopLengthMs,\n nMels,\n fMin = 0,\n fMax,\n windowType = 'hann',\n normalize = false,\n logScale = true,\n decodingOptions,\n startTimeMs,\n endTimeMs,\n logger,\n } = options\n\n if (isWeb) {\n // Create audio context\n const audioContext = new (window.AudioContext ||\n (window as any).webkitAudioContext)()\n\n try {\n // Process audio data using the existing utility\n const processedAudio: ProcessedAudioData = await processAudioBuffer(\n {\n arrayBuffer,\n fileUri,\n targetSampleRate:\n decodingOptions?.targetSampleRate || 16000,\n targetChannels: decodingOptions?.targetChannels || 1,\n normalizeAudio: decodingOptions?.normalizeAudio ?? false,\n startTimeMs,\n endTimeMs,\n audioContext,\n logger: options.logger,\n }\n )\n\n // Calculate window and hop size in samples\n const sampleRate = processedAudio.sampleRate\n const windowSize = Math.floor((windowSizeMs * sampleRate) / 1000)\n const hopLength = Math.floor((hopLengthMs * sampleRate) / 1000)\n const maxFreq = fMax || sampleRate / 2\n\n // Extract the mel spectrogram from the processed audio\n const spectrogram = computeMelSpectrogram(\n processedAudio.channelData,\n sampleRate,\n nMels,\n windowSize,\n hopLength,\n fMin,\n maxFreq,\n windowType,\n normalize,\n logScale\n )\n\n const timeSteps = spectrogram.length\n\n return {\n spectrogram,\n sampleRate,\n nMels,\n timeSteps,\n durationMs: processedAudio.durationMs,\n }\n } catch (error) {\n logger?.error('Error extracting mel spectrogram:', error)\n throw error\n } finally {\n // Close the audio context\n await audioContext.close()\n }\n }\n return ExpoAudioStreamModule.extractMelSpectrogram(options)\n}\n\n/**\n * Computes a mel spectrogram from audio data\n *\n * @experimental This is a placeholder implementation that returns dummy data.\n * The actual implementation will be added in a future version.\n */\nfunction computeMelSpectrogram(\n audioData: Float32Array,\n sampleRate: number,\n nMels: number,\n windowSize: number,\n hopLength: number,\n fMin: number,\n fMax: number,\n windowType: 'hann' | 'hamming',\n normalize: boolean,\n logScale: boolean\n): number[][] {\n // Placeholder for the actual implementation\n // This would include:\n // 1. Windowing the audio data using the specified window type\n // 2. Computing the STFT (Short-Time Fourier Transform)\n // 3. Converting to power spectrogram\n // 4. Applying mel filterbanks\n // 5. Taking the logarithm if logScale is true\n // 6. Normalizing if normalize is true\n\n // For now, return a dummy implementation\n const numFrames =\n Math.floor((audioData.length - windowSize) / hopLength) + 1\n const spectrogram: number[][] = []\n\n // Create dummy mel spectrogram data\n for (let i = 0; i < numFrames; i++) {\n spectrogram.push(Array(nMels).fill(0))\n }\n\n return spectrogram\n}\n"]}
@@ -1,8 +1,8 @@
1
- import crc32 from 'crc-32';
2
1
  import { requireNativeModule } from 'expo-modules-core';
3
2
  import { Platform } from 'react-native';
4
3
  import { ExpoAudioStreamWeb, } from './ExpoAudioStream.web';
5
4
  import { processAudioBuffer } from './utils/audioProcessing';
5
+ import crc32 from './utils/crc32';
6
6
  import { writeWavHeader } from './utils/writeWavHeader';
7
7
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
8
  let ExpoAudioStreamModule;
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoAudioStreamModule.js","sourceRoot":"","sources":["../src/ExpoAudioStreamModule.ts"],"names":[],"mappings":"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","sourcesContent":["import crc32 from 'crc-32'\nimport { requireNativeModule } from 'expo-modules-core'\nimport { Platform } from 'react-native'\n\nimport {\n ExtractAudioDataOptions,\n ExtractedAudioData,\n BitDepth,\n TrimAudioOptions,\n TrimAudioResult,\n} from './ExpoAudioStream.types'\nimport {\n ExpoAudioStreamWeb,\n ExpoAudioStreamWebProps,\n} from './ExpoAudioStream.web'\nimport { processAudioBuffer } from './utils/audioProcessing'\nimport { writeWavHeader } from './utils/writeWavHeader'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet ExpoAudioStreamModule: any\n\nif (Platform.OS === 'web') {\n let instance: ExpoAudioStreamWeb | null = null\n\n ExpoAudioStreamModule = (webProps: ExpoAudioStreamWebProps) => {\n if (!instance) {\n instance = new ExpoAudioStreamWeb(webProps)\n }\n return instance\n }\n ExpoAudioStreamModule.requestPermissionsAsync = async () => {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n })\n stream.getTracks().forEach((track) => track.stop())\n return {\n status: 'granted',\n expires: 'never',\n canAskAgain: true,\n granted: true,\n }\n } catch {\n return {\n status: 'denied',\n expires: 'never',\n canAskAgain: true,\n granted: false,\n }\n }\n }\n ExpoAudioStreamModule.getPermissionsAsync = async () => {\n let maybeStatus: string | null = null\n\n if (navigator?.permissions?.query) {\n try {\n const { state } = await navigator.permissions.query({\n name: 'microphone' as PermissionName,\n })\n maybeStatus = state\n } catch {\n maybeStatus = null\n }\n }\n\n switch (maybeStatus) {\n case 'granted':\n return {\n status: 'granted',\n expires: 'never',\n canAskAgain: true,\n granted: true,\n }\n case 'denied':\n return {\n status: 'denied',\n expires: 'never',\n canAskAgain: true,\n granted: false,\n }\n default:\n return await ExpoAudioStreamModule.requestPermissionsAsync()\n }\n }\n ExpoAudioStreamModule.extractAudioData = async (\n options: ExtractAudioDataOptions\n ): Promise<ExtractedAudioData> => {\n try {\n const {\n fileUri,\n position,\n length,\n startTimeMs,\n endTimeMs,\n decodingOptions,\n includeNormalizedData,\n includeBase64Data,\n includeWavHeader = false,\n logger,\n } = options\n\n logger?.debug('EXTRACT AUDIO - Step 1: Initial request', {\n fileUri,\n extractionParams: {\n position,\n length,\n startTimeMs,\n endTimeMs,\n },\n decodingOptions: {\n targetSampleRate:\n decodingOptions?.targetSampleRate ?? 16000,\n targetChannels: decodingOptions?.targetChannels ?? 1,\n targetBitDepth: decodingOptions?.targetBitDepth ?? 16,\n normalizeAudio: decodingOptions?.normalizeAudio ?? false,\n },\n outputOptions: {\n includeNormalizedData,\n includeBase64Data,\n includeWavHeader,\n },\n })\n\n // Process the audio using shared helper function\n const processedBuffer = await processAudioBuffer({\n fileUri,\n targetSampleRate: decodingOptions?.targetSampleRate ?? 16000,\n targetChannels: decodingOptions?.targetChannels ?? 1,\n normalizeAudio: decodingOptions?.normalizeAudio ?? false,\n position,\n length,\n startTimeMs,\n endTimeMs,\n logger,\n })\n\n logger?.debug('EXTRACT AUDIO - Step 2: Audio processing complete', {\n processedData: {\n samples: processedBuffer.samples,\n sampleRate: processedBuffer.sampleRate,\n channels: processedBuffer.channels,\n durationMs: processedBuffer.durationMs,\n },\n })\n\n const channelData = processedBuffer.channelData\n const bitDepth = (decodingOptions?.targetBitDepth ?? 16) as BitDepth\n const bytesPerSample = bitDepth / 8\n const numSamples = processedBuffer.samples\n\n logger?.debug('EXTRACT AUDIO - Step 3: PCM conversion setup', {\n channelData: {\n length: channelData.length,\n first: channelData[0],\n last: channelData[channelData.length - 1],\n },\n calculation: {\n bitDepth,\n bytesPerSample,\n numSamples,\n expectedBytes: numSamples * bytesPerSample,\n },\n })\n\n // Create PCM data with correct length based on original byte length\n const pcmData = new Uint8Array(numSamples * bytesPerSample)\n let offset = 0\n\n // Convert Float32 samples to PCM format\n for (let i = 0; i < numSamples; i++) {\n const sample = channelData[i]\n const value = Math.max(-1, Math.min(1, sample))\n // Convert to 16-bit signed integer\n let intValue = Math.round(value * 32767)\n\n // Handle negative values correctly\n if (intValue < 0) {\n intValue = 65536 + intValue\n }\n\n // Write as little-endian\n pcmData[offset++] = intValue & 255 // Low byte\n pcmData[offset++] = (intValue >> 8) & 255 // High byte\n }\n\n const durationMs = Math.round(\n (numSamples / processedBuffer.sampleRate) * 1000\n )\n\n logger?.debug('EXTRACT AUDIO - Step 4: Final output', {\n pcmData: {\n length: pcmData.length,\n first: pcmData[0],\n last: pcmData[pcmData.length - 1],\n },\n timing: {\n numSamples,\n sampleRate: processedBuffer.sampleRate,\n durationMs,\n shouldBe3000ms: endTimeMs\n ? endTimeMs - (startTimeMs ?? 0) === 3000\n : undefined,\n },\n })\n\n const result: ExtractedAudioData = {\n pcmData: new Uint8Array(pcmData.buffer),\n sampleRate: processedBuffer.sampleRate,\n channels: processedBuffer.channels,\n bitDepth,\n durationMs,\n format: `pcm_${bitDepth}bit` as const,\n samples: numSamples,\n }\n\n // Add WAV header if requested\n if (includeWavHeader) {\n logger?.debug('EXTRACT AUDIO - Step 4: Adding WAV header', {\n originalLength: pcmData.length,\n newLength: result.pcmData.length,\n firstBytes: Array.from(result.pcmData.slice(0, 44)), // WAV header is 44 bytes\n })\n const wavBuffer = writeWavHeader({\n buffer: pcmData.buffer.slice(0, pcmData.length),\n sampleRate: processedBuffer.sampleRate,\n numChannels: processedBuffer.channels,\n bitDepth,\n })\n result.pcmData = new Uint8Array(wavBuffer)\n result.hasWavHeader = true\n }\n\n if (includeNormalizedData) {\n // // Simple approach: Create normalized data directly from the PCM data\n // // Just convert to -1 to 1 range without any amplification\n // const normalizedData = new Float32Array(numSamples)\n\n // // Convert the PCM data to float values\n // for (let i = 0; i < numSamples; i++) {\n // // Get the 16-bit PCM value (little endian)\n // const lowByte = pcmData[i * 2]\n // const highByte = pcmData[i * 2 + 1]\n // const pcmValue = (highByte << 8) | lowByte\n\n // // Convert to signed 16-bit value\n // const signedValue =\n // pcmValue > 32767 ? pcmValue - 65536 : pcmValue\n\n // // Normalize to float between -1 and 1\n // normalizedData[i] = signedValue / 32768.0\n // }\n // Store the normalized data in the result\n result.normalizedData = channelData\n }\n\n if (includeBase64Data) {\n // Convert the PCM data to a base64 string\n const binary = Array.from(new Uint8Array(pcmData.buffer))\n .map((b) => String.fromCharCode(b))\n .join('')\n result.base64Data = btoa(binary)\n }\n\n if (options.computeChecksum) {\n result.checksum = crc32.buf(pcmData)\n }\n\n logger?.debug('EXTRACT AUDIO - Step 3: PCM conversion complete', {\n pcmStats: {\n length: pcmData.length,\n bytesPerSample,\n totalSamples: numSamples,\n firstBytes: Array.from(pcmData.slice(0, 16)),\n lastBytes: Array.from(pcmData.slice(-16)),\n },\n })\n\n return result\n } catch (error) {\n options.logger?.error('EXTRACT AUDIO - Error:', error)\n throw error\n }\n }\n\n ExpoAudioStreamModule.trimAudio = async (\n options: TrimAudioOptions\n ): Promise<TrimAudioResult> => {\n try {\n const startTime = performance.now()\n const {\n fileUri,\n mode = 'single',\n startTimeMs,\n endTimeMs,\n ranges,\n outputFileName,\n outputFormat,\n } = options\n\n // Validate inputs\n if (!fileUri) {\n throw new Error('fileUri is required')\n }\n\n if (\n mode === 'single' &&\n startTimeMs === undefined &&\n endTimeMs === undefined\n ) {\n throw new Error(\n 'At least one of startTimeMs or endTimeMs must be provided in single mode'\n )\n }\n\n if (\n (mode === 'keep' || mode === 'remove') &&\n (!ranges || ranges.length === 0)\n ) {\n throw new Error(\n 'ranges must be provided and non-empty for keep or remove modes'\n )\n }\n\n // Create AudioContext\n const audioContext = new (window.AudioContext ||\n (window as any).webkitAudioContext)()\n\n // First, load the entire audio file to get its properties\n const response = await fetch(fileUri)\n const arrayBuffer = await response.arrayBuffer()\n const originalAudioBuffer =\n await audioContext.decodeAudioData(arrayBuffer)\n\n // Get original audio properties\n const originalSampleRate = originalAudioBuffer.sampleRate\n const originalChannels = originalAudioBuffer.numberOfChannels\n\n // Add more detailed logging\n console.log(`Original audio details:`, {\n sampleRate: originalSampleRate,\n channels: originalChannels,\n duration: originalAudioBuffer.duration,\n length: originalAudioBuffer.length,\n // Log a few samples to verify content\n firstSamples: Array.from(\n originalAudioBuffer.getChannelData(0).slice(0, 5)\n ),\n })\n\n // Determine output format - use original values as defaults if not specified\n let format = outputFormat?.format || 'wav'\n const targetSampleRate =\n outputFormat?.sampleRate || originalSampleRate\n const targetChannels = outputFormat?.channels || originalChannels\n const targetBitDepth = outputFormat?.bitDepth || 16\n\n // Get file info from the URL\n const filename =\n outputFileName ||\n fileUri.split('/').pop() ||\n 'trimmed-audio.wav'\n\n // Process based on mode\n let resultBuffer: AudioBuffer\n\n // Report initial progress\n ExpoAudioStreamModule.sendEvent('TrimProgress', {\n progress: 10,\n })\n\n if (mode === 'single') {\n // Single mode: extract a single range\n // Use original sample rate and channels for extraction to preserve quality\n const { buffer } = await processAudioBuffer({\n fileUri,\n targetSampleRate, // Use the requested sample rate\n targetChannels,\n normalizeAudio: false,\n startTimeMs,\n endTimeMs,\n audioContext,\n })\n\n console.log(`Processed buffer details:`, {\n sampleRate: buffer.sampleRate,\n channels: buffer.numberOfChannels,\n duration: buffer.duration,\n length: buffer.length,\n // Log a few samples to verify content\n firstSamples: Array.from(\n buffer.getChannelData(0).slice(0, 5)\n ),\n })\n\n resultBuffer = buffer\n\n // If we need to change sample rate or channels, do it after extraction\n if (\n targetSampleRate !== originalSampleRate ||\n targetChannels !== originalChannels\n ) {\n console.log(\n `Resampling from ${originalSampleRate}Hz to ${targetSampleRate}Hz`\n )\n resultBuffer = await resampleAudioBuffer(\n audioContext,\n buffer,\n targetSampleRate,\n targetChannels\n )\n }\n } else {\n // For keep or remove modes\n const fullDuration = originalAudioBuffer.duration * 1000 // in ms\n\n type ProcessSegment = {\n startTimeMs: number\n endTimeMs: number\n }\n\n let segmentsToProcess: ProcessSegment[] = []\n\n if (mode === 'keep') {\n // For keep mode, use the ranges directly\n segmentsToProcess = ranges!\n } else {\n // mode === 'remove'\n // For remove mode, invert the ranges\n const sortedRanges = [...ranges!].sort(\n (a, b) => a.startTimeMs - b.startTimeMs\n )\n\n // Add segment from start to first range if needed\n if (\n sortedRanges.length > 0 &&\n sortedRanges[0].startTimeMs > 0\n ) {\n segmentsToProcess.push({\n startTimeMs: 0,\n endTimeMs: sortedRanges[0].startTimeMs,\n })\n }\n\n // Add segments between ranges\n for (let i = 0; i < sortedRanges.length - 1; i++) {\n segmentsToProcess.push({\n startTimeMs: sortedRanges[i].endTimeMs,\n endTimeMs: sortedRanges[i + 1].startTimeMs,\n })\n }\n\n // Add segment from last range to end if needed\n if (\n sortedRanges.length > 0 &&\n sortedRanges[sortedRanges.length - 1].endTimeMs <\n fullDuration\n ) {\n segmentsToProcess.push({\n startTimeMs:\n sortedRanges[sortedRanges.length - 1].endTimeMs,\n endTimeMs: fullDuration,\n })\n }\n }\n\n // Filter out empty or invalid segments\n segmentsToProcess = segmentsToProcess.filter(\n (segment) =>\n segment.startTimeMs < segment.endTimeMs &&\n segment.endTimeMs - segment.startTimeMs > 1\n ) // 1ms minimum\n\n if (segmentsToProcess.length === 0) {\n throw new Error(\n 'No valid segments to process after filtering ranges'\n )\n }\n\n // Process each segment using original sample rate and channels\n const segmentBuffers: AudioBuffer[] = []\n\n for (let i = 0; i < segmentsToProcess.length; i++) {\n const segment = segmentsToProcess[i]\n\n // Report progress for each segment\n ExpoAudioStreamModule.sendEvent('TrimProgress', {\n progress:\n 10 +\n Math.round((i / segmentsToProcess.length) * 40),\n })\n\n // Use processAudioBuffer to extract this segment\n const { buffer: segmentBuffer } = await processAudioBuffer({\n fileUri,\n targetSampleRate: originalSampleRate, // Use original sample rate\n targetChannels: originalChannels, // Use original channels\n normalizeAudio: false,\n startTimeMs: segment.startTimeMs,\n endTimeMs: segment.endTimeMs,\n audioContext,\n })\n\n segmentBuffers.push(segmentBuffer)\n }\n\n // Concatenate all segments\n const totalSamples = segmentBuffers.reduce(\n (sum, buffer) => sum + buffer.length,\n 0\n )\n\n // Create buffer with original properties first\n const concatenatedBuffer = audioContext.createBuffer(\n originalChannels,\n totalSamples,\n originalSampleRate\n )\n\n let offset = 0\n for (const segmentBuffer of segmentBuffers) {\n for (\n let channel = 0;\n channel < originalChannels;\n channel++\n ) {\n const outputData =\n concatenatedBuffer.getChannelData(channel)\n const segmentData =\n segmentBuffer.getChannelData(channel)\n\n for (let i = 0; i < segmentBuffer.length; i++) {\n outputData[offset + i] = segmentData[i]\n }\n }\n offset += segmentBuffer.length\n }\n\n resultBuffer = concatenatedBuffer\n\n // If we need to change sample rate or channels, do it after concatenation\n if (\n targetSampleRate !== originalSampleRate ||\n targetChannels !== originalChannels\n ) {\n console.log(\n `Resampling concatenated buffer from ${originalSampleRate}Hz to ${targetSampleRate}Hz`\n )\n resultBuffer = await resampleAudioBuffer(\n audioContext,\n concatenatedBuffer,\n targetSampleRate,\n targetChannels\n )\n }\n }\n\n // Report progress (50% - processing complete)\n ExpoAudioStreamModule.sendEvent('TrimProgress', {\n progress: 50,\n })\n\n // Encode the result based on the requested format\n let outputData: ArrayBuffer\n let outputMimeType: string\n let compressionInfo: any = null\n\n // Check if AAC was requested on web and show a warning\n if (format === 'aac' && Platform.OS === 'web') {\n console.warn(\n 'AAC format is not supported on web platforms. Falling back to OPUS format.'\n )\n format = 'opus'\n }\n\n if (format === 'wav') {\n // Create a properly interleaved buffer for WAV format\n // For WAV, we need to convert Float32Array to Int16Array (for 16-bit audio)\n const numSamples =\n resultBuffer.length * resultBuffer.numberOfChannels\n const interleavedData = new Int16Array(numSamples)\n\n // Log detailed information about the buffer before encoding\n console.log(`Creating WAV file:`, {\n bufferSampleRate: resultBuffer.sampleRate,\n bufferChannels: resultBuffer.numberOfChannels,\n bufferLength: resultBuffer.length,\n targetSampleRate,\n targetChannels,\n targetBitDepth,\n // Log a few samples to verify content\n firstSamples: Array.from(\n resultBuffer.getChannelData(0).slice(0, 5)\n ),\n })\n\n // Interleave channels properly\n for (let i = 0; i < resultBuffer.length; i++) {\n for (\n let channel = 0;\n channel < resultBuffer.numberOfChannels;\n channel++\n ) {\n // Convert float (-1.0 to 1.0) to int16 (-32768 to 32767)\n const floatSample =\n resultBuffer.getChannelData(channel)[i]\n // Clamp the value to -1.0 to 1.0\n const clampedSample = Math.max(\n -1.0,\n Math.min(1.0, floatSample)\n )\n // Convert to int16\n const intSample = Math.round(clampedSample * 32767)\n // Store in interleaved buffer\n interleavedData[\n i * resultBuffer.numberOfChannels + channel\n ] = intSample\n }\n }\n\n // Convert Int16Array to ArrayBuffer for WAV header\n const rawBuffer = interleavedData.buffer\n\n // IMPORTANT: Make sure we're using the ACTUAL sample rate of the buffer\n // not just what was requested in the options\n console.log(\n `Creating WAV with ${resultBuffer.numberOfChannels} channels at ${resultBuffer.sampleRate}Hz`\n )\n\n outputData = writeWavHeader({\n buffer: rawBuffer as ArrayBuffer,\n sampleRate: resultBuffer.sampleRate, // Use the actual buffer's sample rate\n numChannels: resultBuffer.numberOfChannels,\n bitDepth: targetBitDepth as BitDepth,\n })\n outputMimeType = 'audio/wav'\n } else if (format === 'opus' || format === 'aac') {\n try {\n // Try to use MediaRecorder for compressed formats\n const { data, bitrate } = await encodeCompressedAudio(\n resultBuffer,\n format,\n outputFormat?.bitrate\n )\n\n outputData = data\n outputMimeType =\n format === 'opus' ? 'audio/webm' : 'audio/aac'\n compressionInfo = {\n format,\n bitrate,\n size: data.byteLength,\n }\n } catch (error) {\n console.warn(\n `Failed to encode to ${format}, falling back to WAV: ${error}`\n )\n\n // Same WAV encoding as above\n const wavData = new Float32Array(\n resultBuffer.length * resultBuffer.numberOfChannels\n )\n\n for (let i = 0; i < resultBuffer.length; i++) {\n for (\n let channel = 0;\n channel < resultBuffer.numberOfChannels;\n channel++\n ) {\n wavData[\n i * resultBuffer.numberOfChannels + channel\n ] = resultBuffer.getChannelData(channel)[i]\n }\n }\n\n outputData = writeWavHeader({\n buffer: wavData.buffer as ArrayBuffer,\n sampleRate: resultBuffer.sampleRate,\n numChannels: resultBuffer.numberOfChannels,\n bitDepth: targetBitDepth as BitDepth,\n })\n outputMimeType = 'audio/wav'\n }\n } else {\n // Default to WAV for unsupported formats\n console.warn(\n `Format ${format} not supported on web, using WAV instead`\n )\n\n // Same WAV encoding as above\n const wavData = new Float32Array(\n resultBuffer.length * resultBuffer.numberOfChannels\n )\n\n for (let i = 0; i < resultBuffer.length; i++) {\n for (\n let channel = 0;\n channel < resultBuffer.numberOfChannels;\n channel++\n ) {\n wavData[i * resultBuffer.numberOfChannels + channel] =\n resultBuffer.getChannelData(channel)[i]\n }\n }\n\n outputData = writeWavHeader({\n buffer: wavData.buffer as ArrayBuffer,\n sampleRate: resultBuffer.sampleRate,\n numChannels: resultBuffer.numberOfChannels,\n bitDepth: targetBitDepth as BitDepth,\n })\n outputMimeType = 'audio/wav'\n }\n\n // Report progress (90% - encoding complete)\n ExpoAudioStreamModule.sendEvent('TrimProgress', {\n progress: 90,\n })\n\n // Create a blob and URL for the result\n const blob = new Blob([outputData], { type: outputMimeType })\n const outputUri = URL.createObjectURL(blob)\n\n // Calculate processing time\n const processingTimeMs = performance.now() - startTime\n\n // Report progress (100% - complete)\n ExpoAudioStreamModule.sendEvent('TrimProgress', {\n progress: 100,\n })\n\n // Create result object\n const result: TrimAudioResult = {\n uri: outputUri,\n filename,\n durationMs: Math.round(resultBuffer.duration * 1000),\n size: outputData.byteLength,\n sampleRate: resultBuffer.sampleRate,\n channels: resultBuffer.numberOfChannels,\n bitDepth: targetBitDepth,\n mimeType: outputMimeType,\n processingInfo: {\n durationMs: processingTimeMs,\n },\n }\n\n // Add compression info if available\n if (compressionInfo) {\n result.compression = compressionInfo\n }\n\n return result\n } catch (error) {\n console.error('Error in trimAudio:', error)\n throw error\n }\n }\n\n // Add a sendEvent method for web\n ExpoAudioStreamModule.sendEvent = (eventName: string, params: any) => {\n // This will be picked up by the LegacyEventEmitter in trimAudio.ts\n if (\n ExpoAudioStreamModule.listeners &&\n ExpoAudioStreamModule.listeners[eventName]\n ) {\n ExpoAudioStreamModule.listeners[eventName].forEach(\n (listener: Function) => {\n listener(params)\n }\n )\n }\n }\n\n // Initialize listeners object\n ExpoAudioStreamModule.listeners = {}\n\n // Add methods for event listeners that LegacyEventEmitter will use\n ExpoAudioStreamModule.addListener = (\n eventName: string,\n listener: Function\n ) => {\n if (!ExpoAudioStreamModule.listeners[eventName]) {\n ExpoAudioStreamModule.listeners[eventName] = []\n }\n ExpoAudioStreamModule.listeners[eventName].push(listener)\n\n // Return an object with a remove method\n return {\n remove: () => {\n const index =\n ExpoAudioStreamModule.listeners[eventName].indexOf(listener)\n if (index !== -1) {\n ExpoAudioStreamModule.listeners[eventName].splice(index, 1)\n }\n },\n }\n }\n\n ExpoAudioStreamModule.removeAllListeners = (eventName: string) => {\n if (ExpoAudioStreamModule.listeners[eventName]) {\n delete ExpoAudioStreamModule.listeners[eventName]\n }\n }\n}\n\n// Move the encodeCompressedAudio function outside the if block to fix the ESLint error\nasync function encodeCompressedAudio(\n buffer: AudioBuffer,\n format: 'opus' | 'aac',\n bitrate?: number\n): Promise<{ data: ArrayBuffer; bitrate: number }> {\n return new Promise((resolve, reject) => {\n try {\n // On web, always use opus if aac is requested\n const actualFormat =\n Platform.OS === 'web' && format === 'aac' ? 'opus' : format\n\n // Check if MediaRecorder supports the requested format\n const mimeType =\n actualFormat === 'opus' ? 'audio/webm;codecs=opus' : 'audio/aac'\n if (!MediaRecorder.isTypeSupported(mimeType)) {\n throw new Error(`MediaRecorder does not support ${mimeType}`)\n }\n\n // Create a new AudioContext and source\n const ctx = new (window.AudioContext ||\n (window as any).webkitAudioContext)()\n const source = ctx.createBufferSource()\n source.buffer = buffer\n\n // Create a MediaStreamDestination to capture the audio\n const destination = ctx.createMediaStreamDestination()\n source.connect(destination)\n\n // Create a MediaRecorder with the requested format\n const recorder = new MediaRecorder(destination.stream, {\n mimeType,\n audioBitsPerSecond:\n bitrate || (actualFormat === 'opus' ? 32000 : 64000),\n })\n\n const chunks: Blob[] = []\n\n recorder.ondataavailable = (e) => {\n if (e.data.size > 0) {\n chunks.push(e.data)\n }\n }\n\n recorder.onstop = async () => {\n try {\n const blob = new Blob(chunks, { type: mimeType })\n const arrayBuffer = await blob.arrayBuffer()\n\n // Get the actual bitrate used\n const actualBitrate = Math.round(\n (arrayBuffer.byteLength * 8) / buffer.duration\n )\n\n resolve({\n data: arrayBuffer,\n bitrate: actualBitrate / 1000, // Convert to kbps\n })\n\n // Clean up\n ctx.close()\n } catch (error) {\n reject(error)\n }\n }\n\n // Start recording and playback\n recorder.start()\n source.start(0)\n\n // Stop recording when the buffer finishes playing\n setTimeout(() => {\n recorder.stop()\n source.stop()\n }, buffer.duration * 1000)\n } catch (error) {\n reject(error)\n }\n })\n}\n\n// Improved resampleAudioBuffer function\nasync function resampleAudioBuffer(\n context: AudioContext,\n buffer: AudioBuffer,\n targetSampleRate: number,\n targetChannels: number\n): Promise<AudioBuffer> {\n // If no change needed, return the original buffer\n if (\n buffer.sampleRate === targetSampleRate &&\n buffer.numberOfChannels === targetChannels\n ) {\n return buffer\n }\n\n console.log(\n `Resampling: ${buffer.sampleRate}Hz → ${targetSampleRate}Hz, ${buffer.numberOfChannels} → ${targetChannels} channels`\n )\n\n // Calculate the new length based on the sample rate change\n const newLength = Math.round(\n (buffer.length * targetSampleRate) / buffer.sampleRate\n )\n\n // Create an offline context for resampling\n const offlineContext = new OfflineAudioContext(\n targetChannels,\n newLength,\n targetSampleRate\n )\n\n // Create a source node\n const source = offlineContext.createBufferSource()\n source.buffer = buffer\n\n // If we need to change channel count\n if (buffer.numberOfChannels !== targetChannels) {\n if (targetChannels === 1 && buffer.numberOfChannels > 1) {\n // Downmix to mono\n const merger = offlineContext.createChannelMerger(1)\n\n // Create a gain node to reduce volume when downmixing to prevent clipping\n const gainNode = offlineContext.createGain()\n gainNode.gain.value = 1.0 / buffer.numberOfChannels\n\n source.connect(gainNode)\n gainNode.connect(merger)\n merger.connect(offlineContext.destination)\n } else if (targetChannels === 2 && buffer.numberOfChannels === 1) {\n // Upmix mono to stereo (duplicate the channel)\n const splitter = offlineContext.createChannelSplitter(1)\n const merger = offlineContext.createChannelMerger(2)\n\n source.connect(splitter)\n splitter.connect(merger, 0, 0)\n splitter.connect(merger, 0, 1)\n merger.connect(offlineContext.destination)\n } else {\n // For other cases, just connect and let the system handle it\n source.connect(offlineContext.destination)\n }\n } else {\n // No channel conversion needed\n source.connect(offlineContext.destination)\n }\n\n // Start rendering\n source.start(0)\n const resampledBuffer = await offlineContext.startRendering()\n\n console.log(\n `Resampling complete: ${resampledBuffer.length} samples at ${resampledBuffer.sampleRate}Hz`\n )\n\n return resampledBuffer\n}\n\nif (Platform.OS !== 'web') {\n ExpoAudioStreamModule = requireNativeModule('ExpoAudioStream')\n}\n\nexport default ExpoAudioStreamModule\n"]}
1
+ {"version":3,"file":"ExpoAudioStreamModule.js","sourceRoot":"","sources":["../src/ExpoAudioStreamModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { requireNativeModule } from 'expo-modules-core'\nimport { Platform } from 'react-native'\n\nimport {\n ExtractAudioDataOptions,\n ExtractedAudioData,\n BitDepth,\n TrimAudioOptions,\n TrimAudioResult,\n} from './ExpoAudioStream.types'\nimport {\n ExpoAudioStreamWeb,\n ExpoAudioStreamWebProps,\n} from './ExpoAudioStream.web'\nimport { processAudioBuffer } from './utils/audioProcessing'\nimport crc32 from './utils/crc32'\nimport { writeWavHeader } from './utils/writeWavHeader'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet ExpoAudioStreamModule: any\n\nif (Platform.OS === 'web') {\n let instance: ExpoAudioStreamWeb | null = null\n\n ExpoAudioStreamModule = (webProps: ExpoAudioStreamWebProps) => {\n if (!instance) {\n instance = new ExpoAudioStreamWeb(webProps)\n }\n return instance\n }\n ExpoAudioStreamModule.requestPermissionsAsync = async () => {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n })\n stream.getTracks().forEach((track) => track.stop())\n return {\n status: 'granted',\n expires: 'never',\n canAskAgain: true,\n granted: true,\n }\n } catch {\n return {\n status: 'denied',\n expires: 'never',\n canAskAgain: true,\n granted: false,\n }\n }\n }\n ExpoAudioStreamModule.getPermissionsAsync = async () => {\n let maybeStatus: string | null = null\n\n if (navigator?.permissions?.query) {\n try {\n const { state } = await navigator.permissions.query({\n name: 'microphone' as PermissionName,\n })\n maybeStatus = state\n } catch {\n maybeStatus = null\n }\n }\n\n switch (maybeStatus) {\n case 'granted':\n return {\n status: 'granted',\n expires: 'never',\n canAskAgain: true,\n granted: true,\n }\n case 'denied':\n return {\n status: 'denied',\n expires: 'never',\n canAskAgain: true,\n granted: false,\n }\n default:\n return await ExpoAudioStreamModule.requestPermissionsAsync()\n }\n }\n ExpoAudioStreamModule.extractAudioData = async (\n options: ExtractAudioDataOptions\n ): Promise<ExtractedAudioData> => {\n try {\n const {\n fileUri,\n position,\n length,\n startTimeMs,\n endTimeMs,\n decodingOptions,\n includeNormalizedData,\n includeBase64Data,\n includeWavHeader = false,\n logger,\n } = options\n\n logger?.debug('EXTRACT AUDIO - Step 1: Initial request', {\n fileUri,\n extractionParams: {\n position,\n length,\n startTimeMs,\n endTimeMs,\n },\n decodingOptions: {\n targetSampleRate:\n decodingOptions?.targetSampleRate ?? 16000,\n targetChannels: decodingOptions?.targetChannels ?? 1,\n targetBitDepth: decodingOptions?.targetBitDepth ?? 16,\n normalizeAudio: decodingOptions?.normalizeAudio ?? false,\n },\n outputOptions: {\n includeNormalizedData,\n includeBase64Data,\n includeWavHeader,\n },\n })\n\n // Process the audio using shared helper function\n const processedBuffer = await processAudioBuffer({\n fileUri,\n targetSampleRate: decodingOptions?.targetSampleRate ?? 16000,\n targetChannels: decodingOptions?.targetChannels ?? 1,\n normalizeAudio: decodingOptions?.normalizeAudio ?? false,\n position,\n length,\n startTimeMs,\n endTimeMs,\n logger,\n })\n\n logger?.debug('EXTRACT AUDIO - Step 2: Audio processing complete', {\n processedData: {\n samples: processedBuffer.samples,\n sampleRate: processedBuffer.sampleRate,\n channels: processedBuffer.channels,\n durationMs: processedBuffer.durationMs,\n },\n })\n\n const channelData = processedBuffer.channelData\n const bitDepth = (decodingOptions?.targetBitDepth ?? 16) as BitDepth\n const bytesPerSample = bitDepth / 8\n const numSamples = processedBuffer.samples\n\n logger?.debug('EXTRACT AUDIO - Step 3: PCM conversion setup', {\n channelData: {\n length: channelData.length,\n first: channelData[0],\n last: channelData[channelData.length - 1],\n },\n calculation: {\n bitDepth,\n bytesPerSample,\n numSamples,\n expectedBytes: numSamples * bytesPerSample,\n },\n })\n\n // Create PCM data with correct length based on original byte length\n const pcmData = new Uint8Array(numSamples * bytesPerSample)\n let offset = 0\n\n // Convert Float32 samples to PCM format\n for (let i = 0; i < numSamples; i++) {\n const sample = channelData[i]\n const value = Math.max(-1, Math.min(1, sample))\n // Convert to 16-bit signed integer\n let intValue = Math.round(value * 32767)\n\n // Handle negative values correctly\n if (intValue < 0) {\n intValue = 65536 + intValue\n }\n\n // Write as little-endian\n pcmData[offset++] = intValue & 255 // Low byte\n pcmData[offset++] = (intValue >> 8) & 255 // High byte\n }\n\n const durationMs = Math.round(\n (numSamples / processedBuffer.sampleRate) * 1000\n )\n\n logger?.debug('EXTRACT AUDIO - Step 4: Final output', {\n pcmData: {\n length: pcmData.length,\n first: pcmData[0],\n last: pcmData[pcmData.length - 1],\n },\n timing: {\n numSamples,\n sampleRate: processedBuffer.sampleRate,\n durationMs,\n shouldBe3000ms: endTimeMs\n ? endTimeMs - (startTimeMs ?? 0) === 3000\n : undefined,\n },\n })\n\n const result: ExtractedAudioData = {\n pcmData: new Uint8Array(pcmData.buffer),\n sampleRate: processedBuffer.sampleRate,\n channels: processedBuffer.channels,\n bitDepth,\n durationMs,\n format: `pcm_${bitDepth}bit` as const,\n samples: numSamples,\n }\n\n // Add WAV header if requested\n if (includeWavHeader) {\n logger?.debug('EXTRACT AUDIO - Step 4: Adding WAV header', {\n originalLength: pcmData.length,\n newLength: result.pcmData.length,\n firstBytes: Array.from(result.pcmData.slice(0, 44)), // WAV header is 44 bytes\n })\n const wavBuffer = writeWavHeader({\n buffer: pcmData.buffer.slice(0, pcmData.length),\n sampleRate: processedBuffer.sampleRate,\n numChannels: processedBuffer.channels,\n bitDepth,\n })\n result.pcmData = new Uint8Array(wavBuffer)\n result.hasWavHeader = true\n }\n\n if (includeNormalizedData) {\n // // Simple approach: Create normalized data directly from the PCM data\n // // Just convert to -1 to 1 range without any amplification\n // const normalizedData = new Float32Array(numSamples)\n\n // // Convert the PCM data to float values\n // for (let i = 0; i < numSamples; i++) {\n // // Get the 16-bit PCM value (little endian)\n // const lowByte = pcmData[i * 2]\n // const highByte = pcmData[i * 2 + 1]\n // const pcmValue = (highByte << 8) | lowByte\n\n // // Convert to signed 16-bit value\n // const signedValue =\n // pcmValue > 32767 ? pcmValue - 65536 : pcmValue\n\n // // Normalize to float between -1 and 1\n // normalizedData[i] = signedValue / 32768.0\n // }\n // Store the normalized data in the result\n result.normalizedData = channelData\n }\n\n if (includeBase64Data) {\n // Convert the PCM data to a base64 string\n const binary = Array.from(new Uint8Array(pcmData.buffer))\n .map((b) => String.fromCharCode(b))\n .join('')\n result.base64Data = btoa(binary)\n }\n\n if (options.computeChecksum) {\n result.checksum = crc32.buf(pcmData)\n }\n\n logger?.debug('EXTRACT AUDIO - Step 3: PCM conversion complete', {\n pcmStats: {\n length: pcmData.length,\n bytesPerSample,\n totalSamples: numSamples,\n firstBytes: Array.from(pcmData.slice(0, 16)),\n lastBytes: Array.from(pcmData.slice(-16)),\n },\n })\n\n return result\n } catch (error) {\n options.logger?.error('EXTRACT AUDIO - Error:', error)\n throw error\n }\n }\n\n ExpoAudioStreamModule.trimAudio = async (\n options: TrimAudioOptions\n ): Promise<TrimAudioResult> => {\n try {\n const startTime = performance.now()\n const {\n fileUri,\n mode = 'single',\n startTimeMs,\n endTimeMs,\n ranges,\n outputFileName,\n outputFormat,\n } = options\n\n // Validate inputs\n if (!fileUri) {\n throw new Error('fileUri is required')\n }\n\n if (\n mode === 'single' &&\n startTimeMs === undefined &&\n endTimeMs === undefined\n ) {\n throw new Error(\n 'At least one of startTimeMs or endTimeMs must be provided in single mode'\n )\n }\n\n if (\n (mode === 'keep' || mode === 'remove') &&\n (!ranges || ranges.length === 0)\n ) {\n throw new Error(\n 'ranges must be provided and non-empty for keep or remove modes'\n )\n }\n\n // Create AudioContext\n const audioContext = new (window.AudioContext ||\n (window as any).webkitAudioContext)()\n\n // First, load the entire audio file to get its properties\n const response = await fetch(fileUri)\n const arrayBuffer = await response.arrayBuffer()\n const originalAudioBuffer =\n await audioContext.decodeAudioData(arrayBuffer)\n\n // Get original audio properties\n const originalSampleRate = originalAudioBuffer.sampleRate\n const originalChannels = originalAudioBuffer.numberOfChannels\n\n // Add more detailed logging\n console.log(`Original audio details:`, {\n sampleRate: originalSampleRate,\n channels: originalChannels,\n duration: originalAudioBuffer.duration,\n length: originalAudioBuffer.length,\n // Log a few samples to verify content\n firstSamples: Array.from(\n originalAudioBuffer.getChannelData(0).slice(0, 5)\n ),\n })\n\n // Determine output format - use original values as defaults if not specified\n let format = outputFormat?.format || 'wav'\n const targetSampleRate =\n outputFormat?.sampleRate || originalSampleRate\n const targetChannels = outputFormat?.channels || originalChannels\n const targetBitDepth = outputFormat?.bitDepth || 16\n\n // Get file info from the URL\n const filename =\n outputFileName ||\n fileUri.split('/').pop() ||\n 'trimmed-audio.wav'\n\n // Process based on mode\n let resultBuffer: AudioBuffer\n\n // Report initial progress\n ExpoAudioStreamModule.sendEvent('TrimProgress', {\n progress: 10,\n })\n\n if (mode === 'single') {\n // Single mode: extract a single range\n // Use original sample rate and channels for extraction to preserve quality\n const { buffer } = await processAudioBuffer({\n fileUri,\n targetSampleRate, // Use the requested sample rate\n targetChannels,\n normalizeAudio: false,\n startTimeMs,\n endTimeMs,\n audioContext,\n })\n\n console.log(`Processed buffer details:`, {\n sampleRate: buffer.sampleRate,\n channels: buffer.numberOfChannels,\n duration: buffer.duration,\n length: buffer.length,\n // Log a few samples to verify content\n firstSamples: Array.from(\n buffer.getChannelData(0).slice(0, 5)\n ),\n })\n\n resultBuffer = buffer\n\n // If we need to change sample rate or channels, do it after extraction\n if (\n targetSampleRate !== originalSampleRate ||\n targetChannels !== originalChannels\n ) {\n console.log(\n `Resampling from ${originalSampleRate}Hz to ${targetSampleRate}Hz`\n )\n resultBuffer = await resampleAudioBuffer(\n audioContext,\n buffer,\n targetSampleRate,\n targetChannels\n )\n }\n } else {\n // For keep or remove modes\n const fullDuration = originalAudioBuffer.duration * 1000 // in ms\n\n type ProcessSegment = {\n startTimeMs: number\n endTimeMs: number\n }\n\n let segmentsToProcess: ProcessSegment[] = []\n\n if (mode === 'keep') {\n // For keep mode, use the ranges directly\n segmentsToProcess = ranges!\n } else {\n // mode === 'remove'\n // For remove mode, invert the ranges\n const sortedRanges = [...ranges!].sort(\n (a, b) => a.startTimeMs - b.startTimeMs\n )\n\n // Add segment from start to first range if needed\n if (\n sortedRanges.length > 0 &&\n sortedRanges[0].startTimeMs > 0\n ) {\n segmentsToProcess.push({\n startTimeMs: 0,\n endTimeMs: sortedRanges[0].startTimeMs,\n })\n }\n\n // Add segments between ranges\n for (let i = 0; i < sortedRanges.length - 1; i++) {\n segmentsToProcess.push({\n startTimeMs: sortedRanges[i].endTimeMs,\n endTimeMs: sortedRanges[i + 1].startTimeMs,\n })\n }\n\n // Add segment from last range to end if needed\n if (\n sortedRanges.length > 0 &&\n sortedRanges[sortedRanges.length - 1].endTimeMs <\n fullDuration\n ) {\n segmentsToProcess.push({\n startTimeMs:\n sortedRanges[sortedRanges.length - 1].endTimeMs,\n endTimeMs: fullDuration,\n })\n }\n }\n\n // Filter out empty or invalid segments\n segmentsToProcess = segmentsToProcess.filter(\n (segment) =>\n segment.startTimeMs < segment.endTimeMs &&\n segment.endTimeMs - segment.startTimeMs > 1\n ) // 1ms minimum\n\n if (segmentsToProcess.length === 0) {\n throw new Error(\n 'No valid segments to process after filtering ranges'\n )\n }\n\n // Process each segment using original sample rate and channels\n const segmentBuffers: AudioBuffer[] = []\n\n for (let i = 0; i < segmentsToProcess.length; i++) {\n const segment = segmentsToProcess[i]\n\n // Report progress for each segment\n ExpoAudioStreamModule.sendEvent('TrimProgress', {\n progress:\n 10 +\n Math.round((i / segmentsToProcess.length) * 40),\n })\n\n // Use processAudioBuffer to extract this segment\n const { buffer: segmentBuffer } = await processAudioBuffer({\n fileUri,\n targetSampleRate: originalSampleRate, // Use original sample rate\n targetChannels: originalChannels, // Use original channels\n normalizeAudio: false,\n startTimeMs: segment.startTimeMs,\n endTimeMs: segment.endTimeMs,\n audioContext,\n })\n\n segmentBuffers.push(segmentBuffer)\n }\n\n // Concatenate all segments\n const totalSamples = segmentBuffers.reduce(\n (sum, buffer) => sum + buffer.length,\n 0\n )\n\n // Create buffer with original properties first\n const concatenatedBuffer = audioContext.createBuffer(\n originalChannels,\n totalSamples,\n originalSampleRate\n )\n\n let offset = 0\n for (const segmentBuffer of segmentBuffers) {\n for (\n let channel = 0;\n channel < originalChannels;\n channel++\n ) {\n const outputData =\n concatenatedBuffer.getChannelData(channel)\n const segmentData =\n segmentBuffer.getChannelData(channel)\n\n for (let i = 0; i < segmentBuffer.length; i++) {\n outputData[offset + i] = segmentData[i]\n }\n }\n offset += segmentBuffer.length\n }\n\n resultBuffer = concatenatedBuffer\n\n // If we need to change sample rate or channels, do it after concatenation\n if (\n targetSampleRate !== originalSampleRate ||\n targetChannels !== originalChannels\n ) {\n console.log(\n `Resampling concatenated buffer from ${originalSampleRate}Hz to ${targetSampleRate}Hz`\n )\n resultBuffer = await resampleAudioBuffer(\n audioContext,\n concatenatedBuffer,\n targetSampleRate,\n targetChannels\n )\n }\n }\n\n // Report progress (50% - processing complete)\n ExpoAudioStreamModule.sendEvent('TrimProgress', {\n progress: 50,\n })\n\n // Encode the result based on the requested format\n let outputData: ArrayBuffer\n let outputMimeType: string\n let compressionInfo: any = null\n\n // Check if AAC was requested on web and show a warning\n if (format === 'aac' && Platform.OS === 'web') {\n console.warn(\n 'AAC format is not supported on web platforms. Falling back to OPUS format.'\n )\n format = 'opus'\n }\n\n if (format === 'wav') {\n // Create a properly interleaved buffer for WAV format\n // For WAV, we need to convert Float32Array to Int16Array (for 16-bit audio)\n const numSamples =\n resultBuffer.length * resultBuffer.numberOfChannels\n const interleavedData = new Int16Array(numSamples)\n\n // Log detailed information about the buffer before encoding\n console.log(`Creating WAV file:`, {\n bufferSampleRate: resultBuffer.sampleRate,\n bufferChannels: resultBuffer.numberOfChannels,\n bufferLength: resultBuffer.length,\n targetSampleRate,\n targetChannels,\n targetBitDepth,\n // Log a few samples to verify content\n firstSamples: Array.from(\n resultBuffer.getChannelData(0).slice(0, 5)\n ),\n })\n\n // Interleave channels properly\n for (let i = 0; i < resultBuffer.length; i++) {\n for (\n let channel = 0;\n channel < resultBuffer.numberOfChannels;\n channel++\n ) {\n // Convert float (-1.0 to 1.0) to int16 (-32768 to 32767)\n const floatSample =\n resultBuffer.getChannelData(channel)[i]\n // Clamp the value to -1.0 to 1.0\n const clampedSample = Math.max(\n -1.0,\n Math.min(1.0, floatSample)\n )\n // Convert to int16\n const intSample = Math.round(clampedSample * 32767)\n // Store in interleaved buffer\n interleavedData[\n i * resultBuffer.numberOfChannels + channel\n ] = intSample\n }\n }\n\n // Convert Int16Array to ArrayBuffer for WAV header\n const rawBuffer = interleavedData.buffer\n\n // IMPORTANT: Make sure we're using the ACTUAL sample rate of the buffer\n // not just what was requested in the options\n console.log(\n `Creating WAV with ${resultBuffer.numberOfChannels} channels at ${resultBuffer.sampleRate}Hz`\n )\n\n outputData = writeWavHeader({\n buffer: rawBuffer as ArrayBuffer,\n sampleRate: resultBuffer.sampleRate, // Use the actual buffer's sample rate\n numChannels: resultBuffer.numberOfChannels,\n bitDepth: targetBitDepth as BitDepth,\n })\n outputMimeType = 'audio/wav'\n } else if (format === 'opus' || format === 'aac') {\n try {\n // Try to use MediaRecorder for compressed formats\n const { data, bitrate } = await encodeCompressedAudio(\n resultBuffer,\n format,\n outputFormat?.bitrate\n )\n\n outputData = data\n outputMimeType =\n format === 'opus' ? 'audio/webm' : 'audio/aac'\n compressionInfo = {\n format,\n bitrate,\n size: data.byteLength,\n }\n } catch (error) {\n console.warn(\n `Failed to encode to ${format}, falling back to WAV: ${error}`\n )\n\n // Same WAV encoding as above\n const wavData = new Float32Array(\n resultBuffer.length * resultBuffer.numberOfChannels\n )\n\n for (let i = 0; i < resultBuffer.length; i++) {\n for (\n let channel = 0;\n channel < resultBuffer.numberOfChannels;\n channel++\n ) {\n wavData[\n i * resultBuffer.numberOfChannels + channel\n ] = resultBuffer.getChannelData(channel)[i]\n }\n }\n\n outputData = writeWavHeader({\n buffer: wavData.buffer as ArrayBuffer,\n sampleRate: resultBuffer.sampleRate,\n numChannels: resultBuffer.numberOfChannels,\n bitDepth: targetBitDepth as BitDepth,\n })\n outputMimeType = 'audio/wav'\n }\n } else {\n // Default to WAV for unsupported formats\n console.warn(\n `Format ${format} not supported on web, using WAV instead`\n )\n\n // Same WAV encoding as above\n const wavData = new Float32Array(\n resultBuffer.length * resultBuffer.numberOfChannels\n )\n\n for (let i = 0; i < resultBuffer.length; i++) {\n for (\n let channel = 0;\n channel < resultBuffer.numberOfChannels;\n channel++\n ) {\n wavData[i * resultBuffer.numberOfChannels + channel] =\n resultBuffer.getChannelData(channel)[i]\n }\n }\n\n outputData = writeWavHeader({\n buffer: wavData.buffer as ArrayBuffer,\n sampleRate: resultBuffer.sampleRate,\n numChannels: resultBuffer.numberOfChannels,\n bitDepth: targetBitDepth as BitDepth,\n })\n outputMimeType = 'audio/wav'\n }\n\n // Report progress (90% - encoding complete)\n ExpoAudioStreamModule.sendEvent('TrimProgress', {\n progress: 90,\n })\n\n // Create a blob and URL for the result\n const blob = new Blob([outputData], { type: outputMimeType })\n const outputUri = URL.createObjectURL(blob)\n\n // Calculate processing time\n const processingTimeMs = performance.now() - startTime\n\n // Report progress (100% - complete)\n ExpoAudioStreamModule.sendEvent('TrimProgress', {\n progress: 100,\n })\n\n // Create result object\n const result: TrimAudioResult = {\n uri: outputUri,\n filename,\n durationMs: Math.round(resultBuffer.duration * 1000),\n size: outputData.byteLength,\n sampleRate: resultBuffer.sampleRate,\n channels: resultBuffer.numberOfChannels,\n bitDepth: targetBitDepth,\n mimeType: outputMimeType,\n processingInfo: {\n durationMs: processingTimeMs,\n },\n }\n\n // Add compression info if available\n if (compressionInfo) {\n result.compression = compressionInfo\n }\n\n return result\n } catch (error) {\n console.error('Error in trimAudio:', error)\n throw error\n }\n }\n\n // Add a sendEvent method for web\n ExpoAudioStreamModule.sendEvent = (eventName: string, params: any) => {\n // This will be picked up by the LegacyEventEmitter in trimAudio.ts\n if (\n ExpoAudioStreamModule.listeners &&\n ExpoAudioStreamModule.listeners[eventName]\n ) {\n ExpoAudioStreamModule.listeners[eventName].forEach(\n (listener: Function) => {\n listener(params)\n }\n )\n }\n }\n\n // Initialize listeners object\n ExpoAudioStreamModule.listeners = {}\n\n // Add methods for event listeners that LegacyEventEmitter will use\n ExpoAudioStreamModule.addListener = (\n eventName: string,\n listener: Function\n ) => {\n if (!ExpoAudioStreamModule.listeners[eventName]) {\n ExpoAudioStreamModule.listeners[eventName] = []\n }\n ExpoAudioStreamModule.listeners[eventName].push(listener)\n\n // Return an object with a remove method\n return {\n remove: () => {\n const index =\n ExpoAudioStreamModule.listeners[eventName].indexOf(listener)\n if (index !== -1) {\n ExpoAudioStreamModule.listeners[eventName].splice(index, 1)\n }\n },\n }\n }\n\n ExpoAudioStreamModule.removeAllListeners = (eventName: string) => {\n if (ExpoAudioStreamModule.listeners[eventName]) {\n delete ExpoAudioStreamModule.listeners[eventName]\n }\n }\n}\n\n// Move the encodeCompressedAudio function outside the if block to fix the ESLint error\nasync function encodeCompressedAudio(\n buffer: AudioBuffer,\n format: 'opus' | 'aac',\n bitrate?: number\n): Promise<{ data: ArrayBuffer; bitrate: number }> {\n return new Promise((resolve, reject) => {\n try {\n // On web, always use opus if aac is requested\n const actualFormat =\n Platform.OS === 'web' && format === 'aac' ? 'opus' : format\n\n // Check if MediaRecorder supports the requested format\n const mimeType =\n actualFormat === 'opus' ? 'audio/webm;codecs=opus' : 'audio/aac'\n if (!MediaRecorder.isTypeSupported(mimeType)) {\n throw new Error(`MediaRecorder does not support ${mimeType}`)\n }\n\n // Create a new AudioContext and source\n const ctx = new (window.AudioContext ||\n (window as any).webkitAudioContext)()\n const source = ctx.createBufferSource()\n source.buffer = buffer\n\n // Create a MediaStreamDestination to capture the audio\n const destination = ctx.createMediaStreamDestination()\n source.connect(destination)\n\n // Create a MediaRecorder with the requested format\n const recorder = new MediaRecorder(destination.stream, {\n mimeType,\n audioBitsPerSecond:\n bitrate || (actualFormat === 'opus' ? 32000 : 64000),\n })\n\n const chunks: Blob[] = []\n\n recorder.ondataavailable = (e) => {\n if (e.data.size > 0) {\n chunks.push(e.data)\n }\n }\n\n recorder.onstop = async () => {\n try {\n const blob = new Blob(chunks, { type: mimeType })\n const arrayBuffer = await blob.arrayBuffer()\n\n // Get the actual bitrate used\n const actualBitrate = Math.round(\n (arrayBuffer.byteLength * 8) / buffer.duration\n )\n\n resolve({\n data: arrayBuffer,\n bitrate: actualBitrate / 1000, // Convert to kbps\n })\n\n // Clean up\n ctx.close()\n } catch (error) {\n reject(error)\n }\n }\n\n // Start recording and playback\n recorder.start()\n source.start(0)\n\n // Stop recording when the buffer finishes playing\n setTimeout(() => {\n recorder.stop()\n source.stop()\n }, buffer.duration * 1000)\n } catch (error) {\n reject(error)\n }\n })\n}\n\n// Improved resampleAudioBuffer function\nasync function resampleAudioBuffer(\n context: AudioContext,\n buffer: AudioBuffer,\n targetSampleRate: number,\n targetChannels: number\n): Promise<AudioBuffer> {\n // If no change needed, return the original buffer\n if (\n buffer.sampleRate === targetSampleRate &&\n buffer.numberOfChannels === targetChannels\n ) {\n return buffer\n }\n\n console.log(\n `Resampling: ${buffer.sampleRate}Hz → ${targetSampleRate}Hz, ${buffer.numberOfChannels} → ${targetChannels} channels`\n )\n\n // Calculate the new length based on the sample rate change\n const newLength = Math.round(\n (buffer.length * targetSampleRate) / buffer.sampleRate\n )\n\n // Create an offline context for resampling\n const offlineContext = new OfflineAudioContext(\n targetChannels,\n newLength,\n targetSampleRate\n )\n\n // Create a source node\n const source = offlineContext.createBufferSource()\n source.buffer = buffer\n\n // If we need to change channel count\n if (buffer.numberOfChannels !== targetChannels) {\n if (targetChannels === 1 && buffer.numberOfChannels > 1) {\n // Downmix to mono\n const merger = offlineContext.createChannelMerger(1)\n\n // Create a gain node to reduce volume when downmixing to prevent clipping\n const gainNode = offlineContext.createGain()\n gainNode.gain.value = 1.0 / buffer.numberOfChannels\n\n source.connect(gainNode)\n gainNode.connect(merger)\n merger.connect(offlineContext.destination)\n } else if (targetChannels === 2 && buffer.numberOfChannels === 1) {\n // Upmix mono to stereo (duplicate the channel)\n const splitter = offlineContext.createChannelSplitter(1)\n const merger = offlineContext.createChannelMerger(2)\n\n source.connect(splitter)\n splitter.connect(merger, 0, 0)\n splitter.connect(merger, 0, 1)\n merger.connect(offlineContext.destination)\n } else {\n // For other cases, just connect and let the system handle it\n source.connect(offlineContext.destination)\n }\n } else {\n // No channel conversion needed\n source.connect(offlineContext.destination)\n }\n\n // Start rendering\n source.start(0)\n const resampledBuffer = await offlineContext.startRendering()\n\n console.log(\n `Resampling complete: ${resampledBuffer.length} samples at ${resampledBuffer.sampleRate}Hz`\n )\n\n return resampledBuffer\n}\n\nif (Platform.OS !== 'web') {\n ExpoAudioStreamModule = requireNativeModule('ExpoAudioStream')\n}\n\nexport default ExpoAudioStreamModule\n"]}
@@ -0,0 +1,7 @@
1
+ interface CRC32 {
2
+ (data: string | Uint8Array): number;
3
+ buf(data: Uint8Array): number;
4
+ }
5
+ declare let crc32Implementation: CRC32;
6
+ export default crc32Implementation;
7
+ //# sourceMappingURL=crc32.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crc32.d.ts","sourceRoot":"","sources":["../../src/utils/crc32.ts"],"names":[],"mappings":"AAEA,UAAU,KAAK;IACX,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IACpC,GAAG,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;CACjC;AAED,QAAA,IAAI,mBAAmB,EAAE,KAAK,CAAA;AAa9B,eAAe,mBAAmB,CAAA"}
@@ -0,0 +1,12 @@
1
+ import { Platform } from 'react-native';
2
+ let crc32Implementation;
3
+ if (Platform.OS === 'web') {
4
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
5
+ crc32Implementation = require('crc-32');
6
+ }
7
+ else {
8
+ // No-op implementation for native platforms
9
+ crc32Implementation = Object.assign(() => 0, { buf: () => 0 });
10
+ }
11
+ export default crc32Implementation;
12
+ //# sourceMappingURL=crc32.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crc32.js","sourceRoot":"","sources":["../../src/utils/crc32.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAOvC,IAAI,mBAA0B,CAAA;AAE9B,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;IACxB,8DAA8D;IAC9D,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;AAC3C,CAAC;KAAM,CAAC;IACJ,4CAA4C;IAC5C,mBAAmB,GAAG,MAAM,CAAC,MAAM,CAC/B,GAAG,EAAE,CAAC,CAAC,EACP,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CACnB,CAAA;AACL,CAAC;AAED,eAAe,mBAAmB,CAAA","sourcesContent":["import { Platform } from 'react-native'\n\ninterface CRC32 {\n (data: string | Uint8Array): number;\n buf(data: Uint8Array): number;\n}\n\nlet crc32Implementation: CRC32\n\nif (Platform.OS === 'web') {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n crc32Implementation = require('crc-32')\n} else {\n // No-op implementation for native platforms\n crc32Implementation = Object.assign(\n () => 0,\n { buf: () => 0 }\n )\n}\n\nexport default crc32Implementation\n"]}
@@ -0,0 +1,2 @@
1
+ export default function crc32(): number;
2
+ //# sourceMappingURL=crc32.native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crc32.native.d.ts","sourceRoot":"","sources":["../../src/utils/crc32.native.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,OAAO,UAAU,KAAK,IAAI,MAAM,CAEtC"}
@@ -0,0 +1,5 @@
1
+ // No-op implementation for native platforms
2
+ export default function crc32() {
3
+ return 0;
4
+ }
5
+ //# sourceMappingURL=crc32.native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crc32.native.js","sourceRoot":"","sources":["../../src/utils/crc32.native.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,MAAM,CAAC,OAAO,UAAU,KAAK;IACzB,OAAO,CAAC,CAAC;AACb,CAAC","sourcesContent":["// No-op implementation for native platforms\nexport default function crc32(): number {\n return 0;\n}"]}
@@ -0,0 +1,4 @@
1
+ /// <reference path="../../src/types/crc-32.d.ts" />
2
+ import crc32 from 'crc-32';
3
+ export default crc32;
4
+ //# sourceMappingURL=crc32.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crc32.web.d.ts","sourceRoot":"","sources":["../../src/utils/crc32.web.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,QAAQ,CAAA;AAE1B,eAAe,KAAK,CAAA"}
@@ -0,0 +1,3 @@
1
+ import crc32 from 'crc-32';
2
+ export default crc32;
3
+ //# sourceMappingURL=crc32.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crc32.web.js","sourceRoot":"","sources":["../../src/utils/crc32.web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,QAAQ,CAAA;AAE1B,eAAe,KAAK,CAAA","sourcesContent":["import crc32 from 'crc-32'\n\nexport default crc32\n"]}
@@ -5,6 +5,9 @@ import Accelerate
5
5
  import AVFoundation
6
6
  import QuartzCore
7
7
 
8
+ // Constants
9
+ private let SILENCE_THRESHOLD_RMS: Float = 0.01
10
+
8
11
  public struct TrimResult {
9
12
  let uri: String
10
13
  let filename: String
@@ -416,7 +419,7 @@ public class AudioProcessor {
416
419
  ) -> DataPoint {
417
420
  let sumSquares: Float = segment.reduce(0) { $0 + $1 * $1 }
418
421
  let rms = sqrt(sumSquares / Float(segment.count))
419
- let silent = rms < 0.01
422
+ let silent = rms < SILENCE_THRESHOLD_RMS
420
423
  let dB = Float(20 * log10(Double(rms)))
421
424
 
422
425
  let features = computeFeatures(
@@ -590,7 +593,7 @@ public class AudioProcessor {
590
593
  amplitude: localMax, // Always use peak amplitude
591
594
  rms: rms, // Use calculated RMS value
592
595
  dB: Float(20 * log10(Double(rms))), // Use RMS for dB calculation
593
- silent: rms < 0.01, // Use RMS for silence detection
596
+ silent: rms < SILENCE_THRESHOLD_RMS, // Use RMS for silence detection
594
597
  features: computeFeatures(
595
598
  segmentData: Array(UnsafeBufferPointer(start: summedData, count: Int(framesToRead))),
596
599
  sampleRate: sampleRate,
@@ -599,7 +602,7 @@ public class AudioProcessor {
599
602
  segmentLength: Int(framesToRead),
600
603
  featureOptions: featureOptions
601
604
  ),
602
- speech: SpeechFeatures(isActive: rms >= 0.01),
605
+ speech: SpeechFeatures(isActive: rms >= SILENCE_THRESHOLD_RMS),
603
606
  startTime: startTime,
604
607
  endTime: endTime,
605
608
  startPosition: Int(currentFrame),
@@ -1229,7 +1232,7 @@ public class AudioProcessor {
1229
1232
  featureOptions: featureOptions)
1230
1233
 
1231
1234
  let rms = features.rms
1232
- let silent = rms < 0.01
1235
+ let silent = rms < SILENCE_THRESHOLD_RMS
1233
1236
  let dB = Float(20 * log10(Double(rms)))
1234
1237
 
1235
1238
  let dataPoint = DataPoint(
@@ -12,6 +12,9 @@ import UIKit
12
12
  import MediaPlayer
13
13
  import UserNotifications
14
14
 
15
+ // Constants
16
+ internal let WAV_HEADER_SIZE: Int64 = 44 // Standard WAV header is 44 bytes
17
+
15
18
  // Helper to convert to little-endian byte array
16
19
  extension UInt32 {
17
20
  var littleEndianBytes: [UInt8] {
@@ -458,9 +461,8 @@ class AudioStreamManager: NSObject {
458
461
  let baseFilename: String
459
462
  if let existingFilename = recordingSettings?.filename {
460
463
  baseFilename = existingFilename
461
- } else if let existingUUID = recordingUUID {
462
- baseFilename = existingUUID.uuidString
463
464
  } else {
465
+ // Always create a new UUID for recording unless a filename is provided
464
466
  let newUUID = UUID()
465
467
  recordingUUID = newUUID
466
468
  baseFilename = newUUID.uuidString
@@ -1096,17 +1098,17 @@ class AudioStreamManager: NSObject {
1096
1098
  - Exists: true
1097
1099
  - Size: \(wavFileSize) bytes
1098
1100
  - Duration: \(finalDuration) seconds
1099
- - Expected minimum size: 44 bytes (WAV header)
1101
+ - Expected minimum size: \(WAV_HEADER_SIZE) bytes (WAV header)
1100
1102
  """)
1101
1103
 
1102
1104
  // Return nil if the file is too small
1103
- if wavFileSize <= 44 {
1104
- Logger.debug("Recording file is too small (≤ 44 bytes), likely no audio data was recorded")
1105
+ if wavFileSize <= WAV_HEADER_SIZE {
1106
+ Logger.debug("Recording file is too small (≤ \(WAV_HEADER_SIZE) bytes), likely no audio data was recorded")
1105
1107
  return nil
1106
1108
  }
1107
1109
 
1108
1110
  // Update the WAV header with the correct file size
1109
- updateWavHeader(fileURL: fileURL, totalDataSize: wavFileSize - 44)
1111
+ updateWavHeader(fileURL: fileURL, totalDataSize: wavFileSize - WAV_HEADER_SIZE)
1110
1112
 
1111
1113
  // Validate compressed file if enabled
1112
1114
  var compression: CompressedRecordingInfo?
@@ -1372,7 +1374,7 @@ class AudioStreamManager: NSObject {
1372
1374
  /// - fileURL: The URL of the WAV file.
1373
1375
  /// - totalDataSize: The total size of the audio data.
1374
1376
  private func updateWavHeader(fileURL: URL, totalDataSize: Int64) {
1375
- // Prevent negative values - minimum WAV file size should be at least the header size (44 bytes)
1377
+ // Prevent negative values - minimum WAV file size should be at least the header size (WAV_HEADER_SIZE bytes)
1376
1378
  guard totalDataSize >= 0 else {
1377
1379
  Logger.debug("Invalid file size: total data size is negative")
1378
1380
  return
@@ -1383,7 +1385,7 @@ class AudioStreamManager: NSObject {
1383
1385
  defer { fileHandle.closeFile() }
1384
1386
 
1385
1387
  // Calculate sizes
1386
- let fileSize = totalDataSize + 44 - 8 // Total file size minus 8 bytes for 'RIFF' and size field itself
1388
+ let fileSize = totalDataSize + WAV_HEADER_SIZE - 8 // Total file size minus 8 bytes for 'RIFF' and size field itself
1387
1389
  let dataSize = totalDataSize // Size of the 'data' sub-chunk
1388
1390
 
1389
1391
  // Update RIFF chunk size at offset 4
@@ -1585,8 +1587,19 @@ class AudioStreamManager: NSObject {
1585
1587
  guard let self = self else { return }
1586
1588
  if let processor = self.audioProcessor {
1587
1589
  Logger.debug("Processing audio buffer of size: \(dataToProcess.count)")
1590
+
1591
+ // Strip WAV header from the first buffer to avoid false amplitude detection
1592
+ let dataToAnalyze: Data
1593
+ if self.totalDataSizeAnalysis == 0 && dataToProcess.count > Int(WAV_HEADER_SIZE) {
1594
+ // This is the first buffer and may contain the WAV header
1595
+ dataToAnalyze = dataToProcess.subdata(in: Int(WAV_HEADER_SIZE)..<dataToProcess.count)
1596
+ Logger.debug("Removed WAV header (\(WAV_HEADER_SIZE) bytes) from first buffer for analysis")
1597
+ } else {
1598
+ dataToAnalyze = dataToProcess
1599
+ }
1600
+
1588
1601
  let processingResult = processor.processAudioBuffer(
1589
- data: dataToProcess,
1602
+ data: dataToAnalyze,
1590
1603
  sampleRate: Float(settings.sampleRate),
1591
1604
  segmentDurationMs: settings.segmentDurationMs,
1592
1605
  featureOptions: settings.featureOptions ?? [:],
@@ -1600,9 +1613,10 @@ class AudioStreamManager: NSObject {
1600
1613
  }
1601
1614
  }
1602
1615
 
1603
- // Update state after emission
1616
+ // Update state after emission
1604
1617
  self.lastEmissionTimeAnalysis = currentTime
1605
- self.lastEmittedSizeAnalysis = totalDataSizeAnalysis
1618
+ // Update the total analysis data size to mark that we've processed data
1619
+ self.totalDataSizeAnalysis = self.totalDataSize
1606
1620
  accumulatedAnalysisData.removeAll()
1607
1621
  }
1608
1622
  }
@@ -8,12 +8,24 @@
8
8
  import AVFoundation
9
9
 
10
10
  public struct DecodingConfig {
11
- let targetSampleRate: Double?
12
- let targetChannels: Int?
13
- let targetBitDepth: Int?
14
- let normalizeAudio: Bool
11
+ public let targetSampleRate: Double?
12
+ public let targetChannels: Int?
13
+ public let targetBitDepth: Int?
14
+ public let normalizeAudio: Bool
15
15
 
16
- static func fromDictionary(_ dict: [String: Any]?) -> DecodingConfig {
16
+ public init(
17
+ targetSampleRate: Double?,
18
+ targetChannels: Int?,
19
+ targetBitDepth: Int?,
20
+ normalizeAudio: Bool = false
21
+ ) {
22
+ self.targetSampleRate = targetSampleRate
23
+ self.targetChannels = targetChannels
24
+ self.targetBitDepth = targetBitDepth
25
+ self.normalizeAudio = normalizeAudio
26
+ }
27
+
28
+ public static func fromDictionary(_ dict: [String: Any]?) -> DecodingConfig {
17
29
  guard let dict = dict else {
18
30
  return DecodingConfig.default
19
31
  }
@@ -26,7 +38,7 @@ public struct DecodingConfig {
26
38
  )
27
39
  }
28
40
 
29
- static var `default`: DecodingConfig {
41
+ public static var `default`: DecodingConfig {
30
42
  return DecodingConfig(
31
43
  targetSampleRate: nil,
32
44
  targetChannels: nil,
@@ -35,7 +47,7 @@ public struct DecodingConfig {
35
47
  )
36
48
  }
37
49
 
38
- func toAudioFormat(baseFormat: AVAudioFormat) -> AVAudioFormat {
50
+ public func toAudioFormat(baseFormat: AVAudioFormat) -> AVAudioFormat {
39
51
  let sampleRate = targetSampleRate ?? baseFormat.sampleRate
40
52
  let channels = targetChannels ?? Int(baseFormat.channelCount)
41
53
 
@@ -10,7 +10,7 @@ Pod::Spec.new do |s|
10
10
  s.license = package['license']
11
11
  s.author = package['author']
12
12
  s.homepage = package['homepage']
13
- s.platforms = { :ios => '13.4', :tvos => '13.4' }
13
+ s.platforms = { :ios => '15.1', :tvos => '15.1' }
14
14
  s.swift_version = '5.4'
15
15
  s.source = { git: 'https://github.com/deeeed/expo-audio-stream' }
16
16
  s.static_framework = true
@@ -2,9 +2,11 @@
2
2
  import ExpoModulesCore
3
3
  import AVFoundation
4
4
 
5
- let audioDataEvent: String = "AudioData"
6
- let audioAnalysisEvent: String = "AudioAnalysis"
7
- let recordingInterruptedEvent: String = "onRecordingInterrupted"
5
+ // Constants
6
+ private let audioDataEvent: String = "AudioData"
7
+ private let audioAnalysisEvent: String = "AudioAnalysis"
8
+ private let recordingInterruptedEvent: String = "onRecordingInterrupted"
9
+ private let DEFAULT_SEGMENT_DURATION_MS = 100
8
10
 
9
11
  public class ExpoAudioStreamModule: Module, AudioStreamManagerDelegate {
10
12
  private var streamManager = AudioStreamManager()
@@ -60,7 +62,7 @@ public class ExpoAudioStreamModule: Module, AudioStreamManagerDelegate {
60
62
 
61
63
  let features = options["features"] as? [String: Bool] ?? [:]
62
64
  let featureOptions = self.extractFeatureOptions(from: features)
63
- let segmentDurationMs = options["segmentDurationMs"] as? Int ?? 100 // Default value of 100ms
65
+ let segmentDurationMs = options["segmentDurationMs"] as? Int ?? DEFAULT_SEGMENT_DURATION_MS // Default value of 100ms
64
66
 
65
67
  DispatchQueue.global().async(execute: {
66
68
  do {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siteed/expo-audio-studio",
3
- "version": "2.1.1",
3
+ "version": "2.3.0",
4
4
  "description": "Comprehensive audio processing library for React Native and Expo with recording, analysis, visualization, and streaming capabilities across iOS, Android, and web",
5
5
  "license": "MIT",
6
6
  "main": "build/index.js",
@@ -116,13 +116,16 @@
116
116
  "react": "*",
117
117
  "react-native": "*"
118
118
  },
119
+ "peerDependenciesMeta": {
120
+ "crc-32": {
121
+ "optional": true
122
+ }
123
+ },
119
124
  "publishConfig": {
120
125
  "access": "public",
121
126
  "registry": "https://registry.npmjs.org"
122
127
  },
123
128
  "dependencies": {
124
- "@siteed/design-system": "^0.35.1",
125
- "crc-32": "^1.2.2",
126
129
  "expo-modules-core": "~2.1.4"
127
130
  }
128
131
  }
@@ -113,10 +113,9 @@ const withRecordingPermission = (config, props) => {
113
113
  ];
114
114
  const optionalPermissions = [
115
115
  enableNotifications && 'android.permission.POST_NOTIFICATIONS',
116
- enablePhoneStateHandling && 'android.permission.READ_PHONE_STATE',
116
+ enablePhoneStateHandling && 'android.permission.READ_PHONE_STATE', // Only add if enabled
117
117
  enableBackgroundAudio && 'android.permission.FOREGROUND_SERVICE',
118
- enableBackgroundAudio &&
119
- 'android.permission.FOREGROUND_SERVICE_MICROPHONE',
118
+ enableBackgroundAudio && 'android.permission.FOREGROUND_SERVICE_MICROPHONE',
120
119
  ].filter(Boolean);
121
120
  const permissionsToAdd = [...basePermissions, ...optionalPermissions];
122
121
  debugLog('📋 Existing Android permissions:', config.modResults.manifest['uses-permission']?.map((p) => p.$?.['android:name']) || []);
@@ -169,10 +169,9 @@ const withRecordingPermission: ConfigPlugin<AudioStreamPluginOptions> = (
169
169
 
170
170
  const optionalPermissions = [
171
171
  enableNotifications && 'android.permission.POST_NOTIFICATIONS',
172
- enablePhoneStateHandling && 'android.permission.READ_PHONE_STATE',
172
+ enablePhoneStateHandling && 'android.permission.READ_PHONE_STATE', // Only add if enabled
173
173
  enableBackgroundAudio && 'android.permission.FOREGROUND_SERVICE',
174
- enableBackgroundAudio &&
175
- 'android.permission.FOREGROUND_SERVICE_MICROPHONE',
174
+ enableBackgroundAudio && 'android.permission.FOREGROUND_SERVICE_MICROPHONE',
176
175
  ].filter(Boolean) as string[]
177
176
 
178
177
  const permissionsToAdd = [...basePermissions, ...optionalPermissions]
@@ -5,8 +5,6 @@
5
5
  * - `extractWavAudioAnalysis`: For analyzing WAV files without decoding, preserving original PCM values.
6
6
  * - `extractPreview`: For generating quick previews of audio waveforms, optimized for UI rendering.
7
7
  */
8
- import crc32 from 'crc-32'
9
-
10
8
  import { ConsoleLike } from '../ExpoAudioStream.types'
11
9
  import ExpoAudioStreamModule from '../ExpoAudioStreamModule'
12
10
  import { isWeb } from '../constants'
@@ -18,6 +16,7 @@ import {
18
16
  } from './AudioAnalysis.types'
19
17
  import { processAudioBuffer } from '../utils/audioProcessing'
20
18
  import { convertPCMToFloat32 } from '../utils/convertPCMToFloat32'
19
+ import crc32 from '../utils/crc32'
21
20
  import { getWavFileInfo, WavFileInfo } from '../utils/getWavFileInfo'
22
21
  import { InlineFeaturesExtractor } from '../workers/InlineFeaturesExtractor.web'
23
22
 
@@ -16,7 +16,7 @@ import {
16
16
 
17
17
  /**
18
18
  * Extracts a mel spectrogram from audio data
19
- *
19
+ *
20
20
  * @experimental This feature is experimental and currently only available on Android.
21
21
  * The iOS implementation will throw an "UNSUPPORTED_PLATFORM" error.
22
22
  * The web implementation is a placeholder that returns dummy data.
@@ -105,7 +105,7 @@ export async function extractMelSpectrogram(
105
105
 
106
106
  /**
107
107
  * Computes a mel spectrogram from audio data
108
- *
108
+ *
109
109
  * @experimental This is a placeholder implementation that returns dummy data.
110
110
  * The actual implementation will be added in a future version.
111
111
  */
@@ -1,4 +1,3 @@
1
- import crc32 from 'crc-32'
2
1
  import { requireNativeModule } from 'expo-modules-core'
3
2
  import { Platform } from 'react-native'
4
3
 
@@ -14,6 +13,7 @@ import {
14
13
  ExpoAudioStreamWebProps,
15
14
  } from './ExpoAudioStream.web'
16
15
  import { processAudioBuffer } from './utils/audioProcessing'
16
+ import crc32 from './utils/crc32'
17
17
  import { writeWavHeader } from './utils/writeWavHeader'
18
18
 
19
19
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -0,0 +1,9 @@
1
+ declare module 'crc-32' {
2
+ interface CRC32 {
3
+ (data: string | Uint8Array): number;
4
+ buf(data: Uint8Array): number;
5
+ }
6
+
7
+ const crc32: CRC32;
8
+ export default crc32;
9
+ }
@@ -0,0 +1,4 @@
1
+ // No-op implementation for native platforms
2
+ export default function crc32(): number {
3
+ return 0;
4
+ }
@@ -0,0 +1,21 @@
1
+ import { Platform } from 'react-native'
2
+
3
+ interface CRC32 {
4
+ (data: string | Uint8Array): number;
5
+ buf(data: Uint8Array): number;
6
+ }
7
+
8
+ let crc32Implementation: CRC32
9
+
10
+ if (Platform.OS === 'web') {
11
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
12
+ crc32Implementation = require('crc-32')
13
+ } else {
14
+ // No-op implementation for native platforms
15
+ crc32Implementation = Object.assign(
16
+ () => 0,
17
+ { buf: () => 0 }
18
+ )
19
+ }
20
+
21
+ export default crc32Implementation
@@ -0,0 +1,3 @@
1
+ import crc32 from 'crc-32'
2
+
3
+ export default crc32