4 Commits
Author SHA1 Message Date
Илья Глазунов 76d6fc2749 Refactor Windows audio capture setup to streamline processing and remove redundant logging
Build and Release / build (x64, ubuntu-latest, linux) (push) Has been skipped
Build and Release / build (arm64, macos-latest, darwin) (push) Has been cancelled
Build and Release / build (x64, macos-latest, darwin) (push) Has been cancelled
Build and Release / build (x64, windows-latest, win32) (push) Has been cancelled
Build and Release / release (push) Has been cancelled
2026-01-15 20:45:34 +03:00
Илья Глазунов 2f013b4751 Bump version to 0.5.8 and add silence detection timer for audio processing
Build and Release / build (x64, ubuntu-latest, linux) (push) Has been skipped
Build and Release / build (arm64, macos-latest, darwin) (push) Has been cancelled
Build and Release / build (x64, macos-latest, darwin) (push) Has been cancelled
Build and Release / build (x64, windows-latest, win32) (push) Has been cancelled
Build and Release / release (push) Has been cancelled
2026-01-15 20:21:06 +03:00
Илья Глазунов 66dce4415a Update author information in package.json 2026-01-15 19:48:41 +03:00
Илья Глазунов 6460349fc7 Bump version to 0.5.7 and update screen capture handler to use system picker
Build and Release / build (x64, ubuntu-latest, linux) (push) Has been skipped
Build and Release / build (arm64, macos-latest, darwin) (push) Has been cancelled
Build and Release / build (x64, macos-latest, darwin) (push) Has been cancelled
Build and Release / build (x64, windows-latest, win32) (push) Has been cancelled
Build and Release / release (push) Has been cancelled
2026-01-15 19:47:44 +03:00
4 changed files with 47 additions and 86 deletions
+3 -3
View File
@@ -1,7 +1,7 @@
{ {
"name": "cheating-daddy", "name": "cheating-daddy",
"productName": "cheating-daddy", "productName": "cheating-daddy",
"version": "0.5.6", "version": "0.5.9",
"description": "cheating daddy", "description": "cheating daddy",
"main": "src/index.js", "main": "src/index.js",
"scripts": { "scripts": {
@@ -19,8 +19,8 @@
"cheating daddy ai assistant for interviews" "cheating daddy ai assistant for interviews"
], ],
"author": { "author": {
"name": "sohzm", "name": "ShiftyX1",
"email": "sohambharambe9@gmail.com" "email": "lead@pyserve.org"
}, },
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
+15 -2
View File
@@ -281,6 +281,7 @@ async function sendImageMessage(base64Image, prompt) {
let audioChunks = []; let audioChunks = [];
let lastAudioTime = 0; let lastAudioTime = 0;
const SILENCE_THRESHOLD_MS = 1500; // 1.5 seconds of silence const SILENCE_THRESHOLD_MS = 1500; // 1.5 seconds of silence
let silenceCheckTimer = null;
async function processAudioChunk(base64Audio, mimeType) { async function processAudioChunk(base64Audio, mimeType) {
if (!openaiClient) { if (!openaiClient) {
@@ -294,8 +295,20 @@ async function processAudioChunk(base64Audio, mimeType) {
audioChunks.push(buffer); audioChunks.push(buffer);
lastAudioTime = now; lastAudioTime = now;
// Check for silence (no new audio for SILENCE_THRESHOLD_MS) // Clear existing timer
// This is a simple approach - in production you'd want proper VAD if (silenceCheckTimer) {
clearTimeout(silenceCheckTimer);
}
// Set timer to check for silence
silenceCheckTimer = setTimeout(async () => {
const silenceDuration = Date.now() - lastAudioTime;
if (silenceDuration >= SILENCE_THRESHOLD_MS && audioChunks.length > 0) {
console.log('Silence detected, flushing audio for transcription...');
await flushAudioAndTranscribe();
}
}, SILENCE_THRESHOLD_MS);
return { success: true, buffering: true }; return { success: true, buffering: true };
} }
+1 -50
View File
@@ -295,9 +295,6 @@ async function startCapture(screenshotIntervalSeconds = 5, imageQuality = 'mediu
console.log('Linux capture started - system audio:', mediaStream.getAudioTracks().length > 0, 'microphone mode:', audioMode); console.log('Linux capture started - system audio:', mediaStream.getAudioTracks().length > 0, 'microphone mode:', audioMode);
} else { } else {
// Windows - use display media with loopback for system audio // Windows - use display media with loopback for system audio
logToMain('info', '=== Starting Windows audio capture ===');
cheatingDaddy.setStatus('Requesting screen & audio...');
mediaStream = await navigator.mediaDevices.getDisplayMedia({ mediaStream = await navigator.mediaDevices.getDisplayMedia({
video: { video: {
frameRate: 1, frameRate: 1,
@@ -313,6 +310,7 @@ async function startCapture(screenshotIntervalSeconds = 5, imageQuality = 'mediu
}, },
}); });
console.log('Windows capture started with loopback audio');
const audioTracks = mediaStream.getAudioTracks(); const audioTracks = mediaStream.getAudioTracks();
const videoTracks = mediaStream.getVideoTracks(); const videoTracks = mediaStream.getVideoTracks();
@@ -328,14 +326,8 @@ async function startCapture(screenshotIntervalSeconds = 5, imageQuality = 'mediu
})), })),
}); });
if (audioTracks.length === 0) {
logToMain('warn', 'WARNING: No audio tracks! User must check "Share audio" in screen picker dialog');
cheatingDaddy.setStatus('Warning: No audio - enable "Share audio" checkbox');
} else {
logToMain('info', 'Audio track acquired, setting up processing...');
// Setup audio processing for Windows loopback audio only // Setup audio processing for Windows loopback audio only
setupWindowsLoopbackProcessing(); setupWindowsLoopbackProcessing();
}
if (audioMode === 'mic_only' || audioMode === 'both') { if (audioMode === 'mic_only' || audioMode === 'both') {
let micStream = null; let micStream = null;
@@ -451,38 +443,16 @@ function setupLinuxSystemAudioProcessing() {
function setupWindowsLoopbackProcessing() { function setupWindowsLoopbackProcessing() {
// Setup audio processing for Windows loopback audio only // Setup audio processing for Windows loopback audio only
logToMain('info', 'Setting up Windows loopback audio processing...');
try {
audioContext = new AudioContext({ sampleRate: SAMPLE_RATE }); audioContext = new AudioContext({ sampleRate: SAMPLE_RATE });
logToMain('info', 'AudioContext created:', {
state: audioContext.state,
sampleRate: audioContext.sampleRate,
});
// Resume AudioContext if suspended (Chrome policy)
if (audioContext.state === 'suspended') {
logToMain('warn', 'AudioContext suspended, attempting resume...');
audioContext.resume().then(() => {
logToMain('info', 'AudioContext resumed successfully');
}).catch(err => {
logToMain('error', 'Failed to resume AudioContext:', err.message);
});
}
const source = audioContext.createMediaStreamSource(mediaStream); const source = audioContext.createMediaStreamSource(mediaStream);
audioProcessor = audioContext.createScriptProcessor(BUFFER_SIZE, 1, 1); audioProcessor = audioContext.createScriptProcessor(BUFFER_SIZE, 1, 1);
let audioBuffer = []; let audioBuffer = [];
const samplesPerChunk = SAMPLE_RATE * AUDIO_CHUNK_DURATION; const samplesPerChunk = SAMPLE_RATE * AUDIO_CHUNK_DURATION;
let chunkCount = 0;
let totalSamples = 0;
audioProcessor.onaudioprocess = async e => { audioProcessor.onaudioprocess = async e => {
const inputData = e.inputBuffer.getChannelData(0); const inputData = e.inputBuffer.getChannelData(0);
audioBuffer.push(...inputData); audioBuffer.push(...inputData);
totalSamples += inputData.length;
// Process audio in chunks // Process audio in chunks
while (audioBuffer.length >= samplesPerChunk) { while (audioBuffer.length >= samplesPerChunk) {
@@ -494,30 +464,11 @@ function setupWindowsLoopbackProcessing() {
data: base64Data, data: base64Data,
mimeType: 'audio/pcm;rate=24000', mimeType: 'audio/pcm;rate=24000',
}); });
chunkCount++;
// Log progress every 100 chunks (~10 seconds)
if (chunkCount === 1) {
logToMain('info', 'First audio chunk sent to AI');
cheatingDaddy.setStatus('Listening...');
} else if (chunkCount % 100 === 0) {
// Calculate max amplitude to check if we're getting real audio
const maxAmp = Math.max(...chunk.map(Math.abs));
logToMain('info', `Audio progress: ${chunkCount} chunks, maxAmplitude: ${maxAmp.toFixed(4)}`);
}
} }
}; };
source.connect(audioProcessor); source.connect(audioProcessor);
audioProcessor.connect(audioContext.destination); audioProcessor.connect(audioContext.destination);
logToMain('info', 'Windows audio processing pipeline connected');
} catch (err) {
logToMain('error', 'Error setting up Windows audio:', err.message, err.stack);
cheatingDaddy.setStatus('Audio error: ' + err.message);
}
} }
async function captureScreenshot(imageQuality = 'medium', isManual = false) { async function captureScreenshot(imageQuality = 'medium', isManual = false) {
+3 -6
View File
@@ -33,14 +33,11 @@ function createWindow(sendToRenderer, geminiSessionRef) {
}); });
const { session, desktopCapturer } = require('electron'); const { session, desktopCapturer } = require('electron');
// Setup display media request handler for screen capture
// Use system picker on all platforms to let user choose screen/window and audio
session.defaultSession.setDisplayMediaRequestHandler( session.defaultSession.setDisplayMediaRequestHandler(
(request, callback) => { (request, callback) => {
// Don't call callback - let system picker handle the selection desktopCapturer.getSources({ types: ['screen'] }).then(sources => {
// The system will call callback with user's choice callback({ video: sources[0], audio: 'loopback' });
console.log('Showing system screen picker dialog...'); });
}, },
{ useSystemPicker: true } { useSystemPicker: true }
); );