@siteed/expo-audio-studio 2.9.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/android/build.gradle +9 -0
  3. package/android/src/androidTest/assets/chorus.wav +0 -0
  4. package/android/src/androidTest/assets/jfk.wav +0 -0
  5. package/android/src/androidTest/assets/osr_us_000_0010_8k.wav +0 -0
  6. package/android/src/androidTest/assets/recorder_hello_world.wav +0 -0
  7. package/android/src/androidTest/java/net/siteed/audiostream/AudioProcessorInstrumentedTest.kt +197 -0
  8. package/android/src/androidTest/java/net/siteed/audiostream/AudioRecorderInstrumentedTest.kt +541 -0
  9. package/android/src/androidTest/java/net/siteed/audiostream/integration/BufferDurationIntegrationTest.kt +324 -0
  10. package/android/src/androidTest/java/net/siteed/audiostream/integration/OutputControlIntegrationTest.kt +340 -0
  11. package/android/src/androidTest/java/net/siteed/audiostream/integration/README.md +95 -0
  12. package/android/src/androidTest/java/net/siteed/audiostream/integration/run_integration_tests.sh +28 -0
  13. package/android/src/main/java/net/siteed/audiostream/AudioFormatUtils.kt +264 -13
  14. package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +3 -13
  15. package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +118 -55
  16. package/android/src/main/java/net/siteed/audiostream/LogUtils.kt +32 -4
  17. package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +50 -15
  18. package/android/src/test/java/net/siteed/audiostream/AudioFileHandlerTest.kt +279 -0
  19. package/android/src/test/java/net/siteed/audiostream/AudioFormatUtilsTest.kt +273 -0
  20. package/android/src/test/resources/chorus.wav +0 -0
  21. package/android/src/test/resources/generate_test_audio.py +94 -0
  22. package/android/src/test/resources/jfk.wav +0 -0
  23. package/android/src/test/resources/osr_us_000_0010_8k.wav +0 -0
  24. package/android/src/test/resources/recorder_hello_world.wav +0 -0
  25. package/build/cjs/ExpoAudioStream.types.js.map +1 -1
  26. package/build/cjs/ExpoAudioStream.web.js +37 -34
  27. package/build/cjs/ExpoAudioStream.web.js.map +1 -1
  28. package/build/cjs/WebRecorder.web.js +12 -10
  29. package/build/cjs/WebRecorder.web.js.map +1 -1
  30. package/build/esm/ExpoAudioStream.types.js.map +1 -1
  31. package/build/esm/ExpoAudioStream.web.js +37 -34
  32. package/build/esm/ExpoAudioStream.web.js.map +1 -1
  33. package/build/esm/WebRecorder.web.js +12 -10
  34. package/build/esm/WebRecorder.web.js.map +1 -1
  35. package/build/types/ExpoAudioStream.types.d.ts +54 -22
  36. package/build/types/ExpoAudioStream.types.d.ts.map +1 -1
  37. package/build/types/ExpoAudioStream.web.d.ts.map +1 -1
  38. package/build/types/WebRecorder.web.d.ts.map +1 -1
  39. package/ios/AudioNotificationManager.swift +2 -6
  40. package/ios/AudioStreamManager.swift +116 -50
  41. package/ios/ExpoAudioStream.podspec +6 -0
  42. package/ios/ExpoAudioStreamModule.swift +11 -8
  43. package/ios/ExpoAudioStudioTests/AudioFileHandlerTests.swift +338 -0
  44. package/ios/ExpoAudioStudioTests/AudioFormatUtilsTests.swift +331 -0
  45. package/ios/ExpoAudioStudioTests/AudioTestHelpers.swift +130 -0
  46. package/ios/ExpoAudioStudioTests/Info.plist +22 -0
  47. package/ios/ExpoAudioStudioTests/SimpleAudioTest.swift +98 -0
  48. package/ios/ExpoAudioStudioTests/TestAudioGenerator.swift +75 -0
  49. package/ios/RecordingSettings.swift +53 -22
  50. package/ios/tests/integration/buffer_duration_test.swift +185 -0
  51. package/ios/tests/integration/output_control_test.swift +322 -0
  52. package/ios/tests/integration/run_integration_tests.sh +27 -0
  53. package/ios/tests/standalone/audio_processing_test.swift +144 -0
  54. package/ios/tests/standalone/audio_recording_test.swift +277 -0
  55. package/ios/tests/standalone/audio_streaming_test.swift +249 -0
  56. package/ios/tests/standalone/standalone_test.swift +144 -0
  57. package/package.json +140 -133
  58. package/src/ExpoAudioStream.types.ts +66 -22
  59. package/src/ExpoAudioStream.web.ts +43 -38
  60. package/src/WebRecorder.web.ts +13 -10
  61. package/android/src/main/test/java/net/siteed/audiostream/AudioProcessorTest.kt +0 -56
  62. package/ios/siteedexpoaudiostudio.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  63. package/ios/siteedexpoaudiostudio.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  64. /package/plugin/build/{index.d.ts → index.d.cts} +0 -0
