node-mac-recorder 2.22.5 → 2.22.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/screen_capture_kit.mm +71 -12
- package/test-noise-reduction.js +0 -80
package/package.json
CHANGED
|
@@ -1383,11 +1383,46 @@ static void SCKPerformRecordingSetup(NSDictionary *config, SCShareableContent *c
|
|
|
1383
1383
|
}
|
|
1384
1384
|
|
|
1385
1385
|
if (targetWindow && targetApp) {
|
|
1386
|
-
|
|
1387
|
-
|
|
1386
|
+
CGFloat windowLogicalWidth = targetWindow.frame.size.width;
|
|
1387
|
+
CGFloat windowLogicalHeight = targetWindow.frame.size.height;
|
|
1388
|
+
|
|
1389
|
+
// Find display containing this window to get scale factor
|
|
1390
|
+
CGFloat scaleFactor = 1.0;
|
|
1391
|
+
CGPoint windowCenter = CGPointMake(
|
|
1392
|
+
targetWindow.frame.origin.x + windowLogicalWidth / 2.0,
|
|
1393
|
+
targetWindow.frame.origin.y + windowLogicalHeight / 2.0
|
|
1394
|
+
);
|
|
1395
|
+
for (SCDisplay *disp in content.displays) {
|
|
1396
|
+
CGRect dispBounds = CGRectMake(disp.frame.origin.x, disp.frame.origin.y,
|
|
1397
|
+
disp.frame.size.width, disp.frame.size.height);
|
|
1398
|
+
if (CGRectContainsPoint(dispBounds, windowCenter)) {
|
|
1399
|
+
NSInteger physW = (NSInteger)CGDisplayPixelsWide(disp.displayID);
|
|
1400
|
+
NSInteger physH = (NSInteger)CGDisplayPixelsHigh(disp.displayID);
|
|
1401
|
+
CGFloat scX = (disp.width > 0) ? (CGFloat)physW / (CGFloat)disp.width : 1.0;
|
|
1402
|
+
CGFloat scY = (disp.height > 0) ? (CGFloat)physH / (CGFloat)disp.height : 1.0;
|
|
1403
|
+
scaleFactor = MAX(scX, scY);
|
|
1404
|
+
break;
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
// Fallback: use main display scale factor
|
|
1408
|
+
if (scaleFactor == 1.0 && content.displays.count > 0) {
|
|
1409
|
+
SCDisplay *mainDisp = content.displays.firstObject;
|
|
1410
|
+
NSInteger physW = (NSInteger)CGDisplayPixelsWide(mainDisp.displayID);
|
|
1411
|
+
NSInteger physH = (NSInteger)CGDisplayPixelsHigh(mainDisp.displayID);
|
|
1412
|
+
CGFloat scX = (mainDisp.width > 0) ? (CGFloat)physW / (CGFloat)mainDisp.width : 1.0;
|
|
1413
|
+
CGFloat scY = (mainDisp.height > 0) ? (CGFloat)physH / (CGFloat)mainDisp.height : 1.0;
|
|
1414
|
+
scaleFactor = MAX(scX, scY);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
NSInteger physicalWindowWidth = (NSInteger)(windowLogicalWidth * scaleFactor);
|
|
1418
|
+
NSInteger physicalWindowHeight = (NSInteger)(windowLogicalHeight * scaleFactor);
|
|
1419
|
+
|
|
1420
|
+
MRLog(@"🪟 Recording window: %@ logical=%ux%u, physical=%ldx%ld, scale=%.2fx",
|
|
1421
|
+
targetWindow.title, (unsigned)windowLogicalWidth, (unsigned)windowLogicalHeight,
|
|
1422
|
+
(long)physicalWindowWidth, (long)physicalWindowHeight, scaleFactor);
|
|
1388
1423
|
filter = [[SCContentFilter alloc] initWithDesktopIndependentWindow:targetWindow];
|
|
1389
|
-
recordingWidth =
|
|
1390
|
-
recordingHeight =
|
|
1424
|
+
recordingWidth = physicalWindowWidth;
|
|
1425
|
+
recordingHeight = physicalWindowHeight;
|
|
1391
1426
|
} else {
|
|
1392
1427
|
NSLog(@"❌ Window ID %@ not found", windowId);
|
|
1393
1428
|
SCKFailScheduling();
|
|
@@ -1421,11 +1456,24 @@ static void SCKPerformRecordingSetup(NSDictionary *config, SCShareableContent *c
|
|
|
1421
1456
|
}
|
|
1422
1457
|
|
|
1423
1458
|
if (targetDisplay) {
|
|
1424
|
-
|
|
1425
|
-
|
|
1459
|
+
// Use physical pixel dimensions for Retina displays (not logical points)
|
|
1460
|
+
CGDirectDisplayID displayID = targetDisplay.displayID;
|
|
1461
|
+
NSInteger physicalWidth = (NSInteger)CGDisplayPixelsWide(displayID);
|
|
1462
|
+
NSInteger physicalHeight = (NSInteger)CGDisplayPixelsHigh(displayID);
|
|
1463
|
+
NSInteger logicalWidth = targetDisplay.width;
|
|
1464
|
+
NSInteger logicalHeight = targetDisplay.height;
|
|
1465
|
+
|
|
1466
|
+
CGFloat scaleX = (logicalWidth > 0) ? (CGFloat)physicalWidth / (CGFloat)logicalWidth : 1.0;
|
|
1467
|
+
CGFloat scaleY = (logicalHeight > 0) ? (CGFloat)physicalHeight / (CGFloat)logicalHeight : 1.0;
|
|
1468
|
+
CGFloat scaleFactor = MAX(scaleX, scaleY);
|
|
1469
|
+
|
|
1470
|
+
MRLog(@"🖥️ Recording display %u: logical=%dx%d, physical=%ldx%ld, scale=%.2fx",
|
|
1471
|
+
displayID, (int)logicalWidth, (int)logicalHeight,
|
|
1472
|
+
(long)physicalWidth, (long)physicalHeight, scaleFactor);
|
|
1473
|
+
|
|
1426
1474
|
filter = [[SCContentFilter alloc] initWithDisplay:targetDisplay excludingWindows:@[]];
|
|
1427
|
-
recordingWidth =
|
|
1428
|
-
recordingHeight =
|
|
1475
|
+
recordingWidth = physicalWidth;
|
|
1476
|
+
recordingHeight = physicalHeight;
|
|
1429
1477
|
} else {
|
|
1430
1478
|
NSLog(@"❌ No display available");
|
|
1431
1479
|
SCKFailScheduling();
|
|
@@ -1437,11 +1485,22 @@ static void SCKPerformRecordingSetup(NSDictionary *config, SCShareableContent *c
|
|
|
1437
1485
|
CGFloat cropWidth = [captureRect[@"width"] doubleValue];
|
|
1438
1486
|
CGFloat cropHeight = [captureRect[@"height"] doubleValue];
|
|
1439
1487
|
if (cropWidth > 0 && cropHeight > 0) {
|
|
1440
|
-
|
|
1441
|
-
|
|
1488
|
+
// Scale crop dimensions for Retina displays
|
|
1489
|
+
CGFloat cropScaleFactor = 1.0;
|
|
1490
|
+
if (targetDisplay) {
|
|
1491
|
+
NSInteger physW = (NSInteger)CGDisplayPixelsWide(targetDisplay.displayID);
|
|
1492
|
+
NSInteger physH = (NSInteger)CGDisplayPixelsHigh(targetDisplay.displayID);
|
|
1493
|
+
CGFloat scX = (targetDisplay.width > 0) ? (CGFloat)physW / (CGFloat)targetDisplay.width : 1.0;
|
|
1494
|
+
CGFloat scY = (targetDisplay.height > 0) ? (CGFloat)physH / (CGFloat)targetDisplay.height : 1.0;
|
|
1495
|
+
cropScaleFactor = MAX(scX, scY);
|
|
1496
|
+
}
|
|
1497
|
+
NSInteger physicalCropWidth = (NSInteger)(cropWidth * cropScaleFactor);
|
|
1498
|
+
NSInteger physicalCropHeight = (NSInteger)(cropHeight * cropScaleFactor);
|
|
1499
|
+
MRLog(@"🔲 Crop area: logical=%.0fx%.0f, physical=%ldx%ld, scale=%.2fx at (%.0f,%.0f)",
|
|
1500
|
+
cropWidth, cropHeight, (long)physicalCropWidth, (long)physicalCropHeight, cropScaleFactor,
|
|
1442
1501
|
[captureRect[@"x"] doubleValue], [captureRect[@"y"] doubleValue]);
|
|
1443
|
-
recordingWidth =
|
|
1444
|
-
recordingHeight =
|
|
1502
|
+
recordingWidth = physicalCropWidth;
|
|
1503
|
+
recordingHeight = physicalCropHeight;
|
|
1445
1504
|
}
|
|
1446
1505
|
}
|
|
1447
1506
|
|
package/test-noise-reduction.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
const MacRecorder = require('./index.js');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
|
|
5
|
-
// Test output directory
|
|
6
|
-
const outputDir = path.join(__dirname, 'test-output');
|
|
7
|
-
if (!fs.existsSync(outputDir)) {
|
|
8
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const timestamp = Date.now();
|
|
12
|
-
const outputPath = path.join(outputDir, `noise-reduction-test-${timestamp}.mov`);
|
|
13
|
-
|
|
14
|
-
console.log('🎙️ Starting microphone recording with noise reduction...');
|
|
15
|
-
console.log('📝 Instructions:');
|
|
16
|
-
console.log(' 1. Speak into your microphone (normal voice)');
|
|
17
|
-
console.log(' 2. Type on your keyboard (heavy typing)');
|
|
18
|
-
console.log(' 3. Click your mouse multiple times');
|
|
19
|
-
console.log(' 4. Speak again to compare');
|
|
20
|
-
console.log('');
|
|
21
|
-
console.log('⏱️ Recording for 15 seconds...');
|
|
22
|
-
console.log('');
|
|
23
|
-
|
|
24
|
-
const recorder = new MacRecorder();
|
|
25
|
-
|
|
26
|
-
recorder.on('recordingStarted', () => {
|
|
27
|
-
console.log('✅ Recording started!');
|
|
28
|
-
console.log('🎤 Custom noise reduction is ACTIVE');
|
|
29
|
-
console.log('');
|
|
30
|
-
console.log('Starting countdown:');
|
|
31
|
-
|
|
32
|
-
let countdown = 15;
|
|
33
|
-
const interval = setInterval(() => {
|
|
34
|
-
process.stdout.write(`\r⏱️ ${countdown} seconds remaining... `);
|
|
35
|
-
countdown--;
|
|
36
|
-
|
|
37
|
-
if (countdown < 0) {
|
|
38
|
-
clearInterval(interval);
|
|
39
|
-
process.stdout.write('\r');
|
|
40
|
-
console.log('⏱️ Time up! Stopping...');
|
|
41
|
-
console.log('');
|
|
42
|
-
|
|
43
|
-
recorder.stopRecording()
|
|
44
|
-
.then(() => {
|
|
45
|
-
console.log('✅ Recording saved to:', outputPath);
|
|
46
|
-
console.log('');
|
|
47
|
-
console.log('🎧 Play the recording to verify:');
|
|
48
|
-
console.log(` open "${outputPath}"`);
|
|
49
|
-
console.log('');
|
|
50
|
-
console.log('Expected results:');
|
|
51
|
-
console.log(' ✅ Voice should be clear');
|
|
52
|
-
console.log(' ✅ Keyboard typing should be significantly reduced');
|
|
53
|
-
console.log(' ✅ Mouse clicks should be filtered out');
|
|
54
|
-
process.exit(0);
|
|
55
|
-
})
|
|
56
|
-
.catch(err => {
|
|
57
|
-
console.error('❌ Error stopping:', err);
|
|
58
|
-
process.exit(1);
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
}, 1000);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
recorder.on('stopped', () => {
|
|
65
|
-
console.log('🛑 Recording stopped');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
recorder.on('completed', (result) => {
|
|
69
|
-
console.log('✅ Recording completed:', result);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// Start recording with microphone
|
|
73
|
-
recorder.startRecording(outputPath, {
|
|
74
|
-
includeMicrophone: true,
|
|
75
|
-
includeSystemAudio: false,
|
|
76
|
-
fps: 1 // Minimal FPS since we're only testing audio
|
|
77
|
-
}).catch(err => {
|
|
78
|
-
console.error('❌ Failed to start recording:', err);
|
|
79
|
-
process.exit(1);
|
|
80
|
-
});
|