/* eslint @typescript-eslint/no-var-requires: "off" */ "use strict"; import { ipcMain } from "electron"; import { backgroundMitt } from '@/modules/emitter'; const portAudio = require('naudiodon'); // Audio in and out stream objects. let ai: typeof portAudio.AudioIO | boolean = false; let ao: typeof portAudio.AudioIO | boolean = false; // Whether activly recording. let record = false; const audioContainer = { input: '', } const audioOptions = { channelCount: 1, sampleFormat: 16, sampleRate: 16000, deviceId: -1, closeOnError: false, } // Toggles record to true to begin capturing chunks. const onRecordingStart = (_event: any, _payload: any) => { console.log("AUDIO: Beginning audio capture.") record = true; } // Returns recorded audio to frontend and sets record to false. const onRecordingEnd = async (_event: any, payload: any) => { console.log("AUDIO:Sending audio to browser.") return new Promise((resolve, reject) => { try { resolve(audioContainer.input); record = false; } catch (e) { reject() } }); }; // Main audio function run by run.ts module. export function initAudioIO(): void { console.log("AUDIO:Starting io streams.") if (!ai) { // Initialize and start input stream. ai = new portAudio.AudioIO({ inOptions: audioOptions }); ai.setEncoding("hex"); ai.start(); // On each data chunk... ai.on('data', (chunk: string) => { // If recording, we capture the data. if (record) { console.log('AUDIO:Recording...') audioContainer.input += chunk; } // Else, we don't capture and also clear audioContainer. else { if (audioContainer.input.length) { audioContainer.input = ""; } } }); } if (!ao) { // Initialize and start input stream. ao = new portAudio.AudioIO({ outOptions: audioOptions }); ao.start(); } // Listen to record. console.log("AUDIO:Adding recording listeners.") ipcMain.removeAllListeners("start-recording"); ipcMain.on("start-recording", onRecordingStart); ipcMain.removeHandler("stop-recording"); ipcMain.handle("stop-recording", onRecordingEnd); } // ---Audio playback-------------------------------------------- // Split Buffer into an array of len-sized Buffers. function bufSplit(buf: Buffer, len: number): Array { const chunks = []; let i = 0; let L = len; while(i < buf.byteLength) { chunks.push(buf.slice(i, L)); i = L; L += len; } return chunks; } // Audio playback. export function play(input: string): void { // Format the audio. const audio = bufSplit( Buffer.from(input as string, 'hex'), 8192 ); // Called on end of write. const callback = () => { // We stop audio playback anim. backgroundMitt.emit('ipc-renderer', { endpoint: 'stop-playback-anim' }); } write(); // Iterate through audio array and write buffers to portAudio writable. function write() { let chunk: Buffer; let ok = true; let i = 0; do { chunk = audio[i]; if (i === audio.length - 1) { // write last chunk. ao.write(chunk, null, callback); } else { // check for backpreassure. ok = ao.write(chunk, null); } i++; } while (i < audio.length && ok); if (i < audio.length) { // Had to stop early! // Write some more once it drains. ao.once('drain', write); } } } // ------------------------------------------------------------- // Get's called on window close. export function stopStream() { console.log("AUDIO:Stopping audio stream.") if (ai) { ai.quit() } if (ao) { ao.quit() } console.log("AUDIO:Audio closed.") }