@@ -285,13 +285,15 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
285
285
  bitDepth: this.bitDepth,
286
286
  channels: recordingConfig.channels ?? 1,
287
287
  sampleRate: recordingConfig.sampleRate ?? 44100,
288
- compression: recordingConfig.compression
288
+ compression: recordingConfig.output?.compressed?.enabled
289
289
  ? {
290
- ...recordingConfig.compression,
291
- bitrate: recordingConfig.compression?.bitrate ?? 128000,
290
+ ...recordingConfig.output.compressed,
291
+ bitrate:
292
+ recordingConfig.output.compressed.bitrate ?? 128000,
292
293
  size: 0,
293
294
  mimeType: 'audio/webm',
294
- format: recordingConfig.compression?.format ?? 'opus',
295
+ format:
296
+ recordingConfig.output.compressed.format ?? 'opus',
295
297
  compressedFileUri: '',
296
298
  }
297
299
  : undefined,
@@ -459,56 +461,56 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
459
461
  let fileUri = `${this.streamUuid}.${this.extension}`
460
462
  let mimeType = `audio/${this.extension}`
461
463
 
462
- // Handle both compressed and uncompressed blobs according to configuration
463
- const compressionEnabled =
464
- this.recordingConfig?.compression?.enabled ?? false
464
+ // Handle both compressed and uncompressed blobs according to new output configuration
465
+ const primaryEnabled =
466
+ this.recordingConfig?.output?.primary?.enabled ?? true
467
+ const compressedEnabled =
468
+ this.recordingConfig?.output?.compressed?.enabled ?? false
465
469
 
