@siteed/expo-audio-stream 1.0.3 → 1.1.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.
Files changed (132) hide show
  1. package/README.md +26 -175
  2. package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +47 -7
  3. package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +1 -0
  4. package/android/src/main/java/net/siteed/audiostream/Constants.kt +5 -0
  5. package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +12 -3
  6. package/build/index.js +8 -7
  7. package/ios/AudioProcessor.swift +7 -5
  8. package/ios/AudioStreamManager.swift +1 -0
  9. package/ios/ExpoAudioStream.podspec +1 -1
  10. package/ios/ExpoAudioStreamModule.swift +36 -0
  11. package/ios/RecordingResult.swift +1 -0
  12. package/package.json +95 -65
  13. package/src/AudioAnalysis/AudioAnalysis.types.ts +59 -60
  14. package/src/AudioAnalysis/extractAudioAnalysis.ts +132 -121
  15. package/src/AudioAnalysis/extractWaveform.ts +18 -18
  16. package/src/AudioRecorder.provider.tsx +53 -53
  17. package/src/ExpoAudioStream.native.ts +2 -2
  18. package/src/ExpoAudioStream.types.ts +59 -53
  19. package/src/ExpoAudioStream.web.ts +231 -205
  20. package/src/ExpoAudioStreamModule.ts +22 -15
  21. package/src/WebRecorder.web.ts +407 -390
  22. package/src/constants.ts +11 -11
  23. package/src/events.ts +27 -13
  24. package/src/index.ts +17 -15
  25. package/src/logger.ts +15 -19
  26. package/src/useAudioRecorder.tsx +394 -389
  27. package/src/utils/BlobFix.ts +550 -0
  28. package/src/utils/concatenateBuffers.ts +24 -0
  29. package/src/utils/convertPCMToFloat32.ts +72 -45
  30. package/src/utils/encodingToBitDepth.ts +14 -14
  31. package/src/utils/getWavFileInfo.ts +106 -99
  32. package/src/utils/writeWavHeader.ts +50 -45
  33. package/src/workers/InlineFeaturesExtractor.web.tsx +296 -286
  34. package/src/workers/inlineAudioWebWorker.web.tsx +230 -222
  35. package/.eslintrc.js +0 -2
  36. package/.size-limit.json +0 -6
  37. package/android/.gradle/8.1.1/checksums/checksums.lock +0 -0
  38. package/android/.gradle/8.1.1/dependencies-accessors/dependencies-accessors.lock +0 -0
  39. package/android/.gradle/8.1.1/dependencies-accessors/gc.properties +0 -0
  40. package/android/.gradle/8.1.1/fileChanges/last-build.bin +0 -0
  41. package/android/.gradle/8.1.1/fileHashes/fileHashes.lock +0 -0
  42. package/android/.gradle/8.1.1/gc.properties +0 -0
  43. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  44. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  45. package/android/.gradle/vcs-1/gc.properties +0 -0
  46. package/app.plugin.js +0 -1
  47. package/build/AudioAnalysis/AudioAnalysis.types.d.ts +0 -76
  48. package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +0 -1
  49. package/build/AudioAnalysis/AudioAnalysis.types.js +0 -3
  50. package/build/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
  51. package/build/AudioAnalysis/extractAudioAnalysis.d.ts +0 -4
  52. package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +0 -1
  53. package/build/AudioAnalysis/extractAudioAnalysis.js +0 -101
  54. package/build/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
  55. package/build/AudioAnalysis/extractWaveform.d.ts +0 -8
  56. package/build/AudioAnalysis/extractWaveform.d.ts.map +0 -1
  57. package/build/AudioAnalysis/extractWaveform.js +0 -14
  58. package/build/AudioAnalysis/extractWaveform.js.map +0 -1
  59. package/build/AudioRecorder.provider.d.ts +0 -23
  60. package/build/AudioRecorder.provider.d.ts.map +0 -1
  61. package/build/AudioRecorder.provider.js +0 -36
  62. package/build/AudioRecorder.provider.js.map +0 -1
  63. package/build/ExpoAudioStream.native.d.ts +0 -3
  64. package/build/ExpoAudioStream.native.d.ts.map +0 -1
  65. package/build/ExpoAudioStream.native.js +0 -6
  66. package/build/ExpoAudioStream.native.js.map +0 -1
  67. package/build/ExpoAudioStream.types.d.ts +0 -60
  68. package/build/ExpoAudioStream.types.d.ts.map +0 -1
  69. package/build/ExpoAudioStream.types.js +0 -2
  70. package/build/ExpoAudioStream.types.js.map +0 -1
  71. package/build/ExpoAudioStream.web.d.ts +0 -42
  72. package/build/ExpoAudioStream.web.d.ts.map +0 -1
  73. package/build/ExpoAudioStream.web.js +0 -185
  74. package/build/ExpoAudioStream.web.js.map +0 -1
  75. package/build/ExpoAudioStreamModule.d.ts +0 -3
  76. package/build/ExpoAudioStreamModule.d.ts.map +0 -1
  77. package/build/ExpoAudioStreamModule.js +0 -18
  78. package/build/ExpoAudioStreamModule.js.map +0 -1
  79. package/build/WebRecorder.web.d.ts +0 -51
  80. package/build/WebRecorder.web.d.ts.map +0 -1
  81. package/build/WebRecorder.web.js +0 -288
  82. package/build/WebRecorder.web.js.map +0 -1
  83. package/build/constants.d.ts +0 -11
  84. package/build/constants.d.ts.map +0 -1
  85. package/build/constants.js +0 -14
  86. package/build/constants.js.map +0 -1
  87. package/build/events.d.ts +0 -6
  88. package/build/events.d.ts.map +0 -1
  89. package/build/events.js +0 -15
  90. package/build/events.js.map +0 -1
  91. package/build/index.d.ts +0 -10
  92. package/build/index.d.ts.map +0 -1
  93. package/build/index.js.map +0 -1
  94. package/build/logger.d.ts +0 -9
  95. package/build/logger.d.ts.map +0 -1
  96. package/build/logger.js +0 -17
  97. package/build/logger.js.map +0 -1
  98. package/build/useAudioRecorder.d.ts +0 -37
  99. package/build/useAudioRecorder.d.ts.map +0 -1
  100. package/build/useAudioRecorder.js +0 -271
  101. package/build/useAudioRecorder.js.map +0 -1
  102. package/build/utils/convertPCMToFloat32.d.ts +0 -11
  103. package/build/utils/convertPCMToFloat32.d.ts.map +0 -1
  104. package/build/utils/convertPCMToFloat32.js +0 -41
  105. package/build/utils/convertPCMToFloat32.js.map +0 -1
  106. package/build/utils/encodingToBitDepth.d.ts +0 -5
  107. package/build/utils/encodingToBitDepth.d.ts.map +0 -1
  108. package/build/utils/encodingToBitDepth.js +0 -13
  109. package/build/utils/encodingToBitDepth.js.map +0 -1
  110. package/build/utils/getWavFileInfo.d.ts +0 -25
  111. package/build/utils/getWavFileInfo.d.ts.map +0 -1
  112. package/build/utils/getWavFileInfo.js +0 -89
  113. package/build/utils/getWavFileInfo.js.map +0 -1
  114. package/build/utils/writeWavHeader.d.ts +0 -9
  115. package/build/utils/writeWavHeader.d.ts.map +0 -1
  116. package/build/utils/writeWavHeader.js +0 -41
  117. package/build/utils/writeWavHeader.js.map +0 -1
  118. package/build/workers/InlineFeaturesExtractor.web.d.ts +0 -2
  119. package/build/workers/InlineFeaturesExtractor.web.d.ts.map +0 -1
  120. package/build/workers/InlineFeaturesExtractor.web.js +0 -303
  121. package/build/workers/InlineFeaturesExtractor.web.js.map +0 -1
  122. package/build/workers/inlineAudioWebWorker.web.d.ts +0 -2
  123. package/build/workers/inlineAudioWebWorker.web.d.ts.map +0 -1
  124. package/build/workers/inlineAudioWebWorker.web.js +0 -243
  125. package/build/workers/inlineAudioWebWorker.web.js.map +0 -1
  126. package/expo-module.config.json +0 -18
  127. package/plugin/build/index.d.ts +0 -5
  128. package/plugin/build/index.js +0 -28
  129. package/plugin/src/index.ts +0 -53
  130. package/plugin/tsconfig.json +0 -14
  131. package/publish.sh +0 -8
  132. package/tsconfig.json +0 -9
package/README.md CHANGED
@@ -1,186 +1,37 @@
1
1
  # @siteed/expo-audio-stream
2
2
 
3
- `@siteed/expo-audio-stream` is a comprehensive library designed to facilitate real-time audio processing and streaming across iOS, Android, and web platforms.
3
+ [![kandi X-Ray](https://kandi.openweaver.com/badges/xray.svg)](https://kandi.openweaver.com/typescript/siteed/expo-audio-stream)
4
+ [![Version](https://img.shields.io/npm/v/@siteed/expo-audio-stream.svg)](https://www.npmjs.com/package/@siteed/expo-audio-stream)
5
+ [![Dependency Status](https://img.shields.io/npm/dt/@siteed/expo-audio-stream.svg)](https://www.npmjs.com/package/@siteed/expo-audio-stream)
6
+ [![License](https://img.shields.io/npm/l/@siteed/expo-audio-stream.svg)](https://www.npmjs.com/package/@siteed/expo-audio-stream)
4
7
 
5
- ## Features
6
-
7
- - Real-time audio streaming across iOS, Android, and web.
8
- - Configurable intervals for audio buffer receipt.
9
- - Automated microphone permissions setup in managed Expo projects.
10
- - IOS is automatically setup to handle background audio recording.
11
- - Listeners for audio data events with detailed event payloads.
12
- - Utility functions for recording control and file management.
13
-
14
- ## Playground Example Application
15
-
16
- The project comes with a fully functional example application that demonstrates how to use the library in a real-world scenario.
17
-
18
- ![Example App](./docs/demo.gif)
19
-
20
-
21
- To try it:
22
- ```bash
23
- git clone https://github.com/deeeed/expo-audio-stream.git
24
- cd expo-audio-stream
25
- yarn
26
- yarn playground ios
27
- yarn playground android
28
- yarn playground web
29
- ```
30
-
31
- ## Installation
32
-
33
- To install `@siteed/expo-audio-stream`, add it to your project using npm or Yarn:
34
-
35
- ```bash
36
- npm install @siteed/expo-audio-stream
37
- # or
38
- yarn add @siteed/expo-audio-stream
39
- ```
40
-
41
-
42
- ### Configuring with app.json
43
-
44
- To ensure expo-audio-stream works correctly with Expo, you must add it as a plugin in your app.json configuration file.
45
-
46
- ```json
47
- {
48
- "expo": {
49
- "plugins": ["@siteed/expo-audio-stream"]
50
- }
51
- }
52
- ```
53
-
54
- Make sure to run `npx expo prebuild` after adding the plugin to your app.json file.
55
-
56
- ## Usage
57
-
58
- This library provides two hooks: `useAudioRecorder` for standalone use and `useSharedAudioRecorder` for accessing shared recording state within a React context.
59
-
60
-
61
- ### Standalone Recording
62
-
63
- The `playground/` folder contains a fully functional React Native application demonstrating how to integrate and use `useAudioRecorder` from `@siteed/expo-audio-stream`. This includes starting and stopping recordings, handling permissions, and processing live audio data.
64
8
 
9
+ <div align="center">
10
+ <h2 align="center">
11
+ <br />
12
+ <strong>Comprehensive library designed to facilitate real-time audio processing and streaming across iOS, Android, and web platforms.
13
+ <br />
14
+ <br />
15
+ <a href="https://deeeed.github.io/expo-audio-stream/playground/">
16
+ <img src="../../docs/demo.gif" alt="Screenshot Playground">
17
+ </a>
18
+ </h2>
19
+ </div>
65
20
 
66
- #### Standalone Usage
21
+ **Give it a GitHub star 🌟, if you found this repo useful.**
22
+ [![GitHub stars](https://img.shields.io/github/stars/deeeed/expo-audio-stream.svg?style=social&label=Star&maxAge=2592000)](https://github.com/deeeed/expo-audio-stream)
67
23
 
68
- ```tsx
69
- import {
70
- useAudioRecorder,
71
- AudioStreamResult,
72
- } from '@siteed/expo-audio-stream';
73
24
 
74
- export default function App() {
75
- const { startRecording, stopRecording, duration, size, isRecording } = useAudioRecorder({
76
- debug: true,
77
- onAudioStream: (audioData: Blob) => {
78
- console.log(`audio event`,audioData);
79
- }
80
- });
81
-
82
- const handleStart = async () => {
83
- const { granted } = await Audio.requestPermissionsAsync();
84
- if (granted) {
85
- const fileUri = await startRecording({interval: 500});
86
- }
87
- };
88
-
89
- const handleStop = async () => {
90
- const result: AudioStreamResult = await stopRecording();
91
- };
92
-
93
- const renderRecording = () => (
94
- <View>
95
- <Text>Duration: {duration} ms</Text>
96
- <Text>Size: {size} bytes</Text>
97
- <Button title="Stop Recording" onPress={handleStop} />
98
- </View>
99
- );
100
-
101
- const renderStopped = () => (
102
- <View>
103
- <Button title="Start Recording" onPress={handleStart} />
104
- </View>
105
- );
106
-
107
- return (
108
- <View>
109
- <Button title="Request Permission" onPress={() => Audio.requestPermissionsAsync()} />
110
- {isRecording ? renderRecording() : renderStopped()}
111
- </View>
112
- );
113
- }
114
- ```
115
-
116
- The library also exposes an `addAudioEventListener` function that provides an `AudioEventPayload` object that you can subscribe to:
117
- ```tsx
118
- import { addAudioEventListener } from '@siteed/expo-audio-stream';
119
-
120
- useEffect(() => {
121
- const subscribe = addAudioEventListener(async ({fileUri, deltaSize, totalSize, from, streamUuid, encoded, mimeType, buffer}) => {
122
- log(`Received audio event:`, {fileUri, deltaSize, totalSize, mimeType, from, streamUuid, encodedLength: encoded?.length, bufferLength: buffer?.length})
123
- });
124
- return () => subscribe.remove();
125
- }, []);
126
- ```
127
-
128
- ### Shared Recording
129
-
130
- To facilitate state sharing across multiple components or screens, useSharedAudioRecorder can be used. It should be wrapped in a AudioRecorderProvider context provider to ensure state is managed at a higher level and shared appropriately.
131
-
132
- #### Shared Recording Usage
133
-
134
- ```tsx
135
- import { AudioRecorderProvider, useSharedAudioRecorder } from '@siteed/expo-audio-stream';
136
-
137
- export default function ParentComponent() {
138
- return (
139
- <AudioRecorderProvider>
140
- <ChildComponent />
141
- </AudioRecorderProvider>
142
- );
143
- }
144
-
145
- function ChildComponent() {
146
- const {
147
- startRecording,
148
- isRecording
149
- } = useSharedAudioRecorder();
150
-
151
- return (
152
- <View>
153
- <Text>{isRecording ? "Recording..." : "Ready to record"}</Text>
154
- <Button title="Toggle Recording" onPress={startRecording} />
155
- </View>
156
- );
157
- }
158
- ```
159
-
160
- ### Add Event Listener
161
-
162
- You can also add an event listener to receive detailed audio event payloads, which is crucial for both standalone and shared usage scenarios.
163
-
164
- ```tsx
165
- import { useEffect } from 'react';
166
- import { addAudioEventListener } from '@siteed/expo-audio-stream';
167
-
168
- function App() {
169
- useEffect(() => {
170
- const subscription = addAudioEventListener(event => {
171
- console.log("Audio event received:", event);
172
- });
173
-
174
- return () => subscription.remove();
175
- }, []);
25
+ ## Features
176
26
 
177
- // UI code here
178
- }
179
- ```
27
+ - Real-time audio streaming across iOS, Android, and web.
28
+ - Configurable intervals for audio buffer receipt.
29
+ - Automated microphone permissions setup in managed Expo projects.
30
+ - Background audio recording on iOS.
31
+ - Audio features extraction during recording.
32
+ - Consistent WAV PCM recording format across all platforms.
180
33
 
181
- ## Recording configuration
34
+ ## Documentation
182
35
 
183
- - on Android and IOS, audio is recorded in wav format, 16khz sample rate, 16 bit depth, 1 channel.
184
- - on web, it usually records in opus but it depends on the browser configuration.
36
+ For detailed documentation, please refer to the [Getting Started Guide](https://deeeed.github.io/expo-audio-stream/docs/).
185
37
 
186
- If you want to process the audio livestream directly, I recommend having another encoding step to align the audio format across platforms.
@@ -56,13 +56,53 @@ class AudioProcessor(private val filesDir: File) {
56
56
  return null
57
57
  }
58
58
 
59
- val header = fileData.sliceArray(0 until Constants.WAV_HEADER_SIZE)
60
- val sampleRate = byteArrayToInt(header.sliceArray(24..27))
61
- val channels = byteArrayToShort(header.sliceArray(22..23))
62
- val bitDepth = byteArrayToShort(header.sliceArray(34..35))
59
+ // Read the WAV header
60
+ val riffHeader = String(fileData.sliceArray(0..3))
61
+ if (riffHeader != "RIFF") {
62
+ Log.e("AudioProcessor", "Invalid RIFF header")
63
+ return null
64
+ }
65
+
66
+ val format = String(fileData.sliceArray(8..11))
67
+ if (format != "WAVE") {
68
+ Log.e("AudioProcessor", "Invalid WAVE format")
69
+ return null
70
+ }
71
+
72
+ var offset = 12
73
+ var dataSize = 0
74
+ var sampleRate = 0
75
+ var channels = 0
76
+ var bitDepth = 0
77
+
78
+ // Parse chunks until we find the 'data' chunk
79
+ while (offset < fileData.size - 8) {
80
+ val chunkId = String(fileData.sliceArray(offset until offset + 4))
81
+ val chunkSize = ByteBuffer.wrap(fileData.sliceArray(offset + 4 until offset + 8)).order(ByteOrder.LITTLE_ENDIAN).int
82
+
83
+ when (chunkId) {
84
+ "fmt " -> {
85
+ channels = ByteBuffer.wrap(fileData.sliceArray(offset + 10 until offset + 12)).order(ByteOrder.LITTLE_ENDIAN).short.toInt()
86
+ sampleRate = ByteBuffer.wrap(fileData.sliceArray(offset + 12 until offset + 16)).order(ByteOrder.LITTLE_ENDIAN).int
87
+ bitDepth = ByteBuffer.wrap(fileData.sliceArray(offset + 22 until offset + 24)).order(ByteOrder.LITTLE_ENDIAN).short.toInt()
88
+ }
89
+ "data" -> {
90
+ dataSize = chunkSize
91
+ offset += 8 // Skip chunk ID and size
92
+ break
93
+ }
94
+ }
95
+
96
+ offset += chunkSize + 8 // Move to the next chunk
97
+ }
98
+
99
+ if (dataSize == 0) {
100
+ Log.e("AudioProcessor", "No data chunk found in WAV file")
101
+ return null
102
+ }
63
103
 
64
104
  val audioData = if (skipWavHeader) {
65
- fileData.sliceArray(Constants.WAV_HEADER_SIZE until fileData.size)
105
+ fileData.sliceArray(offset until offset + dataSize)
66
106
  } else {
67
107
  fileData
68
108
  }
@@ -158,8 +198,8 @@ class AudioProcessor(private val filesDir: File) {
158
198
  val rms = features.rms
159
199
  val silent = rms < 0.01
160
200
  val dB = if (featureOptions["dB"] == true) 20 * log10(rms.toDouble()).toFloat() else 0f
161
- minAmplitude = min(minAmplitude, rms)
162
- maxAmplitude = max(maxAmplitude, rms)
201
+ minAmplitude = min(minAmplitude, localMinAmplitude)
202
+ maxAmplitude = max(maxAmplitude, localMaxAmplitude)
163
203
 
164
204
  val bytesPerSample = bitDepth / 8
165
205
  val startPosition = start * bytesPerSample * config.channels
@@ -330,6 +330,7 @@ class AudioRecorderManager(
330
330
  // Create result bundle
331
331
  val result = bundleOf(
332
332
  "fileUri" to audioFile?.toURI().toString(),
333
+ "filename" to audioFile?.name,
333
334
  "durationMs" to duration,
334
335
  "channels" to recordingConfig.channels,
335
336
  "bitDepth" to when (recordingConfig.encoding) {
@@ -9,5 +9,10 @@ object Constants {
9
9
  const val DEFAULT_INTERVAL = 1000L
10
10
  const val MIN_INTERVAL = 100L // Minimum interval in ms for emitting audio data
11
11
  const val WAV_HEADER_SIZE = 44
12
+ const val RIFF_HEADER = 0x52494646 // "RIFF"
13
+ const val WAVE_HEADER = 0x57415645 // "WAVE"
14
+ const val FMT_CHUNK_ID = 0x666d7420 // "fmt "
15
+ const val DATA_CHUNK_ID = 0x64617461 // "data"
16
+ const val INFO_CHUNK_ID = 0x494E464F // "info"
12
17
  const val TAG = "AudioRecorderModule"
13
18
  }
@@ -1,5 +1,6 @@
1
1
  package net.siteed.audiostream
2
2
 
3
+ import android.Manifest
3
4
  import android.os.Build
4
5
  import android.os.Bundle
5
6
  import android.util.Log
@@ -7,6 +8,7 @@ import androidx.annotation.RequiresApi
7
8
  import expo.modules.kotlin.Promise
8
9
  import expo.modules.kotlin.modules.Module
9
10
  import expo.modules.kotlin.modules.ModuleDefinition
11
+ import expo.modules.interfaces.permissions.Permissions
10
12
 
11
13
  class ExpoAudioStreamModule() : Module(), EventSender {
12
14
  private lateinit var audioRecorderManager: AudioRecorderManager
@@ -44,18 +46,17 @@ class ExpoAudioStreamModule() : Module(), EventSender {
44
46
  audioRecorderManager.pauseRecording(promise)
45
47
  }
46
48
 
47
-
48
49
  AsyncFunction("extractAudioAnalysis") { options: Map<String, Any>, promise: Promise ->
49
50
  val fileUri = options["fileUri"] as? String
50
51
  val pointsPerSecond = (options["pointsPerSecond"] as? Double) ?: 20.0
51
- val algorithm = options["algorithm"] as? String ?: "rms"
52
+ val algorithm = options["algorithm"] as? String ?: "peak"
52
53
  val featuresMap = options["features"] as? Map<*, *>
53
54
  val features = featuresMap?.filterKeys { it is String }
54
55
  ?.filterValues { it is Boolean }
55
56
  ?.mapKeys { it.key as String }
56
57
  ?.mapValues { it.value as Boolean }
57
58
  ?: emptyMap()
58
- val skipWavHeader = (options["skipWavHeader"] as? Boolean) ?: false
59
+ val skipWavHeader = (options["skipWavHeader"] as? Boolean) ?: true
59
60
 
60
61
  if (fileUri == null) {
61
62
  promise.reject("INVALID_ARGUMENTS", "fileUri is required", null)
@@ -94,6 +95,14 @@ class ExpoAudioStreamModule() : Module(), EventSender {
94
95
  AsyncFunction("stopRecording") { promise: Promise ->
95
96
  audioRecorderManager.stopRecording(promise)
96
97
  }
98
+
99
+ AsyncFunction("requestPermissionsAsync") { promise: Promise ->
100
+ Permissions.askForPermissionsWithPermissionsManager(appContext.permissions, promise, Manifest.permission.RECORD_AUDIO)
101
+ }
102
+
103
+ AsyncFunction("getPermissionsAsync") { promise: Promise ->
104
+ Permissions.getPermissionsWithPermissionsManager(appContext.permissions, promise, Manifest.permission.RECORD_AUDIO)
105
+ }
97
106
  }
98
107
 
99
108
  private fun initializeManager() {
package/build/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  // src/index.ts
2
- import { extractAudioAnalysis } from "./AudioAnalysis/extractAudioAnalysis";
3
- import { AudioRecorderProvider, useSharedAudioRecorder, } from "./AudioRecorder.provider";
4
- import { useAudioRecorder } from "./useAudioRecorder";
5
- export * from "./utils/getWavFileInfo";
6
- export * from "./utils/convertPCMToFloat32";
7
- export * from "./utils/writeWavHeader";
8
- export { AudioRecorderProvider, extractAudioAnalysis, useAudioRecorder, useSharedAudioRecorder, };
2
+ import { extractAudioAnalysis } from './AudioAnalysis/extractAudioAnalysis';
3
+ import { AudioRecorderProvider, useSharedAudioRecorder, } from './AudioRecorder.provider';
4
+ import ExpoAudioStreamModule from './ExpoAudioStreamModule';
5
+ import { useAudioRecorder } from './useAudioRecorder';
6
+ export * from './utils/convertPCMToFloat32';
7
+ export * from './utils/getWavFileInfo';
8
+ export * from './utils/writeWavHeader';
9
+ export { AudioRecorderProvider, ExpoAudioStreamModule, extractAudioAnalysis, useAudioRecorder, useSharedAudioRecorder, };
9
10
  //# sourceMappingURL=index.js.map
@@ -202,12 +202,14 @@ public class AudioProcessor {
202
202
  updateSegmentData(channelData: channelData, index: i, sumSquares: &sumSquares, zeroCrossings: &zeroCrossings, prevValue: &prevValue, localMinAmplitude: &localMinAmplitude, localMaxAmplitude: &localMaxAmplitude, segmentData: &segmentData)
203
203
 
204
204
  if (i + 1) % pointInterval == 0 || i == length - 1 {
205
- let features = computeFeatures(segmentData: segmentData, sampleRate: sampleRate, sumSquares: sumSquares, zeroCrossings: zeroCrossings, segmentLength: (i % pointInterval) + 1, featureOptions: featureOptions)
205
+ var features = computeFeatures(segmentData: segmentData, sampleRate: sampleRate, sumSquares: sumSquares, zeroCrossings: zeroCrossings, segmentLength: (i % pointInterval) + 1, featureOptions: featureOptions)
206
+ features.minAmplitude = localMinAmplitude
207
+ features.maxAmplitude = localMaxAmplitude
206
208
  let rms = features.rms
207
209
  let silent = rms < 0.01
208
210
  let dB = featureOptions["dB"] == true ? 20 * log10(rms) : 0
209
- minAmplitude = min(minAmplitude, rms)
210
- maxAmplitude = max(maxAmplitude, rms)
211
+ minAmplitude = min(minAmplitude, localMinAmplitude)
212
+ maxAmplitude = max(maxAmplitude, localMaxAmplitude)
211
213
 
212
214
  let segmentSize = segmentData.count
213
215
  let segmentDuration = Float(segmentSize) / sampleRate
@@ -294,8 +296,8 @@ public class AudioProcessor {
294
296
  energy: energy,
295
297
  mfcc: mfcc,
296
298
  rms: rms,
297
- minAmplitude: 0,
298
- maxAmplitude: 0,
299
+ minAmplitude: 0, // computed before and will be overwritten
300
+ maxAmplitude: 0, // computed before and will be overwritten
299
301
  zcr: zcr,
300
302
  spectralCentroid: spectralCentroid,
301
303
  spectralFlatness: spectralFlatness,
@@ -379,6 +379,7 @@ class AudioStreamManager: NSObject {
379
379
 
380
380
  let result = RecordingResult(
381
381
  fileUri: fileURL.absoluteString,
382
+ filename: fileURL.lastPathComponent,
382
383
  mimeType: mimeType,
383
384
  duration: duration,
384
385
  size: fileSize,
@@ -23,5 +23,5 @@ Pod::Spec.new do |s|
23
23
  'SWIFT_COMPILATION_MODE' => 'wholemodule'
24
24
  }
25
25
 
26
- s.source_files = "**/*.{h,m,swift}"
26
+ s.source_files = "**/*.{h,m,mm,swift}"
27
27
  end
@@ -197,6 +197,7 @@ public class ExpoAudioStreamModule: Module, AudioStreamManagerDelegate {
197
197
  // Convert RecordingResult to a dictionary
198
198
  let resultDict: [String: Any] = [
199
199
  "fileUri": recordingResult.fileUri,
200
+ "filename": recordingResult.filename,
200
201
  "durationMs": recordingResult.duration,
201
202
  "size": recordingResult.size,
202
203
  "channels": recordingResult.channels,
@@ -224,6 +225,41 @@ public class ExpoAudioStreamModule: Module, AudioStreamManagerDelegate {
224
225
  Function("clearAudioFiles") {
225
226
  clearAudioFiles()
226
227
  }
228
+
229
+
230
+ /// Requests audio recording permissions.
231
+ ///
232
+ /// - Parameters:
233
+ /// - promise: A promise to resolve with the permission status or reject with an error.
234
+ /// - Returns: Promise to be resolved with the permission status.
235
+ AsyncFunction("requestPermissionsAsync") { (promise: Promise) in
236
+ AVAudioSession.sharedInstance().requestRecordPermission { granted in
237
+ if granted {
238
+ promise.resolve(["status": "granted"])
239
+ } else {
240
+ promise.resolve(["status": "denied"])
241
+ }
242
+ }
243
+ }
244
+
245
+ /// Gets the current audio recording permissions.
246
+ ///
247
+ /// - Parameters:
248
+ /// - promise: A promise to resolve with the permission status or reject with an error.
249
+ /// - Returns: Promise to be resolved with the permission status.
250
+ AsyncFunction("getPermissionsAsync") { (promise: Promise) in
251
+ let permissionStatus = AVAudioSession.sharedInstance().recordPermission
252
+ switch permissionStatus {
253
+ case .granted:
254
+ promise.resolve(["status": "granted"])
255
+ case .denied:
256
+ promise.resolve(["status": "denied"])
257
+ case .undetermined:
258
+ promise.resolve(["status": "undetermined"])
259
+ @unknown default:
260
+ promise.reject("UNKNOWN_ERROR", "Unknown permission status")
261
+ }
262
+ }
227
263
  }
228
264
 
229
265
  /// Handles the reception of audio data from the AudioStreamManager.
@@ -2,6 +2,7 @@
2
2
 
3
3
  struct RecordingResult {
4
4
  var fileUri: String
5
+ var filename: String
5
6
  var mimeType: String
6
7
  var duration: Int64
7
8
  var size: Int64
package/package.json CHANGED
@@ -1,67 +1,97 @@
1
1
  {
2
- "name": "@siteed/expo-audio-stream",
3
- "version": "1.0.3",
4
- "description": "stream audio crossplatform",
5
- "license": "MIT",
6
- "main": "build/index.js",
7
- "types": "build/index.d.ts",
8
- "author": "Arthur Breton <abreton@siteed.net> (https://github.com/deeeed)",
9
- "homepage": "https://github.com/deeeed/expo-audio-stream#readme",
10
- "repository": {
11
- "type": "git",
12
- "url": "git+https://github.com/deeeed/expo-audio-stream.git",
13
- "directory": "packages/expo-audio-stream"
14
- },
15
- "bugs": {
16
- "url": "https://github.com/deeeed/expo-audio-stream/issues"
17
- },
18
- "keywords": [
19
- "react-native",
20
- "expo",
21
- "expo-audio-stream",
22
- "ExpoAudioStream"
23
- ],
24
- "scripts": {
25
- "build": "expo-module build",
26
- "clean": "expo-module clean",
27
- "lint": "expo-module lint",
28
- "test": "expo-module test",
29
- "prepare": "expo-module prepare",
30
- "prepublishOnly": "expo-module prepublishOnly",
31
- "expo-module": "expo-module",
32
- "open:ios": "open -a \"Xcode\" ../../apps/playground/ios",
33
- "open:android": "open -a \"Android Studio\" ../../apps/playground/android",
34
- "size": "bundle-size && size-limit"
35
- },
36
- "devDependencies": {
37
- "@expo/config-plugins": "^7.9.1",
38
- "@size-limit/preset-big-lib": "^11.1.4",
39
- "@types/debug": "^4.1.12",
40
- "@types/node": "^20.12.7",
41
- "@types/react": "^18.0.25",
42
- "@typescript-eslint/eslint-plugin": "^7.7.0",
43
- "@typescript-eslint/parser": "^7.7.0",
44
- "bundle-size": "^1.1.5",
45
- "eslint": "^8.56.0",
46
- "eslint-config-prettier": "^9.1.0",
47
- "eslint-config-universe": "^12.0.0",
48
- "eslint-plugin-import": "^2.29.1",
49
- "eslint-plugin-prettier": "^5.1.3",
50
- "eslint-plugin-promise": "^6.1.1",
51
- "eslint-plugin-react": "^7.34.1",
52
- "expo-module-scripts": "^3.5.2",
53
- "expo-modules-core": "^1.12.19",
54
- "prettier": "^3.2.5",
55
- "react-native": "^0.74.3",
56
- "size-limit": "^11.1.4"
57
- },
58
- "peerDependencies": {
59
- "expo": "*",
60
- "react": "*",
61
- "react-native": "*"
62
- },
63
- "publishConfig": {
64
- "access": "public",
65
- "registry": "https://registry.npmjs.org"
66
- }
2
+ "name": "@siteed/expo-audio-stream",
3
+ "version": "1.1.1",
4
+ "description": "stream audio crossplatform",
5
+ "license": "MIT",
6
+ "main": "build/index.js",
7
+ "types": "build/index.d.ts",
8
+ "author": "Arthur Breton <abreton@siteed.net> (https://github.com/deeeed)",
9
+ "homepage": "https://github.com/deeeed/expo-audio-stream#readme",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/deeeed/expo-audio-stream.git",
13
+ "directory": "packages/expo-audio-stream"
14
+ },
15
+ "bugs": {
16
+ "url": "https://github.com/deeeed/expo-audio-stream/issues"
17
+ },
18
+ "keywords": [
19
+ "react-native",
20
+ "expo",
21
+ "expo-audio-stream",
22
+ "ExpoAudioStream"
23
+ ],
24
+ "files": [
25
+ "src",
26
+ "android",
27
+ "ios",
28
+ "cpp",
29
+ "generated",
30
+ "README.md",
31
+ "package.json",
32
+ "*.podspec",
33
+ "!ios/build",
34
+ "!android/build",
35
+ "!android/gradle",
36
+ "!android/gradlew",
37
+ "!android/gradlew.bat",
38
+ "!android/local.properties",
39
+ "!**/__tests__",
40
+ "!**/__fixtures__",
41
+ "!**/__mocks__",
42
+ "!**/.*"
43
+ ],
44
+ "scripts": {
45
+ "build": "expo-module build",
46
+ "clean": "expo-module clean",
47
+ "lint": "expo-module lint",
48
+ "test": "expo-module test",
49
+ "typecheck": "tsc --noEmit",
50
+ "docgen": "typedoc src/index.ts --plugin typedoc-plugin-markdown --readme none --out ../../documentation_site/docs/api-reference/API",
51
+ "prepare": "expo-module prepare",
52
+ "prepublishOnly": "expo-module prepublishOnly",
53
+ "expo-module": "expo-module",
54
+ "open:ios": "open -a \"Xcode\" ../../apps/playground/ios",
55
+ "open:android": "open -a \"Android Studio\" ../../apps/playground/android",
56
+ "size": "bundle-size && size-limit",
57
+ "release": "./publish.sh"
58
+ },
59
+ "devDependencies": {
60
+ "@expo/config-plugins": "^7.9.1",
61
+ "@size-limit/preset-big-lib": "^11.1.4",
62
+ "@types/jest": "^29.5.12",
63
+ "@types/node": "^20.12.7",
64
+ "@types/react": "^18.0.25",
65
+ "@typescript-eslint/eslint-plugin": "^7.7.0",
66
+ "@typescript-eslint/parser": "^7.7.0",
67
+ "bundle-size": "^1.1.5",
68
+ "eslint": "^8.56.0",
69
+ "eslint-config-prettier": "^9.1.0",
70
+ "eslint-config-universe": "^12.0.0",
71
+ "eslint-plugin-import": "^2.29.1",
72
+ "eslint-plugin-prettier": "^5.1.3",
73
+ "eslint-plugin-promise": "^6.1.1",
74
+ "eslint-plugin-react": "^7.34.1",
75
+ "expo-module-scripts": "^3.5.2",
76
+ "expo-modules-core": "^1.12.19",
77
+ "jest": "^29.7.0",
78
+ "prettier": "^3.2.5",
79
+ "react-native": "^0.74.4",
80
+ "size-limit": "^11.1.4",
81
+ "typedoc": "^0.26.5",
82
+ "typedoc-plugin-markdown": "^4.2.3",
83
+ "typescript": "^5.5.4"
84
+ },
85
+ "peerDependencies": {
86
+ "expo": "*",
87
+ "react": "*",
88
+ "react-native": "*"
89
+ },
90
+ "publishConfig": {
91
+ "access": "public",
92
+ "registry": "https://registry.npmjs.org"
93
+ },
94
+ "dependencies": {
95
+ "@siteed/react-native-logger": "^0.9.3"
96
+ }
67
97
  }