@siteed/expo-audio-stream 1.5.2 → 1.7.0

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 (94) hide show
  1. package/CHANGELOG.md +19 -27
  2. package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +13 -2
  3. package/build/AudioAnalysis/AudioAnalysis.types.d.ts +74 -0
  4. package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -0
  5. package/build/AudioAnalysis/AudioAnalysis.types.js +3 -0
  6. package/build/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
  7. package/build/AudioAnalysis/extractAudioAnalysis.d.ts +22 -0
  8. package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -0
  9. package/build/AudioAnalysis/extractAudioAnalysis.js +86 -0
  10. package/build/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
  11. package/build/AudioAnalysis/extractWaveform.d.ts +8 -0
  12. package/build/AudioAnalysis/extractWaveform.d.ts.map +1 -0
  13. package/build/AudioAnalysis/extractWaveform.js +11 -0
  14. package/build/AudioAnalysis/extractWaveform.js.map +1 -0
  15. package/build/AudioRecorder.provider.d.ts +11 -0
  16. package/build/AudioRecorder.provider.d.ts.map +1 -0
  17. package/build/AudioRecorder.provider.js +36 -0
  18. package/build/AudioRecorder.provider.js.map +1 -0
  19. package/build/ExpoAudioStream.native.d.ts +3 -0
  20. package/build/ExpoAudioStream.native.d.ts.map +1 -0
  21. package/build/ExpoAudioStream.native.js +6 -0
  22. package/build/ExpoAudioStream.native.js.map +1 -0
  23. package/build/ExpoAudioStream.types.d.ts +127 -0
  24. package/build/ExpoAudioStream.types.d.ts.map +1 -0
  25. package/build/ExpoAudioStream.types.js +2 -0
  26. package/build/ExpoAudioStream.types.js.map +1 -0
  27. package/build/ExpoAudioStream.web.d.ts +44 -0
  28. package/build/ExpoAudioStream.web.d.ts.map +1 -0
  29. package/build/ExpoAudioStream.web.js +206 -0
  30. package/build/ExpoAudioStream.web.js.map +1 -0
  31. package/build/ExpoAudioStreamModule.d.ts +3 -0
  32. package/build/ExpoAudioStreamModule.d.ts.map +1 -0
  33. package/build/ExpoAudioStreamModule.js +35 -0
  34. package/build/ExpoAudioStreamModule.js.map +1 -0
  35. package/build/WebRecorder.web.d.ts +54 -0
  36. package/build/WebRecorder.web.d.ts.map +1 -0
  37. package/build/WebRecorder.web.js +336 -0
  38. package/build/WebRecorder.web.js.map +1 -0
  39. package/build/constants.d.ts +11 -0
  40. package/build/constants.d.ts.map +1 -0
  41. package/build/constants.js +14 -0
  42. package/build/constants.js.map +1 -0
  43. package/build/events.d.ts +18 -0
  44. package/build/events.d.ts.map +1 -0
  45. package/build/events.js +11 -0
  46. package/build/events.js.map +1 -0
  47. package/build/index.d.ts +11 -0
  48. package/build/index.d.ts.map +1 -0
  49. package/build/index.js.map +1 -0
  50. package/build/useAudioRecorder.d.ts +20 -0
  51. package/build/useAudioRecorder.d.ts.map +1 -0
  52. package/build/useAudioRecorder.js +311 -0
  53. package/build/useAudioRecorder.js.map +1 -0
  54. package/build/utils/BlobFix.d.ts +9 -0
  55. package/build/utils/BlobFix.d.ts.map +1 -0
  56. package/build/utils/BlobFix.js +498 -0
  57. package/build/utils/BlobFix.js.map +1 -0
  58. package/build/utils/concatenateBuffers.d.ts +8 -0
  59. package/build/utils/concatenateBuffers.d.ts.map +1 -0
  60. package/build/utils/concatenateBuffers.js +21 -0
  61. package/build/utils/concatenateBuffers.js.map +1 -0
  62. package/build/utils/convertPCMToFloat32.d.ts +13 -0
  63. package/build/utils/convertPCMToFloat32.d.ts.map +1 -0
  64. package/build/utils/convertPCMToFloat32.js +120 -0
  65. package/build/utils/convertPCMToFloat32.js.map +1 -0
  66. package/build/utils/encodingToBitDepth.d.ts +5 -0
  67. package/build/utils/encodingToBitDepth.d.ts.map +1 -0
  68. package/build/utils/encodingToBitDepth.js +13 -0
  69. package/build/utils/encodingToBitDepth.js.map +1 -0
  70. package/build/utils/getWavFileInfo.d.ts +26 -0
  71. package/build/utils/getWavFileInfo.d.ts.map +1 -0
  72. package/build/utils/getWavFileInfo.js +92 -0
  73. package/build/utils/getWavFileInfo.js.map +1 -0
  74. package/build/utils/writeWavHeader.d.ts +49 -0
  75. package/build/utils/writeWavHeader.d.ts.map +1 -0
  76. package/build/utils/writeWavHeader.js +91 -0
  77. package/build/utils/writeWavHeader.js.map +1 -0
  78. package/build/workers/InlineFeaturesExtractor.web.d.ts +2 -0
  79. package/build/workers/InlineFeaturesExtractor.web.d.ts.map +1 -0
  80. package/build/workers/InlineFeaturesExtractor.web.js +311 -0
  81. package/build/workers/InlineFeaturesExtractor.web.js.map +1 -0
  82. package/build/workers/inlineAudioWebWorker.web.d.ts +2 -0
  83. package/build/workers/inlineAudioWebWorker.web.d.ts.map +1 -0
  84. package/build/workers/inlineAudioWebWorker.web.js +251 -0
  85. package/build/workers/inlineAudioWebWorker.web.js.map +1 -0
  86. package/expo-module.config.json +9 -0
  87. package/ios/AudioStreamManager.swift +394 -184
  88. package/ios/ExpoAudioStreamModule.swift +2 -2
  89. package/package.json +8 -4
  90. package/plugin/build/index.d.ts +3 -0
  91. package/plugin/build/index.js +132 -0
  92. package/plugin/src/index.ts +176 -0
  93. package/plugin/tsconfig.json +10 -0
  94. package/plugin/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoAudioStream.types.js","sourceRoot":"","sources":["../src/ExpoAudioStream.types.ts"],"names":[],"mappings":"","sourcesContent":["// packages/expo-audio-stream/src/ExpoAudioStream.types.ts\nimport {\n AmplitudeAlgorithm,\n AudioAnalysis,\n AudioFeaturesOptions,\n} from './AudioAnalysis/AudioAnalysis.types'\nimport { AudioAnalysisEvent } from './events'\n\nexport interface AudioStreamStatus {\n isRecording: boolean\n isPaused: boolean\n durationMs: number\n size: number\n interval: number\n mimeType: string\n}\n\nexport interface AudioDataEvent {\n data: string | Float32Array\n position: number\n fileUri: string\n eventDataSize: number\n totalSize: number\n}\n\nexport type EncodingType = 'pcm_32bit' | 'pcm_16bit' | 'pcm_8bit'\nexport type SampleRate = 16000 | 44100 | 48000\nexport type BitDepth = 8 | 16 | 32\n\nexport type ConsoleLike = {\n log: (message: string, ...args: unknown[]) => void\n debug: (message: string, ...args: unknown[]) => void\n warn: (message: string, ...args: unknown[]) => void\n error: (message: string, ...args: unknown[]) => void\n}\n\nexport interface Chunk {\n text: string\n timestamp: [number, number | null]\n}\n\nexport interface TranscriberData {\n id: string\n isBusy: boolean\n text: string\n startTime: number\n endTime: number\n chunks: Chunk[]\n}\n\nexport interface AudioRecording {\n fileUri: string\n filename: string\n durationMs: number\n size: number\n mimeType: string\n channels: number\n bitDepth: BitDepth\n sampleRate: SampleRate\n transcripts?: TranscriberData[]\n wavPCMData?: Float32Array // Full PCM data for the recording in WAV format (only on web, for native use the fileUri)\n analysisData?: AudioAnalysis // Analysis data for the recording depending on enableProcessing flag\n}\n\nexport interface StartRecordingResult {\n fileUri: string\n mimeType: string\n channels?: number\n bitDepth?: BitDepth\n sampleRate?: SampleRate\n}\n\nexport interface AudioSessionConfig {\n category?:\n | 'Ambient'\n | 'SoloAmbient'\n | 'Playback'\n | 'Record'\n | 'PlayAndRecord'\n | 'MultiRoute'\n mode?:\n | 'Default'\n | 'VoiceChat'\n | 'VideoChat'\n | 'GameChat'\n | 'VideoRecording'\n | 'Measurement'\n | 'MoviePlayback'\n | 'SpokenAudio'\n categoryOptions?: (\n | 'MixWithOthers'\n | 'DuckOthers'\n | 'InterruptSpokenAudioAndMixWithOthers'\n | 'AllowBluetooth'\n | 'AllowBluetoothA2DP'\n | 'AllowAirPlay'\n | 'DefaultToSpeaker'\n )[]\n}\n\nexport interface IOSConfig {\n audioSession?: AudioSessionConfig\n}\n\nexport interface RecordingConfig {\n // Sample rate for recording (16000, 44100, or 48000 Hz)\n sampleRate?: SampleRate\n\n // Number of audio channels (1 for mono, 2 for stereo)\n channels?: 1 | 2\n\n // Encoding type for the recording (pcm_32bit, pcm_16bit, pcm_8bit)\n encoding?: EncodingType\n\n // Interval in milliseconds at which to emit recording data\n interval?: number\n\n // Keep the device awake while recording (default is false)\n keepAwake?: boolean\n\n // Show a notification during recording (default is false)\n showNotification?: boolean\n\n // Show waveform in the notification (Android only, when showNotification is true)\n showWaveformInNotification?: boolean\n\n // Configuration for the notification\n notification?: NotificationConfig\n\n // Enable audio processing (default is false)\n enableProcessing?: boolean\n\n // iOS-specific configuration\n ios?: IOSConfig\n\n // Number of data points to extract per second of audio (default is 1000)\n pointsPerSecond?: number\n\n // Algorithm to use for amplitude computation (default is \"rms\")\n algorithm?: AmplitudeAlgorithm\n\n // Feature options to extract (default is empty)\n features?: AudioFeaturesOptions\n\n // Callback function to handle audio stream\n onAudioStream?: (_: AudioDataEvent) => Promise<void>\n\n // Callback function to handle audio features extraction results\n onAudioAnalysis?: (_: AudioAnalysisEvent) => Promise<void>\n}\n\nexport interface NotificationConfig {\n // Title of the notification\n title?: string\n\n // Main text content of the notification\n text?: string\n\n // Icon to be displayed in the notification (resource name or URI)\n icon?: string\n\n // Android-specific notification configuration\n android?: {\n // Unique identifier for the notification channel\n channelId?: string\n\n // User-visible name of the notification channel\n channelName?: string\n\n // User-visible description of the notification channel\n channelDescription?: string\n\n // Unique identifier for this notification\n notificationId?: number\n\n // List of actions that can be performed from the notification\n actions?: NotificationAction[]\n\n // Configuration for the waveform visualization in the notification\n waveform?: WaveformConfig\n\n // Color of the notification LED (if device supports it)\n lightColor?: string\n\n // Priority of the notification (affects how it's displayed)\n priority?: 'min' | 'low' | 'default' | 'high' | 'max'\n\n // Accent color for the notification (used for the app icon and buttons)\n accentColor?: string\n }\n\n // iOS-specific notification configuration\n ios?: {\n // Identifier for the notification category (used for grouping similar notifications)\n categoryIdentifier?: string\n }\n}\n\nexport interface NotificationAction {\n // Display title for the action\n title: string\n\n // Unique identifier for the action\n identifier: string\n\n // Icon to be displayed for the action (Android only)\n icon?: string\n}\n\nexport interface WaveformConfig {\n color?: string // The color of the waveform (e.g., \"#FFFFFF\" for white)\n opacity?: number // Opacity of the waveform (0.0 - 1.0)\n strokeWidth?: number // Width of the waveform line (default: 1.5)\n style?: 'stroke' | 'fill' // Drawing style: \"stroke\" for outline, \"fill\" for solid\n mirror?: boolean // Whether to mirror the waveform (symmetrical display)\n height?: number // Height of the waveform view in dp (default: 64)\n}\n\nexport interface UseAudioRecorderState {\n startRecording: (_: RecordingConfig) => Promise<StartRecordingResult>\n stopRecording: () => Promise<AudioRecording | null>\n pauseRecording: () => Promise<void>\n resumeRecording: () => Promise<void>\n isRecording: boolean\n isPaused: boolean\n durationMs: number // Duration of the recording\n size: number // Size in bytes of the recorded audio\n analysisData?: AudioAnalysis // Analysis data for the recording depending on enableProcessing flag\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import { LegacyEventEmitter } from 'expo-modules-core';