466
- // Process compressed blob if available
467
- if (compressedBlob) {
470
+ // Process compressed blob if available and enabled
471
+ if (compressedBlob && compressedEnabled) {
468
472
  const compressedUri = URL.createObjectURL(compressedBlob)
469
473
  const compressedInfo = {
470
474
  compressedFileUri: compressedUri,
471
475
  size: compressedBlob.size,
472
476
  mimeType: 'audio/webm',
473
- format: 'opus',
477
+ format:
478
+ this.recordingConfig?.output?.compressed?.format ??
479
+ 'opus',
474
480
  bitrate:
475
- this.recordingConfig?.compression?.bitrate ?? 128000,
481
+ this.recordingConfig?.output?.compressed?.bitrate ??
482
+ 128000,
476
483
  }
477
484
 
478
- // If compression is enabled, use compressed blob as primary format
479
- if (compressionEnabled) {
485
+ // Store compression info
486
+ compression = compressedInfo
487
+
488
+ // If primary is disabled, use compressed as main file
489
+ if (!primaryEnabled) {
480
490
  this.logger?.debug(
481
- 'Using compressed audio as primary output'
491
+ 'Using compressed audio as primary output (primary disabled)'
482
492
  )
483
493
  fileUri = compressedUri
484
494
  mimeType = 'audio/webm'
485
-
486
- // Store compression info
487
- compression = compressedInfo
488
- } else {
489
- // Compression was enabled during recording but not set as primary
490
- // Store as alternate format
491
- compression = compressedInfo
492
495
  }
493
496
  }
494
497
 
495
- // Process uncompressed WAV if available
496
- if (uncompressedBlob) {
498
+ // Process uncompressed WAV if available and primary is enabled
499
+ if (uncompressedBlob && primaryEnabled) {
497
500
  const wavUri = URL.createObjectURL(uncompressedBlob)
498
-
499
- // If compression is disabled or no compressed blob is available,
500
- // use WAV as primary format
501
- if (!compressionEnabled || !compressedBlob) {
502
- this.logger?.debug(
503
- 'Using uncompressed WAV as primary output'
504
- )
505
- fileUri = wavUri
506
- mimeType = 'audio/wav'
507
- }
501
+ fileUri = wavUri
502
+ mimeType = 'audio/wav'
503
+ } else if (!primaryEnabled && !compressedEnabled) {
504
+ // No outputs enabled - streaming only mode
505
+ this.logger?.debug('No outputs enabled - streaming only mode')
506
+ fileUri = ''
507
+ mimeType = 'audio/wav'
508
508
  }
509
509
 
510
510
  // Use the stored streamUuid for the final filename
511
- const filename = `${this.streamUuid}.${this.extension}`
511
+ const filename = fileUri
512
+ ? `${this.streamUuid}.${this.extension}`
513
+ : 'stream-only'
512
514
  const result: AudioRecording = {
513
515
  fileUri,
514
516
  filename,
@@ -517,7 +519,7 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
517
519
  channels: this.recordingConfig?.channels ?? 1,
518
520
  sampleRate: this.recordingConfig?.sampleRate ?? 44100,
519
521
  durationMs: this.currentDurationMs,
520
- size: this.currentSize,
522
+ size: primaryEnabled ? this.currentSize : 0,
521
523
  mimeType,
522
524
  compression,
523
525
  }
@@ -630,13 +632,16 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
630
632
  interval: this.currentInterval,
631
633
  intervalAnalysis: this.currentIntervalAnalysis,
632
634
  mimeType: `audio/${this.extension}`,
633
- compression: this.recordingConfig?.compression?.enabled
635
+ compression: this.recordingConfig?.output?.compressed?.enabled
634
636
  ? {
635
637
  size: this.totalCompressedSize,
636
638
  mimeType: 'audio/webm',
637
- format: this.recordingConfig.compression.format ?? 'opus',
639
+ format:
640
+ this.recordingConfig.output.compressed.format ??
641
+ 'opus',
638
642
  bitrate:
639
- this.recordingConfig.compression.bitrate ?? 128000,
643
+ this.recordingConfig.output.compressed.bitrate ??
644
+ 128000,
640
645
  compressedFileUri: `${this.streamUuid}.webm`,
641
646
  }
642
647
  : undefined,
@@ -154,7 +154,7 @@ export class WebRecorder {
154
154
  }
155
155
 
156
156
  // Initialize compressed recording if enabled
157
- if (recordingConfig.compression?.enabled) {
157
+ if (recordingConfig.output?.compressed?.enabled) {
158
158
  this.initializeCompressedRecorder()
159
159
  }
160
160
 
@@ -234,9 +234,9 @@ export class WebRecorder {
234
234
  )
235
235
  const samples = chunk.length // Number of samples in this chunk
236
236
 
237
- // Only store PCM data if web.storeUncompressedAudio is not explicitly false
237
+ // Only store PCM data if primary output is enabled
238
238
  const shouldStoreUncompressed =
239
- this.config.web?.storeUncompressedAudio !== false
239
+ this.config.output?.primary?.enabled ?? true
240
240
 
241
241
  // Store PCM chunks when needed - this is for the final WAV file
242
242
  if (shouldStoreUncompressed) {
@@ -275,9 +275,12 @@ export class WebRecorder {
275
275
  size: this.pendingCompressedChunk.size,
276
276
  totalSize: this.compressedSize,
277
277
  mimeType: 'audio/webm',
278
- format: 'opus',
278
+ format:
279
+ this.config.output?.compressed?.format ??
280
+ 'opus',
279
281
  bitrate:
280
- this.config.compression?.bitrate ?? 128000,
282
+ this.config.output?.compressed?.bitrate ??
283
+ 128000,
281
284
  }
282
285
  : undefined
283
286
 
@@ -312,11 +315,11 @@ export class WebRecorder {
312
315
  interval,
313
316
  position: this.position,
314
317
  deviceId: this.config.deviceId ?? 'default',
315
- compression: this.config.compression
318
+ compression: this.config.output?.compressed
316
319
  ? {
317
- enabled: this.config.compression.enabled,
318
- format: this.config.compression.format,
319
- bitrate: this.config.compression.bitrate,
320
+ enabled: this.config.output.compressed.enabled,
321
+ format: this.config.output.compressed.format,
322
+ bitrate: this.config.output.compressed.bitrate,
320
323
  }
321
324
  : 'disabled',
322
325
  })
@@ -843,7 +846,7 @@ export class WebRecorder {
843
846
  {
844
847
  mimeType,
845
848
  audioBitsPerSecond:
846
- this.config.compression?.bitrate ?? 128000,
849
+ this.config.output?.compressed?.bitrate ?? 128000,
847
850
  }
848
851
  )
849
852
 
@@ -1,56 +0,0 @@
1
- package net.siteed.audiostream
2
-
3
- import org.junit.Test
4
- import org.junit.Assert.*
5
-
6
- class AudioProcessorTest {
7
-
8
- private val sampleRate = 44100
9
- private val channels = 1
10
- private val encoding = "pcm_16bit"
11
- private val pointsPerSecond = 1000
12
- private val algorithm = "rms"
13
- private val features = mapOf("rms" to true, "zcr" to true)
14
-
15
- private val recordingConfig = RecordingConfig(
16
- sampleRate = sampleRate,
17
- channels = channels,
18
- encoding = encoding,
19
- interval = 1000,
20
- enableProcessing = true,
21
- pointsPerSecond = pointsPerSecond,
22
- algorithm = algorithm,
23
- features = features
24
- )
25
-
26
- private val audioProcessor = AudioProcessor()
27
-
28
- @Test
29
- fun testProcessAudioData() {
30
- val data = generateSineWave(440.0, sampleRate, 2.0)
31
-
32
- val result = audioProcessor.processAudioData(data, recordingConfig)
33
-
34
- assertNotNull(result)
35
- assertEquals(pointsPerSecond, result.pointsPerSecond)
36
- assertEquals((data.size / sampleRate) * 1000, result.durationMs.toInt())
37
- assertEquals(16, result.bitDepth)
38
- assertEquals(channels, result.numberOfChannels)
39
- assertEquals(sampleRate, result.sampleRate.toInt())
40
- }
41
-
42
- // Helper function to generate a sine wave
43
- private fun generateSineWave(frequency: Double, sampleRate: Int, durationSeconds: Double): ByteArray {
44
- val numSamples = (sampleRate * durationSeconds).toInt()
45
- val output = ByteArray(numSamples * 2) // 16-bit PCM
46
-
47
- for (i in 0 until numSamples) {
48
- val time = i / sampleRate.toDouble()
49
- val amplitude = (Math.sin(2.0 * Math.PI * frequency * time) * 32767).toInt()
50
- output[i * 2] = (amplitude and 0xff).toByte()
51
- output[i * 2 + 1] = ((amplitude shr 8) and 0xff).toByte()
52
- }
53
-
54
- return output
55
- }
56
- }
@@ -1,7 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <Workspace
3
- version = "1.0">
4
- <FileRef
5
- location = "self:">
6
- </FileRef>
7
- </Workspace>
@@ -1,8 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>IDEDidComputeMac32BitWarning</key>
6
- <true/>
7
- </dict>
8
- </plist>
File without changes