native-recorder-nodejs 1.0.1
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/CMakeLists.txt +110 -0
- package/README.md +380 -0
- package/dist/bindings.d.ts +2 -0
- package/dist/bindings.js +31 -0
- package/dist/index.d.ts +104 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +110 -0
- package/dist/index.js.map +1 -0
- package/dist/recorder.d.ts +56 -0
- package/dist/recorder.d.ts.map +1 -0
- package/dist/recorder.js +343 -0
- package/dist/recorder.js.map +1 -0
- package/dist/types.d.ts +108 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +49 -0
- package/dist/types.js.map +1 -0
- package/native/AudioController.cpp +233 -0
- package/native/AudioController.h +26 -0
- package/native/AudioEngine.h +75 -0
- package/native/Factory.cpp +18 -0
- package/native/mac/AVFEngine.h +24 -0
- package/native/mac/AVFEngine.mm +274 -0
- package/native/mac/SCKAudioCapture.h +13 -0
- package/native/mac/SCKAudioCapture.mm +213 -0
- package/native/main.cpp +9 -0
- package/native/win/WASAPIEngine.cpp +449 -0
- package/native/win/WASAPIEngine.h +44 -0
- package/package.json +74 -0
- package/prebuilds/native-audio-recorder-v0.1.0-napi-v8-darwin-arm64.tar.gz +0 -0
- package/prebuilds/native-recorder-nodejs-v1.0.0-napi-v8-darwin-arm64.tar.gz +0 -0
- package/src/bindings.ts +39 -0
- package/src/index.ts +206 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import bindings from "./bindings";
|
|
2
|
+
import { EventEmitter } from "events";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Special device ID for system-wide audio capture (macOS)
|
|
6
|
+
*/
|
|
7
|
+
export const SYSTEM_AUDIO_DEVICE_ID = "system";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Device type classification
|
|
11
|
+
*/
|
|
12
|
+
export type DeviceType = "input" | "output";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Permission type for requesting access
|
|
16
|
+
*/
|
|
17
|
+
export type PermissionType = "mic" | "system";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Permission status for audio recording
|
|
21
|
+
*/
|
|
22
|
+
export interface PermissionStatus {
|
|
23
|
+
/** Microphone permission granted */
|
|
24
|
+
mic: boolean;
|
|
25
|
+
/** System audio permission granted (screen recording permission on macOS) */
|
|
26
|
+
system: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Represents an audio device
|
|
31
|
+
*/
|
|
32
|
+
export interface AudioDevice {
|
|
33
|
+
/** Unique device identifier (always has a value) */
|
|
34
|
+
id: string;
|
|
35
|
+
/** Human-readable device name */
|
|
36
|
+
name: string;
|
|
37
|
+
/** Device type: 'input' for microphones, 'output' for system audio */
|
|
38
|
+
type: DeviceType;
|
|
39
|
+
/** Whether this is the default device for its type */
|
|
40
|
+
isDefault: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Audio format information
|
|
45
|
+
*/
|
|
46
|
+
export interface AudioFormat {
|
|
47
|
+
/** Sample rate in Hz (e.g., 44100, 48000) */
|
|
48
|
+
sampleRate: number;
|
|
49
|
+
/** Number of channels (1 = Mono, 2 = Stereo) */
|
|
50
|
+
channels: number;
|
|
51
|
+
/** Output bit depth (currently fixed at 16) */
|
|
52
|
+
bitDepth: number;
|
|
53
|
+
/** Native device bit depth */
|
|
54
|
+
rawBitDepth: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Recording configuration
|
|
59
|
+
* Both deviceType and deviceId are required for consistent cross-platform behavior
|
|
60
|
+
*/
|
|
61
|
+
export interface RecordingConfig {
|
|
62
|
+
/**
|
|
63
|
+
* Type of device to record from.
|
|
64
|
+
* - 'input': Record from microphone
|
|
65
|
+
* - 'output': Record system audio (loopback)
|
|
66
|
+
*/
|
|
67
|
+
deviceType: DeviceType;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Device ID to record from (obtained from getDevices()).
|
|
71
|
+
* Every device has a valid ID - use the ID from the device list.
|
|
72
|
+
*/
|
|
73
|
+
deviceId: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Define the native controller interface
|
|
77
|
+
interface NativeAudioController {
|
|
78
|
+
start(
|
|
79
|
+
config: RecordingConfig,
|
|
80
|
+
callback: (error: Error | null, data: Buffer | null) => void
|
|
81
|
+
): void;
|
|
82
|
+
stop(): void;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Define the native module interface
|
|
86
|
+
interface NativeModule {
|
|
87
|
+
AudioController: {
|
|
88
|
+
new (): NativeAudioController;
|
|
89
|
+
getDevices(): AudioDevice[];
|
|
90
|
+
getDeviceFormat(deviceId: string): AudioFormat;
|
|
91
|
+
checkPermission(): PermissionStatus;
|
|
92
|
+
requestPermission(type: PermissionType): boolean;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const native = bindings as NativeModule;
|
|
97
|
+
|
|
98
|
+
export class AudioRecorder extends EventEmitter {
|
|
99
|
+
private controller: NativeAudioController;
|
|
100
|
+
private isRecording: boolean = false;
|
|
101
|
+
|
|
102
|
+
constructor() {
|
|
103
|
+
super();
|
|
104
|
+
this.controller = new native.AudioController();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Starts the recording session.
|
|
109
|
+
* @param config Configuration object with deviceType and deviceId (both required)
|
|
110
|
+
*/
|
|
111
|
+
async start(config: RecordingConfig): Promise<void> {
|
|
112
|
+
if (this.isRecording) {
|
|
113
|
+
throw new Error("Already recording");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Validate config
|
|
117
|
+
if (!config.deviceType || !config.deviceId) {
|
|
118
|
+
throw new Error("Both deviceType and deviceId are required");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (config.deviceType !== "input" && config.deviceType !== "output") {
|
|
122
|
+
throw new Error("deviceType must be 'input' or 'output'");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
try {
|
|
127
|
+
this.controller.start(
|
|
128
|
+
config,
|
|
129
|
+
(error: Error | null, data: Buffer | null) => {
|
|
130
|
+
if (error) {
|
|
131
|
+
this.emit("error", error);
|
|
132
|
+
} else if (data) {
|
|
133
|
+
this.emit("data", data);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
this.isRecording = true;
|
|
138
|
+
resolve();
|
|
139
|
+
} catch (error) {
|
|
140
|
+
reject(error);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async stop(): Promise<void> {
|
|
146
|
+
if (!this.isRecording) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return new Promise((resolve, reject) => {
|
|
151
|
+
try {
|
|
152
|
+
this.controller.stop();
|
|
153
|
+
this.isRecording = false;
|
|
154
|
+
resolve();
|
|
155
|
+
} catch (error) {
|
|
156
|
+
reject(error);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Lists available audio devices.
|
|
163
|
+
* @param type Optional filter by device type
|
|
164
|
+
* @returns Array of AudioDevice objects (all with valid id values)
|
|
165
|
+
*/
|
|
166
|
+
static getDevices(type?: DeviceType): AudioDevice[] {
|
|
167
|
+
const devices = native.AudioController.getDevices();
|
|
168
|
+
if (type) {
|
|
169
|
+
return devices.filter((d) => d.type === type);
|
|
170
|
+
}
|
|
171
|
+
return devices;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Gets the audio format of a specific device.
|
|
176
|
+
* @param deviceId The device ID to query
|
|
177
|
+
* @returns AudioFormat object
|
|
178
|
+
*/
|
|
179
|
+
static getDeviceFormat(deviceId: string): AudioFormat {
|
|
180
|
+
return native.AudioController.getDeviceFormat(deviceId);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Checks the current permission status for audio recording.
|
|
185
|
+
* On Windows, always returns { mic: true, system: true } as no explicit permissions are required.
|
|
186
|
+
* On macOS, checks actual permission status for microphone and screen recording.
|
|
187
|
+
* @returns PermissionStatus object with mic and system boolean fields
|
|
188
|
+
*/
|
|
189
|
+
static checkPermission(): PermissionStatus {
|
|
190
|
+
return native.AudioController.checkPermission();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Requests permission for the specified type.
|
|
195
|
+
* On Windows, always returns true as no explicit permissions are required.
|
|
196
|
+
* On macOS, prompts the user to grant the requested permission.
|
|
197
|
+
* @param type The permission type to request: 'mic' for microphone, 'system' for system audio
|
|
198
|
+
* @returns true if permission was granted, false otherwise
|
|
199
|
+
*/
|
|
200
|
+
static requestPermission(type: PermissionType): boolean {
|
|
201
|
+
return native.AudioController.requestPermission(type);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Export raw bindings for testing if needed
|
|
206
|
+
export const nativeBindings = bindings;
|