@siteed/expo-audio-studio 2.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 (188) hide show
  1. package/CHANGELOG.md +210 -0
  2. package/LICENSE +21 -0
  3. package/README.md +269 -0
  4. package/android/build.gradle +105 -0
  5. package/android/src/main/AndroidManifest.xml +27 -0
  6. package/android/src/main/java/net/siteed/audiostream/AudioAnalysisData.kt +166 -0
  7. package/android/src/main/java/net/siteed/audiostream/AudioDataEncoder.kt +9 -0
  8. package/android/src/main/java/net/siteed/audiostream/AudioFileHandler.kt +131 -0
  9. package/android/src/main/java/net/siteed/audiostream/AudioFormatUtils.kt +103 -0
  10. package/android/src/main/java/net/siteed/audiostream/AudioNotificationsManager.kt +435 -0
  11. package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +2235 -0
  12. package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +1437 -0
  13. package/android/src/main/java/net/siteed/audiostream/AudioRecordingService.kt +166 -0
  14. package/android/src/main/java/net/siteed/audiostream/AudioTrimmer.kt +1099 -0
  15. package/android/src/main/java/net/siteed/audiostream/Constants.kt +21 -0
  16. package/android/src/main/java/net/siteed/audiostream/EventSender.kt +7 -0
  17. package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +739 -0
  18. package/android/src/main/java/net/siteed/audiostream/FFT.kt +99 -0
  19. package/android/src/main/java/net/siteed/audiostream/Features.kt +98 -0
  20. package/android/src/main/java/net/siteed/audiostream/NotificationConfig.kt +70 -0
  21. package/android/src/main/java/net/siteed/audiostream/PermissionUtils.kt +59 -0
  22. package/android/src/main/java/net/siteed/audiostream/RecordingActionReceiver.kt +59 -0
  23. package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +205 -0
  24. package/android/src/main/java/net/siteed/audiostream/WaveformConfig.kt +19 -0
  25. package/android/src/main/java/net/siteed/audiostream/WaveformRenderer.kt +159 -0
  26. package/android/src/main/res/drawable/ic_default_action_icon.xml +16 -0
  27. package/android/src/main/res/drawable/ic_microphone.xml +13 -0
  28. package/android/src/main/res/drawable/ic_pause.xml +10 -0
  29. package/android/src/main/res/drawable/ic_play.xml +10 -0
  30. package/android/src/main/res/drawable/ic_stop.xml +10 -0
  31. package/android/src/main/res/layout/notification_recording.xml +37 -0
  32. package/android/src/main/test/java/net/siteed/audiostream/AudioProcessorTest.kt +56 -0
  33. package/app.plugin.js +1 -0
  34. package/build/AudioAnalysis/AudioAnalysis.types.d.ts +179 -0
  35. package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -0
  36. package/build/AudioAnalysis/AudioAnalysis.types.js +3 -0
  37. package/build/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
  38. package/build/AudioAnalysis/extractAudioAnalysis.d.ts +68 -0
  39. package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -0
  40. package/build/AudioAnalysis/extractAudioAnalysis.js +203 -0
  41. package/build/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
  42. package/build/AudioAnalysis/extractAudioData.d.ts +3 -0
  43. package/build/AudioAnalysis/extractAudioData.d.ts.map +1 -0
  44. package/build/AudioAnalysis/extractAudioData.js +5 -0
  45. package/build/AudioAnalysis/extractAudioData.js.map +1 -0
  46. package/build/AudioAnalysis/extractMelSpectrogram.d.ts +14 -0
  47. package/build/AudioAnalysis/extractMelSpectrogram.d.ts.map +1 -0
  48. package/build/AudioAnalysis/extractMelSpectrogram.js +85 -0
  49. package/build/AudioAnalysis/extractMelSpectrogram.js.map +1 -0
  50. package/build/AudioAnalysis/extractPreview.d.ts +11 -0
  51. package/build/AudioAnalysis/extractPreview.d.ts.map +1 -0
  52. package/build/AudioAnalysis/extractPreview.js +25 -0
  53. package/build/AudioAnalysis/extractPreview.js.map +1 -0
  54. package/build/AudioAnalysis/extractWaveform.d.ts +8 -0
  55. package/build/AudioAnalysis/extractWaveform.d.ts.map +1 -0
  56. package/build/AudioAnalysis/extractWaveform.js +11 -0
  57. package/build/AudioAnalysis/extractWaveform.js.map +1 -0
  58. package/build/AudioRecorder.provider.d.ts +11 -0
  59. package/build/AudioRecorder.provider.d.ts.map +1 -0
  60. package/build/AudioRecorder.provider.js +37 -0
  61. package/build/AudioRecorder.provider.js.map +1 -0
  62. package/build/ExpoAudioStream.native.d.ts +3 -0
  63. package/build/ExpoAudioStream.native.d.ts.map +1 -0
  64. package/build/ExpoAudioStream.native.js +6 -0
  65. package/build/ExpoAudioStream.native.js.map +1 -0
  66. package/build/ExpoAudioStream.types.d.ts +532 -0
  67. package/build/ExpoAudioStream.types.d.ts.map +1 -0
  68. package/build/ExpoAudioStream.types.js +2 -0
  69. package/build/ExpoAudioStream.types.js.map +1 -0
  70. package/build/ExpoAudioStream.web.d.ts +59 -0
  71. package/build/ExpoAudioStream.web.d.ts.map +1 -0
  72. package/build/ExpoAudioStream.web.js +285 -0
  73. package/build/ExpoAudioStream.web.js.map +1 -0
  74. package/build/ExpoAudioStreamModule.d.ts +3 -0
  75. package/build/ExpoAudioStreamModule.d.ts.map +1 -0
  76. package/build/ExpoAudioStreamModule.js +693 -0
  77. package/build/ExpoAudioStreamModule.js.map +1 -0
  78. package/build/WebRecorder.web.d.ts +119 -0
  79. package/build/WebRecorder.web.d.ts.map +1 -0
  80. package/build/WebRecorder.web.js +436 -0
  81. package/build/WebRecorder.web.js.map +1 -0
  82. package/build/constants.d.ts +11 -0
  83. package/build/constants.d.ts.map +1 -0
  84. package/build/constants.js +14 -0
  85. package/build/constants.js.map +1 -0
  86. package/build/events.d.ts +26 -0
  87. package/build/events.d.ts.map +1 -0
  88. package/build/events.js +21 -0
  89. package/build/events.js.map +1 -0
  90. package/build/index.d.ts +15 -0
  91. package/build/index.d.ts.map +1 -0
  92. package/build/index.js +14 -0
  93. package/build/index.js.map +1 -0
  94. package/build/trimAudio.d.ts +25 -0
  95. package/build/trimAudio.d.ts.map +1 -0
  96. package/build/trimAudio.js +67 -0
  97. package/build/trimAudio.js.map +1 -0
  98. package/build/useAudioRecorder.d.ts +21 -0
  99. package/build/useAudioRecorder.d.ts.map +1 -0
  100. package/build/useAudioRecorder.js +427 -0
  101. package/build/useAudioRecorder.js.map +1 -0
  102. package/build/utils/BlobFix.d.ts +9 -0
  103. package/build/utils/BlobFix.d.ts.map +1 -0
  104. package/build/utils/BlobFix.js +498 -0
  105. package/build/utils/BlobFix.js.map +1 -0
  106. package/build/utils/audioProcessing.d.ts +24 -0
  107. package/build/utils/audioProcessing.d.ts.map +1 -0
  108. package/build/utils/audioProcessing.js +133 -0
  109. package/build/utils/audioProcessing.js.map +1 -0
  110. package/build/utils/concatenateBuffers.d.ts +8 -0
  111. package/build/utils/concatenateBuffers.d.ts.map +1 -0
  112. package/build/utils/concatenateBuffers.js +21 -0
  113. package/build/utils/concatenateBuffers.js.map +1 -0
  114. package/build/utils/convertPCMToFloat32.d.ts +13 -0
  115. package/build/utils/convertPCMToFloat32.d.ts.map +1 -0
  116. package/build/utils/convertPCMToFloat32.js +120 -0
  117. package/build/utils/convertPCMToFloat32.js.map +1 -0
  118. package/build/utils/encodingToBitDepth.d.ts +5 -0
  119. package/build/utils/encodingToBitDepth.d.ts.map +1 -0
  120. package/build/utils/encodingToBitDepth.js +13 -0
  121. package/build/utils/encodingToBitDepth.js.map +1 -0
  122. package/build/utils/getWavFileInfo.d.ts +26 -0
  123. package/build/utils/getWavFileInfo.d.ts.map +1 -0
  124. package/build/utils/getWavFileInfo.js +92 -0
  125. package/build/utils/getWavFileInfo.js.map +1 -0
  126. package/build/utils/writeWavHeader.d.ts +49 -0
  127. package/build/utils/writeWavHeader.d.ts.map +1 -0
  128. package/build/utils/writeWavHeader.js +91 -0
  129. package/build/utils/writeWavHeader.js.map +1 -0
  130. package/build/workers/InlineFeaturesExtractor.web.d.ts +2 -0
  131. package/build/workers/InlineFeaturesExtractor.web.d.ts.map +1 -0
  132. package/build/workers/InlineFeaturesExtractor.web.js +828 -0
  133. package/build/workers/InlineFeaturesExtractor.web.js.map +1 -0
  134. package/build/workers/inlineAudioWebWorker.web.d.ts +2 -0
  135. package/build/workers/inlineAudioWebWorker.web.d.ts.map +1 -0
  136. package/build/workers/inlineAudioWebWorker.web.js +157 -0
  137. package/build/workers/inlineAudioWebWorker.web.js.map +1 -0
  138. package/expo-module.config.json +9 -0
  139. package/ios/AudioAnalysisData.swift +74 -0
  140. package/ios/AudioNotificationManager.swift +135 -0
  141. package/ios/AudioProcessingHelpers.swift +743 -0
  142. package/ios/AudioProcessor.swift +1313 -0
  143. package/ios/AudioStreamError.swift +7 -0
  144. package/ios/AudioStreamManager.swift +1708 -0
  145. package/ios/AudioStreamManagerDelegate.swift +16 -0
  146. package/ios/DataPoint.swift +54 -0
  147. package/ios/DecodingConfig.swift +47 -0
  148. package/ios/ExpoAudioStream.podspec +27 -0
  149. package/ios/ExpoAudioStreamModule.swift +805 -0
  150. package/ios/FFT.swift +62 -0
  151. package/ios/Features.swift +95 -0
  152. package/ios/Logger.swift +7 -0
  153. package/ios/NotificationExtension.swift +15 -0
  154. package/ios/RecordingResult.swift +22 -0
  155. package/ios/RecordingSettings.swift +265 -0
  156. package/ios/WaveformExtractor.swift +105 -0
  157. package/package.json +128 -0
  158. package/plugin/build/index.d.ts +21 -0
  159. package/plugin/build/index.js +192 -0
  160. package/plugin/src/index.ts +279 -0
  161. package/plugin/tsconfig.json +10 -0
  162. package/plugin/tsconfig.tsbuildinfo +1 -0
  163. package/src/AudioAnalysis/AudioAnalysis.types.ts +202 -0
  164. package/src/AudioAnalysis/extractAudioAnalysis.ts +333 -0
  165. package/src/AudioAnalysis/extractAudioData.ts +6 -0
  166. package/src/AudioAnalysis/extractMelSpectrogram.ts +144 -0
  167. package/src/AudioAnalysis/extractPreview.ts +34 -0
  168. package/src/AudioAnalysis/extractWaveform.ts +22 -0
  169. package/src/AudioRecorder.provider.tsx +54 -0
  170. package/src/ExpoAudioStream.native.ts +6 -0
  171. package/src/ExpoAudioStream.types.ts +641 -0
  172. package/src/ExpoAudioStream.web.ts +359 -0
  173. package/src/ExpoAudioStreamModule.ts +967 -0
  174. package/src/WebRecorder.web.ts +580 -0
  175. package/src/constants.ts +18 -0
  176. package/src/events.ts +60 -0
  177. package/src/index.ts +36 -0
  178. package/src/trimAudio.ts +90 -0
  179. package/src/useAudioRecorder.tsx +620 -0
  180. package/src/utils/BlobFix.ts +559 -0
  181. package/src/utils/audioProcessing.ts +205 -0
  182. package/src/utils/concatenateBuffers.ts +24 -0
  183. package/src/utils/convertPCMToFloat32.ts +170 -0
  184. package/src/utils/encodingToBitDepth.ts +18 -0
  185. package/src/utils/getWavFileInfo.ts +132 -0
  186. package/src/utils/writeWavHeader.ts +114 -0
  187. package/src/workers/InlineFeaturesExtractor.web.tsx +827 -0
  188. package/src/workers/inlineAudioWebWorker.web.tsx +156 -0
