@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.
- package/CHANGELOG.md +14 -1
- package/android/src/main/AndroidManifest.xml +1 -1
- package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +8 -4
- package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +23 -16
- package/build/AudioAnalysis/extractAudioAnalysis.d.ts +6 -0
- package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -1
- package/build/AudioAnalysis/extractAudioAnalysis.js +1 -8
- package/build/AudioAnalysis/extractAudioAnalysis.js.map +1 -1
- package/build/AudioAnalysis/extractMelSpectrogram.js.map +1 -1
- package/build/ExpoAudioStreamModule.js +1 -1
- package/build/ExpoAudioStreamModule.js.map +1 -1
- package/build/utils/crc32.d.ts +7 -0
- package/build/utils/crc32.d.ts.map +1 -0
- package/build/utils/crc32.js +12 -0
- package/build/utils/crc32.js.map +1 -0
- package/build/utils/crc32.native.d.ts +2 -0
- package/build/utils/crc32.native.d.ts.map +1 -0
- package/build/utils/crc32.native.js +5 -0
- package/build/utils/crc32.native.js.map +1 -0
- package/build/utils/crc32.web.d.ts +4 -0
- package/build/utils/crc32.web.d.ts.map +1 -0
- package/build/utils/crc32.web.js +3 -0
- package/build/utils/crc32.web.js.map +1 -0
- package/ios/AudioProcessor.swift +7 -4
- package/ios/AudioStreamManager.swift +25 -11
- package/ios/DecodingConfig.swift +19 -7
- package/ios/ExpoAudioStream.podspec +1 -1
- package/ios/ExpoAudioStreamModule.swift +6 -4
- package/package.json +6 -3
- package/plugin/build/index.js +2 -3
- package/plugin/src/index.ts +2 -3
- package/src/AudioAnalysis/extractAudioAnalysis.ts +1 -2
- package/src/AudioAnalysis/extractMelSpectrogram.ts +2 -2
- package/src/ExpoAudioStreamModule.ts +1 -1
- package/src/types/crc-32.d.ts +9 -0
- package/src/utils/crc32.native.ts +4 -0
- package/src/utils/crc32.ts +21 -0
- 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.
|
|
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
|
-
|
|
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
|
|
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
|
|
718
|
-
|
|
719
|
-
val
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
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
|
-
|
|
728
|
-
this
|
|
733
|
+
audioDataEncoder,
|
|
734
|
+
this,
|
|
735
|
+
enablePhoneStateHandling
|
|
729
736
|
)
|
|
730
|
-
audioProcessor = AudioProcessor(
|
|
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":"
|
|
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
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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"]}
|
package/ios/AudioProcessor.swift
CHANGED
|
@@ -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 <
|
|
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 <
|
|
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 >=
|
|
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 <
|
|
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:
|
|
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 <=
|
|
1104
|
-
Logger.debug("Recording file is too small (≤
|
|
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 -
|
|
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 (
|
|
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 +
|
|
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:
|
|
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
|
-
|
|
1616
|
+
// Update state after emission
|
|
1604
1617
|
self.lastEmissionTimeAnalysis = currentTime
|
|
1605
|
-
|
|
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
|
}
|
package/ios/DecodingConfig.swift
CHANGED
|
@@ -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
|
-
|
|
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
|
+
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
|
-
|
|
6
|
-
let
|
|
7
|
-
let
|
|
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 ??
|
|
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.
|
|
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
|
}
|
package/plugin/build/index.js
CHANGED
|
@@ -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']) || []);
|
package/plugin/src/index.ts
CHANGED
|
@@ -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,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
|