cheat-exam/src/audioUtils.js
Илья Глазунов beb9034cd4 initial commit
2026-01-14 22:57:19 +03:00

136 lines
4.0 KiB
JavaScript

const fs = require('fs');
const path = require('path');
// Convert raw PCM to WAV format for easier playback and verification
function pcmToWav(pcmBuffer, outputPath, sampleRate = 24000, channels = 1, bitDepth = 16) {
const byteRate = sampleRate * channels * (bitDepth / 8);
const blockAlign = channels * (bitDepth / 8);
const dataSize = pcmBuffer.length;
// Create WAV header
const header = Buffer.alloc(44);
// "RIFF" chunk descriptor
header.write('RIFF', 0);
header.writeUInt32LE(dataSize + 36, 4); // File size - 8
header.write('WAVE', 8);
// "fmt " sub-chunk
header.write('fmt ', 12);
header.writeUInt32LE(16, 16); // Subchunk1Size (16 for PCM)
header.writeUInt16LE(1, 20); // AudioFormat (1 for PCM)
header.writeUInt16LE(channels, 22); // NumChannels
header.writeUInt32LE(sampleRate, 24); // SampleRate
header.writeUInt32LE(byteRate, 28); // ByteRate
header.writeUInt16LE(blockAlign, 32); // BlockAlign
header.writeUInt16LE(bitDepth, 34); // BitsPerSample
// "data" sub-chunk
header.write('data', 36);
header.writeUInt32LE(dataSize, 40); // Subchunk2Size
// Combine header and PCM data
const wavBuffer = Buffer.concat([header, pcmBuffer]);
// Write to file
fs.writeFileSync(outputPath, wavBuffer);
return outputPath;
}
// Analyze audio buffer for debugging
function analyzeAudioBuffer(buffer, label = 'Audio') {
const int16Array = new Int16Array(buffer.buffer, buffer.byteOffset, buffer.length / 2);
let minValue = 32767;
let maxValue = -32768;
let avgValue = 0;
let rmsValue = 0;
let silentSamples = 0;
for (let i = 0; i < int16Array.length; i++) {
const sample = int16Array[i];
minValue = Math.min(minValue, sample);
maxValue = Math.max(maxValue, sample);
avgValue += sample;
rmsValue += sample * sample;
if (Math.abs(sample) < 100) {
silentSamples++;
}
}
avgValue /= int16Array.length;
rmsValue = Math.sqrt(rmsValue / int16Array.length);
const silencePercentage = (silentSamples / int16Array.length) * 100;
console.log(`${label} Analysis:`);
console.log(` Samples: ${int16Array.length}`);
console.log(` Min: ${minValue}, Max: ${maxValue}`);
console.log(` Average: ${avgValue.toFixed(2)}`);
console.log(` RMS: ${rmsValue.toFixed(2)}`);
console.log(` Silence: ${silencePercentage.toFixed(1)}%`);
console.log(` Dynamic Range: ${20 * Math.log10(maxValue / (rmsValue || 1))} dB`);
return {
minValue,
maxValue,
avgValue,
rmsValue,
silencePercentage,
sampleCount: int16Array.length,
};
}
// Save audio buffer with metadata for debugging
function saveDebugAudio(buffer, type, timestamp = Date.now()) {
const homeDir = require('os').homedir();
const debugDir = path.join(homeDir, 'cheating-daddy-debug');
if (!fs.existsSync(debugDir)) {
fs.mkdirSync(debugDir, { recursive: true });
}
const pcmPath = path.join(debugDir, `${type}_${timestamp}.pcm`);
const wavPath = path.join(debugDir, `${type}_${timestamp}.wav`);
const metaPath = path.join(debugDir, `${type}_${timestamp}.json`);
// Save raw PCM
fs.writeFileSync(pcmPath, buffer);
// Convert to WAV for easy playback
pcmToWav(buffer, wavPath);
// Analyze and save metadata
const analysis = analyzeAudioBuffer(buffer, type);
fs.writeFileSync(
metaPath,
JSON.stringify(
{
timestamp,
type,
bufferSize: buffer.length,
analysis,
format: {
sampleRate: 24000,
channels: 1,
bitDepth: 16,
},
},
null,
2
)
);
console.log(`Debug audio saved: ${wavPath}`);
return { pcmPath, wavPath, metaPath };
}
module.exports = {
pcmToWav,
analyzeAudioBuffer,
saveDebugAudio,
};