@@ -0,0 +1,166 @@
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 SpeechFeatures(
8
+ val isActive: Boolean,
9
+ val speakerId: Int? = null
10
+ ) {
11
+ fun toDictionary(): Map<String, Any?> {
12
+ return mapOf(
13
+ "isActive" to isActive,
14
+ "speakerId" to speakerId
15
+ )
16
+ }
17
+
18
+ fun toBundle(): Bundle {
19
+ return bundleOf(
20
+ "isActive" to isActive,
21
+ "speakerId" to speakerId
22
+ )
23
+ }
24
+ }
25
+
26
+ data class DataPoint(
27
+ val id: Long,
28
+ val amplitude: Float,
29
+ val rms: Float,
30
+ val dB: Float,
31
+ val silent: Boolean,
32
+ val features: Features? = null,
33
+ val speech: SpeechFeatures? = null,
34
+ val startTime: Float? = null,
35
+ val endTime: Float? = null,
36
+ val startPosition: Int? = null,
37
+ val endPosition: Int? = null,
38
+ val samples: Int = 0
39
+ ) {
40
+ fun toDictionary(): Map<String, Any?> {
41
+ return mapOf(
42
+ "id" to id,
43
+ "amplitude" to amplitude,
44
+ "rms" to rms,
45
+ "dB" to dB,
46
+ "silent" to silent,
47
+ "features" to features?.toDictionary(),
48
+ "speech" to speech?.toDictionary(),
49
+ "startTime" to startTime,
50
+ "endTime" to endTime,
51
+ "startPosition" to startPosition,
52
+ "endPosition" to endPosition,
53
+ "samples" to samples
54
+ )
55
+ }
56
+
57
+ fun toBundle(): Bundle {
58
+ return bundleOf(
59
+ "id" to id,
60
+ "amplitude" to amplitude,
61
+ "rms" to rms,
62
+ "dB" to dB,
63
+ "silent" to silent,
64
+ "features" to features?.toBundle(),
65
+ "speech" to speech?.toBundle(),
66
+ "startTime" to startTime,
67
+ "endTime" to endTime,
68
+ "startPosition" to startPosition,
69
+ "endPosition" to endPosition,
70
+ "samples" to samples
71
+ )
72
+ }
73
+ }
74
+
75
+ data class AudioAnalysisData(
76
+ val segmentDurationMs: Int,
77
+ val durationMs: Int,
78
+ val bitDepth: Int,
79
+ val numberOfChannels: Int,
80
+ val sampleRate: Int,
81
+ val samples: Int,
82
+ val dataPoints: List<DataPoint>,
83
+ val amplitudeRange: AmplitudeRange,
84
+ val rmsRange: AmplitudeRange,
85
+ val speechAnalysis: SpeechAnalysis? = null,
86
+ val extractionTimeMs: Float
87
+ ) {
88
+ data class AmplitudeRange(val min: Float, val max: Float) {
89
+ fun toDictionary(): Map<String, Float> {
90
+ return mapOf("min" to min, "max" to max)
91
+ }
92
+
93
+ fun toBundle(): Bundle {
94
+ return bundleOf("min" to min, "max" to max)
95
+ }
96
+ }
97
+
98
+ data class SpeechAnalysis(
99
+ val speakerChanges: List<SpeakerChange>
100
+ ) {
101
+ fun toDictionary(): Map<String, Any> {
102
+ return mapOf(
103
+ "speakerChanges" to speakerChanges.map { it.toDictionary() }
104
+ )
105
+ }
106
+
107
+ fun toBundle(): Bundle {
108
+ return bundleOf(
109
+ "speakerChanges" to speakerChanges.map { it.toBundle() }.toTypedArray()
110
+ )
111
+ }
112
+ }
113
+
114
+ data class SpeakerChange(
115
+ val timestamp: Long,
116
+ val speakerId: Int
117
+ ) {
118
+ fun toDictionary(): Map<String, Any> {
119
+ return mapOf(
120
+ "timestamp" to timestamp,
121
+ "speakerId" to speakerId
122
+ )
123
+ }
124
+
125
+ fun toBundle(): Bundle {
126
+ return bundleOf(
127
+ "timestamp" to timestamp,
128
+ "speakerId" to speakerId
129
+ )
130
+ }
131
+ }
132
+
133
+ fun toDictionary(): Map<String, Any?> {
134
+ return mapOf(
135
+ "segmentDurationMs" to segmentDurationMs,
136
+ "durationMs" to durationMs,
137
+ "bitDepth" to bitDepth,
138
+ "numberOfChannels" to numberOfChannels,
139
+ "sampleRate" to sampleRate,
140
+ "samples" to samples,
141
+ "dataPoints" to dataPoints.map { it.toDictionary() },
142
+ "amplitudeRange" to amplitudeRange.toDictionary(),
143
+ "rmsRange" to rmsRange.toDictionary(),
144
+ "speechAnalysis" to speechAnalysis?.toDictionary(),
145
+ "extractionTimeMs" to extractionTimeMs
146
+ )
147
+ }
148
+
149
+ fun toBundle(): Bundle {
150
+ val dataPointsBundleArray = dataPoints.map { it.toBundle() }.toTypedArray()
151
+
152
+ return bundleOf(
153
+ "segmentDurationMs" to segmentDurationMs,
154
+ "durationMs" to durationMs,
155
+ "bitDepth" to bitDepth,
156
+ "numberOfChannels" to numberOfChannels,
157
+ "sampleRate" to sampleRate,
158
+ "samples" to samples,
159
+ "dataPoints" to dataPointsBundleArray,
160
+ "amplitudeRange" to amplitudeRange.toBundle(),
161
+ "rmsRange" to rmsRange.toBundle(),
162
+ "speechAnalysis" to speechAnalysis?.toBundle(),
163
+ "extractionTimeMs" to extractionTimeMs
164
+ )
165
+ }
166
+ }
@@ -0,0 +1,9 @@
1
+ package net.siteed.audiostream
2
+
3
+ import android.util.Base64
4
+
5
+ class AudioDataEncoder {
6
+ public fun encodeToBase64(rawData: ByteArray): String {
7
+ return Base64.encodeToString(rawData, Base64.NO_WRAP)
8
+ }
9
+ }
@@ -0,0 +1,131 @@
1
+ package net.siteed.audiostream
2
+
3
+ import android.util.Log
4
+ import java.io.File
5
+ import java.io.IOException
6
+ import java.io.OutputStream
7
+ import java.io.RandomAccessFile
8
+ import java.util.UUID
9
+
10
+ class AudioFileHandler(private val filesDir: File) {
11
+ // Method to write WAV file header
12
+ fun writeWavHeader(out: OutputStream, sampleRateInHz: Int, channels: Int, bitDepth: Int) {
13
+ val header = ByteArray(44)
14
+ val byteRate = sampleRateInHz * channels * bitDepth / 8
15
+ val blockAlign = channels * bitDepth / 8
16
+
17
+ // RIFF/WAVE header
18
+ "RIFF".toByteArray().copyInto(header, 0)
19
+ // (file size - 8) to be updated later
20
+ header[4] = 0 // Placeholder
21
+ header[5] = 0 // Placeholder
22
+ header[6] = 0 // Placeholder
23
+ header[7] = 0 // Placeholder
24
+ "WAVE".toByteArray().copyInto(header, 8)
25
+ "fmt ".toByteArray().copyInto(header, 12)
26
+
27
+ // 16 for PCM
28
+ header[16] = 16
29
+ header[17] = 0
30
+ header[18] = 0
31
+ header[19] = 0
32
+
33
+ // PCM format ID
34
+ header[20] = 1 // Audio format 1 for PCM (not compressed)
35
+ header[21] = 0
36
+
37
+ // Number of channels
38
+ header[22] = (channels and 0xff).toByte()
39
+ header[23] = (channels shr 8 and 0xff).toByte()
40
+
41
+ // Sample rate
42
+ header[24] = (sampleRateInHz and 0xff).toByte()
43
+ header[25] = (sampleRateInHz shr 8 and 0xff).toByte()
44
+ header[26] = (sampleRateInHz shr 16 and 0xff).toByte()
45
+ header[27] = (sampleRateInHz shr 24 and 0xff).toByte()
46
+
47
+ // Byte rate
48
+ header[28] = (byteRate and 0xff).toByte()
49
+ header[29] = (byteRate shr 8 and 0xff).toByte()
50
+ header[30] = (byteRate shr 16 and 0xff).toByte()
51
+ header[31] = (byteRate shr 24 and 0xff).toByte()
52
+
53
+ // Block align
54
+ header[32] = (blockAlign and 0xff).toByte()
55
+ header[33] = (blockAlign shr 8 and 0xff).toByte()
56
+
57
+ // Bits per sample
58
+ header[34] = (bitDepth and 0xff).toByte()
59
+ header[35] = (bitDepth shr 8 and 0xff).toByte()
60
+
61
+ // Data chunk
62
+ "data".toByteArray().copyInto(header, 36)
63
+ // Data size to be updated later
64
+ header[40] = 0 // Placeholder
65
+ header[41] = 0 // Placeholder
66
+ header[42] = 0 // Placeholder
67
+ header[43] = 0 // Placeholder
68
+
69
+ out.write(header, 0, 44)
70
+ }
71
+
72
+ fun updateWavHeader(file: File) {
73
+ try {
74
+ RandomAccessFile(file, "rw").use { raf ->
75
+ val fileSize = raf.length()
76
+ val dataSize = fileSize - 44 // Subtract the header size
77
+
78
+ raf.seek(4) // Write correct file size, excluding the first 8 bytes of the RIFF header
79
+ raf.writeInt(Integer.reverseBytes((dataSize + 36).toInt()))
80
+
81
+ raf.seek(40) // Go to the data size position
82
+ raf.writeInt(Integer.reverseBytes(dataSize.toInt())) // Write the size of the data segment
83
+ }
84
+ } catch (e: IOException) {
85
+ println("Could not update WAV header: ${e.message}")
86
+ }
87
+ }
88
+
89
+ fun clearAudioStorage() {
90
+ filesDir.listFiles()?.forEach {
91
+ it.delete()
92
+ }
93
+ }
94
+
95
+ fun createAudioFile(extension: String): File {
96
+ val timestamp = System.currentTimeMillis()
97
+ val uuid = UUID.randomUUID().toString()
98
+ val filename = "recording_${timestamp}_${uuid}.${extension}"
99
+
100
+ return try {
101
+ File(filesDir, filename).apply {
102
+ parentFile?.mkdirs() // Create directories if they don't exist
103
+ createNewFile() // Create the file
104
+ }
105
+ } catch (e: Exception) {
106
+ Log.e(Constants.TAG, "Failed to create audio file", e)
107
+ throw e
108
+ }
109
+ }
110
+
111
+ fun deleteFile(file: File?): Boolean {
112
+ return try {
113
+ if (file == null) {
114
+ Log.w(Constants.TAG, "Attempted to delete null file")
115
+ false
116
+ } else if (!file.exists()) {
117
+ Log.w(Constants.TAG, "File does not exist: ${file.absolutePath}")
118
+ false
119
+ } else {
120
+ val wasDeleted = file.delete()
121
+ if (!wasDeleted) {
122
+ Log.w(Constants.TAG, "Failed to delete file: ${file.absolutePath}")
123
+ }
124
+ wasDeleted
125
+ }
126
+ } catch (e: Exception) {
127
+ Log.e(Constants.TAG, "Error deleting file: ${file?.absolutePath}", e)
128
+ false
129
+ }
130
+ }
131
+ }
@@ -0,0 +1,103 @@
1
+ package net.siteed.audiostream
2
+
3
+ import android.media.AudioFormat
4
+ import java.nio.ByteBuffer
5
+ import java.nio.ByteOrder
6
+
7
+ object AudioFormatUtils {
8
+ /**
9
+ * Converts a byte array of audio data to a float array based on the given encoding.
10
+ * @param audioData The raw audio data in bytes.
11
+ * @param encoding The encoding format (e.g., "pcm_8bit", "pcm_16bit", "pcm_32bit").
12
+ * @return A float array with normalized audio samples in the range [-1.0, 1.0].
13
+ */
14
+ fun convertByteArrayToFloatArray(audioData: ByteArray, encoding: String): FloatArray {
15
+ return when (encoding) {
16
+ "pcm_8bit" -> {
17
+ val floatArray = FloatArray(audioData.size)
18
+ for (i in audioData.indices) {
19
+ // Convert unsigned 8-bit to float in range [-1.0, 1.0]
20
+ floatArray[i] = ((audioData[i].toInt() and 0xFF) - 128) / 128.0f
21
+ }
22
+ floatArray
23
+ }
24
+ "pcm_16bit" -> {
25
+ val floatArray = FloatArray(audioData.size / 2)
26
+ val buffer = ByteBuffer.wrap(audioData).order(ByteOrder.LITTLE_ENDIAN)
27
+ for (i in floatArray.indices) {
28
+ floatArray[i] = buffer.short / 32768.0f // Normalize to [-1.0, 1.0]
29
+ }
30
+ floatArray
31
+ }
32
+ "pcm_32bit" -> {
33
+ val floatArray = FloatArray(audioData.size / 4)
34
+ val buffer = ByteBuffer.wrap(audioData).order(ByteOrder.LITTLE_ENDIAN)
35
+ for (i in floatArray.indices) {
36
+ floatArray[i] = buffer.int / 2_147_483_648.0f // Normalize to [-1.0, 1.0]
37
+ }
38
+ floatArray
39
+ }
40
+ else -> {
41
+ // Default to 16-bit PCM if encoding is not recognized
42
+ val floatArray = FloatArray(audioData.size / 2)
43
+ val buffer = ByteBuffer.wrap(audioData).order(ByteOrder.LITTLE_ENDIAN)
44
+ for (i in floatArray.indices) {
45
+ floatArray[i] = buffer.short / 32768.0f
46
+ }
47
+ floatArray
48
+ }
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Calculates the bit depth (number of bits per sample) based on the encoding string.
54
+ * @param encoding The encoding format (e.g., "pcm_8bit", "pcm_16bit", "pcm_32bit").
55
+ * @return The bit depth as an integer.
56
+ */
57
+ fun getBitDepth(encoding: String): Int {
58
+ return when (encoding) {
59
+ "pcm_8bit" -> 8
60
+ "pcm_16bit" -> 16
61
+ "pcm_32bit" -> 32
62
+ else -> 16 // Default to 16-bit if not recognized
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Determines the AudioFormat encoding constant based on the encoding string.
68
+ * @param encoding The encoding format (e.g., "pcm_8bit", "pcm_16bit", "pcm_32bit").
69
+ * @return The corresponding AudioFormat constant.
70
+ */
71
+ fun getAudioFormat(encoding: String): Int {
72
+ return when (encoding) {
73
+ "pcm_8bit" -> AudioFormat.ENCODING_PCM_8BIT
74
+ "pcm_16bit" -> AudioFormat.ENCODING_PCM_16BIT
75
+ "pcm_32bit" -> AudioFormat.ENCODING_PCM_FLOAT
76
+ else -> AudioFormat.ENCODING_PCM_16BIT // Default to 16-bit PCM
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Converts audio data between different bit depths
82
+ * @param audioData The raw audio data
83
+ * @param sourceBitDepth The original bit depth
84
+ * @param targetBitDepth The desired bit depth
85
+ * @return The converted audio data
86
+ */
87
+ fun convertBitDepth(audioData: ByteArray, sourceBitDepth: Int, targetBitDepth: Int): ByteArray {
88
+ // First convert to float array for normalization
89
+ val floatArray = convertByteArrayToFloatArray(audioData, "pcm_${sourceBitDepth}bit")
90
+
91
+ // Convert back to bytes with new bit depth
92
+ return when (targetBitDepth) {
93
+ 8 -> floatArray.map { ((it + 1.0f) * 127.5f).toInt().toByte() }.toByteArray()
94
+ 16 -> ByteBuffer.allocate(floatArray.size * 2).order(ByteOrder.LITTLE_ENDIAN).apply {
95
+ floatArray.forEach { asShortBuffer().put((it * 32767f).toInt().toShort()) }
96
+ }.array()
97
+ 32 -> ByteBuffer.allocate(floatArray.size * 4).order(ByteOrder.LITTLE_ENDIAN).apply {
98
+ floatArray.forEach { putFloat(it) }
99
+ }.array()
100
+ else -> throw IllegalArgumentException("Unsupported target bit depth: $targetBitDepth")
101
+ }
102
+ }
103
+ }