@ohm_studio/sdk-react-native 0.2.0 → 0.3.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.
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +53 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +147 -1
- package/dist/react/index.js.map +1 -1
- package/dist/recorder.d.ts +121 -25
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +279 -32
- package/dist/recorder.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { OHMCoreClient } from "@ohm_studio/sdk-core";
|
|
2
|
-
import type {
|
|
2
|
+
import type { OHMInit } from "@ohm_studio/sdk-core";
|
|
3
3
|
export * from "@ohm_studio/sdk-core";
|
|
4
|
-
export { ExpoRecorder, isExpoAvAvailable } from "./recorder";
|
|
5
|
-
export type { ExpoRecorderOptions, RNFile } from "./recorder";
|
|
4
|
+
export { ExpoRecorder, RecorderError, isExpoAvAvailable, } from "./recorder";
|
|
5
|
+
export type { ExpoRecorderOptions, RNFile, RecorderState, RecorderErrorCode, } from "./recorder";
|
|
6
6
|
/**
|
|
7
7
|
* OHM Studio SDK for React Native.
|
|
8
8
|
*
|
|
@@ -26,7 +26,7 @@ export type { ExpoRecorderOptions, RNFile } from "./recorder";
|
|
|
26
26
|
* });
|
|
27
27
|
*/
|
|
28
28
|
export declare class OHM extends OHMCoreClient {
|
|
29
|
-
constructor(
|
|
29
|
+
constructor(init: OHMInit);
|
|
30
30
|
protected runMultipart<T>(opts: {
|
|
31
31
|
path: string;
|
|
32
32
|
file: any;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,EAAoB,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEtE,cAAc,sBAAsB,CAAC;AACrC,OAAO,EACL,YAAY,EACZ,aAAa,EACb,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,mBAAmB,EACnB,MAAM,EACN,aAAa,EACb,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAYpB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,GAAI,SAAQ,aAAa;gBACxB,IAAI,EAAE,OAAO;cAiBT,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE;QACpC,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,GAAG,CAAC;QACV,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACjC,GAAG,OAAO,CAAC,CAAC,CAAC;cAQE,kBAAkB,CAChC,IAAI,EAAE,OAAO,EACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,OAAO,CAAC,OAAO,CAAC;CAuBpB;AAED,eAAe,GAAG,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { OHMConfigError, OHMCoreClient } from "@ohm_studio/sdk-core";
|
|
2
2
|
export * from "@ohm_studio/sdk-core";
|
|
3
|
-
export { ExpoRecorder, isExpoAvAvailable } from "./recorder";
|
|
3
|
+
export { ExpoRecorder, RecorderError, isExpoAvAvailable, } from "./recorder";
|
|
4
4
|
/** Detect React Native runtime — used to gate the bundled-key safeguard. */
|
|
5
5
|
function isReactNative() {
|
|
6
6
|
return (
|
|
@@ -32,7 +32,8 @@ function isReactNative() {
|
|
|
32
32
|
* });
|
|
33
33
|
*/
|
|
34
34
|
export class OHM extends OHMCoreClient {
|
|
35
|
-
constructor(
|
|
35
|
+
constructor(init) {
|
|
36
|
+
const opts = typeof init === "string" ? { apiKey: init } : init;
|
|
36
37
|
if (!opts.mock &&
|
|
37
38
|
isReactNative() &&
|
|
38
39
|
opts.apiKey?.startsWith("ohms_live_") &&
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrE,cAAc,sBAAsB,CAAC;AACrC,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrE,cAAc,sBAAsB,CAAC;AACrC,OAAO,EACL,YAAY,EACZ,aAAa,EACb,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAQpB,4EAA4E;AAC5E,SAAS,aAAa;IACpB,OAAO;IACL,+BAA+B;IAC/B,OAAO,SAAS,KAAK,WAAW;QAChC,aAAa;QACb,SAAS,CAAC,OAAO,KAAK,aAAa,CACpC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,GAAI,SAAQ,aAAa;IACpC,YAAY,IAAa;QACvB,MAAM,IAAI,GACR,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACrD,IACE,CAAC,IAAI,CAAC,IAAI;YACV,aAAa,EAAE;YACf,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,YAAY,CAAC;YACrC,CAAC,IAAI,CAAC,qBAAqB,EAC3B,CAAC;YACD,MAAM,IAAI,cAAc,CAAC;gBACvB,OAAO,EACL,kOAAkO;aACrO,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,CAAC;IACd,CAAC;IAES,KAAK,CAAC,YAAY,CAAI,IAI/B;QACC,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,CAAC,kBAAkB,CACvC,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,MAAM,CACZ,CAAwB,CAAC;QAC1B,OAAO,IAAI,CAAC,UAAU,CAAI,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAC7D,CAAC;IAES,KAAK,CAAC,kBAAkB,CAChC,IAAa,EACb,MAA+B;QAE/B,MAAM,EAAE,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC1B,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,IAAK,IAAY,EAAE,CAAC;YAC/D,MAAM,CAAC,GAAG,IAAqD,CAAC;YAChE,8DAA8D;YAC9D,kDAAkD;YAClD,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE;gBAChB,GAAG,EAAE,CAAC,CAAC,GAAG;gBACV,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,WAAW;gBAC3B,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,WAAW;aAC5B,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,YAAY,IAAI,EAAE,CAAC;YAC/D,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,IAAW,EAAE,WAAW,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CACb,qEAAqE,CACtE,CAAC;QACJ,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;YAClD,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;CACF;AAED,eAAe,GAAG,CAAC"}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import type { AudioExtractInput, AudioExtractResult, ExtractInput, ExtractResult, SummarizeInput, SummarizeResult } from "@ohm_studio/sdk-core";
|
|
3
3
|
import { OHM } from "../index";
|
|
4
|
+
import { RecorderError, type ExpoRecorderOptions, type RecorderState, type RNFile } from "../recorder";
|
|
4
5
|
export declare function OhmProvider({ client, children, }: {
|
|
5
6
|
client: OHM;
|
|
6
7
|
children: ReactNode;
|
|
@@ -33,4 +34,56 @@ export declare function useOhmSummarize(): {
|
|
|
33
34
|
error: Error | null;
|
|
34
35
|
isPending: boolean;
|
|
35
36
|
};
|
|
37
|
+
export interface UseRecorderOptions extends Omit<ExpoRecorderOptions, "onStateChange" | "onLevel" | "onError"> {
|
|
38
|
+
/**
|
|
39
|
+
* Pass `Audio` from `expo-av` here. Required.
|
|
40
|
+
*
|
|
41
|
+
* import { Audio } from "expo-av";
|
|
42
|
+
* useRecorder({ audio: Audio, ... });
|
|
43
|
+
*/
|
|
44
|
+
audio: any;
|
|
45
|
+
/**
|
|
46
|
+
* If supplied, on stop the recording is automatically sent to
|
|
47
|
+
* `ohm.audio.extract({ apiSlug, file })` and exposed on `data`/`transcript`.
|
|
48
|
+
* Requires <OhmProvider client={...}> above.
|
|
49
|
+
*/
|
|
50
|
+
apiSlug?: string;
|
|
51
|
+
extractInputs?: Record<string, unknown>;
|
|
52
|
+
extractLanguage?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface UseRecorderReturn<T> {
|
|
55
|
+
state: RecorderState;
|
|
56
|
+
isRecording: boolean;
|
|
57
|
+
isPaused: boolean;
|
|
58
|
+
/** Linear level 0–1, derived from Expo's dB metering. */
|
|
59
|
+
level: number;
|
|
60
|
+
/** Recorded duration in seconds. */
|
|
61
|
+
durationSec: number;
|
|
62
|
+
file: RNFile | null;
|
|
63
|
+
error: RecorderError | null;
|
|
64
|
+
start: () => Promise<void>;
|
|
65
|
+
stop: () => Promise<RNFile | null>;
|
|
66
|
+
pause: () => Promise<void>;
|
|
67
|
+
resume: () => Promise<void>;
|
|
68
|
+
cancel: () => Promise<void>;
|
|
69
|
+
reset: () => void;
|
|
70
|
+
extracting: boolean;
|
|
71
|
+
transcript: string | null;
|
|
72
|
+
data: T | null;
|
|
73
|
+
extractError: Error | null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* `useRecorder` for React Native — same shape as the web hook.
|
|
77
|
+
*
|
|
78
|
+
* import { Audio } from "expo-av";
|
|
79
|
+
* const r = useRecorder({
|
|
80
|
+
* audio: Audio,
|
|
81
|
+
* apiSlug: "opd",
|
|
82
|
+
* silenceAutoStop: { ms: 6000 },
|
|
83
|
+
* });
|
|
84
|
+
* <Pressable onPress={r.isRecording ? r.stop : r.start}>
|
|
85
|
+
* <Text>{r.isRecording ? `Stop ${r.durationSec.toFixed(0)}s` : "Record"}</Text>
|
|
86
|
+
* </Pressable>
|
|
87
|
+
*/
|
|
88
|
+
export declare function useRecorder<T = Record<string, unknown>>(opts: UseRecorderOptions): UseRecorderReturn<T>;
|
|
36
89
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAEL,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,MAAM,EACZ,MAAM,aAAa,CAAC;AAarB,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,EAAE,SAAS,CAAC;CACrB,uFAEA;AAoDD,wBAAgB,aAAa,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE;IAC/D,OAAO,EAAE,MAAM,CAAC;CACjB;;;;;WAxCQ,KAAK,GAAG,IAAI;eACR,OAAO;EA4CnB;AAED,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE;IACpE,OAAO,EAAE,MAAM,CAAC;CACjB;;;;;WAjDQ,KAAK,GAAG,IAAI;eACR,OAAO;EAqDnB;AAED,wBAAgB,eAAe;;;;;WAxDtB,KAAK,GAAG,IAAI;eACR,OAAO;EA2DnB;AAID,MAAM,WAAW,kBACf,SAAQ,IAAI,CAAC,mBAAmB,EAAE,eAAe,GAAG,SAAS,GAAG,SAAS,CAAC;IAC1E;;;;;OAKG;IACH,KAAK,EAAE,GAAG,CAAC;IAEX;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,KAAK,EAAE,aAAa,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACnC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,KAAK,EAAE,MAAM,IAAI,CAAC;IAElB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,YAAY,EAAE,KAAK,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrD,IAAI,EAAE,kBAAkB,GACvB,iBAAiB,CAAC,CAAC,CAAC,CA2ItB"}
|
package/dist/react/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { createContext, createElement, useCallback, useContext, useState, } from "react";
|
|
1
|
+
import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
2
|
+
import { ExpoRecorder, } from "../recorder";
|
|
2
3
|
/**
|
|
3
4
|
* React hooks for @ohm_studio/sdk. Mirrors `@ohm_studio/sdk-react-native/react` so a
|
|
4
5
|
* snippet copies cleanly between web and mobile codebases.
|
|
@@ -55,4 +56,149 @@ export function useOhmAudioExtract(opts) {
|
|
|
55
56
|
export function useOhmSummarize() {
|
|
56
57
|
return useOhmMutation((c, input) => c.summarize(input));
|
|
57
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* `useRecorder` for React Native — same shape as the web hook.
|
|
61
|
+
*
|
|
62
|
+
* import { Audio } from "expo-av";
|
|
63
|
+
* const r = useRecorder({
|
|
64
|
+
* audio: Audio,
|
|
65
|
+
* apiSlug: "opd",
|
|
66
|
+
* silenceAutoStop: { ms: 6000 },
|
|
67
|
+
* });
|
|
68
|
+
* <Pressable onPress={r.isRecording ? r.stop : r.start}>
|
|
69
|
+
* <Text>{r.isRecording ? `Stop ${r.durationSec.toFixed(0)}s` : "Record"}</Text>
|
|
70
|
+
* </Pressable>
|
|
71
|
+
*/
|
|
72
|
+
export function useRecorder(opts) {
|
|
73
|
+
const client = useContext(OhmContext);
|
|
74
|
+
const recRef = useRef(null);
|
|
75
|
+
const [state, setState] = useState("idle");
|
|
76
|
+
const [level, setLevel] = useState(0);
|
|
77
|
+
const [durationSec, setDurationSec] = useState(0);
|
|
78
|
+
const [file, setFile] = useState(null);
|
|
79
|
+
const [error, setError] = useState(null);
|
|
80
|
+
const [extracting, setExtracting] = useState(false);
|
|
81
|
+
const [transcript, setTranscript] = useState(null);
|
|
82
|
+
const [data, setData] = useState(null);
|
|
83
|
+
const [extractError, setExtractError] = useState(null);
|
|
84
|
+
const optsRef = useRef(opts);
|
|
85
|
+
optsRef.current = opts;
|
|
86
|
+
const ensureRecorder = useCallback(() => {
|
|
87
|
+
if (recRef.current)
|
|
88
|
+
return recRef.current;
|
|
89
|
+
const { audio, apiSlug, extractInputs, extractLanguage, ...recOpts } = optsRef.current;
|
|
90
|
+
const r = new ExpoRecorder(audio, {
|
|
91
|
+
...recOpts,
|
|
92
|
+
onStateChange: (s) => setState(s),
|
|
93
|
+
onLevel: (l) => setLevel(l),
|
|
94
|
+
onStatus: (s) => setDurationSec(s.durationMillis / 1000),
|
|
95
|
+
onError: (e) => setError(e),
|
|
96
|
+
});
|
|
97
|
+
recRef.current = r;
|
|
98
|
+
return r;
|
|
99
|
+
}, []);
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
return () => {
|
|
102
|
+
void recRef.current?.cancel().catch(() => {
|
|
103
|
+
/* ignore */
|
|
104
|
+
});
|
|
105
|
+
recRef.current = null;
|
|
106
|
+
};
|
|
107
|
+
}, []);
|
|
108
|
+
const start = useCallback(async () => {
|
|
109
|
+
setError(null);
|
|
110
|
+
setFile(null);
|
|
111
|
+
setTranscript(null);
|
|
112
|
+
setData(null);
|
|
113
|
+
setExtractError(null);
|
|
114
|
+
setDurationSec(0);
|
|
115
|
+
const r = ensureRecorder();
|
|
116
|
+
await r.start();
|
|
117
|
+
}, [ensureRecorder]);
|
|
118
|
+
const stop = useCallback(async () => {
|
|
119
|
+
const r = recRef.current;
|
|
120
|
+
if (!r)
|
|
121
|
+
return null;
|
|
122
|
+
const f = await r.stop();
|
|
123
|
+
setFile(f);
|
|
124
|
+
if (optsRef.current.apiSlug && client) {
|
|
125
|
+
setExtracting(true);
|
|
126
|
+
try {
|
|
127
|
+
const res = await client.audio.extract({
|
|
128
|
+
apiSlug: optsRef.current.apiSlug,
|
|
129
|
+
file: f,
|
|
130
|
+
inputs: optsRef.current.extractInputs,
|
|
131
|
+
language: optsRef.current.extractLanguage,
|
|
132
|
+
});
|
|
133
|
+
setTranscript(res.transcript ?? null);
|
|
134
|
+
setData(res.data);
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
setExtractError(e);
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
setExtracting(false);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return f;
|
|
144
|
+
}, [client]);
|
|
145
|
+
const pause = useCallback(async () => {
|
|
146
|
+
await recRef.current?.pause();
|
|
147
|
+
}, []);
|
|
148
|
+
const resume = useCallback(async () => {
|
|
149
|
+
await recRef.current?.resume();
|
|
150
|
+
}, []);
|
|
151
|
+
const cancel = useCallback(async () => {
|
|
152
|
+
await recRef.current?.cancel();
|
|
153
|
+
}, []);
|
|
154
|
+
const reset = useCallback(() => {
|
|
155
|
+
void recRef.current?.cancel().catch(() => {
|
|
156
|
+
/* ignore */
|
|
157
|
+
});
|
|
158
|
+
recRef.current = null;
|
|
159
|
+
setState("idle");
|
|
160
|
+
setLevel(0);
|
|
161
|
+
setDurationSec(0);
|
|
162
|
+
setFile(null);
|
|
163
|
+
setError(null);
|
|
164
|
+
setTranscript(null);
|
|
165
|
+
setData(null);
|
|
166
|
+
setExtractError(null);
|
|
167
|
+
}, []);
|
|
168
|
+
return useMemo(() => ({
|
|
169
|
+
state,
|
|
170
|
+
isRecording: state === "recording",
|
|
171
|
+
isPaused: state === "paused",
|
|
172
|
+
level,
|
|
173
|
+
durationSec,
|
|
174
|
+
file,
|
|
175
|
+
error,
|
|
176
|
+
start,
|
|
177
|
+
stop,
|
|
178
|
+
pause,
|
|
179
|
+
resume,
|
|
180
|
+
cancel,
|
|
181
|
+
reset,
|
|
182
|
+
extracting,
|
|
183
|
+
transcript,
|
|
184
|
+
data,
|
|
185
|
+
extractError,
|
|
186
|
+
}), [
|
|
187
|
+
state,
|
|
188
|
+
level,
|
|
189
|
+
durationSec,
|
|
190
|
+
file,
|
|
191
|
+
error,
|
|
192
|
+
start,
|
|
193
|
+
stop,
|
|
194
|
+
pause,
|
|
195
|
+
resume,
|
|
196
|
+
cancel,
|
|
197
|
+
reset,
|
|
198
|
+
extracting,
|
|
199
|
+
transcript,
|
|
200
|
+
data,
|
|
201
|
+
extractError,
|
|
202
|
+
]);
|
|
203
|
+
}
|
|
58
204
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,aAAa,EACb,WAAW,EACX,UAAU,EACV,QAAQ,GAET,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"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"}
|
package/dist/recorder.d.ts
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
2
|
+
* React Native audio recorder for clinical consults — Expo flavour.
|
|
3
|
+
*
|
|
4
|
+
* Wraps `expo-av`'s `Audio.Recording` with clinically-tuned defaults
|
|
5
|
+
* (16 kHz mono AAC), iOS audio-session setup so recording works in
|
|
6
|
+
* silent mode, dB → linear level metering, pause/resume, duration
|
|
7
|
+
* tracking, silence auto-stop, max-duration cap, and typed errors.
|
|
8
8
|
*
|
|
9
|
-
* @example
|
|
10
9
|
* import { Audio } from "expo-av";
|
|
11
10
|
* import { ExpoRecorder } from "@ohm_studio/sdk-react-native";
|
|
12
11
|
*
|
|
13
|
-
* const rec = new ExpoRecorder(Audio
|
|
12
|
+
* const rec = new ExpoRecorder(Audio, {
|
|
13
|
+
* onLevel: setVu,
|
|
14
|
+
* silenceAutoStop: { ms: 6000 },
|
|
15
|
+
* maxDurationMs: 15 * 60_000,
|
|
16
|
+
* });
|
|
14
17
|
* await rec.start();
|
|
15
|
-
* const file = await rec.stop(); // { uri, name, type }
|
|
18
|
+
* const file = await rec.stop(); // → { uri, name, type }
|
|
19
|
+
* const { transcript, data } = await ohm.audio.extract({ apiSlug, file });
|
|
16
20
|
*
|
|
17
|
-
*
|
|
21
|
+
* We don't take a hard dependency on `expo-av` so apps using bare RN
|
|
22
|
+
* with `react-native-audio-recorder-player` aren't forced to install
|
|
23
|
+
* Expo. The Audio module is passed in as a parameter at runtime.
|
|
18
24
|
*/
|
|
19
25
|
/** The shape `ohm.audio.extract` expects on React Native. */
|
|
20
26
|
export interface RNFile {
|
|
@@ -22,33 +28,111 @@ export interface RNFile {
|
|
|
22
28
|
name: string;
|
|
23
29
|
type: string;
|
|
24
30
|
}
|
|
25
|
-
|
|
31
|
+
export type RecorderState = "idle" | "starting" | "recording" | "paused" | "stopping";
|
|
32
|
+
export type RecorderErrorCode = "NotSupported" | "PermissionDenied" | "NoMicrophone" | "InvalidState" | "Interrupted" | "Unknown";
|
|
33
|
+
export declare class RecorderError extends Error {
|
|
34
|
+
code: RecorderErrorCode;
|
|
35
|
+
cause?: unknown;
|
|
36
|
+
constructor(code: RecorderErrorCode, message: string, cause?: unknown);
|
|
37
|
+
}
|
|
38
|
+
/** Minimal subset of `expo-av`'s Audio module we depend on. */
|
|
26
39
|
interface ExpoAudioModule {
|
|
27
40
|
requestPermissionsAsync: () => Promise<{
|
|
28
41
|
status: string;
|
|
29
42
|
granted?: boolean;
|
|
30
43
|
}>;
|
|
44
|
+
getPermissionsAsync?: () => Promise<{
|
|
45
|
+
status: string;
|
|
46
|
+
granted?: boolean;
|
|
47
|
+
}>;
|
|
48
|
+
setAudioModeAsync?: (mode: any) => Promise<void>;
|
|
31
49
|
Recording: {
|
|
32
|
-
createAsync: (options: any) => Promise<{
|
|
50
|
+
createAsync: (options: any, onStatusUpdate?: ((status: any) => void) | null, progressUpdateIntervalMillis?: number) => Promise<{
|
|
33
51
|
recording: ExpoAudioRecording;
|
|
52
|
+
status: any;
|
|
34
53
|
}>;
|
|
35
54
|
};
|
|
36
|
-
RecordingOptionsPresets
|
|
37
|
-
HIGH_QUALITY
|
|
55
|
+
RecordingOptionsPresets?: {
|
|
56
|
+
HIGH_QUALITY?: any;
|
|
38
57
|
LOW_QUALITY?: any;
|
|
39
58
|
};
|
|
59
|
+
IOSOutputFormat?: {
|
|
60
|
+
LINEARPCM?: number;
|
|
61
|
+
MPEG4AAC?: number;
|
|
62
|
+
};
|
|
63
|
+
AndroidOutputFormat?: {
|
|
64
|
+
MPEG_4?: number;
|
|
65
|
+
};
|
|
66
|
+
AndroidAudioEncoder?: {
|
|
67
|
+
AAC?: number;
|
|
68
|
+
};
|
|
69
|
+
IOSAudioQuality?: {
|
|
70
|
+
HIGH?: number;
|
|
71
|
+
MEDIUM?: number;
|
|
72
|
+
};
|
|
73
|
+
InterruptionModeIOS?: {
|
|
74
|
+
DoNotMix?: number;
|
|
75
|
+
MixWithOthers?: number;
|
|
76
|
+
};
|
|
40
77
|
}
|
|
41
78
|
interface ExpoAudioRecording {
|
|
79
|
+
startAsync?: () => Promise<unknown>;
|
|
80
|
+
pauseAsync?: () => Promise<unknown>;
|
|
42
81
|
stopAndUnloadAsync: () => Promise<unknown>;
|
|
43
82
|
getURI: () => string | null;
|
|
83
|
+
setProgressUpdateInterval?: (ms: number) => void;
|
|
84
|
+
setOnRecordingStatusUpdate?: (cb: (s: any) => void) => void;
|
|
44
85
|
}
|
|
45
86
|
export interface ExpoRecorderOptions {
|
|
46
|
-
/**
|
|
47
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Override the recording options entirely. If omitted, the SDK uses a
|
|
89
|
+
* 16 kHz mono AAC clinical preset. Pass an Expo `RecordingOptionsPresets`
|
|
90
|
+
* value or a fully custom RecordingOptions object.
|
|
91
|
+
*/
|
|
92
|
+
recordingOptions?: any;
|
|
48
93
|
/** Mime type tag attached to the file shape — default `audio/mp4`. */
|
|
49
94
|
mimeType?: string;
|
|
50
95
|
/** Filename tag attached to the file shape — default `rec.m4a`. */
|
|
51
96
|
fileName?: string;
|
|
97
|
+
/**
|
|
98
|
+
* Set up the iOS audio session before recording so the mic still works
|
|
99
|
+
* when the device is in silent mode. Default true.
|
|
100
|
+
*/
|
|
101
|
+
configureIosAudioSession?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Hard cap — auto-stop after this many ms of recording. Default off.
|
|
104
|
+
*/
|
|
105
|
+
maxDurationMs?: number;
|
|
106
|
+
/**
|
|
107
|
+
* Auto-stop after sustained silence. dB below `thresholdDb` (default
|
|
108
|
+
* -40 dBFS) for `ms` continuous milliseconds triggers stop. Default off.
|
|
109
|
+
*/
|
|
110
|
+
silenceAutoStop?: {
|
|
111
|
+
ms?: number;
|
|
112
|
+
thresholdDb?: number;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Optional keep-awake hooks. Wire to `expo-keep-awake`'s
|
|
116
|
+
* `activateKeepAwake()` / `deactivateKeepAwake()` to prevent the device
|
|
117
|
+
* from sleeping during long consults. Optional — not bundled.
|
|
118
|
+
*/
|
|
119
|
+
keepAwake?: {
|
|
120
|
+
activate: () => void | Promise<void>;
|
|
121
|
+
deactivate: () => void | Promise<void>;
|
|
122
|
+
};
|
|
123
|
+
/** Recording state changes. */
|
|
124
|
+
onStateChange?: (state: RecorderState) => void;
|
|
125
|
+
/** Periodic linear level 0–1. Wire to a VU meter. */
|
|
126
|
+
onLevel?: (linearLevel: number) => void;
|
|
127
|
+
/** Progress updates from Expo (durationMillis, metering, isDoneRecording…). */
|
|
128
|
+
onStatus?: (status: {
|
|
129
|
+
durationMillis: number;
|
|
130
|
+
metering?: number;
|
|
131
|
+
isRecording: boolean;
|
|
132
|
+
isDoneRecording: boolean;
|
|
133
|
+
}) => void;
|
|
134
|
+
/** All recorder errors flow through here in addition to the awaited rejection. */
|
|
135
|
+
onError?: (err: RecorderError) => void;
|
|
52
136
|
}
|
|
53
137
|
/** True if `expo-av` is installed in the host RN app. */
|
|
54
138
|
export declare function isExpoAvAvailable(): boolean;
|
|
@@ -56,23 +140,35 @@ export declare class ExpoRecorder {
|
|
|
56
140
|
private audio;
|
|
57
141
|
private recording;
|
|
58
142
|
private opts;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
143
|
+
private state;
|
|
144
|
+
private durationMs;
|
|
145
|
+
private maxStopTimer;
|
|
146
|
+
private silenceSinceMs;
|
|
147
|
+
private keepAwakeActive;
|
|
64
148
|
constructor(expoAudio: ExpoAudioModule, opts?: ExpoRecorderOptions);
|
|
65
|
-
/**
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
149
|
+
/** Probe permission without prompting. */
|
|
150
|
+
probePermission(): Promise<"granted" | "denied" | "undetermined">;
|
|
151
|
+
/** Current state. */
|
|
152
|
+
get currentState(): RecorderState;
|
|
153
|
+
/** Recorded duration in ms (live; zero when idle). */
|
|
154
|
+
getDuration(): number;
|
|
155
|
+
/** Request mic permission and start recording. */
|
|
69
156
|
start(): Promise<void>;
|
|
157
|
+
/** Pause recording (resumable via `resume()`). */
|
|
158
|
+
pause(): Promise<void>;
|
|
159
|
+
/** Resume after `pause()`. */
|
|
160
|
+
resume(): Promise<void>;
|
|
70
161
|
/**
|
|
71
162
|
* Stop recording and return the file shape `ohm.audio.extract` expects.
|
|
72
163
|
*/
|
|
73
164
|
stop(): Promise<RNFile>;
|
|
165
|
+
/** Convenience — stop after `ms` from now. */
|
|
166
|
+
stopAfter(ms: number): Promise<RNFile>;
|
|
74
167
|
/** Discard the in-progress recording without producing a file. */
|
|
75
168
|
cancel(): Promise<void>;
|
|
169
|
+
private handleStatusUpdate;
|
|
170
|
+
private cleanup;
|
|
171
|
+
private setState;
|
|
76
172
|
}
|
|
77
173
|
export {};
|
|
78
174
|
//# sourceMappingURL=recorder.d.ts.map
|
package/dist/recorder.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"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"}
|
package/dist/recorder.js
CHANGED
|
@@ -1,21 +1,91 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
2
|
+
* React Native audio recorder for clinical consults — Expo flavour.
|
|
3
|
+
*
|
|
4
|
+
* Wraps `expo-av`'s `Audio.Recording` with clinically-tuned defaults
|
|
5
|
+
* (16 kHz mono AAC), iOS audio-session setup so recording works in
|
|
6
|
+
* silent mode, dB → linear level metering, pause/resume, duration
|
|
7
|
+
* tracking, silence auto-stop, max-duration cap, and typed errors.
|
|
8
8
|
*
|
|
9
|
-
* @example
|
|
10
9
|
* import { Audio } from "expo-av";
|
|
11
10
|
* import { ExpoRecorder } from "@ohm_studio/sdk-react-native";
|
|
12
11
|
*
|
|
13
|
-
* const rec = new ExpoRecorder(Audio
|
|
12
|
+
* const rec = new ExpoRecorder(Audio, {
|
|
13
|
+
* onLevel: setVu,
|
|
14
|
+
* silenceAutoStop: { ms: 6000 },
|
|
15
|
+
* maxDurationMs: 15 * 60_000,
|
|
16
|
+
* });
|
|
14
17
|
* await rec.start();
|
|
15
|
-
* const file = await rec.stop(); // { uri, name, type }
|
|
18
|
+
* const file = await rec.stop(); // → { uri, name, type }
|
|
19
|
+
* const { transcript, data } = await ohm.audio.extract({ apiSlug, file });
|
|
20
|
+
*
|
|
21
|
+
* We don't take a hard dependency on `expo-av` so apps using bare RN
|
|
22
|
+
* with `react-native-audio-recorder-player` aren't forced to install
|
|
23
|
+
* Expo. The Audio module is passed in as a parameter at runtime.
|
|
24
|
+
*/
|
|
25
|
+
export class RecorderError extends Error {
|
|
26
|
+
code;
|
|
27
|
+
cause;
|
|
28
|
+
constructor(code, message, cause) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = "RecorderError";
|
|
31
|
+
this.code = code;
|
|
32
|
+
this.cause = cause;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Clinical-tuned RecordingOptions: 16 kHz mono AAC, 32 kbps. Plenty for
|
|
37
|
+
* speech; keeps file size small for upload over flaky hospital wifi.
|
|
16
38
|
*
|
|
17
|
-
*
|
|
39
|
+
* We build this dynamically so we tolerate constant renames across Expo SDKs.
|
|
40
|
+
*/
|
|
41
|
+
function clinicalRecordingOptions(audio) {
|
|
42
|
+
// Expo SDK 50+ uses string outputs ("aac ") on iOS; older SDKs used
|
|
43
|
+
// numeric enums via Audio.IOSOutputFormat. We pass both shapes so
|
|
44
|
+
// whichever the runtime understands wins.
|
|
45
|
+
const iosOutputFormat = audio.IOSOutputFormat?.MPEG4AAC ?? "aac ";
|
|
46
|
+
const iosAudioQuality = audio.IOSAudioQuality?.MEDIUM ?? 0x40;
|
|
47
|
+
const androidOutputFormat = audio.AndroidOutputFormat?.MPEG_4 ?? 2;
|
|
48
|
+
const androidAudioEncoder = audio.AndroidAudioEncoder?.AAC ?? 3;
|
|
49
|
+
return {
|
|
50
|
+
isMeteringEnabled: true,
|
|
51
|
+
android: {
|
|
52
|
+
extension: ".m4a",
|
|
53
|
+
outputFormat: androidOutputFormat,
|
|
54
|
+
audioEncoder: androidAudioEncoder,
|
|
55
|
+
sampleRate: 16_000,
|
|
56
|
+
numberOfChannels: 1,
|
|
57
|
+
bitRate: 32_000,
|
|
58
|
+
},
|
|
59
|
+
ios: {
|
|
60
|
+
extension: ".m4a",
|
|
61
|
+
outputFormat: iosOutputFormat,
|
|
62
|
+
audioQuality: iosAudioQuality,
|
|
63
|
+
sampleRate: 16_000,
|
|
64
|
+
numberOfChannels: 1,
|
|
65
|
+
bitRate: 32_000,
|
|
66
|
+
linearPCMBitDepth: 16,
|
|
67
|
+
linearPCMIsBigEndian: false,
|
|
68
|
+
linearPCMIsFloat: false,
|
|
69
|
+
},
|
|
70
|
+
web: {
|
|
71
|
+
mimeType: "audio/webm;codecs=opus",
|
|
72
|
+
bitsPerSecond: 32_000,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Convert Expo's `metering` (dBFS, typically -160..0) to a linear 0..1
|
|
78
|
+
* level useful for a VU meter. Mic floor is mapped to 0, peak to 1.
|
|
18
79
|
*/
|
|
80
|
+
function dbToLinear(db, floorDb = -50) {
|
|
81
|
+
if (!Number.isFinite(db))
|
|
82
|
+
return 0;
|
|
83
|
+
if (db <= floorDb)
|
|
84
|
+
return 0;
|
|
85
|
+
if (db >= 0)
|
|
86
|
+
return 1;
|
|
87
|
+
return (db - floorDb) / -floorDb;
|
|
88
|
+
}
|
|
19
89
|
/** True if `expo-av` is installed in the host RN app. */
|
|
20
90
|
export function isExpoAvAvailable() {
|
|
21
91
|
try {
|
|
@@ -31,46 +101,159 @@ export class ExpoRecorder {
|
|
|
31
101
|
audio;
|
|
32
102
|
recording = null;
|
|
33
103
|
opts;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
104
|
+
state = "idle";
|
|
105
|
+
durationMs = 0;
|
|
106
|
+
maxStopTimer = null;
|
|
107
|
+
silenceSinceMs = 0;
|
|
108
|
+
keepAwakeActive = false;
|
|
39
109
|
constructor(expoAudio, opts = {}) {
|
|
40
110
|
this.audio = expoAudio;
|
|
41
111
|
this.opts = opts;
|
|
42
112
|
}
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
113
|
+
/** Probe permission without prompting. */
|
|
114
|
+
async probePermission() {
|
|
115
|
+
if (!this.audio.getPermissionsAsync)
|
|
116
|
+
return "undetermined";
|
|
117
|
+
const r = await this.audio.getPermissionsAsync();
|
|
118
|
+
return r?.status || "undetermined";
|
|
119
|
+
}
|
|
120
|
+
/** Current state. */
|
|
121
|
+
get currentState() {
|
|
122
|
+
return this.state;
|
|
123
|
+
}
|
|
124
|
+
/** Recorded duration in ms (live; zero when idle). */
|
|
125
|
+
getDuration() {
|
|
126
|
+
return this.durationMs;
|
|
127
|
+
}
|
|
128
|
+
/** Request mic permission and start recording. */
|
|
47
129
|
async start() {
|
|
48
|
-
|
|
130
|
+
if (this.state !== "idle") {
|
|
131
|
+
throw new RecorderError("InvalidState", `Cannot start while "${this.state}".`);
|
|
132
|
+
}
|
|
133
|
+
this.setState("starting");
|
|
134
|
+
let perm;
|
|
135
|
+
try {
|
|
136
|
+
perm = await this.audio.requestPermissionsAsync();
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
this.setState("idle");
|
|
140
|
+
const err = new RecorderError("Unknown", "Failed to request microphone permission", e);
|
|
141
|
+
this.opts.onError?.(err);
|
|
142
|
+
throw err;
|
|
143
|
+
}
|
|
49
144
|
if (perm.status !== "granted") {
|
|
50
|
-
|
|
145
|
+
this.setState("idle");
|
|
146
|
+
const err = new RecorderError("PermissionDenied", "Microphone permission denied");
|
|
147
|
+
this.opts.onError?.(err);
|
|
148
|
+
throw err;
|
|
51
149
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
150
|
+
if (this.opts.configureIosAudioSession !== false && this.audio.setAudioModeAsync) {
|
|
151
|
+
try {
|
|
152
|
+
await this.audio.setAudioModeAsync({
|
|
153
|
+
allowsRecordingIOS: true,
|
|
154
|
+
playsInSilentModeIOS: true,
|
|
155
|
+
staysActiveInBackground: false,
|
|
156
|
+
interruptionModeIOS: this.audio.InterruptionModeIOS?.DoNotMix ?? 1,
|
|
157
|
+
shouldDuckAndroid: true,
|
|
158
|
+
playThroughEarpieceAndroid: false,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
/* best-effort */
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const recordingOptions = this.opts.recordingOptions ?? clinicalRecordingOptions(this.audio);
|
|
166
|
+
let result;
|
|
167
|
+
try {
|
|
168
|
+
result = await this.audio.Recording.createAsync(recordingOptions, (status) => this.handleStatusUpdate(status), 100);
|
|
169
|
+
}
|
|
170
|
+
catch (e) {
|
|
171
|
+
this.setState("idle");
|
|
172
|
+
const err = new RecorderError("Unknown", "Failed to start recording: " + (e?.message || ""), e);
|
|
173
|
+
this.opts.onError?.(err);
|
|
174
|
+
throw err;
|
|
175
|
+
}
|
|
176
|
+
this.recording = result.recording;
|
|
177
|
+
this.durationMs = 0;
|
|
178
|
+
this.silenceSinceMs = 0;
|
|
179
|
+
if (this.opts.maxDurationMs && this.opts.maxDurationMs > 0) {
|
|
180
|
+
this.maxStopTimer = setTimeout(() => {
|
|
181
|
+
this.stop().catch(() => {
|
|
182
|
+
/* swallow */
|
|
183
|
+
});
|
|
184
|
+
}, this.opts.maxDurationMs);
|
|
185
|
+
}
|
|
186
|
+
if (this.opts.keepAwake) {
|
|
187
|
+
try {
|
|
188
|
+
await this.opts.keepAwake.activate();
|
|
189
|
+
this.keepAwakeActive = true;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
/* best-effort */
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
this.setState("recording");
|
|
196
|
+
}
|
|
197
|
+
/** Pause recording (resumable via `resume()`). */
|
|
198
|
+
async pause() {
|
|
199
|
+
if (this.state !== "recording" || !this.recording) {
|
|
200
|
+
throw new RecorderError("InvalidState", `Cannot pause while "${this.state}".`);
|
|
201
|
+
}
|
|
202
|
+
if (typeof this.recording.pauseAsync !== "function") {
|
|
203
|
+
throw new RecorderError("NotSupported", "This Expo SDK doesn't support pause().");
|
|
204
|
+
}
|
|
205
|
+
await this.recording.pauseAsync();
|
|
206
|
+
this.setState("paused");
|
|
207
|
+
}
|
|
208
|
+
/** Resume after `pause()`. */
|
|
209
|
+
async resume() {
|
|
210
|
+
if (this.state !== "paused" || !this.recording) {
|
|
211
|
+
throw new RecorderError("InvalidState", `Cannot resume while "${this.state}".`);
|
|
212
|
+
}
|
|
213
|
+
if (typeof this.recording.startAsync !== "function") {
|
|
214
|
+
throw new RecorderError("NotSupported", "This Expo SDK doesn't support resume().");
|
|
215
|
+
}
|
|
216
|
+
await this.recording.startAsync();
|
|
217
|
+
this.setState("recording");
|
|
56
218
|
}
|
|
57
219
|
/**
|
|
58
220
|
* Stop recording and return the file shape `ohm.audio.extract` expects.
|
|
59
221
|
*/
|
|
60
222
|
async stop() {
|
|
61
|
-
if (!this.recording)
|
|
62
|
-
throw new
|
|
63
|
-
|
|
223
|
+
if (!this.recording || (this.state !== "recording" && this.state !== "paused")) {
|
|
224
|
+
throw new RecorderError("InvalidState", `Cannot stop while "${this.state}".`);
|
|
225
|
+
}
|
|
226
|
+
this.setState("stopping");
|
|
227
|
+
try {
|
|
228
|
+
await this.recording.stopAndUnloadAsync();
|
|
229
|
+
}
|
|
230
|
+
catch (e) {
|
|
231
|
+
const err = new RecorderError("Unknown", "Failed to stop recording", e);
|
|
232
|
+
this.opts.onError?.(err);
|
|
233
|
+
this.cleanup();
|
|
234
|
+
throw err;
|
|
235
|
+
}
|
|
64
236
|
const uri = this.recording.getURI();
|
|
65
|
-
this.
|
|
66
|
-
if (!uri)
|
|
67
|
-
|
|
237
|
+
this.cleanup();
|
|
238
|
+
if (!uri) {
|
|
239
|
+
const err = new RecorderError("Unknown", "Recording produced no file URI");
|
|
240
|
+
this.opts.onError?.(err);
|
|
241
|
+
throw err;
|
|
242
|
+
}
|
|
68
243
|
return {
|
|
69
244
|
uri,
|
|
70
245
|
name: this.opts.fileName ?? "rec.m4a",
|
|
71
246
|
type: this.opts.mimeType ?? "audio/mp4",
|
|
72
247
|
};
|
|
73
248
|
}
|
|
249
|
+
/** Convenience — stop after `ms` from now. */
|
|
250
|
+
stopAfter(ms) {
|
|
251
|
+
return new Promise((resolve, reject) => {
|
|
252
|
+
setTimeout(() => {
|
|
253
|
+
this.stop().then(resolve, reject);
|
|
254
|
+
}, ms);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
74
257
|
/** Discard the in-progress recording without producing a file. */
|
|
75
258
|
async cancel() {
|
|
76
259
|
if (this.recording) {
|
|
@@ -80,8 +263,72 @@ export class ExpoRecorder {
|
|
|
80
263
|
catch {
|
|
81
264
|
/* ignore */
|
|
82
265
|
}
|
|
83
|
-
this.recording = null;
|
|
84
266
|
}
|
|
267
|
+
this.cleanup();
|
|
268
|
+
}
|
|
269
|
+
// ─── internals ──────────────────────────────────────────────────────
|
|
270
|
+
handleStatusUpdate(status) {
|
|
271
|
+
if (!status)
|
|
272
|
+
return;
|
|
273
|
+
if (typeof status.durationMillis === "number") {
|
|
274
|
+
this.durationMs = status.durationMillis;
|
|
275
|
+
}
|
|
276
|
+
let linear = 0;
|
|
277
|
+
if (typeof status.metering === "number") {
|
|
278
|
+
linear = dbToLinear(status.metering);
|
|
279
|
+
this.opts.onLevel?.(linear);
|
|
280
|
+
}
|
|
281
|
+
this.opts.onStatus?.({
|
|
282
|
+
durationMillis: this.durationMs,
|
|
283
|
+
metering: status.metering,
|
|
284
|
+
isRecording: !!status.isRecording,
|
|
285
|
+
isDoneRecording: !!status.isDoneRecording,
|
|
286
|
+
});
|
|
287
|
+
// Silence auto-stop
|
|
288
|
+
if (this.opts.silenceAutoStop &&
|
|
289
|
+
this.state === "recording" &&
|
|
290
|
+
typeof status.metering === "number") {
|
|
291
|
+
const silenceMs = this.opts.silenceAutoStop.ms ?? 6000;
|
|
292
|
+
const thresholdDb = this.opts.silenceAutoStop.thresholdDb ?? -40;
|
|
293
|
+
if (status.metering < thresholdDb) {
|
|
294
|
+
if (this.silenceSinceMs === 0)
|
|
295
|
+
this.silenceSinceMs = Date.now();
|
|
296
|
+
else if (Date.now() - this.silenceSinceMs >= silenceMs) {
|
|
297
|
+
this.silenceSinceMs = 0;
|
|
298
|
+
this.stop().catch(() => {
|
|
299
|
+
/* swallow */
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
this.silenceSinceMs = 0;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
cleanup() {
|
|
309
|
+
if (this.maxStopTimer) {
|
|
310
|
+
clearTimeout(this.maxStopTimer);
|
|
311
|
+
this.maxStopTimer = null;
|
|
312
|
+
}
|
|
313
|
+
if (this.keepAwakeActive && this.opts.keepAwake) {
|
|
314
|
+
try {
|
|
315
|
+
this.opts.keepAwake.deactivate();
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
/* ignore */
|
|
319
|
+
}
|
|
320
|
+
this.keepAwakeActive = false;
|
|
321
|
+
}
|
|
322
|
+
this.recording = null;
|
|
323
|
+
this.durationMs = 0;
|
|
324
|
+
this.silenceSinceMs = 0;
|
|
325
|
+
this.setState("idle");
|
|
326
|
+
}
|
|
327
|
+
setState(next) {
|
|
328
|
+
if (this.state === next)
|
|
329
|
+
return;
|
|
330
|
+
this.state = next;
|
|
331
|
+
this.opts.onStateChange?.(next);
|
|
85
332
|
}
|
|
86
333
|
}
|
|
87
334
|
//# sourceMappingURL=recorder.js.map
|
package/dist/recorder.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recorder.js","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"recorder.js","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED