diff --git a/rawAudio.raw b/rawAudio.raw
deleted file mode 100644
index e69de29..0000000
diff --git a/rawAudio.wav b/rawAudio.wav
new file mode 100644
index 0000000..5582bc0
Binary files /dev/null and b/rawAudio.wav differ
diff --git a/src/background.ts b/src/background.ts
index 56d3419..71e7d68 100644
--- a/src/background.ts
+++ b/src/background.ts
@@ -6,8 +6,9 @@ import installExtension, { VUEJS_DEVTOOLS } from "electron-devtools-installer";
const isDevelopment = process.env.NODE_ENV !== "production";
import * as path from "path";
const fs = require('fs');
-
+import WebSocket from "ws";
const portAudio = require('naudiodon');
+console.log(portAudio.getDevices());
// Create an instance of AudioIO with inOptions (defaults are as below), which will return a ReadableStream
const audioOptions = {
@@ -18,12 +19,20 @@ const audioOptions = {
closeOnError: true // Close the stream if an audio error is detected, if set false then just log the error
}
+// Create an instance of AudioIO with outOptions (defaults are as below), which will return a WritableStream
+const ao = new portAudio.AudioIO({
+ outOptions: audioOptions
+});
+
let ai = new portAudio.AudioIO({
inOptions: audioOptions
});
-import WebSocket from "ws";
+const filename = "rawAudio.wav";
+// Create a write stream to write out to a raw audio file
+const writeStream = fs.createWriteStream(filename);
+ai.pipe(writeStream);
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win: BrowserWindow | null;
@@ -88,15 +97,19 @@ function createWindow() {
ipcMain.on('update-recorder', (Event: any, record: boolean) => {
if (record) {
ai.start();
- ai.on('data', (buf: Buffer) => {
- const base64Data = buf.toString('base64');
- audioData += base64Data;
- });
+ // ai.on('data', (buf: Buffer) => {
+ // const base64Data = buf.toString('base64');
+ // audioData += base64Data;
+ // });
} else {
ai.quit();
+
+ const audio = {
+ content: fs.readFileSync(filename).toString('base64'),
+ }
const message = {
text: "",
- audio: audioData
+ audio
}
ws.send(JSON.stringify(message))
ai = new portAudio.AudioIO({
diff --git a/src/components/UI/UIDetails/titleBar.vue b/src/components/UI/UIDetails/titleBar.vue
deleted file mode 100644
index e90db9c..0000000
--- a/src/components/UI/UIDetails/titleBar.vue
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/components/input/inputItem.vue b/src/components/input/inputItem.vue
deleted file mode 100644
index 2f41513..0000000
--- a/src/components/input/inputItem.vue
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
diff --git a/src/components/inputVisualizer/index.vue b/src/components/inputVisualizer/index.vue
new file mode 100644
index 0000000..a1cc955
--- /dev/null
+++ b/src/components/inputVisualizer/index.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
diff --git a/src/components/inputVisualizer/visualizer.ts b/src/components/inputVisualizer/visualizer.ts
new file mode 100644
index 0000000..63f6b33
--- /dev/null
+++ b/src/components/inputVisualizer/visualizer.ts
@@ -0,0 +1,92 @@
+import { ref, watch, onUnmounted, onMounted } from "vue";
+
+import useKeyDownHandler from "@/composables/keyDownHandler/useKeyDownHandler";
+
+import useMediaStream from "@/utils/mediaStream/useMediaStream";
+
+import useStreamVisualizer from './visualizerDetails/audioVisualizer';
+
+import useTextVisualizer from './visualizerDetails/textVisualizer';
+
+/*
+* Input visualizer
+* Will visualize text input by default as user types or
+* if user holds space bar down, input audio stream
+* will be visualized & recorded
+*/
+// NOTE: input params should probably be input and MediaStream
+export default function useInputVisualizer() {
+ const visualizeStream = ref(false);
+ const paths = ref([]);
+ const audioBubbleWidth = ref(10);
+ let stream: MediaStream;
+
+ const { keyDownHandler, input } = useKeyDownHandler();
+ const { textInputXoffset, renderTextInput } = useTextVisualizer(input);
+ const { visualizeStreamAsPaths, expandBubble } = useStreamVisualizer(visualizeStream);
+
+ // stops stream recording on space key up
+ function keyupHandler(e: any) {
+ if (e.key === " " && visualizeStream.value) {
+ console.log("stop recording");
+
+ // window.postMessage({
+ // myTypeField: 'update-recorder',
+ // record: false
+ // }, '*')
+
+ visualizeStream.value = false;
+ paths.value = []
+ audioBubbleWidth.value = 10;
+ input.value = ''
+ }
+ }
+
+ // add window event keyboard listeners
+ window.addEventListener("keydown", keyDownHandler);
+ window.addEventListener('keyup', keyupHandler);
+
+ // get stream & initialize recorder object on mount
+ onMounted(async () => {
+ stream = await useMediaStream();
+ })
+
+ // determine whether to render text or audio based one the keyboard input
+ watch(input, (input, prevInput) => {
+ if (prevInput !== "" || prevInput.length > 0) {
+ if (!visualizeStream.value) {
+ renderTextInput();
+ }
+ return;
+ }
+ if (input === " " && visualizeStream.value === false) {
+ console.log("record MediaStream");
+ visualizeStream.value = true;
+
+ // window.postMessage({
+ // myTypeField: 'update-recorder',
+ // record: true
+ // }, '*')
+
+ expandBubble(audioBubbleWidth);
+ visualizeStreamAsPaths(stream, paths, audioBubbleWidth);
+ return;
+ }
+ renderTextInput();
+ });
+
+ // remove Event Listeners on component unMount
+ onUnmounted(() => {
+ window.removeEventListener("keydown", keyDownHandler);
+ window.removeEventListener("keyup", keyupHandler);
+ });
+
+ return {
+ input,
+ visualizeStream,
+ textInputXoffset,
+ audioBubbleWidth,
+ paths
+ }
+
+}
diff --git a/src/components/input/inputDetails/audioInput.vue b/src/components/inputVisualizer/visualizerDetails/audio.vue
similarity index 50%
rename from src/components/input/inputDetails/audioInput.vue
rename to src/components/inputVisualizer/visualizerDetails/audio.vue
index 3c8b72b..09a395a 100644
--- a/src/components/input/inputDetails/audioInput.vue
+++ b/src/components/inputVisualizer/visualizerDetails/audio.vue
@@ -1,19 +1,14 @@
-
@@ -45,17 +40,14 @@
diff --git a/src/components/inputVisualizer/visualizerDetails/textAnime.ts b/src/components/inputVisualizer/visualizerDetails/textAnime.ts
new file mode 100644
index 0000000..8f5eafa
--- /dev/null
+++ b/src/components/inputVisualizer/visualizerDetails/textAnime.ts
@@ -0,0 +1,57 @@
+import AnimeFunc from "@/types/animejs/index";
+import { inject } from "vue";
+import { Ref } from "@/types/vueRef/index";
+/*
+ * Anime js animations used by input textController
+*/
+export default function useTextInputAnims() {
+
+ // imports animejs safely
+ let anime: AnimeFunc;
+ const animeInject: AnimeFunc | undefined = inject("animejs");
+ if (animeInject) anime = animeInject;
+
+ const animateSend = (el: HTMLElement, elHeight: number, input: Ref) => {
+ anime({
+ targets: el,
+ translateY: -elHeight + 15,
+ duration: 200,
+ easing: "easeInQuad",
+ complete: function() {
+ anime({
+ targets: el,
+ translateY: 50,
+ duration: 200,
+ easing: "easeOutQuad",
+ complete: function() {
+ input.value = "";
+ }
+ })
+ }
+ })
+ }
+
+ const inputAppear = (el: HTMLElement) => {
+ anime({
+ targets: el,
+ translateY: -10,
+ duration: 0
+ });
+ }
+
+ const verticalShiftInput = (el: HTMLElement, yTrans: number) => {
+ anime({
+ targets: el,
+ translateY: yTrans,
+ easing: "easeOutQuad",
+ duration: 150,
+ });
+ }
+
+ return {
+ animateSend,
+ verticalShiftInput,
+ inputAppear
+ }
+
+}
diff --git a/src/components/inputVisualizer/visualizerDetails/textVisualizer.ts b/src/components/inputVisualizer/visualizerDetails/textVisualizer.ts
new file mode 100644
index 0000000..6a27d48
--- /dev/null
+++ b/src/components/inputVisualizer/visualizerDetails/textVisualizer.ts
@@ -0,0 +1,49 @@
+import { ref, computed, inject } from 'vue';
+import useTextInputAnims from "./textAnime";
+import { Ref } from "@/types/vueRef/index";
+/*
+ * text rendering logic used by inputController
+*/
+export default function useTextVisualizer(input: Ref) {
+ const inputHeight = ref(42);
+ const inputWidth = ref(0);
+
+ const emitter: any = inject("mitt");
+ const { animateSend, verticalShiftInput, inputAppear } = useTextInputAnims();
+
+ async function renderTextInput() {
+ // first, get input dimensions
+ const inputDivs = await document.getElementsByClassName("inputBubble");
+ const height = inputDivs[0].getBoundingClientRect().height;
+ inputWidth.value = inputDivs[0].getBoundingClientRect().width;
+ // then, query for foreignObject div
+ const foreignObjectDiv = document.getElementsByClassName(
+ "inputContainer"
+ );
+ const foreignEl = foreignObjectDiv[0] as HTMLElement;
+ if (height !== inputHeight.value) {
+ inputHeight.value = height;
+ }
+ if (inputHeight.value === 0) {
+ inputAppear(foreignEl)
+ } else if (inputHeight.value > 36) {
+ const yTrans = -inputHeight.value + 30;
+ verticalShiftInput(foreignEl, yTrans);
+ }
+ }
+
+ emitter.on("newSelfMessage", async (payload: any) => {
+ const inputEl = document.getElementsByClassName("inputContainer");
+ const foreignEl = inputEl[0] as HTMLElement;
+ animateSend(foreignEl, inputHeight.value, input);
+ });
+
+ const textInputXoffset = computed(() => {
+ return `calc(50% - ${inputWidth.value / 2})`;
+ });
+
+ return {
+ textInputXoffset,
+ renderTextInput
+ }
+}
diff --git a/src/components/UI/index.vue b/src/components/messenger/index.vue
similarity index 72%
rename from src/components/UI/index.vue
rename to src/components/messenger/index.vue
index 8351c94..a79e2ac 100644
--- a/src/components/UI/index.vue
+++ b/src/components/messenger/index.vue
@@ -6,24 +6,26 @@
xmlns:xlink="http://www.w3.org/1999/xlink"
@click="emittMessage"
>
-
-
-
-
+
+
diff --git a/testAudio.js b/testAudio.js
new file mode 100644
index 0000000..dd6b6a5
--- /dev/null
+++ b/testAudio.js
@@ -0,0 +1,21 @@
+const fs = require('fs');
+const portAudio = require('naudiodon');
+
+// Create an instance of AudioIO with outOptions (defaults are as below), which will return a WritableStream
+const ao = new portAudio.AudioIO({
+ outOptions: {
+ channelCount: 2,
+ sampleFormat: portAudio.SampleFormat16Bit,
+ sampleRate: 48000,
+ deviceId: -1, // Use -1 or omit the deviceId to select the default device
+ closeOnError: true // Close the stream if an audio error is detected, if set false then just log the error
+ }
+});
+
+// Create a stream to pipe into the AudioOutput
+// Note that this does not strip the WAV header so a click will be heard at the beginning
+const rs = fs.createReadStream('rawAudio.wav');
+
+// Start piping data and start streaming
+rs.pipe(ao);
+ao.start();
\ No newline at end of file