124 lines
2.9 KiB
TypeScript
124 lines
2.9 KiB
TypeScript
const FFT = require('fft.js');
|
|
import { globalShortcut } from "electron";
|
|
const nodeAudio = require('@crimata/nodeaudio');
|
|
|
|
import { sendMessage } from "@/io";
|
|
import { updateTray } from "@/tray";
|
|
import { backgroundMitt, ipcEmit } from '@/composables/useEmitter';
|
|
|
|
let inputDevice: number;
|
|
let outputDevice: number;
|
|
|
|
let setWriteId: ReturnType<typeof setTimeout>;
|
|
let setStreamsId: ReturnType<typeof setTimeout>;
|
|
|
|
let playbackId: string;
|
|
|
|
const streamState = { rec: false, pb: false };
|
|
|
|
const chunks: Int16Array[] = [];
|
|
|
|
const fft = new FFT(4);
|
|
|
|
function calcFFT(int16Arr: Int16Array)
|
|
{
|
|
const out = fft.createComplexArray();
|
|
fft.realTransform(out, int16Arr);
|
|
return out;
|
|
}
|
|
|
|
backgroundMitt.on("read", (int16Arr: Int16Array) =>
|
|
{
|
|
if (streamState.rec)
|
|
{
|
|
chunks.push(int16Arr);
|
|
ipcEmit("recording", calcFFT(int16Arr)); /* fft used for animation */
|
|
}
|
|
}
|
|
);
|
|
|
|
backgroundMitt.on("write", (int16Arr: Int16Array) =>
|
|
{
|
|
ipcEmit("playback", { uid: playbackId, fft: calcFFT(int16Arr) });
|
|
streamState.pb = true;
|
|
|
|
clearTimeout(setWriteId);
|
|
setWriteId = setTimeout(() => streamState.pb = false, 1000);
|
|
}
|
|
);
|
|
|
|
function setStreams()
|
|
{
|
|
const defaultInput = nodeAudio.core.GetDefaultInputDevice();
|
|
const defaultOutput = nodeAudio.core.GetDefaultOutputDevice();
|
|
|
|
if (inputDevice !== defaultInput)
|
|
{
|
|
inputDevice = defaultInput;
|
|
nodeAudio.core.CloseInputStream(inputDevice);
|
|
nodeAudio.core.OpenInputStream(inputDevice);
|
|
}
|
|
|
|
if (outputDevice !== defaultOutput)
|
|
{
|
|
outputDevice = defaultOutput;
|
|
nodeAudio.core.CloseOutputStream(outputDevice);
|
|
nodeAudio.core.OpenOutputStream(outputDevice);
|
|
}
|
|
}
|
|
|
|
function startRecording()
|
|
{
|
|
streamState.rec = true;
|
|
updateTray("recording", streamState.rec);
|
|
}
|
|
|
|
function stopRecording()
|
|
{
|
|
streamState.rec = false;
|
|
updateTray("recording", streamState.rec);
|
|
|
|
/* Send the data to the platform right away */
|
|
sendMessage({
|
|
text: false,
|
|
audio: nodeAudio.utils.mergeChunks(chunks)
|
|
} as Raw);
|
|
|
|
chunks.length = 0;
|
|
}
|
|
|
|
function InitAudio()
|
|
{
|
|
nodeAudio.core.Initialize(backgroundMitt.emit.bind(backgroundMitt));
|
|
setStreamsId = setInterval(setStreams, 2000); // sense device
|
|
|
|
/* Toggle recording switch with global shortcut */
|
|
globalShortcut.register('CommandOrControl+R', () =>
|
|
{
|
|
if (streamState.rec)
|
|
{
|
|
stopRecording();
|
|
}
|
|
else
|
|
{
|
|
startRecording();
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
/* Will override an existing playback process */
|
|
function playback(audio: Int16Array, uid: string)
|
|
{
|
|
playbackId = uid; /* uid of message */
|
|
// nodeAudio.core.CancelPlayback(); /* terminate any current playback */
|
|
nodeAudio.core.WriteToOutputStream(audio.buffer);
|
|
}
|
|
|
|
function TerminateAudio()
|
|
{
|
|
clearInterval(setStreamsId);
|
|
nodeAudio.core.Terminate();
|
|
}
|
|
|
|
export { InitAudio, TerminateAudio, playback, streamState };
|