@siteed/expo-audio-stream 1.3.0 → 1.3.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 (54) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/android/build/.transforms/0588d516016bbd2e5b51abc80cc3d78d/transformed/classes/classes_dex/classes.dex +0 -0
  3. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
  4. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
  5. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
  6. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
  7. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
  8. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
  9. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
  10. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
  11. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
  12. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
  13. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
  14. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
  15. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
  16. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
  17. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
  18. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
  19. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
  20. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
  21. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
  22. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at +0 -0
  23. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab +1 -1
  24. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
  25. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
  26. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
  27. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
  28. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
  29. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
  30. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
  31. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
  32. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
  33. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
  34. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
  35. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
  36. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values +0 -0
  37. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
  38. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.s +1 -0
  39. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
  40. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  41. package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
  42. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  43. package/android/build.gradle +13 -5
  44. package/package.json +2 -3
  45. package/plugin/src/index.ts +5 -4
  46. package/src/AudioAnalysis/extractAudioAnalysis.ts +13 -13
  47. package/src/AudioAnalysis/extractWaveform.ts +0 -3
  48. package/src/ExpoAudioStream.types.ts +7 -0
  49. package/src/ExpoAudioStream.web.ts +8 -6
  50. package/src/WebRecorder.web.ts +15 -14
  51. package/src/events.ts +0 -4
  52. package/src/useAudioRecorder.tsx +32 -36
  53. package/src/utils/convertPCMToFloat32.ts +5 -5
  54. package/src/logger.ts +0 -22
package/CHANGELOG.md CHANGED
@@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  ## [Unreleased]
9
9
 
10
10
 
11
+ ## [1.3.1] - 2024-12-05
12
+ - feat(web): implement throttling and optimize event processing (#49) ([da28765](https://github.com/deeeed/expo-audio-stream/commit/da2876524c2c9d6e0a980fde40a0197b929d8a7f))
13
+
11
14
  ## [1.3.0] - 2024-11-28
12
15
  ### Added
13
16
  - refactor(permissions): standardize permission status response structure across platforms (#44) ([7c9c800](https://github.com/deeeed/expo-audio-stream/commit/7c9c800d83b7cea3516643371484d5e1f3b99e4c))
@@ -16,11 +19,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
16
19
  - feat: latest expo fixes ([9cc5ac3](https://github.com/deeeed/expo-audio-stream/commit/9cc5ac39751999e5b33e11c16355557143d68d10))
17
20
  - feat: latest expo sdk ([258ef6c](https://github.com/deeeed/expo-audio-stream/commit/258ef6cf68e70c7855f696a01204f79b0793fdc0))
18
21
 
22
+
19
23
  ## [1.2.5] - 2024-11-12
20
24
  ### Added
21
25
  - docs(license): add MIT license to all packages (6 files changed)
22
26
  - fix(expo-audio-stream): return actual recording settings from startRecording on iOS #37
23
27
 
28
+
24
29
  ## [1.2.4] - 2024-11-05
25
30
  ### Changed
26
31
  - Android minimum audio interval set to 10ms.
@@ -29,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
29
34
  ### Fixed
30
35
  - Remove frequently firing log statements on web.
31
36
 
37
+
32
38
  ## [1.2.0] - 2024-10-24
33
39
  ### Added
34
40
  - Feature: Keep device awake during recording with `keepAwake` option
@@ -37,11 +43,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
37
43
  - Android: Configurable notification actions, colors, and priorities
38
44
  - iOS: Integration with media player
39
45
 
46
+
40
47
  ## [1.1.17] - 2024-10-21
41
48
  ### Added
42
49
  - Support bluetooth headset on ios
43
50
  - Fixes: android not reading custom interval audio update
44
51
 
52
+
45
53
  ## [1.0.0] - 2024-04-01
46
54
 
47
55
  ### Added
@@ -53,5 +61,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
53
61
  - Feature: Audio features extraction during recording.
54
62
  - Feature: Consistent WAV PCM recording format across all platforms.
55
63
 
64
+ [unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.3.1...HEAD
65
+ [1.3.1]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.3.0...@siteed/expo-audio-stream@1.3.1
56
66
  [Unreleased]: https://github.com/deeeed/expo-audio-stream/compare/v1.0.0...HEAD
57
67
  [1.0.0]: https://github.com/deeeed/expo-audio-stream/releases/tag/v1.0.0
@@ -51,17 +51,19 @@ afterEvaluate {
51
51
  }
52
52
 
53
53
  android {
54
- compileSdkVersion safeExtGet("compileSdkVersion", 33)
54
+ compileSdkVersion safeExtGet("compileSdkVersion", 34)
55
55
 
56
56
  def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
57
57
  if (agpVersion.tokenize('.')[0].toInteger() < 8) {
58
58
  compileOptions {
59
- sourceCompatibility JavaVersion.VERSION_11
60
- targetCompatibility JavaVersion.VERSION_11
59
+ sourceCompatibility JavaVersion.VERSION_17
60
+ targetCompatibility JavaVersion.VERSION_17
61
61
  }
62
62
 
63
- kotlinOptions {
64
- jvmTarget = JavaVersion.VERSION_11.majorVersion
63
+ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
64
+ kotlinOptions {
65
+ jvmTarget = '17'
66
+ }
65
67
  }
66
68
  }
67
69
 
@@ -95,3 +97,9 @@ dependencies {
95
97
  testImplementation 'org.jetbrains.kotlin:kotlin-test:1.8.10'
96
98
  testImplementation 'org.jetbrains.kotlin:kotlin-test-junit:1.8.10'
97
99
  }
100
+
101
+ kotlin {
102
+ jvmToolchain {
103
+ languageVersion.set(JavaLanguageVersion.of(17))
104
+ }
105
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siteed/expo-audio-stream",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "stream audio crossplatform",
5
5
  "license": "MIT",
6
6
  "main": "build/index.js",
@@ -39,7 +39,7 @@
39
39
  "devDependencies": {
40
40
  "@expo/config-plugins": "~8.0.0",
41
41
  "@siteed/publisher": "^0.4.15",
42
- "@siteed/react-native-logger": "^0.10.0",
42
+ "@siteed/react-native-logger": "^1.0.1",
43
43
  "@size-limit/preset-big-lib": "^11.1.4",
44
44
  "@types/jest": "^29.5.12",
45
45
  "@types/node": "^20.12.7",
@@ -67,7 +67,6 @@
67
67
  "typescript": "~5.3.3"
68
68
  },
69
69
  "peerDependencies": {
70
- "@siteed/react-native-logger": "*",
71
70
  "expo": "*",
72
71
  "react": "*",
73
72
  "react-native": "*"
@@ -1,16 +1,17 @@
1
1
  import {
2
- AndroidConfig,
3
2
  ConfigPlugin,
4
3
  withAndroidManifest,
5
4
  withInfoPlist,
5
+ AndroidConfig,
6
6
  } from '@expo/config-plugins'
7
+ import { ExpoConfig } from '@expo/config-types'
7
8
 
8
9
  const MICROPHONE_USAGE = 'Allow $(PRODUCT_NAME) to access your microphone'
9
10
  const NOTIFICATION_USAGE = 'Show recording notifications and controls'
10
11
 
11
- const withRecordingPermission: ConfigPlugin = (config) => {
12
+ const withRecordingPermission: ConfigPlugin = (config: ExpoConfig) => {
12
13
  // iOS Configuration
13
- config = withInfoPlist(config as any, (config) => {
14
+ config = withInfoPlist(config, (config) => {
14
15
  // Existing microphone permission
15
16
  config.modResults['NSMicrophoneUsageDescription'] =
16
17
  config.modResults['NSMicrophoneUsageDescription'] ||
@@ -38,7 +39,7 @@ const withRecordingPermission: ConfigPlugin = (config) => {
38
39
  })
39
40
 
40
41
  // Android Configuration
41
- config = withAndroidManifest(config as any, (config) => {
42
+ config = withAndroidManifest(config, (config) => {
42
43
  const androidManifest = config.modResults
43
44
  if (!androidManifest.manifest) {
44
45
  console.warn(
@@ -1,18 +1,16 @@
1
1
  // packages/expo-audio-stream/src/AudioAnalysis/extractAudioAnalysis.ts
2
+ import { ConsoleLike } from '../ExpoAudioStream.types'
3
+ import ExpoAudioStreamModule from '../ExpoAudioStreamModule'
4
+ import { isWeb } from '../constants'
2
5
  import {
3
6
  AmplitudeAlgorithm,
4
7
  AudioAnalysis,
5
8
  AudioFeaturesOptions,
6
9
  } from './AudioAnalysis.types'
7
- import ExpoAudioStreamModule from '../ExpoAudioStreamModule'
8
- import { isWeb } from '../constants'
9
- import { getLogger } from '../logger'
10
10
  import { convertPCMToFloat32 } from '../utils/convertPCMToFloat32'
11
11
  import { getWavFileInfo, WavFileInfo } from '../utils/getWavFileInfo'
12
12
  import { InlineFeaturesExtractor } from '../workers/InlineFeaturesExtractor.web'
13
13
 
14
- const logger = getLogger('extractAudioAnalysis')
15
-
16
14
  export interface ExtractAudioAnalysisProps {
17
15
  fileUri?: string // should provide either fileUri or arrayBuffer
18
16
  wavMetadata?: WavFileInfo
@@ -28,6 +26,7 @@ export interface ExtractAudioAnalysisProps {
28
26
  pointsPerSecond?: number // Optional number of points per second. Use to reduce the number of points and compute the number of datapoints to return.
29
27
  features?: AudioFeaturesOptions
30
28
  featuresExtratorUrl?: string
29
+ logger?: ConsoleLike
31
30
  }
32
31
 
33
32
  export const extractAudioAnalysis = async ({
@@ -42,6 +41,7 @@ export const extractAudioAnalysis = async ({
42
41
  algorithm = 'rms',
43
42
  features,
44
43
  featuresExtratorUrl,
44
+ logger,
45
45
  }: ExtractAudioAnalysisProps): Promise<AudioAnalysis> => {
46
46
  if (isWeb) {
47
47
  if (!arrayBuffer && !fileUri) {
@@ -49,7 +49,7 @@ export const extractAudioAnalysis = async ({
49
49
  }
50
50
 
51
51
  if (!arrayBuffer) {
52
- logger.log(`fetching fileUri`, fileUri)
52
+ logger?.log(`fetching fileUri`, fileUri)
53
53
  const response = await fetch(fileUri!)
54
54
 
55
55
  if (!response.ok) {
@@ -59,25 +59,25 @@ export const extractAudioAnalysis = async ({
59
59
  }
60
60
 
61
61
  arrayBuffer = await response.arrayBuffer()
62
- logger.log(`fetched fileUri`, arrayBuffer.byteLength, arrayBuffer)
62
+ logger?.log(`fetched fileUri`, arrayBuffer.byteLength, arrayBuffer)
63
63
  }
64
64
 
65
65
  // Create a new copy of the ArrayBuffer to avoid detachment issues
66
66
  const bufferCopy = arrayBuffer.slice(0)
67
- logger.log(
67
+ logger?.log(
68
68
  `extractAudioAnalysis skipWavHeader=${skipWavHeader} bitDepth=${bitDepth} len=${bufferCopy.byteLength}`,
69
69
  bufferCopy.slice(0, 100)
70
70
  )
71
71
 
72
72
  let actualBitDepth = bitDepth
73
73
  if (!actualBitDepth) {
74
- logger.log(
74
+ logger?.log(
75
75
  `extractAudioAnalysis bitDepth not provided -- getting wav file info`
76
76
  )
77
77
  const fileInfo = await getWavFileInfo(bufferCopy)
78
78
  actualBitDepth = fileInfo.bitDepth
79
79
  }
80
- logger.log(`extractAudioAnalysis actualBitDepth=${actualBitDepth}`)
80
+ logger?.log(`extractAudioAnalysis actualBitDepth=${actualBitDepth}`)
81
81
 
82
82
  const {
83
83
  pcmValues: channelData,
@@ -88,7 +88,7 @@ export const extractAudioAnalysis = async ({
88
88
  bitDepth: actualBitDepth,
89
89
  skipWavHeader,
90
90
  })
91
- logger.log(
91
+ logger?.log(
92
92
  `extractAudioAnalysis skipWaveHeader=${skipWavHeader} convertPCMToFloat32 length=${channelData.length} range: [ ${min} :: ${max} ]`
93
93
  )
94
94
 
@@ -129,7 +129,7 @@ export const extractAudioAnalysis = async ({
129
129
  if (!fileUri) {
130
130
  throw new Error('fileUri is required')
131
131
  }
132
- logger.log(`extractAudioAnalysis`, {
132
+ logger?.log(`extractAudioAnalysis`, {
133
133
  fileUri,
134
134
  pointsPerSecond,
135
135
  algorithm,
@@ -141,7 +141,7 @@ export const extractAudioAnalysis = async ({
141
141
  algorithm,
142
142
  features,
143
143
  })
144
- logger.log(`extractAudioAnalysis`, res)
144
+ logger?.log(`extractAudioAnalysis`, res)
145
145
  return res
146
146
  }
147
147
  }
@@ -1,7 +1,5 @@
1
1
  import ExpoAudioStreamModule from '../ExpoAudioStreamModule'
2
- import { getLogger } from '../logger'
3
2
 
4
- const logger = getLogger('extractWaveform')
5
3
  export interface ExtractWaveformProps {
6
4
  fileUri: string
7
5
  numberOfSamples: number
@@ -20,6 +18,5 @@ export const extractWaveform = async ({
20
18
  offset,
21
19
  length,
22
20
  })
23
- logger.log(`extractWaveform`, res)
24
21
  return res
25
22
  }
@@ -27,6 +27,13 @@ export type EncodingType = 'pcm_32bit' | 'pcm_16bit' | 'pcm_8bit'
27
27
  export type SampleRate = 16000 | 44100 | 48000
28
28
  export type BitDepth = 8 | 16 | 32
29
29
 
30
+ export type ConsoleLike = {
31
+ log: (message: string, ...args: unknown[]) => void
32
+ debug: (message: string, ...args: unknown[]) => void
33
+ warn: (message: string, ...args: unknown[]) => void
34
+ error: (message: string, ...args: unknown[]) => void
35
+ }
36
+
30
37
  export interface Chunk {
31
38
  text: string
32
39
  timestamp: [number, number | null]
@@ -6,12 +6,12 @@ import {
6
6
  AudioRecording,
7
7
  AudioStreamStatus,
8
8
  BitDepth,
9
+ ConsoleLike,
9
10
  RecordingConfig,
10
11
  StartRecordingResult,
11
12
  } from './ExpoAudioStream.types'
12
13
  import { WebRecorder } from './WebRecorder.web'
13
14
  import { AudioEventPayload } from './events'
14
- import { getLogger } from './logger'
15
15
  import { encodingToBitDepth } from './utils/encodingToBitDepth'
16
16
  import { WavHeaderOptions, writeWavHeader } from './utils/writeWavHeader'
17
17
 
@@ -23,12 +23,11 @@ export type EmitAudioEventFunction = (_: EmitAudioEventProps) => void
23
23
  export type EmitAudioAnalysisFunction = (_: AudioAnalysis) => void
24
24
 
25
25
  export interface ExpoAudioStreamWebProps {
26
+ logger?: ConsoleLike
26
27
  audioWorkletUrl: string
27
28
  featuresExtratorUrl: string
28
29
  }
29
30
 
30
- const logger = getLogger('ExpoAudioStreamWeb')
31
-
32
31
  export class ExpoAudioStreamWeb extends LegacyEventEmitter {
33
32
  customRecorder: WebRecorder | null
34
33
  audioChunks: ArrayBuffer[]
@@ -47,10 +46,12 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
47
46
  bitDepth: BitDepth // Bit depth of the audio
48
47
  audioWorkletUrl: string
49
48
  featuresExtratorUrl: string
49
+ logger?: ConsoleLike
50
50
 
51
51
  constructor({
52
52
  audioWorkletUrl,
53
53
  featuresExtratorUrl,
54
+ logger,
54
55
  }: ExpoAudioStreamWebProps) {
55
56
  const mockNativeModule = {
56
57
  addListener: () => {
@@ -62,6 +63,7 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
62
63
  }
63
64
  super(mockNativeModule) // Pass the mock native module to the parent class
64
65
 
66
+ this.logger = logger
65
67
  this.customRecorder = null
66
68
  this.audioChunks = []
67
69
  this.isRecording = false
@@ -123,7 +125,7 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
123
125
  this.lastEmittedSize = this.currentSize
124
126
  },
125
127
  emitAudioAnalysisCallback: (audioAnalysisData: AudioAnalysis) => {
126
- logger.log(`Emitted AudioAnalysis:`, audioAnalysisData)
128
+ this.logger?.log(`Emitted AudioAnalysis:`, audioAnalysisData)
127
129
  this.emit('AudioAnalysis', audioAnalysisData)
128
130
  },
129
131
  })
@@ -181,7 +183,7 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
181
183
  const fullPcmBufferArray = await this.customRecorder.stop()
182
184
 
183
185
  // concat all audio chunks
184
- logger.debug(`Stopped recording`, fullPcmBufferArray)
186
+ this.logger?.debug(`Stopped recording`, fullPcmBufferArray)
185
187
  this.isRecording = false
186
188
  this.isPaused = false
187
189
  this.currentDurationMs = Date.now() - this.recordingStartTime
@@ -193,7 +195,7 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
193
195
  numChannels: this.recordingConfig?.channels ?? 1,
194
196
  bitDepth: this.bitDepth,
195
197
  }
196
- logger.debug(`Writing wav header`, wavConfig)
198
+ this.logger?.debug(`Writing wav header`, wavConfig)
197
199
  const wavBuffer = writeWavHeader(wavConfig).slice(0)
198
200
 
199
201
  // Create blob fileUri from audio chunks
@@ -1,13 +1,11 @@
1
1
  // src/WebRecorder.ts
2
- import { buffer } from 'stream/consumers'
3
2
 
4
3
  import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types'
5
- import { RecordingConfig } from './ExpoAudioStream.types'
4
+ import { ConsoleLike, RecordingConfig } from './ExpoAudioStream.types'
6
5
  import {
7
6
  EmitAudioAnalysisFunction,
8
7
  EmitAudioEventFunction,
9
8
  } from './ExpoAudioStream.web'
10
- import { getLogger } from './logger'
11
9
  import { convertPCMToFloat32 } from './utils/convertPCMToFloat32'
12
10
  import { encodingToBitDepth } from './utils/encodingToBitDepth'
13
11
  import { writeWavHeader } from './utils/writeWavHeader'
@@ -36,7 +34,6 @@ const DEFAULT_WEB_NUMBER_OF_CHANNELS = 1
36
34
  const DEFAULT_ALGORITHM = 'rms'
37
35
 
38
36
  const TAG = 'WebRecorder'
39
- const logger = getLogger(TAG)
40
37
 
41
38
  export class WebRecorder {
42
39
  private audioContext: AudioContext
@@ -55,6 +52,7 @@ export class WebRecorder {
55
52
  private audioBufferSize: number // Keep track of the buffer size
56
53
  private audioAnalysisData: AudioAnalysis // Keep updating the full audio analysis data with latest events
57
54
  private packetCount: number = 0
55
+ private logger?: ConsoleLike
58
56
 
59
57
  constructor({
60
58
  audioContext,
@@ -63,6 +61,7 @@ export class WebRecorder {
63
61
  audioWorkletUrl,
64
62
  emitAudioEventCallback,
65
63
  emitAudioAnalysisCallback,
64
+ logger,
66
65
  }: {
67
66
  audioContext: AudioContext
68
67
  source: MediaStreamAudioSourceNode
@@ -70,6 +69,7 @@ export class WebRecorder {
70
69
  audioWorkletUrl: string
71
70
  emitAudioEventCallback: EmitAudioEventFunction
72
71
  emitAudioAnalysisCallback: EmitAudioAnalysisFunction
72
+ logger?: ConsoleLike
73
73
  }) {
74
74
  this.audioContext = audioContext
75
75
  this.source = source
@@ -78,11 +78,12 @@ export class WebRecorder {
78
78
  this.emitAudioAnalysisCallback = emitAudioAnalysisCallback
79
79
  this.config = recordingConfig
80
80
  this.position = 0
81
+ this.logger = logger
81
82
 
82
83
  const audioContextFormat = this.checkAudioContextFormat({
83
84
  sampleRate: this.audioContext.sampleRate,
84
85
  })
85
- logger.debug('Initialized WebRecorder with config:', {
86
+ this.logger?.debug('Initialized WebRecorder with config:', {
86
87
  sampleRate: audioContextFormat.sampleRate,
87
88
  bitDepth: audioContextFormat.bitDepth,
88
89
  numberOfChannels: audioContextFormat.numberOfChannels,
@@ -150,12 +151,12 @@ export class WebRecorder {
150
151
  const pcmBufferFloat = event.data.recordedData
151
152
 
152
153
  if (!pcmBufferFloat) {
153
- logger.warn('Received empty audio buffer', event)
154
+ this.logger?.warn('Received empty audio buffer', event)
154
155
  return
155
156
  }
156
157
 
157
158
  // Handle the audio blob (e.g., send it to the server or process it further)
158
- logger.debug(
159
+ this.logger?.debug(
159
160
  `Received audio blob from processor len:${pcmBufferFloat?.length}`,
160
161
  event
161
162
  )
@@ -220,7 +221,7 @@ export class WebRecorder {
220
221
  )
221
222
  }
222
223
 
223
- logger.debug(
224
+ this.logger?.debug(
224
225
  `WebRecorder initialized -- recordSampleRate=${this.audioContext.sampleRate}`,
225
226
  this.config
226
227
  )
@@ -281,7 +282,7 @@ export class WebRecorder {
281
282
  this.featureExtractorWorker.onerror = (error) => {
282
283
  console.error(`[${TAG}] Default Inline worker failed`, error)
283
284
  }
284
- logger.log('Inline worker initialized successfully')
285
+ this.logger?.log('Inline worker initialized successfully')
285
286
  } catch (error) {
286
287
  console.error(
287
288
  `[${TAG}] Failed to initialize Inline Feature Extractor worker`,
@@ -317,8 +318,8 @@ export class WebRecorder {
317
318
  }
318
319
  }
319
320
  // Handle the extracted features (e.g., emit an event or log them)
320
- logger.debug('features event segmentResult', segmentResult)
321
- logger.debug(
321
+ this.logger?.debug('features event segmentResult', segmentResult)
322
+ this.logger?.debug(
322
323
  `features event audioAnalysisData duration=${this.audioAnalysisData.durationMs}`,
323
324
  this.audioAnalysisData
324
325
  )
@@ -373,10 +374,10 @@ export class WebRecorder {
373
374
  (this.audioContext.sampleRate *
374
375
  (this.exportBitDepth /
375
376
  this.numberOfChannels))
376
- logger.debug(
377
+ this.logger?.debug(
377
378
  `Received recorded data -- Duration: ${duration} vs ${rawPCMDataFull.byteLength / this.audioContext.sampleRate} seconds`
378
379
  )
379
- logger.debug(
380
+ this.logger?.debug(
380
381
  `recordedData.length=${rawPCMDataFull.byteLength} vs transmittedData.length=${this.audioBufferSize}`
381
382
  )
382
383
 
@@ -448,7 +449,7 @@ export class WebRecorder {
448
449
  bufferSource.buffer = audioBuffer
449
450
  bufferSource.connect(this.audioContext.destination)
450
451
  bufferSource.start()
451
- logger.debug('Playing recorded data', recordedData)
452
+ this.logger?.debug('Playing recorded data', recordedData)
452
453
  } catch (error) {
453
454
  console.error(`[${TAG}] Failed to play recorded data:`, error)
454
455
  }
package/src/events.ts CHANGED
@@ -4,10 +4,8 @@ import { LegacyEventEmitter, type EventSubscription } from 'expo-modules-core'
4
4
 
5
5
  import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types'
6
6
  import ExpoAudioStreamModule from './ExpoAudioStreamModule'
7
- import { getLogger } from './logger'
8
7
 
9
8
  const emitter = new LegacyEventEmitter(ExpoAudioStreamModule)
10
- const logger = getLogger('events')
11
9
 
12
10
  export interface AudioEventPayload {
13
11
  encoded?: string
@@ -24,7 +22,6 @@ export interface AudioEventPayload {
24
22
  export function addAudioEventListener(
25
23
  listener: (event: AudioEventPayload) => Promise<void>
26
24
  ): EventSubscription {
27
- logger.log('Adding listener for AudioData event')
28
25
  return emitter.addListener<AudioEventPayload>('AudioData', listener)
29
26
  }
30
27
 
@@ -34,6 +31,5 @@ export interface AudioAnalysisEvent extends AudioAnalysis {}
34
31
  export function addAudioAnalysisListener(
35
32
  listener: (event: AudioAnalysisEvent) => Promise<void>
36
33
  ): EventSubscription {
37
- logger.log('Adding listener for AudioAnalysis event')
38
34
  return emitter.addListener<AudioAnalysisEvent>('AudioAnalysis', listener)
39
35
  }
@@ -1,5 +1,5 @@
1
1
  // src/useAudioRecorder.ts
2
- import { Platform, EventSubscription } from 'expo-modules-core'
2
+ import { EventSubscription, Platform } from 'expo-modules-core'
3
3
  import { useCallback, useEffect, useReducer, useRef } from 'react'
4
4
 
5
5
  import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types'
@@ -7,6 +7,7 @@ import {
7
7
  AudioDataEvent,
8
8
  AudioRecording,
9
9
  AudioStreamStatus,
10
+ ConsoleLike,
10
11
  RecordingConfig,
11
12
  StartRecordingResult,
12
13
  } from './ExpoAudioStream.types'
@@ -16,12 +17,9 @@ import {
16
17
  addAudioEventListener,
17
18
  AudioEventPayload,
18
19
  } from './events'
19
- import { getLogger } from './logger'
20
20
 
21
- const TAG = 'useAudioRecorder'
22
- const logger = getLogger(TAG)
23
21
  export interface UseAudioRecorderProps {
24
- debug?: boolean
22
+ logger?: ConsoleLike
25
23
  audioWorkletUrl?: string
26
24
  featuresExtratorUrl?: string
27
25
  }
@@ -122,7 +120,7 @@ interface HandleAudioAnalysisProps {
122
120
  }
123
121
 
124
122
  export function useAudioRecorder({
125
- debug = false,
123
+ logger,
126
124
  audioWorkletUrl,
127
125
  featuresExtratorUrl,
128
126
  }: UseAudioRecorderProps = {}): UseAudioRecorderState {
@@ -141,12 +139,15 @@ export function useAudioRecorder({
141
139
  const fullAnalysisRef = useRef<AudioAnalysis>({
142
140
  ...defaultAnalysis,
143
141
  })
144
- const globalIdRef = useRef(0);
145
142
 
146
143
  // Instantiate the module for web with URLs
147
144
  const ExpoAudioStream =
148
145
  Platform.OS === 'web'
149
- ? ExpoAudioStreamModule({ audioWorkletUrl, featuresExtratorUrl })
146
+ ? ExpoAudioStreamModule({
147
+ audioWorkletUrl,
148
+ featuresExtratorUrl,
149
+ logger,
150
+ })
150
151
  : ExpoAudioStreamModule
151
152
 
152
153
  const onAudioStreamRef = useRef<
@@ -164,7 +165,7 @@ export function useAudioRecorder({
164
165
 
165
166
  const maxDuration = visualizationDuration
166
167
 
167
- logger.debug(
168
+ logger?.debug(
168
169
  `[handleAudioAnalysis] Received audio analysis: maxDuration=${maxDuration} analysis.dataPoints=${analysis.dataPoints.length} analysisData.dataPoints=${savedAnalysisData.dataPoints.length}`,
169
170
  analysis
170
171
  )
@@ -186,7 +187,7 @@ export function useAudioRecorder({
186
187
  const maxDataPoints =
187
188
  (pointsPerSecond * visualizationDuration) / 1000
188
189
 
189
- logger.debug(
190
+ logger?.debug(
190
191
  `[handleAudioAnalysis] Combined data points before trimming: pointsPerSecond=${pointsPerSecond} visualizationDuration=${visualizationDuration} combinedDataPointsLength=${combinedDataPoints.length} vs maxDataPoints=${maxDataPoints}`
191
192
  )
192
193
 
@@ -230,7 +231,7 @@ export function useAudioRecorder({
230
231
  max: newMax,
231
232
  }
232
233
 
233
- logger.debug(
234
+ logger?.debug(
234
235
  `[handleAudioAnalysis] Updated analysis data: durationMs=${savedAnalysisData.durationMs}`,
235
236
  savedAnalysisData
236
237
  )
@@ -261,7 +262,7 @@ export function useAudioRecorder({
261
262
  mimeType,
262
263
  buffer,
263
264
  } = eventData
264
- logger.debug(`[handleAudioEvent] Received audio event:`, {
265
+ logger?.debug(`[handleAudioEvent] Received audio event:`, {
265
266
  fileUri,
266
267
  deltaSize,
267
268
  totalSize,
@@ -280,7 +281,7 @@ export function useAudioRecorder({
280
281
  if (Platform.OS !== 'web') {
281
282
  // Read the audio file as a base64 string for comparison
282
283
  if (!encoded) {
283
- console.error(`${TAG} Encoded audio data is missing`)
284
+ logger?.error(`Encoded audio data is missing`)
284
285
  throw new Error('Encoded audio data is missing')
285
286
  }
286
287
  onAudioStreamRef.current?.({
@@ -300,13 +301,13 @@ export function useAudioRecorder({
300
301
  totalSize,
301
302
  }
302
303
  onAudioStreamRef.current?.(webEvent)
303
- logger.debug(
304
+ logger?.debug(
304
305
  `[handleAudioEvent] Audio data sent to onAudioStream`,
305
306
  webEvent
306
307
  )
307
308
  }
308
309
  } catch (error) {
309
- console.error(`${TAG} Error processing audio event:`, error)
310
+ logger?.error(`Error processing audio event:`, error)
310
311
  }
311
312
  },
312
313
  []
@@ -315,11 +316,9 @@ export function useAudioRecorder({
315
316
  const checkStatus = useCallback(async () => {
316
317
  try {
317
318
  const status: AudioStreamStatus = ExpoAudioStream.status()
318
- if (debug) {
319
- logger.debug(
320
- `Status: paused: ${status.isPaused} durationMs: ${status.durationMs} size: ${status.size}`
321
- )
322
- }
319
+ logger?.debug(
320
+ `Status: paused: ${status.isPaused} durationMs: ${status.durationMs} size: ${status.size}`
321
+ )
323
322
 
324
323
  // Check and update recording state
325
324
  if (
@@ -349,13 +348,13 @@ export function useAudioRecorder({
349
348
  })
350
349
  }
351
350
  } catch (error) {
352
- console.error(`${TAG} Error getting status:`, error)
351
+ logger?.error(`Error getting status:`, error)
353
352
  }
354
353
  }, [state.isRecording])
355
354
 
356
355
  const startRecording = useCallback(
357
356
  async (recordingOptions: RecordingConfig) => {
358
- logger.debug(`start recoding`, recordingOptions)
357
+ logger?.debug(`start recoding`, recordingOptions)
359
358
 
360
359
  analysisRef.current = { ...defaultAnalysis } // Reset analysis data
361
360
  fullAnalysisRef.current = { ...defaultAnalysis }
@@ -366,10 +365,7 @@ export function useAudioRecorder({
366
365
  if (typeof onAudioStream === 'function') {
367
366
  onAudioStreamRef.current = onAudioStream
368
367
  } else {
369
- console.warn(
370
- `${TAG} onAudioStream is not a function`,
371
- onAudioStream
372
- )
368
+ logger?.warn(`onAudioStream is not a function`, onAudioStream)
373
369
  onAudioStreamRef.current = null
374
370
  }
375
371
  const startResult: StartRecordingResult =
@@ -377,7 +373,7 @@ export function useAudioRecorder({
377
373
  dispatch({ type: 'START' })
378
374
 
379
375
  if (enableProcessing) {
380
- logger.debug(`Enabling audio analysis listener`)
376
+ logger?.debug(`Enabling audio analysis listener`)
381
377
  const listener = addAudioAnalysisListener(
382
378
  async (analysisData) => {
383
379
  try {
@@ -386,8 +382,8 @@ export function useAudioRecorder({
386
382
  visualizationDuration: maxRecentDataDuration,
387
383
  })
388
384
  } catch (error) {
389
- console.warn(
390
- `${TAG} Error processing audio analysis:`,
385
+ logger?.warn(
386
+ `Error processing audio analysis:`,
391
387
  error
392
388
  )
393
389
  }
@@ -403,7 +399,7 @@ export function useAudioRecorder({
403
399
  )
404
400
 
405
401
  const stopRecording = useCallback(async () => {
406
- logger.debug(`stoping recording`)
402
+ logger?.debug(`stoping recording`)
407
403
 
408
404
  const stopResult: AudioRecording = await ExpoAudioStream.stopRecording()
409
405
  stopResult.analysisData = fullAnalysisRef.current
@@ -413,20 +409,20 @@ export function useAudioRecorder({
413
409
  analysisListenerRef.current = null
414
410
  }
415
411
  onAudioStreamRef.current = null
416
- logger.debug(`recording stopped`, stopResult)
412
+ logger?.debug(`recording stopped`, stopResult)
417
413
  dispatch({ type: 'STOP' })
418
414
  return stopResult
419
415
  }, [dispatch])
420
416
 
421
417
  const pauseRecording = useCallback(async () => {
422
- logger.debug(`pause recording`)
418
+ logger?.debug(`pause recording`)
423
419
  const pauseResult = await ExpoAudioStream.pauseRecording()
424
420
  dispatch({ type: 'PAUSE' })
425
421
  return pauseResult
426
422
  }, [dispatch])
427
423
 
428
424
  const resumeRecording = useCallback(async () => {
429
- logger.debug(`resume recording`)
425
+ logger?.debug(`resume recording`)
430
426
  const resumeResult = await ExpoAudioStream.resumeRecording()
431
427
  dispatch({ type: 'RESUME' })
432
428
  return resumeResult
@@ -445,10 +441,10 @@ export function useAudioRecorder({
445
441
  }, [checkStatus, state.isRecording, state.isPaused])
446
442
 
447
443
  useEffect(() => {
448
- logger.debug(`Registering audio event listener`)
444
+ logger?.debug(`Registering audio event listener`)
449
445
  const subscribeAudio = addAudioEventListener(handleAudioEvent)
450
446
 
451
- logger.debug(
447
+ logger?.debug(
452
448
  `Subscribed to audio event listener and analysis listener`,
453
449
  {
454
450
  subscribeAudio,
@@ -456,7 +452,7 @@ export function useAudioRecorder({
456
452
  )
457
453
 
458
454
  return () => {
459
- logger.debug(`Removing audio event listener`)
455
+ logger?.debug(`Removing audio event listener`)
460
456
  subscribeAudio.remove()
461
457
  }
462
458
  }, [handleAudioEvent, handleAudioAnalysis])
@@ -1,10 +1,8 @@
1
+ import { ConsoleLike } from '../ExpoAudioStream.types'
1
2
  import { getWavFileInfo, WavFileInfo } from './getWavFileInfo'
2
- import { getLogger } from '../logger'
3
3
 
4
4
  export const WAV_HEADER_SIZE = 44
5
5
 
6
- const logger = getLogger('convertPCMToFloat32')
7
-
8
6
  const convertSample = (
9
7
  dataView: DataView,
10
8
  offset: number,
@@ -35,13 +33,15 @@ export const convertPCMToFloat32 = async ({
35
33
  bitDepth,
36
34
  buffer,
37
35
  skipWavHeader = false,
36
+ logger,
38
37
  }: {
39
38
  buffer: ArrayBuffer
40
39
  bitDepth: number
41
40
  skipWavHeader?: boolean
41
+ logger?: ConsoleLike
42
42
  }): Promise<{ pcmValues: Float32Array; min: number; max: number }> => {
43
43
  try {
44
- logger.debug(
44
+ logger?.debug(
45
45
  `Converting PCM to Float32: bitDepth: ${bitDepth}, buffer.byteLength: ${buffer.byteLength}`
46
46
  )
47
47
  const dataView = new DataView(buffer)
@@ -69,7 +69,7 @@ export const convertPCMToFloat32 = async ({
69
69
 
70
70
  return { pcmValues: float32Array, min, max }
71
71
  } catch (error: unknown) {
72
- logger.error(`Error converting PCM to Float32`, error)
72
+ logger?.error(`Error converting PCM to Float32`, error)
73
73
  return { pcmValues: new Float32Array(), min: 0, max: 0 }
74
74
  }
75
75
  }
package/src/logger.ts DELETED
@@ -1,22 +0,0 @@
1
- // packages/expo-audio-stream/src/logger.ts
2
- import { getLogger as siteedGetLogger } from '@siteed/react-native-logger'
3
-
4
- import { DEBUG_NAMESPACE } from './constants'
5
-
6
- type ConsoleLike = {
7
- log: (message: string, ...args: unknown[]) => void
8
- debug: (message: string, ...args: unknown[]) => void
9
- warn: (message: string, ...args: unknown[]) => void
10
- error: (message: string, ...args: unknown[]) => void
11
- }
12
-
13
- export const getLogger = (tag: string): ConsoleLike => {
14
- const baseLogger = siteedGetLogger(`${DEBUG_NAMESPACE}:${tag}`)
15
-
16
- return {
17
- log: (...args: unknown[]) => baseLogger.log(...(args as [unknown])),
18
- debug: (...args: unknown[]) => baseLogger.debug(...(args as [unknown])),
19
- error: (...args: unknown[]) => baseLogger.error(...(args as [unknown])),
20
- warn: (...args: unknown[]) => baseLogger.warn(...(args as [unknown])),
21
- }
22
- }