@ohm_studio/sdk-react-native 0.2.0 → 0.5.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/bare-recorder.d.ts +96 -0
- package/dist/bare-recorder.d.ts.map +1 -0
- package/dist/bare-recorder.js +242 -0
- package/dist/bare-recorder.js.map +1 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -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 +8 -4
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bare React Native recorder — adapter around `react-native-audio-recorder-player`.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors `ExpoRecorder`'s lifecycle (state machine, typed errors, level
|
|
5
|
+
* metering, max-duration cap) so apps can switch between Expo and bare
|
|
6
|
+
* RN without touching their UI code. Also mirrors the same `RNFile`
|
|
7
|
+
* shape so `ohm.audio.extract({ file })` works identically.
|
|
8
|
+
*
|
|
9
|
+
* import AudioRecorderPlayer from "react-native-audio-recorder-player";
|
|
10
|
+
* import { BareRecorder } from "@ohm_studio/sdk-react-native";
|
|
11
|
+
*
|
|
12
|
+
* const rec = new BareRecorder(AudioRecorderPlayer);
|
|
13
|
+
* await rec.start();
|
|
14
|
+
* const file = await rec.stop();
|
|
15
|
+
* const { data } = await ohm.audio.extract({ apiSlug, file });
|
|
16
|
+
*
|
|
17
|
+
* We don't take a hard dependency on the underlying lib — pass either
|
|
18
|
+
* the class itself OR an existing instance.
|
|
19
|
+
*/
|
|
20
|
+
import { RecorderError, type RecorderState, type RNFile } from "./recorder";
|
|
21
|
+
interface ARPInstance {
|
|
22
|
+
startRecorder: (uri?: string, audioSet?: any, meteringEnabled?: boolean) => Promise<string>;
|
|
23
|
+
stopRecorder: () => Promise<string>;
|
|
24
|
+
pauseRecorder?: () => Promise<unknown>;
|
|
25
|
+
resumeRecorder?: () => Promise<unknown>;
|
|
26
|
+
addRecordBackListener?: (cb: (e: any) => void) => void;
|
|
27
|
+
removeRecordBackListener?: () => void;
|
|
28
|
+
setSubscriptionDuration?: (sec: number) => Promise<unknown>;
|
|
29
|
+
}
|
|
30
|
+
type ARPCtor = new () => ARPInstance;
|
|
31
|
+
export interface BareRecorderOptions {
|
|
32
|
+
/** File URI to record into. If omitted, the lib picks a default. */
|
|
33
|
+
uri?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Native `audioSet` passed to `startRecorder`. If omitted, a clinical
|
|
36
|
+
* preset is built (16 kHz mono AAC at 32 kbps).
|
|
37
|
+
*/
|
|
38
|
+
audioSet?: any;
|
|
39
|
+
/** Mime type tag attached to the file shape — default `audio/mp4`. */
|
|
40
|
+
mimeType?: string;
|
|
41
|
+
/** Filename tag attached to the file shape — default `rec.m4a`. */
|
|
42
|
+
fileName?: string;
|
|
43
|
+
/** Hard cap — auto-stop after this many ms. Default off. */
|
|
44
|
+
maxDurationMs?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Auto-stop after `ms` of sustained metering below `thresholdDb`.
|
|
47
|
+
* dBFS in the same scale as Expo's metering.
|
|
48
|
+
*/
|
|
49
|
+
silenceAutoStop?: {
|
|
50
|
+
ms?: number;
|
|
51
|
+
thresholdDb?: number;
|
|
52
|
+
};
|
|
53
|
+
/** Periodic linear level 0–1, derived from the lib's metering callback. */
|
|
54
|
+
onLevel?: (linearLevel: number) => void;
|
|
55
|
+
/** Recorder state changes. */
|
|
56
|
+
onStateChange?: (state: RecorderState) => void;
|
|
57
|
+
/** All errors flow through here in addition to the awaited rejection. */
|
|
58
|
+
onError?: (err: RecorderError) => void;
|
|
59
|
+
/** Per-tick status from the recorder back-listener. */
|
|
60
|
+
onStatus?: (s: {
|
|
61
|
+
durationMillis: number;
|
|
62
|
+
metering?: number;
|
|
63
|
+
}) => void;
|
|
64
|
+
}
|
|
65
|
+
export declare class BareRecorder {
|
|
66
|
+
private arp;
|
|
67
|
+
private opts;
|
|
68
|
+
private state;
|
|
69
|
+
private uri;
|
|
70
|
+
private startedAt;
|
|
71
|
+
private pausedAccumMs;
|
|
72
|
+
private pauseStartedAt;
|
|
73
|
+
private durationMs;
|
|
74
|
+
private silenceSinceMs;
|
|
75
|
+
private maxStopTimer;
|
|
76
|
+
/**
|
|
77
|
+
* @param libOrInstance Pass `AudioRecorderPlayer` (the class) or an
|
|
78
|
+
* existing `new AudioRecorderPlayer()` instance. We use one
|
|
79
|
+
* instance per recorder.
|
|
80
|
+
*/
|
|
81
|
+
constructor(libOrInstance: ARPCtor | ARPInstance, opts?: BareRecorderOptions);
|
|
82
|
+
get currentState(): RecorderState;
|
|
83
|
+
getDuration(): number;
|
|
84
|
+
start(): Promise<void>;
|
|
85
|
+
pause(): Promise<void>;
|
|
86
|
+
resume(): Promise<void>;
|
|
87
|
+
stop(): Promise<RNFile>;
|
|
88
|
+
cancel(): Promise<void>;
|
|
89
|
+
private onTick;
|
|
90
|
+
private cleanupListeners;
|
|
91
|
+
private cleanup;
|
|
92
|
+
private setState;
|
|
93
|
+
private fail;
|
|
94
|
+
}
|
|
95
|
+
export {};
|
|
96
|
+
//# sourceMappingURL=bare-recorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bare-recorder.d.ts","sourceRoot":"","sources":["../src/bare-recorder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,aAAa,EACb,KAAK,aAAa,EAElB,KAAK,MAAM,EACZ,MAAM,YAAY,CAAC;AAEpB,UAAU,WAAW;IACnB,aAAa,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,eAAe,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5F,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,qBAAqB,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,KAAK,IAAI,CAAC;IACvD,wBAAwB,CAAC,EAAE,MAAM,IAAI,CAAC;IACtC,uBAAuB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7D;AAED,KAAK,OAAO,GAAG,UAAU,WAAW,CAAC;AAErC,MAAM,WAAW,mBAAmB;IAClC,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,QAAQ,CAAC,EAAE,GAAG,CAAC;IAEf,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,eAAe,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAExD,2EAA2E;IAC3E,OAAO,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,8BAA8B;IAC9B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,yEAAyE;IACzE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IACvC,uDAAuD;IACvD,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACvE;AAwBD,qBAAa,YAAY;IACvB,OAAO,CAAC,GAAG,CAAc;IACzB,OAAO,CAAC,IAAI,CAAsB;IAClC,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,GAAG,CAAuB;IAClC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,YAAY,CAA8C;IAElE;;;;OAIG;gBAED,aAAa,EAAE,OAAO,GAAG,WAAW,EACpC,IAAI,GAAE,mBAAwB;IAShC,IAAI,YAAY,IAAI,aAAa,CAEhC;IAED,WAAW,IAAI,MAAM;IAQf,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiDtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAetB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAevB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IA4BvB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAa7B,OAAO,CAAC,MAAM;IA8Bd,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,OAAO;IAef,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,IAAI;CASb"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bare React Native recorder — adapter around `react-native-audio-recorder-player`.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors `ExpoRecorder`'s lifecycle (state machine, typed errors, level
|
|
5
|
+
* metering, max-duration cap) so apps can switch between Expo and bare
|
|
6
|
+
* RN without touching their UI code. Also mirrors the same `RNFile`
|
|
7
|
+
* shape so `ohm.audio.extract({ file })` works identically.
|
|
8
|
+
*
|
|
9
|
+
* import AudioRecorderPlayer from "react-native-audio-recorder-player";
|
|
10
|
+
* import { BareRecorder } from "@ohm_studio/sdk-react-native";
|
|
11
|
+
*
|
|
12
|
+
* const rec = new BareRecorder(AudioRecorderPlayer);
|
|
13
|
+
* await rec.start();
|
|
14
|
+
* const file = await rec.stop();
|
|
15
|
+
* const { data } = await ohm.audio.extract({ apiSlug, file });
|
|
16
|
+
*
|
|
17
|
+
* We don't take a hard dependency on the underlying lib — pass either
|
|
18
|
+
* the class itself OR an existing instance.
|
|
19
|
+
*/
|
|
20
|
+
import { RecorderError, } from "./recorder";
|
|
21
|
+
const CLINICAL_AUDIO_SET = {
|
|
22
|
+
// iOS
|
|
23
|
+
AVSampleRateKeyIOS: 16_000,
|
|
24
|
+
AVNumberOfChannelsKeyIOS: 1,
|
|
25
|
+
AVEncoderAudioQualityKeyIOS: "medium",
|
|
26
|
+
AVFormatIDKeyIOS: "aac",
|
|
27
|
+
// Android
|
|
28
|
+
AudioEncoderAndroid: 3, // AAC
|
|
29
|
+
AudioSourceAndroid: 6, // VOICE_RECOGNITION — boosts speech, attenuates noise
|
|
30
|
+
OutputFormatAndroid: 2, // MPEG_4
|
|
31
|
+
AudioSamplingRateAndroid: 16_000,
|
|
32
|
+
AudioChannelsAndroid: 1,
|
|
33
|
+
AudioEncodingBitRateAndroid: 32_000,
|
|
34
|
+
};
|
|
35
|
+
function dbToLinear(db, floorDb = -50) {
|
|
36
|
+
if (!Number.isFinite(db))
|
|
37
|
+
return 0;
|
|
38
|
+
if (db <= floorDb)
|
|
39
|
+
return 0;
|
|
40
|
+
if (db >= 0)
|
|
41
|
+
return 1;
|
|
42
|
+
return (db - floorDb) / -floorDb;
|
|
43
|
+
}
|
|
44
|
+
export class BareRecorder {
|
|
45
|
+
arp;
|
|
46
|
+
opts;
|
|
47
|
+
state = "idle";
|
|
48
|
+
uri = null;
|
|
49
|
+
startedAt = 0;
|
|
50
|
+
pausedAccumMs = 0;
|
|
51
|
+
pauseStartedAt = 0;
|
|
52
|
+
durationMs = 0;
|
|
53
|
+
silenceSinceMs = 0;
|
|
54
|
+
maxStopTimer = null;
|
|
55
|
+
/**
|
|
56
|
+
* @param libOrInstance Pass `AudioRecorderPlayer` (the class) or an
|
|
57
|
+
* existing `new AudioRecorderPlayer()` instance. We use one
|
|
58
|
+
* instance per recorder.
|
|
59
|
+
*/
|
|
60
|
+
constructor(libOrInstance, opts = {}) {
|
|
61
|
+
this.arp =
|
|
62
|
+
typeof libOrInstance === "function"
|
|
63
|
+
? new libOrInstance()
|
|
64
|
+
: libOrInstance;
|
|
65
|
+
this.opts = opts;
|
|
66
|
+
}
|
|
67
|
+
get currentState() {
|
|
68
|
+
return this.state;
|
|
69
|
+
}
|
|
70
|
+
getDuration() {
|
|
71
|
+
if (this.state === "idle")
|
|
72
|
+
return 0;
|
|
73
|
+
if (this.state === "paused") {
|
|
74
|
+
return this.pauseStartedAt - this.startedAt - this.pausedAccumMs;
|
|
75
|
+
}
|
|
76
|
+
return Date.now() - this.startedAt - this.pausedAccumMs;
|
|
77
|
+
}
|
|
78
|
+
async start() {
|
|
79
|
+
if (this.state !== "idle") {
|
|
80
|
+
throw this.fail("InvalidState", `Cannot start while "${this.state}".`);
|
|
81
|
+
}
|
|
82
|
+
this.setState("starting");
|
|
83
|
+
if (typeof this.arp.setSubscriptionDuration === "function") {
|
|
84
|
+
try {
|
|
85
|
+
await this.arp.setSubscriptionDuration(0.1); // 100 ms metering ticks
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
/* best-effort */
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (typeof this.arp.addRecordBackListener === "function") {
|
|
92
|
+
this.arp.addRecordBackListener((e) => this.onTick(e));
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
this.uri = await this.arp.startRecorder(this.opts.uri, this.opts.audioSet ?? CLINICAL_AUDIO_SET, true);
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
this.cleanupListeners();
|
|
99
|
+
this.setState("idle");
|
|
100
|
+
throw this.fail("Unknown", "Could not start recording: " + (e?.message || ""), e);
|
|
101
|
+
}
|
|
102
|
+
this.startedAt = Date.now();
|
|
103
|
+
this.pausedAccumMs = 0;
|
|
104
|
+
this.silenceSinceMs = 0;
|
|
105
|
+
this.durationMs = 0;
|
|
106
|
+
this.setState("recording");
|
|
107
|
+
if (this.opts.maxDurationMs && this.opts.maxDurationMs > 0) {
|
|
108
|
+
this.maxStopTimer = setTimeout(() => {
|
|
109
|
+
this.stop().catch(() => {
|
|
110
|
+
/* swallow */
|
|
111
|
+
});
|
|
112
|
+
}, this.opts.maxDurationMs);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async pause() {
|
|
116
|
+
if (this.state !== "recording") {
|
|
117
|
+
throw this.fail("InvalidState", `Cannot pause while "${this.state}".`);
|
|
118
|
+
}
|
|
119
|
+
if (typeof this.arp.pauseRecorder !== "function") {
|
|
120
|
+
throw this.fail("NotSupported", "This native module doesn't support pause().");
|
|
121
|
+
}
|
|
122
|
+
await this.arp.pauseRecorder();
|
|
123
|
+
this.pauseStartedAt = Date.now();
|
|
124
|
+
this.setState("paused");
|
|
125
|
+
}
|
|
126
|
+
async resume() {
|
|
127
|
+
if (this.state !== "paused") {
|
|
128
|
+
throw this.fail("InvalidState", `Cannot resume while "${this.state}".`);
|
|
129
|
+
}
|
|
130
|
+
if (typeof this.arp.resumeRecorder !== "function") {
|
|
131
|
+
throw this.fail("NotSupported", "This native module doesn't support resume().");
|
|
132
|
+
}
|
|
133
|
+
this.pausedAccumMs += Date.now() - this.pauseStartedAt;
|
|
134
|
+
await this.arp.resumeRecorder();
|
|
135
|
+
this.setState("recording");
|
|
136
|
+
}
|
|
137
|
+
async stop() {
|
|
138
|
+
if (this.state !== "recording" && this.state !== "paused") {
|
|
139
|
+
throw this.fail("InvalidState", `Cannot stop while "${this.state}".`);
|
|
140
|
+
}
|
|
141
|
+
this.setState("stopping");
|
|
142
|
+
let finalUri;
|
|
143
|
+
try {
|
|
144
|
+
finalUri = await this.arp.stopRecorder();
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
this.cleanup();
|
|
148
|
+
throw this.fail("Unknown", "Could not stop recording: " + (e?.message || ""), e);
|
|
149
|
+
}
|
|
150
|
+
const uri = finalUri || this.uri || "";
|
|
151
|
+
this.cleanup();
|
|
152
|
+
if (!uri) {
|
|
153
|
+
throw this.fail("Unknown", "Recording produced no file URI");
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
uri,
|
|
157
|
+
name: this.opts.fileName ?? "rec.m4a",
|
|
158
|
+
type: this.opts.mimeType ?? "audio/mp4",
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
async cancel() {
|
|
162
|
+
if (this.state === "recording" || this.state === "paused") {
|
|
163
|
+
try {
|
|
164
|
+
await this.arp.stopRecorder();
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
/* ignore */
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
this.cleanup();
|
|
171
|
+
}
|
|
172
|
+
// ─── internals ─────────────────────────────────────────────────────
|
|
173
|
+
onTick(e) {
|
|
174
|
+
if (!e)
|
|
175
|
+
return;
|
|
176
|
+
const dur = Number(e.currentPosition ?? e.current_position ?? 0);
|
|
177
|
+
if (Number.isFinite(dur))
|
|
178
|
+
this.durationMs = dur;
|
|
179
|
+
const meter = Number(e.currentMetering ?? e.current_metering ?? NaN);
|
|
180
|
+
if (Number.isFinite(meter)) {
|
|
181
|
+
const linear = dbToLinear(meter);
|
|
182
|
+
this.opts.onLevel?.(linear);
|
|
183
|
+
if (this.opts.silenceAutoStop && this.state === "recording") {
|
|
184
|
+
const silenceMs = this.opts.silenceAutoStop.ms ?? 6000;
|
|
185
|
+
const thresholdDb = this.opts.silenceAutoStop.thresholdDb ?? -40;
|
|
186
|
+
if (meter < thresholdDb) {
|
|
187
|
+
if (this.silenceSinceMs === 0)
|
|
188
|
+
this.silenceSinceMs = Date.now();
|
|
189
|
+
else if (Date.now() - this.silenceSinceMs >= silenceMs) {
|
|
190
|
+
this.silenceSinceMs = 0;
|
|
191
|
+
this.stop().catch(() => {
|
|
192
|
+
/* swallow */
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
this.silenceSinceMs = 0;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
this.opts.onStatus?.({
|
|
202
|
+
durationMillis: this.durationMs,
|
|
203
|
+
metering: Number.isFinite(meter) ? meter : undefined,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
cleanupListeners() {
|
|
207
|
+
if (typeof this.arp.removeRecordBackListener === "function") {
|
|
208
|
+
try {
|
|
209
|
+
this.arp.removeRecordBackListener();
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
/* ignore */
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
cleanup() {
|
|
217
|
+
if (this.maxStopTimer) {
|
|
218
|
+
clearTimeout(this.maxStopTimer);
|
|
219
|
+
this.maxStopTimer = null;
|
|
220
|
+
}
|
|
221
|
+
this.cleanupListeners();
|
|
222
|
+
this.uri = null;
|
|
223
|
+
this.durationMs = 0;
|
|
224
|
+
this.silenceSinceMs = 0;
|
|
225
|
+
this.startedAt = 0;
|
|
226
|
+
this.pausedAccumMs = 0;
|
|
227
|
+
this.pauseStartedAt = 0;
|
|
228
|
+
this.setState("idle");
|
|
229
|
+
}
|
|
230
|
+
setState(next) {
|
|
231
|
+
if (this.state === next)
|
|
232
|
+
return;
|
|
233
|
+
this.state = next;
|
|
234
|
+
this.opts.onStateChange?.(next);
|
|
235
|
+
}
|
|
236
|
+
fail(code, message, cause) {
|
|
237
|
+
const err = new RecorderError(code, message, cause);
|
|
238
|
+
this.opts.onError?.(err);
|
|
239
|
+
return err;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=bare-recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bare-recorder.js","sourceRoot":"","sources":["../src/bare-recorder.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
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
|
+
export { BareRecorder } from "./bare-recorder";
|
|
7
|
+
export type { BareRecorderOptions } from "./bare-recorder";
|
|
6
8
|
/**
|
|
7
9
|
* OHM Studio SDK for React Native.
|
|
8
10
|
*
|
|
@@ -26,7 +28,7 @@ export type { ExpoRecorderOptions, RNFile } from "./recorder";
|
|
|
26
28
|
* });
|
|
27
29
|
*/
|
|
28
30
|
export declare class OHM extends OHMCoreClient {
|
|
29
|
-
constructor(
|
|
31
|
+
constructor(init: OHMInit);
|
|
30
32
|
protected runMultipart<T>(opts: {
|
|
31
33
|
path: string;
|
|
32
34
|
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;AACpB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAY3D;;;;;;;;;;;;;;;;;;;;;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,7 @@
|
|
|
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
|
+
export { BareRecorder } from "./bare-recorder";
|
|
4
5
|
/** Detect React Native runtime — used to gate the bundled-key safeguard. */
|
|
5
6
|
function isReactNative() {
|
|
6
7
|
return (
|
|
@@ -32,7 +33,8 @@ function isReactNative() {
|
|
|
32
33
|
* });
|
|
33
34
|
*/
|
|
34
35
|
export class OHM extends OHMCoreClient {
|
|
35
|
-
constructor(
|
|
36
|
+
constructor(init) {
|
|
37
|
+
const opts = typeof init === "string" ? { apiKey: init } : init;
|
|
36
38
|
if (!opts.mock &&
|
|
37
39
|
isReactNative() &&
|
|
38
40
|
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;AAOpB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG/C,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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ohm_studio/sdk-react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "OHM Studio SDK for React Native. Voice-to-structured-JSON clinical extraction APIs in your mobile app.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -39,8 +39,11 @@
|
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
|
-
"
|
|
43
|
-
"
|
|
42
|
+
"clean": "rm -rf dist",
|
|
43
|
+
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
44
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"test:watch": "vitest"
|
|
44
47
|
},
|
|
45
48
|
"dependencies": {
|
|
46
49
|
"@ohm_studio/sdk-core": "workspace:*"
|
|
@@ -56,6 +59,7 @@
|
|
|
56
59
|
"devDependencies": {
|
|
57
60
|
"@types/react": "^18.3.23",
|
|
58
61
|
"react": "^18.3.1",
|
|
59
|
-
"typescript": "^5.8.3"
|
|
62
|
+
"typescript": "^5.8.3",
|
|
63
|
+
"vitest": "^4.1.5"
|
|
60
64
|
}
|
|
61
65
|
}
|