@siteed/expo-audio-stream 1.0.0 → 1.0.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/README.md +7 -18
- package/android/build.gradle +5 -0
- package/android/src/main/java/net/siteed/audiostream/AudioAnalysisData.kt +120 -0
- package/android/src/main/java/net/siteed/audiostream/AudioFileHandler.kt +34 -4
- package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +635 -0
- package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +194 -79
- package/android/src/main/java/net/siteed/audiostream/Constants.kt +1 -0
- package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +48 -2
- package/android/src/main/java/net/siteed/audiostream/FFT.kt +44 -0
- package/android/src/main/java/net/siteed/audiostream/Features.kt +56 -0
- package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +12 -0
- package/android/src/main/test/java/net/siteed/audiostream/AudioProcessorTest.kt +56 -0
- package/app.plugin.js +1 -1
- package/build/AudioRecorder.provider.js +1 -1
- package/build/AudioRecorder.provider.js.map +1 -1
- package/build/ExpoAudioStream.native.d.ts +3 -0
- package/build/ExpoAudioStream.native.d.ts.map +1 -0
- package/build/ExpoAudioStream.native.js +6 -0
- package/build/ExpoAudioStream.native.js.map +1 -0
- package/build/ExpoAudioStream.types.d.ts +79 -6
- package/build/ExpoAudioStream.types.d.ts.map +1 -1
- package/build/ExpoAudioStream.types.js.map +1 -1
- package/build/ExpoAudioStream.web.d.ts +41 -0
- package/build/ExpoAudioStream.web.d.ts.map +1 -0
- package/build/ExpoAudioStream.web.js +184 -0
- package/build/ExpoAudioStream.web.js.map +1 -0
- package/build/ExpoAudioStreamModule.d.ts +2 -2
- package/build/ExpoAudioStreamModule.d.ts.map +1 -1
- package/build/ExpoAudioStreamModule.js +12 -3
- package/build/ExpoAudioStreamModule.js.map +1 -1
- package/build/WebRecorder.d.ts +47 -0
- package/build/WebRecorder.d.ts.map +1 -0
- package/build/WebRecorder.js +243 -0
- package/build/WebRecorder.js.map +1 -0
- package/build/index.d.ts +14 -5
- package/build/index.d.ts.map +1 -1
- package/build/index.js +106 -7
- package/build/index.js.map +1 -1
- package/build/inlineAudioWebWorker.d.ts +3 -0
- package/build/inlineAudioWebWorker.d.ts.map +1 -0
- package/build/inlineAudioWebWorker.js +340 -0
- package/build/inlineAudioWebWorker.js.map +1 -0
- package/build/useAudioRecording.d.ts +24 -9
- package/build/useAudioRecording.d.ts.map +1 -1
- package/build/useAudioRecording.js +107 -29
- package/build/useAudioRecording.js.map +1 -1
- package/build/utils.d.ts +31 -0
- package/build/utils.d.ts.map +1 -0
- package/build/utils.js +143 -0
- package/build/utils.js.map +1 -0
- package/expo-module.config.json +13 -4
- package/ios/AudioAnalysisData.swift +39 -0
- package/ios/AudioProcessingHelpers.swift +59 -0
- package/ios/AudioProcessor.swift +317 -0
- package/ios/AudioStreamError.swift +7 -0
- package/ios/AudioStreamManager.swift +204 -52
- package/ios/AudioStreamManagerDelegate.swift +4 -0
- package/ios/DataPoint.swift +41 -0
- package/ios/ExpoAudioStreamModule.swift +188 -6
- package/ios/Features.swift +44 -0
- package/ios/RecordingResult.swift +19 -0
- package/ios/RecordingSettings.swift +13 -0
- package/ios/WaveformExtractor.swift +105 -0
- package/package.json +9 -9
- package/plugin/tsconfig.json +13 -8
- package/publish.sh +8 -0
- package/src/AudioRecorder.provider.tsx +1 -1
- package/src/ExpoAudioStream.native.ts +6 -0
- package/src/ExpoAudioStream.types.ts +97 -11
- package/src/ExpoAudioStream.web.ts +228 -0
- package/src/ExpoAudioStreamModule.ts +17 -3
- package/src/WebRecorder.ts +364 -0
- package/src/index.ts +166 -20
- package/src/inlineAudioWebWorker.tsx +340 -0
- package/src/useAudioRecording.tsx +410 -0
- package/src/utils.ts +189 -0
- package/build/ExpoAudioStreamModule.web.d.ts +0 -37
- package/build/ExpoAudioStreamModule.web.d.ts.map +0 -1
- package/build/ExpoAudioStreamModule.web.js +0 -156
- package/build/ExpoAudioStreamModule.web.js.map +0 -1
- package/docs/demo.gif +0 -0
- package/release-it.js +0 -18
- package/src/ExpoAudioStreamModule.web.ts +0 -181
- package/src/useAudioRecording.ts +0 -268
- package/yarn-error.log +0 -7793
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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
|
+
`@siteed/expo-audio-stream` is a comprehensive library designed to facilitate real-time audio processing and streaming across iOS, Android, and web platforms.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
- Listeners for audio data events with detailed event payloads.
|
|
12
12
|
- Utility functions for recording control and file management.
|
|
13
13
|
|
|
14
|
-
## Example Application
|
|
14
|
+
## Playground Example Application
|
|
15
15
|
|
|
16
16
|
The project comes with a fully functional example application that demonstrates how to use the library in a real-world scenario.
|
|
17
17
|
|
|
@@ -23,9 +23,9 @@ To try it:
|
|
|
23
23
|
git clone https://github.com/deeeed/expo-audio-stream.git
|
|
24
24
|
cd expo-audio-stream
|
|
25
25
|
yarn
|
|
26
|
-
yarn
|
|
27
|
-
yarn
|
|
28
|
-
yarn
|
|
26
|
+
yarn playground ios
|
|
27
|
+
yarn playground android
|
|
28
|
+
yarn playground web
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
## Installation
|
|
@@ -38,13 +38,10 @@ npm install @siteed/expo-audio-stream
|
|
|
38
38
|
yarn add @siteed/expo-audio-stream
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
Make sure that you have Expo set up in your project. For details on setting up Expo, refer to the Expo documentation.
|
|
42
41
|
|
|
43
42
|
### Configuring with app.json
|
|
44
43
|
|
|
45
|
-
To ensure expo-audio-stream works correctly with Expo, you must add it as a plugin in your app.json configuration file.
|
|
46
|
-
|
|
47
|
-
Add the plugin to your app.json like so:
|
|
44
|
+
To ensure expo-audio-stream works correctly with Expo, you must add it as a plugin in your app.json configuration file.
|
|
48
45
|
|
|
49
46
|
```json
|
|
50
47
|
{
|
|
@@ -63,7 +60,7 @@ This library provides two hooks: `useAudioRecorder` for standalone use and `useS
|
|
|
63
60
|
|
|
64
61
|
### Standalone Recording
|
|
65
62
|
|
|
66
|
-
The `
|
|
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.
|
|
67
64
|
|
|
68
65
|
|
|
69
66
|
#### Standalone Usage
|
|
@@ -187,11 +184,3 @@ function App() {
|
|
|
187
184
|
- on web, it usually records in opus but it depends on the browser configuration.
|
|
188
185
|
|
|
189
186
|
If you want to process the audio livestream directly, I recommend having another encoding step to align the audio format across platforms.
|
|
190
|
-
|
|
191
|
-
## TODO
|
|
192
|
-
this package is still in development, and there are a few things that need to be done:
|
|
193
|
-
- Add resume (vs currently use start) support and implement pause on iOS.
|
|
194
|
-
- Multi format support: Extend support to other audio formats beyond WAV, such as MP3 or AAC
|
|
195
|
-
- Integrate an audio processing library for optional audio analysis, such as equalization, noise reduction, or volume normalization.
|
|
196
|
-
- Implement a more robust error handling system to provide detailed error messages and recovery options.
|
|
197
|
-
|
package/android/build.gradle
CHANGED
|
@@ -89,4 +89,9 @@ repositories {
|
|
|
89
89
|
dependencies {
|
|
90
90
|
implementation project(':expo-modules-core')
|
|
91
91
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
|
|
92
|
+
|
|
93
|
+
// Add testing dependencies
|
|
94
|
+
testImplementation 'junit:junit:4.13.2'
|
|
95
|
+
testImplementation 'org.jetbrains.kotlin:kotlin-test:1.8.10'
|
|
96
|
+
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit:1.8.10'
|
|
92
97
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// net/siteed/audiostream/AudioAnalysisData.kt
|
|
2
|
+
package net.siteed.audiostream
|
|
3
|
+
|
|
4
|
+
import android.os.Bundle
|
|
5
|
+
import androidx.core.os.bundleOf
|
|
6
|
+
|
|
7
|
+
data class DataPoint(
|
|
8
|
+
val id: Long,
|
|
9
|
+
val amplitude: Float,
|
|
10
|
+
val activeSpeech: Boolean? = null,
|
|
11
|
+
val dB: Float? = null,
|
|
12
|
+
val silent: Boolean? = null,
|
|
13
|
+
val features: Features? = null,
|
|
14
|
+
val startTime: Float? = null,
|
|
15
|
+
val endTime: Float? = null,
|
|
16
|
+
val startPosition: Int? = null,
|
|
17
|
+
val endPosition: Int? = null,
|
|
18
|
+
val samples: Int = 0,
|
|
19
|
+
val speaker: Int? = null
|
|
20
|
+
) {
|
|
21
|
+
fun toDictionary(): Map<String, Any?> {
|
|
22
|
+
return mapOf(
|
|
23
|
+
"id" to id,
|
|
24
|
+
"amplitude" to amplitude,
|
|
25
|
+
"activeSpeech" to activeSpeech,
|
|
26
|
+
"dB" to dB,
|
|
27
|
+
"silent" to silent,
|
|
28
|
+
"features" to features?.toDictionary(),
|
|
29
|
+
"startTime" to startTime,
|
|
30
|
+
"endTime" to endTime,
|
|
31
|
+
"startPosition" to startPosition,
|
|
32
|
+
"endPosition" to endPosition,
|
|
33
|
+
"samples" to samples,
|
|
34
|
+
"speaker" to speaker
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
fun toBundle(): Bundle {
|
|
39
|
+
return bundleOf(
|
|
40
|
+
"id" to id,
|
|
41
|
+
"amplitude" to amplitude,
|
|
42
|
+
"activeSpeech" to activeSpeech,
|
|
43
|
+
"dB" to dB,
|
|
44
|
+
"silent" to silent,
|
|
45
|
+
"features" to features?.toBundle(),
|
|
46
|
+
"startTime" to startTime,
|
|
47
|
+
"endTime" to endTime,
|
|
48
|
+
"startPosition" to startPosition,
|
|
49
|
+
"endPosition" to endPosition,
|
|
50
|
+
"samples" to samples,
|
|
51
|
+
"speaker" to speaker
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
data class AudioAnalysisData(
|
|
57
|
+
val pointsPerSecond: Double,
|
|
58
|
+
val durationMs: Int,
|
|
59
|
+
val bitDepth: Int,
|
|
60
|
+
val numberOfChannels: Int,
|
|
61
|
+
val sampleRate: Int,
|
|
62
|
+
val samples: Int,
|
|
63
|
+
val dataPoints: List<DataPoint>,
|
|
64
|
+
val amplitudeRange: AmplitudeRange,
|
|
65
|
+
val speakerChanges: List<SpeakerChange>,
|
|
66
|
+
val extractionTimeMs: Float
|
|
67
|
+
) {
|
|
68
|
+
data class AmplitudeRange(val min: Float, val max: Float) {
|
|
69
|
+
fun toDictionary(): Map<String, Float> {
|
|
70
|
+
return mapOf("min" to min, "max" to max)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
fun toBundle(): Bundle {
|
|
74
|
+
return bundleOf("min" to min, "max" to max)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
data class SpeakerChange(val timestamp: Float, val speaker: Int) {
|
|
79
|
+
fun toDictionary(): Map<String, Any> {
|
|
80
|
+
return mapOf("timestamp" to timestamp, "speaker" to speaker)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fun toBundle(): Bundle {
|
|
84
|
+
return bundleOf("timestamp" to timestamp, "speaker" to speaker)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fun toDictionary(): Map<String, Any> {
|
|
89
|
+
return mapOf(
|
|
90
|
+
"pointsPerSecond" to pointsPerSecond,
|
|
91
|
+
"durationMs" to durationMs,
|
|
92
|
+
"bitDepth" to bitDepth,
|
|
93
|
+
"numberOfChannels" to numberOfChannels,
|
|
94
|
+
"sampleRate" to sampleRate,
|
|
95
|
+
"samples" to samples,
|
|
96
|
+
"dataPoints" to dataPoints.map { it.toDictionary() },
|
|
97
|
+
"amplitudeRange" to amplitudeRange.toDictionary(),
|
|
98
|
+
"speakerChanges" to speakerChanges.map { it.toDictionary() },
|
|
99
|
+
"extractionTimeMs" to extractionTimeMs
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
fun toBundle(): Bundle {
|
|
104
|
+
val dataPointsBundleArray = dataPoints.map { it.toBundle() }.toTypedArray()
|
|
105
|
+
val speakerChangesBundleArray = speakerChanges.map { it.toBundle() }.toTypedArray()
|
|
106
|
+
|
|
107
|
+
return bundleOf(
|
|
108
|
+
"pointsPerSecond" to pointsPerSecond,
|
|
109
|
+
"durationMs" to durationMs,
|
|
110
|
+
"bitDepth" to bitDepth,
|
|
111
|
+
"numberOfChannels" to numberOfChannels,
|
|
112
|
+
"sampleRate" to sampleRate,
|
|
113
|
+
"samples" to samples,
|
|
114
|
+
"dataPoints" to dataPointsBundleArray,
|
|
115
|
+
"amplitudeRange" to amplitudeRange.toBundle(),
|
|
116
|
+
"speakerChanges" to speakerChangesBundleArray,
|
|
117
|
+
"extractionTimeMs" to extractionTimeMs
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -14,25 +14,55 @@ class AudioFileHandler(private val filesDir: File) {
|
|
|
14
14
|
|
|
15
15
|
// RIFF/WAVE header
|
|
16
16
|
"RIFF".toByteArray().copyInto(header, 0)
|
|
17
|
-
|
|
17
|
+
// (file size - 8) to be updated later
|
|
18
|
+
header[4] = 0 // Placeholder
|
|
19
|
+
header[5] = 0 // Placeholder
|
|
20
|
+
header[6] = 0 // Placeholder
|
|
21
|
+
header[7] = 0 // Placeholder
|
|
18
22
|
"WAVE".toByteArray().copyInto(header, 8)
|
|
19
23
|
"fmt ".toByteArray().copyInto(header, 12)
|
|
20
24
|
|
|
21
25
|
// 16 for PCM
|
|
22
26
|
header[16] = 16
|
|
27
|
+
header[17] = 0
|
|
28
|
+
header[18] = 0
|
|
29
|
+
header[19] = 0
|
|
30
|
+
|
|
31
|
+
// PCM format ID
|
|
23
32
|
header[20] = 1 // Audio format 1 for PCM (not compressed)
|
|
24
|
-
header[
|
|
33
|
+
header[21] = 0
|
|
34
|
+
|
|
35
|
+
// Number of channels
|
|
36
|
+
header[22] = (channels and 0xff).toByte()
|
|
37
|
+
header[23] = (channels shr 8 and 0xff).toByte()
|
|
38
|
+
|
|
39
|
+
// Sample rate
|
|
25
40
|
header[24] = (sampleRateInHz and 0xff).toByte()
|
|
26
41
|
header[25] = (sampleRateInHz shr 8 and 0xff).toByte()
|
|
27
42
|
header[26] = (sampleRateInHz shr 16 and 0xff).toByte()
|
|
28
43
|
header[27] = (sampleRateInHz shr 24 and 0xff).toByte()
|
|
44
|
+
|
|
45
|
+
// Byte rate
|
|
29
46
|
header[28] = (byteRate and 0xff).toByte()
|
|
30
47
|
header[29] = (byteRate shr 8 and 0xff).toByte()
|
|
31
48
|
header[30] = (byteRate shr 16 and 0xff).toByte()
|
|
32
49
|
header[31] = (byteRate shr 24 and 0xff).toByte()
|
|
33
|
-
|
|
34
|
-
|
|
50
|
+
|
|
51
|
+
// Block align
|
|
52
|
+
header[32] = (blockAlign and 0xff).toByte()
|
|
53
|
+
header[33] = (blockAlign shr 8 and 0xff).toByte()
|
|
54
|
+
|
|
55
|
+
// Bits per sample
|
|
56
|
+
header[34] = (bitDepth and 0xff).toByte()
|
|
57
|
+
header[35] = (bitDepth shr 8 and 0xff).toByte()
|
|
58
|
+
|
|
59
|
+
// Data chunk
|
|
35
60
|
"data".toByteArray().copyInto(header, 36)
|
|
61
|
+
// Data size to be updated later
|
|
62
|
+
header[40] = 0 // Placeholder
|
|
63
|
+
header[41] = 0 // Placeholder
|
|
64
|
+
header[42] = 0 // Placeholder
|
|
65
|
+
header[43] = 0 // Placeholder
|
|
36
66
|
|
|
37
67
|
out.write(header, 0, 44)
|
|
38
68
|
}
|