179 lines
4.1 KiB
TypeScript
179 lines
4.1 KiB
TypeScript
/* 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<Buffer> {
|
|
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.")
|
|
// }
|
|
|
|
|