rnww-plugin-microphone 1.0.4 → 1.1.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.
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* 마이크 브릿지 핸들러
|
|
3
3
|
* 의존성 주입을 통해 동작하는 순수한 브릿지 로직
|
|
4
4
|
*/
|
|
5
|
-
import type { IBridge, IPlatform
|
|
5
|
+
import type { IBridge, IPlatform } from '../types';
|
|
6
6
|
/**
|
|
7
7
|
* 마이크 브릿지 설정
|
|
8
8
|
*/
|
|
@@ -15,11 +15,6 @@ export interface MicrophoneBridgeConfig {
|
|
|
15
15
|
* 플랫폼 구현체
|
|
16
16
|
*/
|
|
17
17
|
platform: IPlatform;
|
|
18
|
-
/**
|
|
19
|
-
* 마이크 모듈 인스턴스
|
|
20
|
-
* 이 패키지의 src/module/index.ts를 직접 전달
|
|
21
|
-
*/
|
|
22
|
-
microphoneModule: IMicrophoneModule;
|
|
23
18
|
/**
|
|
24
19
|
* 로거 (선택적)
|
|
25
20
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"microphone-bridge.d.ts","sourceRoot":"","sources":["../../src/bridge/microphone-bridge.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACR,OAAO,EACP,SAAS,
|
|
1
|
+
{"version":3,"file":"microphone-bridge.d.ts","sourceRoot":"","sources":["../../src/bridge/microphone-bridge.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACR,OAAO,EACP,SAAS,EACZ,MAAM,UAAU,CAAC;AAIlB;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACnC;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB;;OAEG;IACH,MAAM,CAAC,EAAE;QACL,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;QAC9B,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;QAC/B,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;KACnC,CAAC;CACL;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B,GAAI,QAAQ,sBAAsB,SAuHxE,CAAC"}
|
|
@@ -3,14 +3,48 @@
|
|
|
3
3
|
* 마이크 브릿지 핸들러
|
|
4
4
|
* 의존성 주입을 통해 동작하는 순수한 브릿지 로직
|
|
5
5
|
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
6
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
40
|
exports.registerMicrophoneHandlers = void 0;
|
|
41
|
+
const MicrophoneModule = __importStar(require("../modules"));
|
|
8
42
|
/**
|
|
9
43
|
* 마이크 브릿지 핸들러를 등록합니다
|
|
10
44
|
* @param config 브릿지 설정
|
|
11
45
|
*/
|
|
12
46
|
const registerMicrophoneHandlers = (config) => {
|
|
13
|
-
const { bridge, platform,
|
|
47
|
+
const { bridge, platform, logger = console } = config;
|
|
14
48
|
// Android와 iOS만 지원
|
|
15
49
|
if (platform.OS !== 'android' && platform.OS !== 'ios') {
|
|
16
50
|
logger.log('[Bridge] Microphone handlers skipped (Android/iOS only)');
|
|
@@ -21,7 +55,7 @@ const registerMicrophoneHandlers = (config) => {
|
|
|
21
55
|
// 마이크 권한 확인
|
|
22
56
|
bridge.registerHandler('checkMicrophonePermission', async (_payload, respond) => {
|
|
23
57
|
try {
|
|
24
|
-
const result = await
|
|
58
|
+
const result = await MicrophoneModule.checkMicrophonePermission();
|
|
25
59
|
respond({ success: true, ...result });
|
|
26
60
|
}
|
|
27
61
|
catch (error) {
|
|
@@ -36,7 +70,7 @@ const registerMicrophoneHandlers = (config) => {
|
|
|
36
70
|
// 마이크 권한 요청
|
|
37
71
|
bridge.registerHandler('requestMicrophonePermission', async (_payload, respond) => {
|
|
38
72
|
try {
|
|
39
|
-
const result = await
|
|
73
|
+
const result = await MicrophoneModule.requestMicrophonePermission();
|
|
40
74
|
respond({ success: true, ...result });
|
|
41
75
|
}
|
|
42
76
|
catch (error) {
|
|
@@ -62,7 +96,7 @@ const registerMicrophoneHandlers = (config) => {
|
|
|
62
96
|
}
|
|
63
97
|
// 새 리스너 등록
|
|
64
98
|
try {
|
|
65
|
-
audioChunkSubscription =
|
|
99
|
+
audioChunkSubscription = MicrophoneModule.addListener('onAudioChunk', (data) => {
|
|
66
100
|
bridge.sendToWeb('onAudioChunk', data);
|
|
67
101
|
});
|
|
68
102
|
}
|
|
@@ -71,7 +105,7 @@ const registerMicrophoneHandlers = (config) => {
|
|
|
71
105
|
}
|
|
72
106
|
// 파라미터 정규화
|
|
73
107
|
const options = payload;
|
|
74
|
-
const result = await
|
|
108
|
+
const result = await MicrophoneModule.startRecording(options);
|
|
75
109
|
respond(result);
|
|
76
110
|
}
|
|
77
111
|
catch (error) {
|
|
@@ -85,7 +119,7 @@ const registerMicrophoneHandlers = (config) => {
|
|
|
85
119
|
// 녹음 중지
|
|
86
120
|
bridge.registerHandler('stopRecording', async (_payload, respond) => {
|
|
87
121
|
try {
|
|
88
|
-
const result = await
|
|
122
|
+
const result = await MicrophoneModule.stopRecording();
|
|
89
123
|
// 이벤트 리스너 정리 (메모리 누수 방지)
|
|
90
124
|
if (audioChunkSubscription) {
|
|
91
125
|
try {
|
|
@@ -108,7 +142,7 @@ const registerMicrophoneHandlers = (config) => {
|
|
|
108
142
|
// 마이크 상태 조회
|
|
109
143
|
bridge.registerHandler('getMicrophoneStatus', async (_payload, respond) => {
|
|
110
144
|
try {
|
|
111
|
-
const status = await
|
|
145
|
+
const status = await MicrophoneModule.getMicrophoneStatus();
|
|
112
146
|
respond(status);
|
|
113
147
|
}
|
|
114
148
|
catch (error) {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Microphone Module (Cross-Platform)
|
|
3
|
+
* 마이크 권한 및 녹음 기능 제공
|
|
4
|
+
* Supports: Android, iOS
|
|
5
|
+
*/
|
|
6
|
+
export interface MicrophonePermissionResult {
|
|
7
|
+
/** 권한 승인 여부 */
|
|
8
|
+
granted: boolean;
|
|
9
|
+
/** 권한 상태 */
|
|
10
|
+
status: 'granted' | 'denied' | 'unavailable' | 'error';
|
|
11
|
+
}
|
|
12
|
+
export interface MicrophoneStatusResult {
|
|
13
|
+
/** 스트리밍 여부 */
|
|
14
|
+
isStreaming: boolean;
|
|
15
|
+
/** 마이크 사용 가능 여부 */
|
|
16
|
+
hasMicrophone: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface AudioChunkEvent {
|
|
19
|
+
/** 이벤트 타입 */
|
|
20
|
+
type: 'audioChunk';
|
|
21
|
+
/** base64 인코딩된 오디오 데이터 */
|
|
22
|
+
base64: string;
|
|
23
|
+
/** 청크 크기 (bytes) */
|
|
24
|
+
chunkSize: number;
|
|
25
|
+
/** 청크 번호 */
|
|
26
|
+
chunkNumber: number;
|
|
27
|
+
/** 타임스탬프 */
|
|
28
|
+
timestamp: number;
|
|
29
|
+
/** 샘플레이트 */
|
|
30
|
+
sampleRate: number;
|
|
31
|
+
/** 인코딩 형식 */
|
|
32
|
+
encoding: 'pcm_16bit';
|
|
33
|
+
}
|
|
34
|
+
export interface MicrophoneRecordingOptions {
|
|
35
|
+
/** 샘플레이트 (8000-48000, 기본값: 44100) */
|
|
36
|
+
sampleRate?: number;
|
|
37
|
+
/** 청크 크기 (512-8192 bytes, 기본값: 2048 약 23ms) */
|
|
38
|
+
chunkSize?: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 마이크 권한 확인
|
|
42
|
+
* @returns 마이크 권한 상태
|
|
43
|
+
*/
|
|
44
|
+
export declare function checkMicrophonePermission(): Promise<MicrophonePermissionResult>;
|
|
45
|
+
/**
|
|
46
|
+
* 마이크 권한 요청
|
|
47
|
+
* @returns 권한 요청 결과
|
|
48
|
+
*/
|
|
49
|
+
export declare function requestMicrophonePermission(): Promise<MicrophonePermissionResult>;
|
|
50
|
+
/**
|
|
51
|
+
* 녹음 시작 (스트리밍)
|
|
52
|
+
* @param options 녹음 옵션
|
|
53
|
+
* @returns 시작 결과
|
|
54
|
+
*/
|
|
55
|
+
export declare function startRecording(options?: MicrophoneRecordingOptions): Promise<{
|
|
56
|
+
success: boolean;
|
|
57
|
+
error?: string;
|
|
58
|
+
}>;
|
|
59
|
+
/**
|
|
60
|
+
* 녹음 중지
|
|
61
|
+
* @returns 녹음 중지 결과
|
|
62
|
+
*/
|
|
63
|
+
export declare function stopRecording(): Promise<{
|
|
64
|
+
success: boolean;
|
|
65
|
+
error?: string;
|
|
66
|
+
}>;
|
|
67
|
+
/**
|
|
68
|
+
* 마이크 상태 확인
|
|
69
|
+
* @returns 현재 마이크 상태
|
|
70
|
+
*/
|
|
71
|
+
export declare function getMicrophoneStatus(): Promise<MicrophoneStatusResult>;
|
|
72
|
+
/**
|
|
73
|
+
* Add listener for microphone events (Expo module style)
|
|
74
|
+
* @param eventName Event name to listen to (e.g., 'onAudioChunk')
|
|
75
|
+
* @param listener Callback function to handle the event
|
|
76
|
+
* @returns Subscription object with remove() method
|
|
77
|
+
*/
|
|
78
|
+
export declare function addListener(eventName: string, listener: (event: AudioChunkEvent) => void): {
|
|
79
|
+
remove: () => void;
|
|
80
|
+
};
|
|
81
|
+
declare const _default: {
|
|
82
|
+
checkMicrophonePermission: typeof checkMicrophonePermission;
|
|
83
|
+
requestMicrophonePermission: typeof requestMicrophonePermission;
|
|
84
|
+
startRecording: typeof startRecording;
|
|
85
|
+
stopRecording: typeof stopRecording;
|
|
86
|
+
getMicrophoneStatus: typeof getMicrophoneStatus;
|
|
87
|
+
addListener: typeof addListener;
|
|
88
|
+
};
|
|
89
|
+
export default _default;
|
|
90
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/modules/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA0BH,MAAM,WAAW,0BAA0B;IACzC,eAAe;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY;IACZ,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,aAAa,GAAG,OAAO,CAAC;CACxD;AAED,MAAM,WAAW,sBAAsB;IACrC,cAAc;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,mBAAmB;IACnB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,aAAa;IACb,IAAI,EAAE,YAAY,CAAC;IACnB,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa;IACb,QAAQ,EAAE,WAAW,CAAC;CACvB;AAED,MAAM,WAAW,0BAA0B;IACzC,qCAAqC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAMrF;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAMvF;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,OAAO,CAAC,EAAE,0BAA0B,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAaxH;AAED;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAMnF;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAS3E;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,GAAG;IAAE,MAAM,EAAE,MAAM,IAAI,CAAA;CAAE,CAOjH;;;;;;;;;AAED,wBAOE"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Microphone Module (Cross-Platform)
|
|
4
|
+
* 마이크 권한 및 녹음 기능 제공
|
|
5
|
+
* Supports: Android, iOS
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.checkMicrophonePermission = checkMicrophonePermission;
|
|
9
|
+
exports.requestMicrophonePermission = requestMicrophonePermission;
|
|
10
|
+
exports.startRecording = startRecording;
|
|
11
|
+
exports.stopRecording = stopRecording;
|
|
12
|
+
exports.getMicrophoneStatus = getMicrophoneStatus;
|
|
13
|
+
exports.addListener = addListener;
|
|
14
|
+
const expo_modules_core_1 = require("expo-modules-core");
|
|
15
|
+
const react_native_1 = require("react-native");
|
|
16
|
+
// Lazy 모듈 로드 (크래시 방지)
|
|
17
|
+
let MicrophoneModule = null;
|
|
18
|
+
function getMicrophoneModule() {
|
|
19
|
+
if (react_native_1.Platform.OS !== 'android' && react_native_1.Platform.OS !== 'ios') {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
if (MicrophoneModule === null) {
|
|
23
|
+
try {
|
|
24
|
+
MicrophoneModule = (0, expo_modules_core_1.requireNativeModule)('Microphone');
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
console.error('[Microphone] Failed to load native module:', error);
|
|
28
|
+
MicrophoneModule = undefined; // 재시도 방지
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return MicrophoneModule === undefined ? null : MicrophoneModule;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 마이크 권한 확인
|
|
36
|
+
* @returns 마이크 권한 상태
|
|
37
|
+
*/
|
|
38
|
+
async function checkMicrophonePermission() {
|
|
39
|
+
const module = getMicrophoneModule();
|
|
40
|
+
if (!module) {
|
|
41
|
+
return { granted: false, status: 'unavailable' };
|
|
42
|
+
}
|
|
43
|
+
return await module.checkMicrophonePermission();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 마이크 권한 요청
|
|
47
|
+
* @returns 권한 요청 결과
|
|
48
|
+
*/
|
|
49
|
+
async function requestMicrophonePermission() {
|
|
50
|
+
const module = getMicrophoneModule();
|
|
51
|
+
if (!module) {
|
|
52
|
+
return { granted: false, status: 'unavailable' };
|
|
53
|
+
}
|
|
54
|
+
return await module.requestMicrophonePermission();
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 녹음 시작 (스트리밍)
|
|
58
|
+
* @param options 녹음 옵션
|
|
59
|
+
* @returns 시작 결과
|
|
60
|
+
*/
|
|
61
|
+
async function startRecording(options) {
|
|
62
|
+
const module = getMicrophoneModule();
|
|
63
|
+
if (!module) {
|
|
64
|
+
return { success: false, error: 'Microphone module not available' };
|
|
65
|
+
}
|
|
66
|
+
// 파라미터 정규화
|
|
67
|
+
const params = {
|
|
68
|
+
sampleRate: options?.sampleRate,
|
|
69
|
+
chunkSize: options?.chunkSize,
|
|
70
|
+
};
|
|
71
|
+
return await module.startRecording(params);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 녹음 중지
|
|
75
|
+
* @returns 녹음 중지 결과
|
|
76
|
+
*/
|
|
77
|
+
async function stopRecording() {
|
|
78
|
+
const module = getMicrophoneModule();
|
|
79
|
+
if (!module) {
|
|
80
|
+
return { success: false, error: 'Microphone module not available' };
|
|
81
|
+
}
|
|
82
|
+
return await module.stopRecording();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 마이크 상태 확인
|
|
86
|
+
* @returns 현재 마이크 상태
|
|
87
|
+
*/
|
|
88
|
+
async function getMicrophoneStatus() {
|
|
89
|
+
const module = getMicrophoneModule();
|
|
90
|
+
if (!module) {
|
|
91
|
+
return {
|
|
92
|
+
isStreaming: false,
|
|
93
|
+
hasMicrophone: false
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return await module.getMicrophoneStatus();
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Add listener for microphone events (Expo module style)
|
|
100
|
+
* @param eventName Event name to listen to (e.g., 'onAudioChunk')
|
|
101
|
+
* @param listener Callback function to handle the event
|
|
102
|
+
* @returns Subscription object with remove() method
|
|
103
|
+
*/
|
|
104
|
+
function addListener(eventName, listener) {
|
|
105
|
+
const module = getMicrophoneModule();
|
|
106
|
+
if (!module || !module.addListener) {
|
|
107
|
+
console.error('[Microphone] addListener not available');
|
|
108
|
+
return { remove: () => { } };
|
|
109
|
+
}
|
|
110
|
+
return module.addListener(eventName, listener);
|
|
111
|
+
}
|
|
112
|
+
exports.default = {
|
|
113
|
+
checkMicrophonePermission,
|
|
114
|
+
requestMicrophonePermission,
|
|
115
|
+
startRecording,
|
|
116
|
+
stopRecording,
|
|
117
|
+
getMicrophoneStatus,
|
|
118
|
+
addListener,
|
|
119
|
+
};
|