2
+ import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types';
3
+ import { AudioRecording, AudioStreamStatus, BitDepth, ConsoleLike, RecordingConfig, StartRecordingResult } from './ExpoAudioStream.types';
4
+ import { WebRecorder } from './WebRecorder.web';
5
+ export interface EmitAudioEventProps {
6
+ data: Float32Array;
7
+ position: number;
8
+ }
9
+ export type EmitAudioEventFunction = (_: EmitAudioEventProps) => void;
10
+ export type EmitAudioAnalysisFunction = (_: AudioAnalysis) => void;
11
+ export interface ExpoAudioStreamWebProps {
12
+ logger?: ConsoleLike;
13
+ audioWorkletUrl: string;
14
+ featuresExtratorUrl: string;
15
+ }
16
+ export declare class ExpoAudioStreamWeb extends LegacyEventEmitter {
17
+ customRecorder: WebRecorder | null;
18
+ audioChunks: Float32Array[];
19
+ isRecording: boolean;
20
+ isPaused: boolean;
21
+ recordingStartTime: number;
22
+ pausedTime: number;
23
+ currentDurationMs: number;
24
+ currentSize: number;
25
+ currentInterval: number;
26
+ lastEmittedSize: number;
27
+ lastEmittedTime: number;
28
+ streamUuid: string | null;
29
+ extension: 'webm' | 'wav';
30
+ recordingConfig?: RecordingConfig;
31
+ bitDepth: BitDepth;
32
+ audioWorkletUrl: string;
33
+ featuresExtratorUrl: string;
34
+ logger?: ConsoleLike;
35
+ constructor({ audioWorkletUrl, featuresExtratorUrl, logger, }: ExpoAudioStreamWebProps);
36
+ getMediaStream(): Promise<MediaStream>;
37
+ startRecording(recordingConfig?: RecordingConfig): Promise<StartRecordingResult>;
38
+ emitAudioEvent({ data, position }: EmitAudioEventProps): void;
39
+ stopRecording(): Promise<AudioRecording>;
40
+ pauseRecording(): Promise<void>;
41
+ resumeRecording(): Promise<void>;
42
+ status(): AudioStreamStatus;
43
+ }
44
+ //# sourceMappingURL=ExpoAudioStream.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoAudioStream.web.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStream.web.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AACnE,OAAO,EACH,cAAc,EACd,iBAAiB,EACjB,QAAQ,EACR,WAAW,EACX,eAAe,EACf,oBAAoB,EACvB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAK/C,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,YAAY,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACnB;AACD,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,EAAE,mBAAmB,KAAK,IAAI,CAAA;AACrE,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,CAAA;AAElE,MAAM,WAAW,uBAAuB;IACpC,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;IACvB,mBAAmB,EAAE,MAAM,CAAA;CAC9B;AAED,qBAAa,kBAAmB,SAAQ,kBAAkB;IACtD,cAAc,EAAE,WAAW,GAAG,IAAI,CAAA;IAClC,WAAW,EAAE,YAAY,EAAE,CAAA;IAC3B,WAAW,EAAE,OAAO,CAAA;IACpB,QAAQ,EAAE,OAAO,CAAA;IACjB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,iBAAiB,EAAE,MAAM,CAAA;IACzB,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,SAAS,EAAE,MAAM,GAAG,KAAK,CAAQ;IACjC,eAAe,CAAC,EAAE,eAAe,CAAA;IACjC,QAAQ,EAAE,QAAQ,CAAA;IAClB,eAAe,EAAE,MAAM,CAAA;IACvB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAA;gBAER,EACR,eAAe,EACf,mBAAmB,EACnB,MAAM,GACT,EAAE,uBAAuB;IA8BpB,cAAc;IAUd,cAAc,CAAC,eAAe,GAAE,eAAoB;IAkE1D,cAAc,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,mBAAmB;IAiBhD,aAAa,IAAI,OAAO,CAAC,cAAc,CAAC;IA6CxC,cAAc;IAad,eAAe;IAarB,MAAM;CAWT"}
@@ -0,0 +1,206 @@
1
+ // src/ExpoAudioStreamModule.web.ts
2
+ import { LegacyEventEmitter } from 'expo-modules-core';
3
+ import { WebRecorder } from './WebRecorder.web';
4
+ import { encodingToBitDepth } from './utils/encodingToBitDepth';
5
+ import { writeWavHeader } from './utils/writeWavHeader';
6
+ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
7
+ customRecorder;
8
+ audioChunks;
9
+ isRecording;
10
+ isPaused;
11
+ recordingStartTime;
12
+ pausedTime;
13
+ currentDurationMs;
14
+ currentSize;
15
+ currentInterval;
16
+ lastEmittedSize;
17
+ lastEmittedTime;
18
+ streamUuid;
19
+ extension = 'wav'; // Default extension is 'webm'
20
+ recordingConfig;
21
+ bitDepth; // Bit depth of the audio
22
+ audioWorkletUrl;
23
+ featuresExtratorUrl;
24
+ logger;
25
+ constructor({ audioWorkletUrl, featuresExtratorUrl, logger, }) {
26
+ const mockNativeModule = {
27
+ addListener: () => {
28
+ // Not used on web
29
+ },
30
+ removeListeners: () => {
31
+ // Not used on web
32
+ },
33
+ };
34
+ super(mockNativeModule); // Pass the mock native module to the parent class
35
+ this.logger = logger;
36
+ this.customRecorder = null;
37
+ this.audioChunks = [];
38
+ this.isRecording = false;
39
+ this.isPaused = false;
40
+ this.recordingStartTime = 0;
41
+ this.pausedTime = 0;
42
+ this.currentDurationMs = 0;
43
+ this.currentSize = 0;
44
+ this.bitDepth = 32; // Default
45
+ this.currentInterval = 1000; // Default interval in ms
46
+ this.lastEmittedSize = 0;
47
+ this.lastEmittedTime = 0;
48
+ this.streamUuid = null; // Initialize UUID on first recording start
49
+ this.audioWorkletUrl = audioWorkletUrl;
50
+ this.featuresExtratorUrl = featuresExtratorUrl;
51
+ }
52
+ // Utility to handle user media stream
53
+ async getMediaStream() {
54
+ try {
55
+ return await navigator.mediaDevices.getUserMedia({ audio: true });
56
+ }
57
+ catch (error) {
58
+ console.error('Failed to get media stream:', error);
59
+ throw error;
60
+ }
61
+ }
62
+ // Start recording with options
63
+ async startRecording(recordingConfig = {}) {
64
+ if (this.isRecording) {
65
+ throw new Error('Recording is already in progress');
66
+ }
67
+ this.bitDepth = encodingToBitDepth({
68
+ encoding: recordingConfig.encoding ?? 'pcm_32bit',
69
+ });
70
+ const audioContext = new (window.AudioContext ||
71
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
72
+ // @ts-ignore - Allow webkitAudioContext for Safari
73
+ window.webkitAudioContext)();
74
+ const stream = await this.getMediaStream();
75
+ const source = audioContext.createMediaStreamSource(stream);
76
+ this.customRecorder = new WebRecorder({
77
+ audioContext,
78
+ source,
79
+ recordingConfig,
80
+ audioWorkletUrl: this.audioWorkletUrl,
81
+ emitAudioEventCallback: ({ data, position, }) => {
82
+ this.audioChunks.push(new Float32Array(data));
83
+ this.currentSize += data.byteLength;
84
+ this.emitAudioEvent({ data, position });
85
+ this.lastEmittedTime = Date.now();
86
+ this.lastEmittedSize = this.currentSize;
87
+ },
88
+ emitAudioAnalysisCallback: (audioAnalysisData) => {
89
+ this.logger?.log(`Emitted AudioAnalysis:`, audioAnalysisData);
90
+ this.emit('AudioAnalysis', audioAnalysisData);
91
+ },
92
+ });
93
+ await this.customRecorder.init();
94
+ this.customRecorder.start();
95
+ // // Set a timer to stop recording after 5 seconds
96
+ // setTimeout(() => {
97
+ // logger.log("AUTO Stopping recording");
98
+ // this.customRecorder?.stopAndPlay();
99
+ // this.isRecording = false;
100
+ // }, 3000);
101
+ this.isRecording = true;
102
+ this.recordingConfig = recordingConfig;
103
+ this.recordingStartTime = Date.now();
104
+ this.pausedTime = 0;
105
+ this.isPaused = false;
106
+ this.lastEmittedSize = 0;
107
+ this.lastEmittedTime = 0;
108
+ this.streamUuid = Date.now().toString();
109
+ const fileUri = `${this.streamUuid}.${this.extension}`;
110
+ const streamConfig = {
111
+ fileUri,
112
+ mimeType: `audio/${this.extension}`,
113
+ bitDepth: this.bitDepth,
114
+ channels: recordingConfig.channels ?? 1,
115
+ sampleRate: recordingConfig.sampleRate ?? 44100,
116
+ };
117
+ return streamConfig;
118
+ }
119
+ emitAudioEvent({ data, position }) {
120
+ const fileUri = `${this.streamUuid}.${this.extension}`;
121
+ const audioEventPayload = {
122
+ fileUri,
123
+ mimeType: `audio/${this.extension}`,
124
+ lastEmittedSize: this.lastEmittedSize, // Since this might be continuously streaming, adjust accordingly
125
+ deltaSize: data.byteLength,
126
+ position,
127
+ totalSize: this.currentSize,
128
+ buffer: data,
129
+ streamUuid: this.streamUuid ?? '', // Generate or manage UUID for stream identification
130
+ };
131
+ this.emit('AudioData', audioEventPayload);
132
+ }
133
+ // Stop recording
134
+ async stopRecording() {
135
+ if (!this.customRecorder) {
136
+ throw new Error('Recorder is not initialized');
137
+ }
138
+ const fullPcmBufferArray = await this.customRecorder.stop();
139
+ // concat all audio chunks
140
+ this.logger?.debug(`Stopped recording`, fullPcmBufferArray);
141
+ this.isRecording = false;
142
+ this.isPaused = false;
143
+ this.currentDurationMs = Date.now() - this.recordingStartTime;
144
+ // Rewrite wav header with correct data size
145
+ const wavConfig = {
146
+ buffer: new ArrayBuffer(fullPcmBufferArray.buffer.byteLength),
147
+ sampleRate: this.recordingConfig?.sampleRate ?? 44100,
148
+ numChannels: this.recordingConfig?.channels ?? 1,
149
+ bitDepth: this.bitDepth,
150
+ };
151
+ this.logger?.debug(`Writing wav header`, wavConfig);
152
+ const wavBuffer = writeWavHeader(wavConfig).slice(0);
153
+ // Create blob fileUri from audio chunks
154
+ const blob = new Blob([wavBuffer], {
155
+ type: `audio/${this.extension}`,
156
+ });
157
+ const fileUri = URL.createObjectURL(blob);
158
+ const result = {
159
+ fileUri,
160
+ filename: `${this.streamUuid}.${this.extension}`,
161
+ wavPCMData: fullPcmBufferArray,
162
+ bitDepth: this.bitDepth,
163
+ channels: this.recordingConfig?.channels ?? 1,
164
+ sampleRate: this.recordingConfig?.sampleRate ?? 44100,
165
+ durationMs: this.currentDurationMs,
166
+ size: this.currentSize,
167
+ mimeType: `audio/${this.extension}`,
168
+ };
169
+ return result;
170
+ }
171
+ // Pause recording
172
+ async pauseRecording() {
173
+ if (!this.isRecording || this.isPaused) {
174
+ throw new Error('Recording is not active or already paused');
175
+ }
176
+ if (this.customRecorder) {
177
+ this.customRecorder.pause();
178
+ }
179
+ this.isPaused = true;
180
+ this.pausedTime = Date.now();
181
+ }
182
+ // Resume recording
183
+ async resumeRecording() {
184
+ if (!this.isPaused) {
185
+ throw new Error('Recording is not paused');
186
+ }
187
+ if (this.customRecorder) {
188
+ this.customRecorder.resume();
189
+ }
190
+ this.isPaused = false;
191
+ this.recordingStartTime += Date.now() - this.pausedTime;
192
+ }
193
+ // Get current status
194
+ status() {
195
+ const status = {
196
+ isRecording: this.isRecording,
197
+ isPaused: this.isPaused,
198
+ durationMs: Date.now() - this.recordingStartTime,
199
+ size: this.currentSize,
200
+ interval: this.currentInterval,
201
+ mimeType: `audio/${this.extension}`,
202
+ };
203
+ return status;
204
+ }
205
+ }
206
+ //# sourceMappingURL=ExpoAudioStream.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoAudioStream.web.js","sourceRoot":"","sources":["../src/ExpoAudioStream.web.ts"],"names":[],"mappings":"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","sourcesContent":["// src/ExpoAudioStreamModule.web.ts\nimport { LegacyEventEmitter } from 'expo-modules-core'\n\nimport { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types'\nimport {\n AudioRecording,\n AudioStreamStatus,\n BitDepth,\n ConsoleLike,\n RecordingConfig,\n StartRecordingResult,\n} from './ExpoAudioStream.types'\nimport { WebRecorder } from './WebRecorder.web'\nimport { AudioEventPayload } from './events'\nimport { encodingToBitDepth } from './utils/encodingToBitDepth'\nimport { WavHeaderOptions, writeWavHeader } from './utils/writeWavHeader'\n\nexport interface EmitAudioEventProps {\n data: Float32Array\n position: number\n}\nexport type EmitAudioEventFunction = (_: EmitAudioEventProps) => void\nexport type EmitAudioAnalysisFunction = (_: AudioAnalysis) => void\n\nexport interface ExpoAudioStreamWebProps {\n logger?: ConsoleLike\n audioWorkletUrl: string\n featuresExtratorUrl: string\n}\n\nexport class ExpoAudioStreamWeb extends LegacyEventEmitter {\n customRecorder: WebRecorder | null\n audioChunks: Float32Array[]\n isRecording: boolean\n isPaused: boolean\n recordingStartTime: number\n pausedTime: number\n currentDurationMs: number\n currentSize: number\n currentInterval: number\n lastEmittedSize: number\n lastEmittedTime: number\n streamUuid: string | null\n extension: 'webm' | 'wav' = 'wav' // Default extension is 'webm'\n recordingConfig?: RecordingConfig\n bitDepth: BitDepth // Bit depth of the audio\n audioWorkletUrl: string\n featuresExtratorUrl: string\n logger?: ConsoleLike\n\n constructor({\n audioWorkletUrl,\n featuresExtratorUrl,\n logger,\n }: ExpoAudioStreamWebProps) {\n const mockNativeModule = {\n addListener: () => {\n // Not used on web\n },\n removeListeners: () => {\n // Not used on web\n },\n }\n super(mockNativeModule) // Pass the mock native module to the parent class\n\n this.logger = logger\n this.customRecorder = null\n this.audioChunks = []\n this.isRecording = false\n this.isPaused = false\n this.recordingStartTime = 0\n this.pausedTime = 0\n this.currentDurationMs = 0\n this.currentSize = 0\n this.bitDepth = 32 // Default\n this.currentInterval = 1000 // Default interval in ms\n this.lastEmittedSize = 0\n this.lastEmittedTime = 0\n this.streamUuid = null // Initialize UUID on first recording start\n this.audioWorkletUrl = audioWorkletUrl\n this.featuresExtratorUrl = featuresExtratorUrl\n }\n\n // Utility to handle user media stream\n async getMediaStream() {\n try {\n return await navigator.mediaDevices.getUserMedia({ audio: true })\n } catch (error) {\n console.error('Failed to get media stream:', error)\n throw error\n }\n }\n\n // Start recording with options\n async startRecording(recordingConfig: RecordingConfig = {}) {\n if (this.isRecording) {\n throw new Error('Recording is already in progress')\n }\n\n this.bitDepth = encodingToBitDepth({\n encoding: recordingConfig.encoding ?? 'pcm_32bit',\n })\n\n const audioContext = new (window.AudioContext ||\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore - Allow webkitAudioContext for Safari\n window.webkitAudioContext)()\n const stream = await this.getMediaStream()\n\n const source = audioContext.createMediaStreamSource(stream)\n\n this.customRecorder = new WebRecorder({\n audioContext,\n source,\n recordingConfig,\n audioWorkletUrl: this.audioWorkletUrl,\n emitAudioEventCallback: ({\n data,\n position,\n }: EmitAudioEventProps) => {\n this.audioChunks.push(new Float32Array(data))\n this.currentSize += data.byteLength\n this.emitAudioEvent({ data, position })\n this.lastEmittedTime = Date.now()\n this.lastEmittedSize = this.currentSize\n },\n emitAudioAnalysisCallback: (audioAnalysisData: AudioAnalysis) => {\n this.logger?.log(`Emitted AudioAnalysis:`, audioAnalysisData)\n this.emit('AudioAnalysis', audioAnalysisData)\n },\n })\n await this.customRecorder.init()\n this.customRecorder.start()\n\n // // Set a timer to stop recording after 5 seconds\n // setTimeout(() => {\n // logger.log(\"AUTO Stopping recording\");\n // this.customRecorder?.stopAndPlay();\n // this.isRecording = false;\n // }, 3000);\n\n this.isRecording = true\n this.recordingConfig = recordingConfig\n this.recordingStartTime = Date.now()\n this.pausedTime = 0\n this.isPaused = false\n this.lastEmittedSize = 0\n this.lastEmittedTime = 0\n this.streamUuid = Date.now().toString()\n const fileUri = `${this.streamUuid}.${this.extension}`\n const streamConfig: StartRecordingResult = {\n fileUri,\n mimeType: `audio/${this.extension}`,\n bitDepth: this.bitDepth,\n channels: recordingConfig.channels ?? 1,\n sampleRate: recordingConfig.sampleRate ?? 44100,\n }\n return streamConfig\n }\n\n emitAudioEvent({ data, position }: EmitAudioEventProps) {\n const fileUri = `${this.streamUuid}.${this.extension}`\n const audioEventPayload: AudioEventPayload = {\n fileUri,\n mimeType: `audio/${this.extension}`,\n lastEmittedSize: this.lastEmittedSize, // Since this might be continuously streaming, adjust accordingly\n deltaSize: data.byteLength,\n position,\n totalSize: this.currentSize,\n buffer: data,\n streamUuid: this.streamUuid ?? '', // Generate or manage UUID for stream identification\n }\n\n this.emit('AudioData', audioEventPayload)\n }\n\n // Stop recording\n async stopRecording(): Promise<AudioRecording> {\n if (!this.customRecorder) {\n throw new Error('Recorder is not initialized')\n }\n\n const fullPcmBufferArray = await this.customRecorder.stop()\n\n // concat all audio chunks\n this.logger?.debug(`Stopped recording`, fullPcmBufferArray)\n this.isRecording = false\n this.isPaused = false\n this.currentDurationMs = Date.now() - this.recordingStartTime\n\n // Rewrite wav header with correct data size\n const wavConfig: WavHeaderOptions = {\n buffer: new ArrayBuffer(fullPcmBufferArray.buffer.byteLength),\n sampleRate: this.recordingConfig?.sampleRate ?? 44100,\n numChannels: this.recordingConfig?.channels ?? 1,\n bitDepth: this.bitDepth,\n }\n this.logger?.debug(`Writing wav header`, wavConfig)\n const wavBuffer = writeWavHeader(wavConfig).slice(0)\n\n // Create blob fileUri from audio chunks\n const blob = new Blob([wavBuffer], {\n type: `audio/${this.extension}`,\n })\n const fileUri = URL.createObjectURL(blob)\n\n const result: AudioRecording = {\n fileUri,\n filename: `${this.streamUuid}.${this.extension}`,\n wavPCMData: fullPcmBufferArray,\n bitDepth: this.bitDepth,\n channels: this.recordingConfig?.channels ?? 1,\n sampleRate: this.recordingConfig?.sampleRate ?? 44100,\n durationMs: this.currentDurationMs,\n size: this.currentSize,\n mimeType: `audio/${this.extension}`,\n }\n\n return result\n }\n\n // Pause recording\n async pauseRecording() {\n if (!this.isRecording || this.isPaused) {\n throw new Error('Recording is not active or already paused')\n }\n\n if (this.customRecorder) {\n this.customRecorder.pause()\n }\n this.isPaused = true\n this.pausedTime = Date.now()\n }\n\n // Resume recording\n async resumeRecording() {\n if (!this.isPaused) {\n throw new Error('Recording is not paused')\n }\n\n if (this.customRecorder) {\n this.customRecorder.resume()\n }\n this.isPaused = false\n this.recordingStartTime += Date.now() - this.pausedTime\n }\n\n // Get current status\n status() {\n const status: AudioStreamStatus = {\n isRecording: this.isRecording,\n isPaused: this.isPaused,\n durationMs: Date.now() - this.recordingStartTime,\n size: this.currentSize,\n interval: this.currentInterval,\n mimeType: `audio/${this.extension}`,\n }\n return status\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ declare let ExpoAudioStreamModule: any;
2
+ export default ExpoAudioStreamModule;
3
+ //# sourceMappingURL=ExpoAudioStreamModule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoAudioStreamModule.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStreamModule.ts"],"names":[],"mappings":"AASA,QAAA,IAAI,qBAAqB,EAAE,GAAG,CAAA;AA+B9B,eAAe,qBAAqB,CAAA"}
@@ -0,0 +1,35 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ import { Platform } from 'react-native';
3
+ import { ExpoAudioStreamWeb, } from './ExpoAudioStream.web';
4
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
+ let ExpoAudioStreamModule;
6
+ if (Platform.OS === 'web') {
7
+ let instance = null;
8
+ ExpoAudioStreamModule = (webProps) => {
9
+ if (!instance) {
10
+ instance = new ExpoAudioStreamWeb(webProps);
11
+ }
12
+ return instance;
13
+ };
14
+ ExpoAudioStreamModule.requestPermissionsAsync = async () => {
15
+ return {
16
+ status: 'granted',
17
+ granted: true,
18
+ expires: 'never',
19
+ canAskAgain: true,
20
+ };
21
+ };
22
+ ExpoAudioStreamModule.getPermissionsAsync = async () => {
23
+ return {
24
+ status: 'granted',
25
+ granted: true,
26
+ expires: 'never',
27
+ canAskAgain: true,
28
+ };
29
+ };
30
+ }
31
+ else {
32
+ ExpoAudioStreamModule = requireNativeModule('ExpoAudioStream');
33
+ }
34
+ export default ExpoAudioStreamModule;
35
+ //# sourceMappingURL=ExpoAudioStreamModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoAudioStreamModule.js","sourceRoot":"","sources":["../src/ExpoAudioStreamModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEvC,OAAO,EACH,kBAAkB,GAErB,MAAM,uBAAuB,CAAA;AAE9B,8DAA8D;AAC9D,IAAI,qBAA0B,CAAA;AAE9B,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;IACxB,IAAI,QAAQ,GAA8B,IAAI,CAAA;IAE9C,qBAAqB,GAAG,CAAC,QAAiC,EAAE,EAAE;QAC1D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACZ,QAAQ,GAAG,IAAI,kBAAkB,CAAC,QAAQ,CAAC,CAAA;QAC/C,CAAC;QACD,OAAO,QAAQ,CAAA;IACnB,CAAC,CAAA;IACD,qBAAqB,CAAC,uBAAuB,GAAG,KAAK,IAAI,EAAE;QACvD,OAAO;YACH,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,OAAO;YAChB,WAAW,EAAE,IAAI;SACpB,CAAA;IACL,CAAC,CAAA;IACD,qBAAqB,CAAC,mBAAmB,GAAG,KAAK,IAAI,EAAE;QACnD,OAAO;YACH,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,OAAO;YAChB,WAAW,EAAE,IAAI;SACpB,CAAA;IACL,CAAC,CAAA;AACL,CAAC;KAAM,CAAC;IACJ,qBAAqB,GAAG,mBAAmB,CAAC,iBAAiB,CAAC,CAAA;AAClE,CAAC;AAED,eAAe,qBAAqB,CAAA","sourcesContent":["import { requireNativeModule } from 'expo-modules-core'\nimport { Platform } from 'react-native'\n\nimport {\n ExpoAudioStreamWeb,\n ExpoAudioStreamWebProps,\n} from './ExpoAudioStream.web'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet ExpoAudioStreamModule: any\n\nif (Platform.OS === 'web') {\n let instance: ExpoAudioStreamWeb | null = null\n\n ExpoAudioStreamModule = (webProps: ExpoAudioStreamWebProps) => {\n if (!instance) {\n instance = new ExpoAudioStreamWeb(webProps)\n }\n return instance\n }\n ExpoAudioStreamModule.requestPermissionsAsync = async () => {\n return {\n status: 'granted',\n granted: true,\n expires: 'never',\n canAskAgain: true,\n }\n }\n ExpoAudioStreamModule.getPermissionsAsync = async () => {\n return {\n status: 'granted',\n granted: true,\n expires: 'never',\n canAskAgain: true,\n }\n }\n} else {\n ExpoAudioStreamModule = requireNativeModule('ExpoAudioStream')\n}\n\nexport default ExpoAudioStreamModule\n"]}
@@ -0,0 +1,54 @@
1
+ import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types';
2
+ import { ConsoleLike, RecordingConfig } from './ExpoAudioStream.types';
3
+ import { EmitAudioAnalysisFunction, EmitAudioEventFunction } from './ExpoAudioStream.web';
4
+ interface AudioFeaturesEvent {
5
+ data: {
6
+ command: string;
7
+ result: AudioAnalysis;
8
+ };
9
+ }
10
+ export declare class WebRecorder {
11
+ private audioContext;
12
+ private audioWorkletNode;
13
+ private featureExtractorWorker?;
14
+ private source;
15
+ private audioWorkletUrl;
16
+ private emitAudioEventCallback;
17
+ private emitAudioAnalysisCallback;
18
+ private config;
19
+ private position;
20
+ private numberOfChannels;
21
+ private bitDepth;
22
+ private exportBitDepth;
23
+ private audioBuffer;
24
+ private audioBufferSize;
25
+ private audioAnalysisData;
26
+ private packetCount;
27
+ private logger?;
28
+ constructor({ audioContext, source, recordingConfig, audioWorkletUrl, emitAudioEventCallback, emitAudioAnalysisCallback, logger, }: {
29
+ audioContext: AudioContext;
30
+ source: MediaStreamAudioSourceNode;
31
+ recordingConfig: RecordingConfig;
32
+ audioWorkletUrl: string;
33
+ emitAudioEventCallback: EmitAudioEventFunction;
34
+ emitAudioAnalysisCallback: EmitAudioAnalysisFunction;
35
+ logger?: ConsoleLike;
36
+ });
37
+ init(): Promise<void>;
38
+ initFeatureExtractorWorker(featuresExtratorUrl?: string): void;
39
+ initFallbackWorker(): void;
40
+ handleWorkerError(error: ErrorEvent): void;
41
+ handleFeatureExtractorMessage(event: AudioFeaturesEvent): void;
42
+ start(): void;
43
+ stop(): Promise<Float32Array>;
44
+ pause(): void;
45
+ stopMediaStreamTracks(): void;
46
+ playRecordedData({ recordedData, }: {
47
+ recordedData: ArrayBuffer;
48
+ mimeType?: string;
49
+ }): Promise<void>;
50
+ private checkAudioContextFormat;
51
+ resume(): void;
52
+ }
53
+ export {};
54
+ //# sourceMappingURL=WebRecorder.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebRecorder.web.d.ts","sourceRoot":"","sources":["../src/WebRecorder.web.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AACnE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EACH,yBAAyB,EACzB,sBAAsB,EACzB,MAAM,uBAAuB,CAAA;AAe9B,UAAU,kBAAkB;IACxB,IAAI,EAAE;QACF,OAAO,EAAE,MAAM,CAAA;QACf,MAAM,EAAE,aAAa,CAAA;KACxB,CAAA;CACJ;AAUD,qBAAa,WAAW;IACpB,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,sBAAsB,CAAC,CAAQ;IACvC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,sBAAsB,CAAwB;IACtD,OAAO,CAAC,yBAAyB,CAA2B;IAC5D,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,gBAAgB,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,iBAAiB,CAAe;IACxC,OAAO,CAAC,WAAW,CAAY;IAC/B,OAAO,CAAC,MAAM,CAAC,CAAa;gBAEhB,EACR,YAAY,EACZ,MAAM,EACN,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,yBAAyB,EACzB,MAAM,GACT,EAAE;QACC,YAAY,EAAE,YAAY,CAAA;QAC1B,MAAM,EAAE,0BAA0B,CAAA;QAClC,eAAe,EAAE,eAAe,CAAA;QAChC,eAAe,EAAE,MAAM,CAAA;QACvB,sBAAsB,EAAE,sBAAsB,CAAA;QAC9C,yBAAyB,EAAE,yBAAyB,CAAA;QACpD,MAAM,CAAC,EAAE,WAAW,CAAA;KACvB;IAqDK,IAAI;IAyHV,0BAA0B,CAAC,mBAAmB,CAAC,EAAE,MAAM;IA0BvD,kBAAkB;IAqBlB,iBAAiB,CAAC,KAAK,EAAE,UAAU;IAInC,6BAA6B,CAAC,KAAK,EAAE,kBAAkB;IAgCvD,KAAK;IAML,IAAI,IAAI,OAAO,CAAC,YAAY,CAAC;IAmF7B,KAAK;IAML,qBAAqB;IAMf,gBAAgB,CAAC,EACnB,YAAY,GACf,EAAE;QACC,YAAY,EAAE,WAAW,CAAA;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;KACpB;IAsBD,OAAO,CAAC,uBAAuB;IAoB/B,MAAM;CAKT"}