l-min-components 1.7.1313 → 1.7.1314

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "l-min-components",
3
- "version": "1.7.1313",
3
+ "version": "1.7.1314",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src/assets",
@@ -59,3 +59,4 @@ export { default as AudioWaveComponent } from "./useAudioPlayer/audioWave";
59
59
  export { default as InputEmoji } from "./InputEmoji";
60
60
  export { default as useAudioRecorder } from "../hooks/recorder-kit";
61
61
  export { default as useCookiePolling } from "../hooks/utils/cookiePolling";
62
+ export { default as convertBlobToWav } from "../utils/webm2wav";
@@ -0,0 +1,296 @@
1
+ # `useAudioRecorder` Hook Documentation
2
+
3
+ **Version:** 1.0.0 (Based on the provided JavaScript code)
4
+
5
+ ## Overview
6
+
7
+ The `useAudioRecorder` is a custom React hook designed to provide headless audio recording functionality. It encapsulates all the logic for interacting with the browser's MediaStream API (`getUserMedia`), `MediaRecorder` API, and `AudioContext` for processing and converting audio to WAV format.
8
+
9
+ By being "headless," this hook does not render any UI itself. Instead, it provides the state, data, and action functions necessary for you to build your own custom audio recorder UI components. This offers maximum flexibility in how the recorder looks and behaves within your application.
10
+
11
+ ## Features
12
+
13
+ - **Microphone Access:** Handles requesting and managing microphone permissions.
14
+ - **Recording Control:** Provides functions to start, pause, resume, and stop recording.
15
+ - **State Management:** Exposes the current state of the recorder (e.g., 'idle', 'recording', 'paused', 'finished', 'permissionDenied').
16
+ - **Timer:** Tracks and exposes the duration of the current recording.
17
+ - **Audio Output:**
18
+ - Provides the recorded audio as raw chunks (`Blob[]`).
19
+ - Converts the recording to a WAV `Blob` (`finalWavBlob`).
20
+ - Generates an `audioURL` (Object URL) for easy playback of the final recording (WAV or raw if WAV conversion fails).
21
+ - **Live Stream Access:** Exposes the live `MediaStream` and `AudioContext` via a callback (`onStreamReady`) for integrations like live waveform visualization (e.g., with WaveSurfer.js).
22
+ - **Error Handling:** Provides error messages for common issues.
23
+ - **Callbacks:** Offers various callbacks for state changes, timer updates, recording completion, errors, and stream readiness.
24
+ - **Configuration:** Allows basic configuration like MIME type for recording.
25
+
26
+ ## Installation & Setup
27
+
28
+ 1. **Dependencies:**
29
+
30
+ - `react`: Ensure you have React installed in your project.
31
+ - `lucide-react` (Optional, for icons): The example `App` component uses `lucide-react` for icons. If you build your own UI, you can use any icon library or none.
32
+
33
+ 2. **Add the Hook to Your Project:**
34
+ Copy the `useAudioRecorder` function and its helper functions (`audioBufferToWav`, `encodeWAV`, `floatTo16BitPCM`, `interleave`, `writeString`) into your React project (e.g., in a `hooks/useAudioRecorder.js` file).
35
+
36
+ ## How to Use
37
+
38
+ Import the hook into your component and call it. It will return an object containing the recorder's state, data, and action functions.
39
+
40
+ ```javascript
41
+ import React, { useEffect } from "react";
42
+ import { useAudioRecorder } from "./path/to/useAudioRecorder"; // Adjust path as needed
43
+ import {
44
+ Mic,
45
+ StopCircle,
46
+ Play,
47
+ Pause,
48
+ Download,
49
+ AlertTriangle,
50
+ CheckCircle,
51
+ RefreshCw,
52
+ } from "lucide-react"; // Example icons
53
+
54
+ function MyAudioRecorderComponent() {
55
+ const {
56
+ recordingState,
57
+ duration,
58
+ finalWavBlob,
59
+ audioURL,
60
+ errorMessage,
61
+ isMicrophoneAvailable,
62
+ actions, // Contains start, pause, resume, stop, requestPermission, reset
63
+ } = useAudioRecorder({
64
+ // Optional props/callbacks
65
+ onStateChange: (payload) => console.log(`New state: ${payload.newState}`),
66
+ onRecordingComplete: (payload) => {
67
+ if (payload.wavBlob) {
68
+ console.log("WAV recording finished:", payload.wavBlob);
69
+ } else {
70
+ console.log(
71
+ "Raw recording finished (WAV conversion failed):",
72
+ payload.audioChunks
73
+ );
74
+ }
75
+ },
76
+ onStreamReady: ({ stream, audioContext }) => {
77
+ console.log(
78
+ "Live stream and AudioContext are ready:",
79
+ stream,
80
+ audioContext
81
+ );
82
+ // Ideal place to integrate with WaveSurfer.js or other live visualizers
83
+ },
84
+ onError: (error) => console.error("Recorder error:", error.message),
85
+ });
86
+
87
+ // Cleanup for audioURL when component unmounts or audioURL changes
88
+ useEffect(() => {
89
+ return () => {
90
+ if (audioURL) {
91
+ URL.revokeObjectURL(audioURL);
92
+ }
93
+ };
94
+ }, [audioURL]);
95
+
96
+ const formatDurationForDisplay = (seconds) => {
97
+ const minutes = Math.floor(seconds / 60);
98
+ const remainingSeconds = seconds % 60;
99
+ return `${String(minutes).padStart(2, "0")}:${String(
100
+ remainingSeconds
101
+ ).padStart(2, "0")}`;
102
+ };
103
+
104
+ // Build your UI using the returned state and actions
105
+ return (
106
+ <div className="my-recorder-ui p-4 border rounded-lg">
107
+ <p>State: {recordingState}</p>
108
+ <p>Duration: {formatDurationForDisplay(duration)}</p>
109
+
110
+ {!isMicrophoneAvailable && (
111
+ <p className="text-red-500">Microphone not available.</p>
112
+ )}
113
+ {errorMessage && <p className="text-red-500">Error: {errorMessage}</p>}
114
+
115
+ {isMicrophoneAvailable && recordingState === "permissionNeeded" && (
116
+ <button
117
+ onClick={actions.requestPermission}
118
+ className="bg-yellow-500 p-2 rounded"
119
+ >
120
+ <CheckCircle size={16} className="inline mr-1" /> Grant Permission
121
+ </button>
122
+ )}
123
+
124
+ {recordingState === "permissionDenied" && (
125
+ <div>
126
+ <p className="text-red-500">
127
+ Permission Denied. Please enable in browser settings.
128
+ </p>
129
+ <button
130
+ onClick={actions.reset}
131
+ className="bg-gray-500 p-2 rounded mt-1"
132
+ >
133
+ <RefreshCw size={16} className="inline mr-1" /> Reset
134
+ </button>
135
+ </div>
136
+ )}
137
+
138
+ {["ready", "recording", "paused", "finished"].includes(
139
+ recordingState
140
+ ) && (
141
+ <div className="controls mt-2 space-x-2">
142
+ {recordingState !== "recording" && recordingState !== "paused" && (
143
+ <button
144
+ onClick={actions.start}
145
+ disabled={recordingState === "permissionDenied"}
146
+ className="bg-blue-500 p-2 rounded"
147
+ >
148
+ <Mic size={16} className="inline mr-1" /> Start
149
+ </button>
150
+ )}
151
+ {recordingState === "recording" && (
152
+ <button
153
+ onClick={actions.pause}
154
+ className="bg-yellow-500 p-2 rounded"
155
+ >
156
+ <Pause size={16} className="inline mr-1" /> Pause
157
+ </button>
158
+ )}
159
+ {recordingState === "paused" && (
160
+ <button
161
+ onClick={actions.resume}
162
+ className="bg-green-500 p-2 rounded"
163
+ >
164
+ <Play size={16} className="inline mr-1" /> Resume
165
+ </button>
166
+ )}
167
+ {(recordingState === "recording" || recordingState === "paused") && (
168
+ <button onClick={actions.stop} className="bg-red-500 p-2 rounded">
169
+ <StopCircle size={16} className="inline mr-1" /> Stop
170
+ </button>
171
+ )}
172
+ </div>
173
+ )}
174
+
175
+ {(recordingState === "ready" || recordingState === "finished") &&
176
+ recordingState !== "recording" &&
177
+ recordingState !== "paused" && (
178
+ <button
179
+ onClick={actions.reset}
180
+ className="bg-gray-500 p-2 rounded mt-2"
181
+ >
182
+ <RefreshCw size={16} className="inline mr-1" /> Reset Recorder
183
+ </button>
184
+ )}
185
+
186
+ {audioURL && recordingState === "finished" && (
187
+ <div className="playback mt-4">
188
+ <h4>Playback:</h4>
189
+ <audio controls src={audioURL}></audio>
190
+ <a
191
+ href={audioURL}
192
+ download={`recording-${new Date().toISOString().slice(0, 10)}.${
193
+ finalWavBlob ? "wav" : "webm"
194
+ }`}
195
+ className="block mt-2 text-blue-500 hover:underline"
196
+ >
197
+ <Download size={16} className="inline mr-1" /> Download{" "}
198
+ {finalWavBlob ? "WAV" : "Raw Audio"}
199
+ </a>
200
+ </div>
201
+ )}
202
+ </div>
203
+ );
204
+ }
205
+
206
+ export default MyAudioRecorderComponent;
207
+ ```
208
+
209
+ ## API Reference
210
+
211
+ ### Hook Props (`props`)
212
+
213
+ The `useAudioRecorder` hook accepts an optional `props` object with the following properties:
214
+
215
+ - **`onStateChange`**: `(payload: { newState: string, oldState: string }) => void`
216
+ - Callback function invoked when the `recordingState` changes.
217
+ - `payload.newState`: The new state.
218
+ - `payload.oldState`: The previous state.
219
+ - **`onTimerUpdate`**: `(currentTime: number) => void`
220
+ - Callback function invoked every second while recording, providing the current recording duration in seconds.
221
+ - **`onRecordingComplete`**: `(payload: { wavBlob: Blob | null, audioChunks: Blob[], duration: number }) => void`
222
+ - Callback function invoked when a recording is stopped and processed.
223
+ - `payload.wavBlob`: The final recording as a WAV `Blob`. Can be `null` if WAV conversion fails.
224
+ - `payload.audioChunks`: An array of raw `Blob` chunks as recorded by `MediaRecorder`.
225
+ - `payload.duration`: The total duration of the recording in seconds.
226
+ - **`onError`**: `(error: { message: string, details?: any }) => void`
227
+ - Callback function invoked when an error occurs within the hook.
228
+ - `error.message`: A string describing the error.
229
+ - `error.details`: Optional additional details about the error.
230
+ - **`onStreamReady`**: `(payload: { stream: MediaStream, audioContext: AudioContext }) => void`
231
+ - Callback function invoked when the microphone `MediaStream` and `AudioContext` are successfully obtained and ready, just before `MediaRecorder` starts. This is useful for live processing or visualization.
232
+ - **`config`**: `object`
233
+ - An optional configuration object for `MediaRecorder`.
234
+ - `config.mimeType`: `string` (e.g., `'audio/webm;codecs=opus'`, `'audio/ogg;codecs=opus'`). Defaults to `'audio/webm'`.
235
+ - `config.audioBitsPerSecond`: `number` (e.g., `128000`). Specifies the audio bitrate.
236
+
237
+ ### Return Value
238
+
239
+ The `useAudioRecorder` hook returns an object with the following properties:
240
+
241
+ - **`recordingState`**: `string`
242
+ - The current state of the recorder. Possible values:
243
+ - `'idle'`: Initial state or after a full reset before permission checks.
244
+ - `'permissionNeeded'`: Microphone permission has not been granted yet and is required.
245
+ - `'requestingPermission'`: Actively requesting microphone permission.
246
+ - `'permissionDenied'`: Microphone permission was denied by the user or browser.
247
+ - `'ready'`: Permission granted, ready to start recording.
248
+ - `'recording'`: Actively recording audio.
249
+ - `'paused'`: Recording is paused.
250
+ - `'stopping'`: Recording is in the process of stopping.
251
+ - `'processing'`: Recorded audio is being processed (e.g., converting to WAV).
252
+ - `'finished'`: Recording and processing are complete. The audio is available.
253
+ - **`duration`**: `number`
254
+ - The current duration of the recording in seconds. Resets when a new recording starts.
255
+ - **`audioChunks`**: `Blob[]`
256
+ - An array of raw `Blob` objects representing the chunks of the last completed recording.
257
+ - **`finalWavBlob`**: `Blob | null`
258
+ - The `Blob` object for the final WAV audio of the last completed recording. `null` if WAV conversion failed or no recording is complete.
259
+ - **`audioURL`**: `string`
260
+ - An object URL (`blob:...`) created from `finalWavBlob` (or the raw `recordedBlob` if WAV conversion fails). This URL can be used as the `src` for an `<audio>` element for playback.
261
+ - **Important:** Remember to call `URL.revokeObjectURL(audioURL)` when the component unmounts or the `audioURL` is no longer needed to free up memory.
262
+ - **`errorMessage`**: `string | null`
263
+ - A message describing the last error that occurred, or `null` if no error.
264
+ - **`isMicrophoneAvailable`**: `boolean`
265
+ - Indicates whether the `navigator.mediaDevices.getUserMedia` API is available in the browser.
266
+ - **`actions`**: `object`
267
+ - An object containing functions to control the recorder:
268
+ - **`start()`**: `async () => Promise<void>`
269
+ - Requests permission if needed, then starts the recording.
270
+ - Triggers `onStreamReady` callback if provided.
271
+ - **`pause()`**: `() => void`
272
+ - Pauses the current recording if it is active.
273
+ - **`resume()`**: `() => void`
274
+ - Resumes a paused recording.
275
+ - **`stop()`**: `async () => Promise<void>`
276
+ - Stops the current recording and initiates processing (e.g., WAV conversion).
277
+ - Triggers `onRecordingComplete` when done.
278
+ - **`requestPermission()`**: `async () => Promise<boolean>`
279
+ - Manually triggers the microphone permission request flow. Returns `true` if permission is granted, `false` otherwise.
280
+ - **`reset()`**: `() => void`
281
+ - Stops any active recording, clears all recorded data and states, releases the microphone stream, and re-evaluates permission status. Sets state to `'permissionNeeded'` or `'permissionDenied'` based on current browser permissions.
282
+
283
+ ## WAV Conversion Internals
284
+
285
+ The hook includes utility functions (`audioBufferToWav`, `encodeWAV`, etc.) to convert the audio recorded by `MediaRecorder` (typically in a format like WebM with Opus codec) into a standard WAV (PCM) format. This is done client-side using the `AudioContext` to decode the initial recording and then re-encoding it as WAV.
286
+
287
+ If the `AudioContext` is unavailable or the decoding/encoding process fails, the `finalWavBlob` will be `null`, but the `audioURL` will still be populated with the raw recorded audio (e.g., the WebM file), and `audioChunks` will contain the raw data.
288
+
289
+ ## Browser Compatibility
290
+
291
+ - **MediaRecorder API:** Check browser compatibility for `MediaRecorder`. Most modern browsers support it.
292
+ - **getUserMedia (Microphone):** Widely supported, but always requires user permission.
293
+ - **AudioContext:** Widely supported.
294
+ - **Permissions API (`navigator.permissions`):** Used for a better UX in checking permission status. If not available, the hook will gracefully degrade but might require more explicit user action to grant permission.
295
+
296
+ Ensure your application is served over **HTTPS** (or `localhost` during development), as `getUserMedia` typically requires a secure context.