@siteed/expo-audio-stream 1.9.0 → 1.9.2

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,24 +8,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  ## [Unreleased]
9
9
 
10
10
 
11
+ ## [1.9.2] - 2025-01-12
12
+ - ios bitrate verification to prevent invalid values ([035a180](https://github.com/deeeed/expo-audio-stream/commit/035a1800833264edcc59724aaa8a2e12d5c78dc2))
13
+
14
+ ## [1.9.1] - 2025-01-12
15
+ - ios potentially missing compressed file info ([88a628c](https://github.com/deeeed/expo-audio-stream/commit/88a628c35f2bfd626a2a5de1eb6950efd814619d))
16
+
17
+
11
18
  ## [1.9.0] - 2025-01-11
12
19
  - feat(web-audio): optimize memory usage and streaming performance for web audio recording (#75) ([7b93e12](https://github.com/deeeed/expo-audio-stream/commit/7b93e12aae4bc0599b06b48ca34a60f65587fc75))
13
20
 
21
+
22
+
14
23
  ## [1.8.0] - 2025-01-10
15
24
  - feat(audio): implement audio compression support ([ff4e060](https://github.com/deeeed/expo-audio-stream/commit/ff4e060fef1061804c1cc0126d4344d2d50daa9a))
16
25
 
17
26
 
27
+
28
+
18
29
  ## [1.7.2] - 2025-01-07
19
30
  - fix(audio-stream): correct WAV header handling in web audio recording ([9ba7de5](https://github.com/deeeed/expo-audio-stream/commit/9ba7de5b96ca4cc937dea261c80d3fda9c99e8f4))
20
31
 
21
32
 
22
33
 
34
+
35
+
23
36
  ## [1.7.1] - 2025-01-07
24
37
  - update notification to avoid triggering new alerts (#71) ([32dcfc5](https://github.com/deeeed/expo-audio-stream/commit/32dcfc55daf3236babefc17016f329c177d466fd))
25
38
 
26
39
 
27
40
 
28
41
 
42
+
43
+
29
44
  ## [1.7.0] - 2025-01-05
30
45
  - feat(playground): enhance app configuration and build setup for production deployment (#58) ([929d443](https://github.com/deeeed/expo-audio-stream/commit/929d443145378b1430d215db5c00b13758420e2b))
31
46
  - chore(expo-audio-stream): release @siteed/expo-audio-stream@1.6.1 ([084e8ad](https://github.com/deeeed/expo-audio-stream/commit/084e8adb91da7874c9e608b55d9c7b2ffd7a8327))
@@ -38,6 +53,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
38
53
 
39
54
 
40
55
 
56
+
57
+
41
58
  ## [1.6.1] - 2024-12-11
42
59
  - chore(expo-audio-stream): remove git commit step from publish script ([4a772ce](https://github.com/deeeed/expo-audio-stream/commit/4a772ce93bb7405d9b8e981f46bdf8941a71ecfe))
43
60
  - chore: more publishing automation ([3693021](https://github.com/deeeed/expo-audio-stream/commit/369302107f9dca9dddd8ae68e6214481a39976ac))
@@ -54,6 +71,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
54
71
 
55
72
 
56
73
 
74
+
75
+
57
76
  ## [1.5.0] - 2024-12-10
58
77
  - UNPUBLISHED because of a bug in the build system
59
78
 
@@ -63,6 +82,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
63
82
 
64
83
 
65
84
 
85
+
86
+
66
87
  ## [1.4.0] - 2024-12-05
67
88
  - chore: remove unusded dependencies ([ad81dd5](https://github.com/deeeed/expo-audio-stream/commit/ad81dd560c93dd1d04995a323a4ae72d4de20f3e))
68
89
 
@@ -72,6 +93,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
72
93
 
73
94
 
74
95
 
96
+
97
+
75
98
  ## [1.3.1] - 2024-12-05
76
99
  - feat(web): implement throttling and optimize event processing (#49) ([da28765](https://github.com/deeeed/expo-audio-stream/commit/da2876524c2c9d6e0a980fde40a0197b929d8a7f))
77
100
 
@@ -81,6 +104,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
81
104
 
82
105
 
83
106
 
107
+
108
+
84
109
  ## [1.3.0] - 2024-11-28
85
110
  ### Added
86
111
  - refactor(permissions): standardize permission status response structure across platforms (#44) ([7c9c800](https://github.com/deeeed/expo-audio-stream/commit/7c9c800d83b7cea3516643371484d5e1f3b99e4c))
@@ -95,6 +120,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
95
120
 
96
121
 
97
122
 
123
+
124
+
98
125
  ## [1.2.5] - 2024-11-12
99
126
  ### Added
100
127
  - docs(license): add MIT license to all packages (6 files changed)
@@ -106,6 +133,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
106
133
 
107
134
 
108
135
 
136
+
137
+
109
138
  ## [1.2.4] - 2024-11-05
110
139
  ### Changed
111
140
  - Android minimum audio interval set to 10ms.
@@ -120,6 +149,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
120
149
 
121
150
 
122
151
 
152
+
153
+
123
154
  ## [1.2.0] - 2024-10-24
124
155
  ### Added
125
156
  - Feature: Keep device awake during recording with `keepAwake` option
@@ -134,6 +165,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
134
165
 
135
166
 
136
167
 
168
+
169
+
137
170
  ## [1.1.17] - 2024-10-21
138
171
  ### Added
139
172
  - Support bluetooth headset on ios
@@ -145,6 +178,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
145
178
 
146
179
 
147
180
 
181
+
182
+
148
183
  ## [1.0.0] - 2024-04-01
149
184
  ### Added
150
185
  - Initial release of @siteed/expo-audio-stream.
@@ -155,7 +190,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
155
190
  - Feature: Audio features extraction during recording.
156
191
  - Feature: Consistent WAV PCM recording format across all platforms.
157
192
 
158
- [unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.9.0...HEAD
193
+ [unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.9.2...HEAD
194
+ [1.9.2]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.9.1...@siteed/expo-audio-stream@1.9.2
195
+ [1.9.1]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.9.0...@siteed/expo-audio-stream@1.9.1
159
196
  [1.9.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.8.0...@siteed/expo-audio-stream@1.9.0
160
197
  [1.8.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.7.2...@siteed/expo-audio-stream@1.8.0
161
198
  [1.7.2]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.7.1...@siteed/expo-audio-stream@1.7.2
@@ -411,10 +411,13 @@ class AudioStreamManager: NSObject {
411
411
  ]
412
412
 
413
413
  // Add compression info if enabled
414
- if settings.enableCompressedOutput, let compressedURL = compressedFileURL {
414
+ if settings.enableCompressedOutput,
415
+ let compressedURL = compressedFileURL,
416
+ FileManager.default.fileExists(atPath: compressedURL.path) {
415
417
  do {
416
418
  let compressedAttributes = try FileManager.default.attributesOfItem(atPath: compressedURL.path)
417
419
  if let compressedSize = compressedAttributes[.size] as? Int64 {
420
+ Logger.debug("Compressed file status - Size: \(compressedSize)")
418
421
  let compressionBundle: [String: Any] = [
419
422
  "fileUri": compressedURL.absoluteString,
420
423
  "mimeType": compressedFormat == "aac" ? "audio/aac" : "audio/opus",
@@ -550,23 +553,55 @@ class AudioStreamManager: NSObject {
550
553
 
551
554
  // Setup compressed recording if enabled
552
555
  if settings.enableCompressedOutput {
553
- let compressedSettings: [String: Any] = [
554
- AVFormatIDKey: settings.compressedFormat == "aac" ? kAudioFormatMPEG4AAC : kAudioFormatOpus,
555
- AVSampleRateKey: settings.sampleRate,
556
- AVNumberOfChannelsKey: settings.numberOfChannels,
557
- AVEncoderBitRateKey: settings.compressedBitRate,
558
- AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
559
- ]
560
-
561
- compressedFileURL = FileManager.default.temporaryDirectory
562
- .appendingPathComponent(UUID().uuidString)
563
- .appendingPathExtension(settings.compressedFormat)
556
+ do {
557
+ let compressedSettings: [String: Any] = [
558
+ AVFormatIDKey: settings.compressedFormat == "aac" ? kAudioFormatMPEG4AAC : kAudioFormatOpus,
559
+ AVSampleRateKey: settings.sampleRate,
560
+ AVNumberOfChannelsKey: settings.numberOfChannels,
561
+ AVEncoderBitRateKey: settings.compressedBitRate,
562
+ AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue,
563
+ AVEncoderBitDepthHintKey: 16
564
+ ]
564
565
 
565
- if let url = compressedFileURL {
566
- compressedRecorder = try AVAudioRecorder(url: url, settings: compressedSettings)
567
- compressedRecorder?.record()
568
- compressedFormat = settings.compressedFormat
569
- compressedBitRate = settings.compressedBitRate
566
+ Logger.debug("Initializing compressed recording with settings: \(compressedSettings)")
567
+
568
+ let tempDirectory = FileManager.default.temporaryDirectory
569
+ try FileManager.default.createDirectory(at: tempDirectory, withIntermediateDirectories: true)
570
+
571
+ // Use the same UUID as the main recording
572
+ if let recordingUUID = recordingUUID {
573
+ compressedFileURL = tempDirectory.appendingPathComponent(recordingUUID.uuidString)
574
+ .appendingPathExtension(settings.compressedFormat)
575
+
576
+ if let url = compressedFileURL {
577
+ // Create empty file first
578
+ if FileManager.default.createFile(atPath: url.path, contents: nil) {
579
+ Logger.debug("Created empty file at: \(url.path)")
580
+ } else {
581
+ Logger.debug("Failed to create empty file at: \(url.path)")
582
+ }
583
+
584
+ // Then initialize recorder
585
+ compressedRecorder = try AVAudioRecorder(url: url, settings: compressedSettings)
586
+ if let recorder = compressedRecorder {
587
+ let prepared = recorder.prepareToRecord()
588
+ Logger.debug("Recorder prepared: \(prepared)")
589
+
590
+ let started = recorder.record()
591
+ Logger.debug("Recorder started: \(started)")
592
+
593
+ Logger.debug("Recorder current time: \(recorder.currentTime)")
594
+
595
+ compressedFormat = settings.compressedFormat
596
+ compressedBitRate = settings.compressedBitRate
597
+ Logger.debug("Compressed recording initialized - Format: \(compressedFormat), Bitrate: \(compressedBitRate)")
598
+ }
599
+ }
600
+ }
601
+ } catch {
602
+ Logger.debug("Failed to setup compressed recording: \(error)")
603
+ compressedFileURL = nil
604
+ compressedRecorder = nil
570
605
  }
571
606
  }
572
607
 
@@ -1192,32 +1227,50 @@ class AudioStreamManager: NSObject {
1192
1227
  var compressionInfo: [String: Any]? = nil
1193
1228
  if settings.enableCompressedOutput, let compressedURL = compressedFileURL {
1194
1229
  do {
1195
- let compressedAttributes = try FileManager.default.attributesOfItem(atPath: compressedURL.path)
1196
- if let compressedSize = compressedAttributes[.size] as? Int64 {
1197
- let eventDataSize = compressedSize - lastEmittedCompressedSize
1198
-
1199
- // Read the new compressed data if there's new data
1200
- var compressedData: String? = nil
1201
- if eventDataSize > 0 {
1202
- let fileHandle = try FileHandle(forReadingFrom: compressedURL)
1203
- fileHandle.seek(toFileOffset: UInt64(lastEmittedCompressedSize))
1204
- let data = fileHandle.readData(ofLength: Int(eventDataSize))
1205
- compressedData = data.base64EncodedString()
1206
- fileHandle.closeFile()
1230
+ // Ensure file exists and has data
1231
+ if FileManager.default.fileExists(atPath: compressedURL.path) {
1232
+ let compressedAttributes = try FileManager.default.attributesOfItem(atPath: compressedURL.path)
1233
+ if let compressedSize = compressedAttributes[.size] as? Int64 {
1234
+ let eventDataSize = compressedSize - lastEmittedCompressedSize
1235
+
1236
+ Logger.debug("Compressed file status - Total size: \(compressedSize), New data size: \(eventDataSize)")
1237
+
1238
+ // Read the new compressed data if there's new data
1239
+ var compressedData: String? = nil
1240
+ if eventDataSize > 0 {
1241
+ do {
1242
+ let fileHandle = try FileHandle(forReadingFrom: compressedURL)
1243
+ defer { fileHandle.closeFile() }
1244
+
1245
+ fileHandle.seek(toFileOffset: UInt64(lastEmittedCompressedSize))
1246
+ let data = fileHandle.readData(ofLength: Int(eventDataSize))
1247
+ compressedData = data.base64EncodedString()
1248
+
1249
+ Logger.debug("Read compressed data of size: \(data.count)")
1250
+ } catch {
1251
+ Logger.debug("Error reading compressed data: \(error)")
1252
+ }
1253
+ }
1254
+
1255
+ lastEmittedCompressedSize = compressedSize
1256
+
1257
+ compressionInfo = [
1258
+ "position": recordingTime * 1000, // Convert to milliseconds
1259
+ "fileUri": compressedURL.absoluteString,
1260
+ "eventDataSize": eventDataSize,
1261
+ "totalSize": compressedSize,
1262
+ "data": compressedData ?? ""
1263
+ ]
1264
+
1265
+ Logger.debug("Compression info prepared: \(String(describing: compressionInfo))")
1266
+ } else {
1267
+ Logger.debug("Could not get compressed file size")
1207
1268
  }
1208
-
1209
- lastEmittedCompressedSize = compressedSize
1210
-
1211
- compressionInfo = [
1212
- "position": recordingTime * 1000, // Convert to milliseconds
1213
- "fileUri": compressedURL.absoluteString,
1214
- "eventDataSize": eventDataSize,
1215
- "totalSize": compressedSize,
1216
- "data": compressedData ?? ""
1217
- ]
1269
+ } else {
1270
+ Logger.debug("Compressed file does not exist at path: \(compressedURL.path)")
1218
1271
  }
1219
1272
  } catch {
1220
- Logger.debug("Failed to read compressed data: \(error)")
1273
+ Logger.debug("Error preparing compression info: \(error)")
1221
1274
  }
1222
1275
  }
1223
1276
 
@@ -23,18 +23,25 @@ struct CompressedRecordingInfo {
23
23
  var bitrate: Int
24
24
  var format: String
25
25
 
26
- static func validate(format: String, bitrate: Int) -> Result<Void, Error> {
26
+ static func validate(format: String, bitrate: Int) -> Result<(String, Int), Error> {
27
27
  // Validate format
28
28
  guard ["aac", "opus"].contains(format.lowercased()) else {
29
29
  return .failure(RecordingError.unsupportedFormat(format))
30
30
  }
31
31
 
32
- // Validate bitrate
33
- guard (8000...960000).contains(bitrate) else {
34
- return .failure(RecordingError.invalidBitrate(bitrate))
32
+ // Adjust bitrate based on format
33
+ let adjustedBitrate: Int
34
+ if format.lowercased() == "aac" {
35
+ // Standard AAC bitrates (bps)
36
+ let standardAACBitrates = [32000, 48000, 64000, 96000, 128000, 160000, 192000, 256000, 320000]
37
+ adjustedBitrate = standardAACBitrates.min(by: { abs($0 - bitrate) < abs($1 - bitrate) }) ?? 128000
38
+ } else {
39
+ // For Opus, allow lower bitrates (especially good for voice)
40
+ // Typical Opus voice bitrates: 8-24 kbps, music: 32-128 kbps
41
+ adjustedBitrate = min(max(bitrate, 8000), 320000)
35
42
  }
36
43
 
37
- return .success(())
44
+ return .success((format, adjustedBitrate))
38
45
  }
39
46
  }
40
47
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siteed/expo-audio-stream",
3
- "version": "1.9.0",
3
+ "version": "1.9.2",
4
4
  "description": "stream audio crossplatform",
5
5
  "license": "MIT",
6
6
  "main": "build/index.js",