@siteed/expo-audio-studio 2.16.2 → 2.18.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
CHANGED
|
@@ -8,6 +8,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
## [2.18.0] - 2025-08-01
|
|
12
|
+
### Changed
|
|
13
|
+
- feat(expo-audio-studio): optimize buffer size on android to prevent oom ([32fcb9b](https://github.com/deeeed/expo-audio-stream/commit/32fcb9b0a965669b3a37c9860998ae46a1d26cd8))
|
|
14
|
+
- fix(expo-audio-studio): invalid paused duration on android ([c107258](https://github.com/deeeed/expo-audio-stream/commit/c107258054ebdbc733298c84b8d84b0f9f416e6e))
|
|
15
|
+
- chore(expo-audio-studio): release @siteed/expo-audio-studio@2.17.0 ([8a303b4](https://github.com/deeeed/expo-audio-stream/commit/8a303b4d96988b97604123d74daaa406d9ec517c))
|
|
16
|
+
## [2.17.0] - 2025-07-31
|
|
17
|
+
### Changed
|
|
18
|
+
- fix(expo-audio-studio): fix OutOfMemoryError by tracking stream position correctly ([b67e521](https://github.com/deeeed/expo-audio-stream/commit/b67e52142154d07873c5c1ec9c183d524d61e528))
|
|
19
|
+
- chore(expo-audio-studio): release @siteed/expo-audio-studio@2.16.2 ([c4291a8](https://github.com/deeeed/expo-audio-stream/commit/c4291a82cc740b4d4790c69ae7e7cc07f1e8fb1a))
|
|
11
20
|
## [2.16.2] - 2025-07-27
|
|
12
21
|
### Changed
|
|
13
22
|
- chore(expo-audio-studio): release @siteed/expo-audio-studio@2.16.1 ([c9614d4](https://github.com/deeeed/expo-audio-stream/commit/c9614d4ebf87d73c3c5b2f7d6e60492fd5e45e64))
|
|
@@ -364,7 +373,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
364
373
|
- Feature: Audio features extraction during recording.
|
|
365
374
|
- Feature: Consistent WAV PCM recording format across all platforms.
|
|
366
375
|
|
|
367
|
-
[unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.
|
|
376
|
+
[unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.18.0...HEAD
|
|
377
|
+
[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
|
+
[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
|
|
368
379
|
[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
|
|
369
380
|
[2.16.1]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.16.0...@siteed/expo-audio-studio@2.16.1
|
|
370
381
|
[2.16.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.15.0...@siteed/expo-audio-studio@2.16.0
|
|
@@ -90,6 +90,7 @@ class AudioRecorderManager(
|
|
|
90
90
|
private var pausedDuration = 0L
|
|
91
91
|
private var lastEmittedSize = 0L
|
|
92
92
|
private var lastEmittedCompressedSize = 0L
|
|
93
|
+
private var streamPosition = 0L // Track total bytes processed in the stream
|
|
93
94
|
private val mainHandler = Handler(Looper.getMainLooper())
|
|
94
95
|
private val audioRecordLock = Any()
|
|
95
96
|
private var audioFileHandler: AudioFileHandler = AudioFileHandler(filesDir)
|
|
@@ -708,19 +709,29 @@ class AudioRecorderManager(
|
|
|
708
709
|
)
|
|
709
710
|
|
|
710
711
|
// Calculate buffer size based on bufferDurationSeconds if provided
|
|
711
|
-
|
|
712
|
+
var requestedBufferSize = recordingConfig.bufferDurationSeconds?.let { bufferDuration ->
|
|
712
713
|
val bytesPerSample = when (recordingConfig.encoding) {
|
|
713
714
|
"pcm_8bit" -> 1
|
|
714
715
|
"pcm_16bit" -> 2
|
|
715
716
|
"pcm_32bit" -> 4
|
|
716
717
|
else -> 2
|
|
717
718
|
}
|
|
718
|
-
|
|
719
|
-
bytesPerSample * recordingConfig.channels).toInt()
|
|
720
|
-
// Use the larger of requested size or minimum buffer size
|
|
721
|
-
maxOf(requestedSize, minBufferSize)
|
|
719
|
+
(bufferDuration * recordingConfig.sampleRate * bytesPerSample * recordingConfig.channels).toInt()
|
|
722
720
|
} ?: minBufferSize
|
|
723
721
|
|
|
722
|
+
LogUtils.d(CLASS_NAME, "Calculated minBufferSize: $minBufferSize bytes")
|
|
723
|
+
LogUtils.d(CLASS_NAME, "Requested buffer size: $requestedBufferSize bytes")
|
|
724
|
+
|
|
725
|
+
// Cap the buffer size to prevent OOM
|
|
726
|
+
val MAX_BUFFER_SIZE = 10485760 // 10MB
|
|
727
|
+
if (requestedBufferSize > MAX_BUFFER_SIZE) {
|
|
728
|
+
LogUtils.w(CLASS_NAME, "Requested buffer size $requestedBufferSize exceeds max limit of $MAX_BUFFER_SIZE, capping to max")
|
|
729
|
+
requestedBufferSize = MAX_BUFFER_SIZE
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
bufferSizeInBytes = maxOf(requestedBufferSize, minBufferSize)
|
|
733
|
+
LogUtils.d(CLASS_NAME, "Final bufferSizeInBytes: $bufferSizeInBytes (after capping and min check)")
|
|
734
|
+
|
|
724
735
|
when {
|
|
725
736
|
bufferSizeInBytes == AudioRecord.ERROR -> {
|
|
726
737
|
LogUtils.e(CLASS_NAME, "Error getting minimum buffer size: ERROR")
|
|
@@ -942,6 +953,7 @@ class AudioRecorderManager(
|
|
|
942
953
|
val bytesRead = audioRecord?.read(remainingData, 0, bufferSizeInBytes) ?: -1
|
|
943
954
|
if (bytesRead > 0) {
|
|
944
955
|
emitAudioData(remainingData.copyOfRange(0, bytesRead), bytesRead)
|
|
956
|
+
streamPosition += bytesRead // Update stream position for final data
|
|
945
957
|
}
|
|
946
958
|
}
|
|
947
959
|
|
|
@@ -966,6 +978,7 @@ class AudioRecorderManager(
|
|
|
966
978
|
if (bytesRead > 0) {
|
|
967
979
|
val emitStartTime = System.currentTimeMillis()
|
|
968
980
|
emitAudioData(audioData.copyOfRange(0, bytesRead), bytesRead)
|
|
981
|
+
streamPosition += bytesRead // Update stream position for final data
|
|
969
982
|
}
|
|
970
983
|
|
|
971
984
|
LogUtils.d(CLASS_NAME, "Stopping recording state = ${audioRecord?.state}")
|
|
@@ -1250,7 +1263,14 @@ class AudioRecorderManager(
|
|
|
1250
1263
|
|
|
1251
1264
|
// Use cached file size instead of file system call
|
|
1252
1265
|
val fileSize = if (recordingConfig.output.primary.enabled) cachedPrimaryFileSize else 0L
|
|
1253
|
-
val duration = if (
|
|
1266
|
+
val duration = if (isPaused.get()) {
|
|
1267
|
+
// Return frozen duration when paused using lastPauseTime
|
|
1268
|
+
if (lastPauseTime > 0) {
|
|
1269
|
+
lastPauseTime - recordingStartTime - pausedDuration
|
|
1270
|
+
} else {
|
|
1271
|
+
0L
|
|
1272
|
+
}
|
|
1273
|
+
} else if (!recordingConfig.output.primary.enabled) {
|
|
1254
1274
|
// For streaming-only mode, calculate duration from actual recording time
|
|
1255
1275
|
val actualRecordingTime = if (recordingStartTime > 0) {
|
|
1256
1276
|
System.currentTimeMillis() - recordingStartTime - pausedDuration
|
|
@@ -1420,7 +1440,7 @@ class AudioRecorderManager(
|
|
|
1420
1440
|
while (_isRecording.get() && !Thread.currentThread().isInterrupted) {
|
|
1421
1441
|
loopCount++
|
|
1422
1442
|
if (loopCount % 100 == 0) {
|
|
1423
|
-
LogUtils.d(CLASS_NAME, "Recording loop iteration $loopCount, isRecording: ${_isRecording.get()}")
|
|
1443
|
+
LogUtils.d(CLASS_NAME, "Recording loop iteration $loopCount, isRecording: ${_isRecording.get()}, accumulatedAudioSize: ${accumulatedAudioData.size()}, accumulatedAnalysisSize: ${accumulatedAnalysisData.size()}")
|
|
1424
1444
|
}
|
|
1425
1445
|
if (isPaused.get()) {
|
|
1426
1446
|
Thread.sleep(100) // Add small delay when paused
|
|
@@ -1468,6 +1488,7 @@ class AudioRecorderManager(
|
|
|
1468
1488
|
accumulatedAudioData.toByteArray(),
|
|
1469
1489
|
accumulatedAudioData.size()
|
|
1470
1490
|
)
|
|
1491
|
+
streamPosition += accumulatedAudioData.size() // Update stream position
|
|
1471
1492
|
lastEmitTime = currentTime
|
|
1472
1493
|
accumulatedAudioData.reset() // Clear the accumulator
|
|
1473
1494
|
}
|
|
@@ -1549,9 +1570,15 @@ class AudioRecorderManager(
|
|
|
1549
1570
|
val from = lastEmittedSize
|
|
1550
1571
|
lastEmittedSize = fileSize
|
|
1551
1572
|
|
|
1552
|
-
// Calculate position in milliseconds
|
|
1553
|
-
val
|
|
1554
|
-
|
|
1573
|
+
// Calculate position in milliseconds using stream position
|
|
1574
|
+
val bytesPerSample = when (recordingConfig.encoding) {
|
|
1575
|
+
"pcm_8bit" -> 1
|
|
1576
|
+
"pcm_16bit" -> 2
|
|
1577
|
+
"pcm_32bit" -> 4
|
|
1578
|
+
else -> 2
|
|
1579
|
+
}
|
|
1580
|
+
val byteRate = recordingConfig.sampleRate * recordingConfig.channels * bytesPerSample
|
|
1581
|
+
val positionInMs = (streamPosition * 1000) / byteRate
|
|
1555
1582
|
|
|
1556
1583
|
val compressionBundle = if (recordingConfig.output.compressed.enabled) {
|
|
1557
1584
|
// For compressed files, we need to get actual size as MediaRecorder handles the writing
|
|
@@ -1700,6 +1727,7 @@ class AudioRecorderManager(
|
|
|
1700
1727
|
totalRecordedTime = 0
|
|
1701
1728
|
pausedDuration = 0
|
|
1702
1729
|
lastEmittedSize = 0
|
|
1730
|
+
streamPosition = 0
|
|
1703
1731
|
recordingStartTime = 0
|
|
1704
1732
|
|
|
1705
1733
|
// Update the WAV header if needed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@siteed/expo-audio-studio",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.18.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
|
"type": "commonjs",
|