@siteed/expo-audio-studio 2.18.0 → 2.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  ## [Unreleased]
9
9
 
10
10
 
11
+ ## [2.18.1] - 2025-08-02
12
+ ### Changed
13
+ - feat: improved memory monitoring ([55dfe16](https://github.com/deeeed/expo-audio-stream/commit/55dfe16d7e8c372392738d1441776a760e7ecdbe))
14
+ - chore(expo-audio-studio): release @siteed/expo-audio-studio@2.18.0 ([cc80ac5](https://github.com/deeeed/expo-audio-stream/commit/cc80ac5fa7ece05fc9fae031f101163acce2aff4))
11
15
  ## [2.18.0] - 2025-08-01
12
16
  ### Changed
13
17
  - feat(expo-audio-studio): optimize buffer size on android to prevent oom ([32fcb9b](https://github.com/deeeed/expo-audio-stream/commit/32fcb9b0a965669b3a37c9860998ae46a1d26cd8))
@@ -373,7 +377,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
373
377
  - Feature: Audio features extraction during recording.
374
378
  - Feature: Consistent WAV PCM recording format across all platforms.
375
379
 
376
- [unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.18.0...HEAD
380
+ [unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.18.1...HEAD
381
+ [2.18.1]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.18.0...@siteed/expo-audio-studio@2.18.1
377
382
  [2.18.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.17.0...@siteed/expo-audio-studio@2.18.0
378
383
  [2.17.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.16.2...@siteed/expo-audio-studio@2.17.0
379
384
  [2.16.2]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.16.1...@siteed/expo-audio-studio@2.16.2
@@ -75,6 +75,9 @@ class AudioRecorderManager(
75
75
  }
76
76
  }
77
77
 
78
+ // Maximum size for analysis buffer to prevent OOM on low-RAM devices with extreme configs
79
+ private val MAX_ANALYSIS_BUFFER_SIZE = 20 * 1024 * 1024 // 20MB
80
+
78
81
  private var audioRecord: AudioRecord? = null
79
82
  private var bufferSizeInBytes = 0
80
83
  private val _isRecording = AtomicBoolean(false)
@@ -1477,9 +1480,14 @@ class AudioRecorderManager(
1477
1480
 
1478
1481
  accumulatedAudioData.write(audioData, 0, bytesRead)
1479
1482
 
1480
- // Also accumulate data for analysis if enabled
1481
- if (shouldProcessAnalysis) {
1482
- accumulatedAnalysisData.write(audioData, 0, bytesRead)
1483
+ // Always accumulate data for analysis if enabled (moved outside shouldProcessAnalysis check)
1484
+ if (recordingConfig.enableProcessing) {
1485
+ // Check buffer size to prevent OOM on low-RAM devices with extreme configs
1486
+ if (accumulatedAnalysisData.size() + bytesRead <= MAX_ANALYSIS_BUFFER_SIZE) {
1487
+ accumulatedAnalysisData.write(audioData, 0, bytesRead)
1488
+ } else {
1489
+ LogUtils.w(CLASS_NAME, "Analysis buffer size limit reached (${accumulatedAnalysisData.size()} bytes). Skipping data to prevent OOM.")
1490
+ }
1483
1491
  }
1484
1492
 
1485
1493
  // Handle regular audio data emission
@@ -1507,31 +1515,37 @@ class AudioRecorderManager(
1507
1515
  if (analysisDataSize > 0) {
1508
1516
  // Add this check to enforce minimum interval
1509
1517
  if (isFirstAnalysis || (currentTime - lastEmissionTimeAnalysis) >= recordingConfig.intervalAnalysis) {
1510
- // Process and emit analysis data
1511
- val analysisData = audioProcessor.processAudioData(
1512
- accumulatedAnalysisData.toByteArray(),
1513
- recordingConfig
1514
- )
1515
-
1516
- LogUtils.d(CLASS_NAME, """
1517
- Analysis data details:
1518
- - Raw data size: ${accumulatedAnalysisData.size()} bytes
1519
- """.trimIndent())
1520
-
1521
- mainHandler.post {
1522
- try {
1523
- eventSender.sendExpoEvent(
1524
- Constants.AUDIO_ANALYSIS_EVENT_NAME,
1525
- analysisData.toBundle()
1526
- )
1527
- } catch (e: Exception) {
1528
- LogUtils.e(CLASS_NAME, "Failed to send audio analysis event", e)
1518
+ try {
1519
+ // Process and emit analysis data
1520
+ val analysisData = audioProcessor.processAudioData(
1521
+ accumulatedAnalysisData.toByteArray(),
1522
+ recordingConfig
1523
+ )
1524
+
1525
+ LogUtils.d(CLASS_NAME, """
1526
+ Analysis data details:
1527
+ - Raw data size: ${accumulatedAnalysisData.size()} bytes
1528
+ """.trimIndent())
1529
+
1530
+ mainHandler.post {
1531
+ try {
1532
+ eventSender.sendExpoEvent(
1533
+ Constants.AUDIO_ANALYSIS_EVENT_NAME,
1534
+ analysisData.toBundle()
1535
+ )
1536
+ } catch (e: Exception) {
1537
+ LogUtils.e(CLASS_NAME, "Failed to send audio analysis event", e)
1538
+ }
1529
1539
  }
1540
+
1541
+ lastEmissionTimeAnalysis = currentTime
1542
+ isFirstAnalysis = false
1543
+ } catch (e: Exception) {
1544
+ LogUtils.e(CLASS_NAME, "Failed to process audio analysis data", e)
1545
+ } finally {
1546
+ // Always reset the buffer to prevent unbounded growth
1547
+ accumulatedAnalysisData.reset()
1530
1548
  }
1531
-
1532
- lastEmissionTimeAnalysis = currentTime
1533
- accumulatedAnalysisData.reset() // Clear the analysis accumulator
1534
- isFirstAnalysis = false
1535
1549
  }
1536
1550
  }
1537
1551
  }
@@ -1637,8 +1651,13 @@ class AudioRecorderManager(
1637
1651
  }
1638
1652
  }
1639
1653
 
1640
- if (recordingConfig.enableProcessing) {
1641
- processAudioData(audioData)
1654
+ // Analysis is already handled in recordingProcess method to avoid duplicate processing
1655
+ // and prevent memory issues from accumulating data in multiple buffers
1656
+
1657
+ // Update notification waveform if needed (moved from processAudioData)
1658
+ if (recordingConfig.showNotification && recordingConfig.showWaveformInNotification) {
1659
+ val floatArray = convertByteArrayToFloatArray(audioData)
1660
+ notificationManager.updateNotification(floatArray)
1642
1661
  }
1643
1662
  }
1644
1663
 
@@ -1651,55 +1670,6 @@ class AudioRecorderManager(
1651
1670
  return floatArray
1652
1671
  }
1653
1672
 
1654
- private fun processAudioData(audioData: ByteArray) {
1655
- // Skip the WAV header only for the first chunk
1656
- val dataToProcess = if (isFirstChunk && audioData.size > Constants.WAV_HEADER_SIZE) {
1657
- audioData.copyOfRange(Constants.WAV_HEADER_SIZE, audioData.size)
1658
- } else {
1659
- audioData
1660
- }
1661
-
1662
- // Accumulate data for analysis
1663
- if (recordingConfig.enableProcessing) {
1664
- synchronized(analysisBuffer) {
1665
- analysisBuffer.write(dataToProcess)
1666
- }
1667
-
1668
- val currentTime = SystemClock.elapsedRealtime()
1669
- if (isFirstAnalysis || (currentTime - lastEmissionTimeAnalysis) >= recordingConfig.intervalAnalysis) {
1670
- synchronized(analysisBuffer) {
1671
- if (analysisBuffer.size() > 0) {
1672
- val analysisData = audioProcessor.processAudioData(
1673
- analysisBuffer.toByteArray(),
1674
- recordingConfig
1675
- )
1676
-
1677
- mainHandler.post {
1678
- eventSender.sendExpoEvent(
1679
- Constants.AUDIO_ANALYSIS_EVENT_NAME,
1680
- analysisData.toBundle()
1681
- )
1682
- }
1683
-
1684
- // Reset buffer after processing
1685
- analysisBuffer.reset()
1686
- lastEmissionTimeAnalysis = currentTime
1687
- isFirstAnalysis = false
1688
- }
1689
- }
1690
- }
1691
- }
1692
-
1693
- // Only update notification if needed
1694
- if (recordingConfig.showNotification && recordingConfig.showWaveformInNotification) {
1695
- val floatArray = convertByteArrayToFloatArray(audioData)
1696
- notificationManager.updateNotification(floatArray)
1697
- }
1698
-
1699
- // Reset isFirstChunk after processing
1700
- isFirstChunk = false
1701
- }
1702
-
1703
1673
  fun cleanup() {
1704
1674
  synchronized(audioRecordLock) {
1705
1675
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siteed/expo-audio-studio",
3
- "version": "2.18.0",
3
+ "version": "2.18.1",
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
  "type": "commonjs",