cleaning some things up
This commit is contained in:
parent
7ea09b99f0
commit
9bf01c0a33
12 changed files with 48 additions and 694 deletions
12
gcloud.json
12
gcloud.json
|
|
@ -1,12 +0,0 @@
|
||||||
{
|
|
||||||
"type": "service_account",
|
|
||||||
"project_id": "crimata",
|
|
||||||
"private_key_id": "3a324fa3fcca4cea589e4b5007f4142db4b3a84d",
|
|
||||||
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCxFJOjhIkYlyhn\nHjtpFC5EOwzNFZem6k6SS7zcgWbNLe9mcBsiTKCjpxItzkIo0pnT32yqw+AKsqpv\n6+LVyzzjfB7vtEUflvg6KgmYMS4ixOTCEbN3e2qj1t2RnITXZR2VGSGdoKKyqZm0\ntWrmexQvSN20cByL1vfxD3E69oYFGhVraa0zmm94WXHAzofT6DkvGdrbK1s1G9jI\nG3mdQ1jN71tghyloeHL+R/1iKQoZFYTkVe6yWvccSNAZelt/5sciCKRTCArXj89X\nLwU6jGOKIsyqx3BeUyWaSS59xpnGX3meSf5Q9U+j45ePUiqO6TTleF+kWiBwjUpp\nfeYcwEIDAgMBAAECggEAElkN9R7p75zV6F1XDYu0QYiWyncmx/o2Gu1zC6vywWa1\ni/korpSe/mX0ub9J1p3/p1balRUHlUQu6brYvYs56140fGTC1sOXQ7uQU+8glySs\niTk5TbOBeKluOsSrdP/6oTTB6Wm4AegVz4YOpgPxsvaLVkNZidnDxfvyIQhjGYsO\nsvRaw48eEmGOa1uvDhgvPOxiDp5YEdxqSue/7ylzjBdKecDyLgTSDDf/p+FwIx5B\n2d/0uRgQBaAk6D/DNOvN8GrYyakFmlDjE72nrBGAJq50UyV6THOSDk+8cK+2QiS1\nEDCFufj+7Dk1EBfBFUFY8ZBB/pphpEWyLoFl7NzGCQKBgQDaj/KOOcj6BkkTj1WM\nRW3KhOE3nhsCQEgQGCflpycEAZj6J7ktBG2/4teDt9sr+Epx8ymnQCZa9pmysdrD\nU0aWku1qUx0IUKTtZbAEbB+AnrWYDYkIWU8kRUUvNHNTBg43VyuohV9wprPih138\nHWMeNac82/XICz2A3rv+Xkx8KwKBgQDPaaEEGWVs3nIhf/p3M3/xWNKt8fhAERpG\n0YpTjgHW3FQFYjiCTTIcuLO3qsDGmfIAB9tL37s2Y3e2wKBGUj/o69Fr7sWVPdsX\nJtUc0+KiG2Bs0hsyZPdk9UQiQX8y2eq1NZW7vEoxWso1bM64WURB5N8ocWePNJJx\nL2Oe75rtiQKBgQDLfQQOiRxmNF3rOSMkAywyRr9NQgXRdbniSiszNQotP7OHDF7q\n29m2suOGfjIv4O6m4wdf8WkEfd4hsleETc9Ft6wVtyYrrLGxWWCk7WnzHVDjLY7s\n2AHIOjostf+9R8EKoz1BnFN8laibev71EQNMiBWZow1VX6m2hymurWs2mwKBgQCb\nuY3n2v14qOb92e1+U89KsEq1yMd/qpeU9jwqEaO14wS+aglNY5ItWEuuqWhFdE3q\n0ftHUzpnUnUOZD+xrI1JXsyEgegc7i0xi7lUBI3S8kUKTxGWW5IXXcKDCbPrxQtg\ndFPweSUnOyg4xnHKnVMPOjyGS+bZ8TnF+zOLoBAtKQKBgF6hAOIwF1DheXT4p8xS\nlSGhl8dBxjsJDVwbnFWQceGzGnq1e3ncy/UItt42UCDVRWTH4r7pQ9eKyd/3foUD\n6KDIcCrYo2KJh6ZNFJ67XF30U+TuEg5U/9jTbWqDOwYVXcMcnzQE4ZiGBxVHL6xr\nNc8FRW5+7xh4zNfzhGUGt5kt\n-----END PRIVATE KEY-----\n",
|
|
||||||
"client_email": "crimata-service-account@crimata.iam.gserviceaccount.com",
|
|
||||||
"client_id": "102914612880037864172",
|
|
||||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
|
||||||
"token_uri": "https://oauth2.googleapis.com/token",
|
|
||||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
|
||||||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/crimata-service-account%40crimata.iam.gserviceaccount.com"
|
|
||||||
}
|
|
||||||
|
|
@ -5,11 +5,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
html,
|
|
||||||
body {
|
html, body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.7 KiB |
201
src/audio.ts
201
src/audio.ts
|
|
@ -1,201 +0,0 @@
|
||||||
function main(
|
|
||||||
encoding = 'LINEAR16',
|
|
||||||
sampleRateHertz = 16000,
|
|
||||||
languageCode = 'en-US',
|
|
||||||
streamingLimit = 290000
|
|
||||||
) {
|
|
||||||
// [START speech_transcribe_infinite_streaming]
|
|
||||||
|
|
||||||
// const encoding = 'LINEAR16';
|
|
||||||
// const sampleRateHertz = 16000;
|
|
||||||
// const languageCode = 'en-US';
|
|
||||||
// const streamingLimit = 10000; // ms - set to low number for demo purposes
|
|
||||||
|
|
||||||
const chalk = require('chalk');
|
|
||||||
const {Writable} = require('stream');
|
|
||||||
const recorder = require('node-record-lpcm16');
|
|
||||||
const fs = require('fs');
|
|
||||||
const writeStream = fs.createWriteStream('test.wav', { encoding: 'binary'});
|
|
||||||
|
|
||||||
// // Imports the Google Cloud client library
|
|
||||||
// // Currently, only v1p1beta1 contains result-end-time
|
|
||||||
// const speech = require('@google-cloud/speech').v1p1beta1;
|
|
||||||
|
|
||||||
// const client = new speech.SpeechClient();
|
|
||||||
|
|
||||||
// const config = {
|
|
||||||
// encoding: encoding,
|
|
||||||
// sampleRateHertz: sampleRateHertz,
|
|
||||||
// languageCode: languageCode,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const request = {
|
|
||||||
// config,
|
|
||||||
// interimResults: true,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// let recognizeStream = null;
|
|
||||||
// let restartCounter = 0;
|
|
||||||
// let audioInput = [];
|
|
||||||
// let lastAudioInput = [];
|
|
||||||
// let resultEndTime = 0;
|
|
||||||
// let isFinalEndTime = 0;
|
|
||||||
// let finalRequestEndTime = 0;
|
|
||||||
// let newStream = true;
|
|
||||||
// let bridgingOffset = 0;
|
|
||||||
// let lastTranscriptWasFinal = false;
|
|
||||||
|
|
||||||
// function startStream() {
|
|
||||||
// // Clear current audioInput
|
|
||||||
// audioInput = [];
|
|
||||||
// // Initiate (Reinitiate) a recognize stream
|
|
||||||
// recognizeStream = client
|
|
||||||
// .streamingRecognize(request)
|
|
||||||
// .on('error', err => {
|
|
||||||
// if (err.code === 11) {
|
|
||||||
// // restartStream();
|
|
||||||
// } else {
|
|
||||||
// console.error('API request error ' + err);
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// .on('data', speechCallback);
|
|
||||||
|
|
||||||
// // Restart stream when streamingLimit expires
|
|
||||||
// setTimeout(restartStream, streamingLimit);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const speechCallback = stream => {
|
|
||||||
// // Convert API result end time from seconds + nanoseconds to milliseconds
|
|
||||||
// resultEndTime =
|
|
||||||
// stream.results[0].resultEndTime.seconds * 1000 +
|
|
||||||
// Math.round(stream.results[0].resultEndTime.nanos / 1000000);
|
|
||||||
|
|
||||||
// // Calculate correct time based on offset from audio sent twice
|
|
||||||
// const correctedTime =
|
|
||||||
// resultEndTime - bridgingOffset + streamingLimit * restartCounter;
|
|
||||||
|
|
||||||
// process.stdout.clearLine();
|
|
||||||
// process.stdout.cursorTo(0);
|
|
||||||
// let stdoutText = '';
|
|
||||||
// if (stream.results[0] && stream.results[0].alternatives[0]) {
|
|
||||||
// stdoutText =
|
|
||||||
// correctedTime + ': ' + stream.results[0].alternatives[0].transcript;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (stream.results[0].isFinal) {
|
|
||||||
// process.stdout.write(chalk.green(`${stdoutText}\n`));
|
|
||||||
|
|
||||||
// isFinalEndTime = resultEndTime;
|
|
||||||
// lastTranscriptWasFinal = true;
|
|
||||||
// } else {
|
|
||||||
// // Make sure transcript does not exceed console character length
|
|
||||||
// if (stdoutText.length > process.stdout.columns) {
|
|
||||||
// stdoutText =
|
|
||||||
// stdoutText.substring(0, process.stdout.columns - 4) + '...';
|
|
||||||
// }
|
|
||||||
// process.stdout.write(chalk.red(`${stdoutText}`));
|
|
||||||
|
|
||||||
// lastTranscriptWasFinal = false;
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const audioInputStreamTransform = new Writable({
|
|
||||||
// write(chunk, encoding, next) {
|
|
||||||
// if (newStream && lastAudioInput.length !== 0) {
|
|
||||||
// // Approximate math to calculate time of chunks
|
|
||||||
// const chunkTime = streamingLimit / lastAudioInput.length;
|
|
||||||
// if (chunkTime !== 0) {
|
|
||||||
// if (bridgingOffset < 0) {
|
|
||||||
// bridgingOffset = 0;
|
|
||||||
// }
|
|
||||||
// if (bridgingOffset > finalRequestEndTime) {
|
|
||||||
// bridgingOffset = finalRequestEndTime;
|
|
||||||
// }
|
|
||||||
// const chunksFromMS = Math.floor(
|
|
||||||
// (finalRequestEndTime - bridgingOffset) / chunkTime
|
|
||||||
// );
|
|
||||||
// bridgingOffset = Math.floor(
|
|
||||||
// (lastAudioInput.length - chunksFromMS) * chunkTime
|
|
||||||
// );
|
|
||||||
|
|
||||||
// for (let i = chunksFromMS; i < lastAudioInput.length; i++) {
|
|
||||||
// recognizeStream.write(lastAudioInput[i]);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// newStream = false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// audioInput.push(chunk);
|
|
||||||
|
|
||||||
// if (recognizeStream) {
|
|
||||||
// recognizeStream.write(chunk);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// next();
|
|
||||||
// },
|
|
||||||
|
|
||||||
// final() {
|
|
||||||
// if (recognizeStream) {
|
|
||||||
// recognizeStream.end();
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
// function restartStream() {
|
|
||||||
// if (recognizeStream) {
|
|
||||||
// recognizeStream.end();
|
|
||||||
// recognizeStream.removeListener('data', speechCallback);
|
|
||||||
// recognizeStream = null;
|
|
||||||
// }
|
|
||||||
// if (resultEndTime > 0) {
|
|
||||||
// finalRequestEndTime = isFinalEndTime;
|
|
||||||
// }
|
|
||||||
// resultEndTime = 0;
|
|
||||||
|
|
||||||
// lastAudioInput = [];
|
|
||||||
// lastAudioInput = audioInput;
|
|
||||||
|
|
||||||
// restartCounter++;
|
|
||||||
|
|
||||||
// if (!lastTranscriptWasFinal) {
|
|
||||||
// process.stdout.write('\n');
|
|
||||||
// }
|
|
||||||
// process.stdout.write(
|
|
||||||
// chalk.yellow(`${streamingLimit * restartCounter}: RESTARTING REQUEST\n`)
|
|
||||||
// );
|
|
||||||
|
|
||||||
// newStream = true;
|
|
||||||
|
|
||||||
// startStream();
|
|
||||||
// }
|
|
||||||
// Start recording and send the microphone input to the Speech API
|
|
||||||
recorder
|
|
||||||
.record({
|
|
||||||
sampleRateHertz: sampleRateHertz,
|
|
||||||
threshold: 0, // Silence threshold
|
|
||||||
silence: 1000,
|
|
||||||
keepSilence: true,
|
|
||||||
recordProgram: 'rec', // Try also "arecord" or "sox"
|
|
||||||
})
|
|
||||||
.stream()
|
|
||||||
.on('error', err => {
|
|
||||||
console.error('Audio recording error ' + err);
|
|
||||||
})
|
|
||||||
.pipe(audioInputStreamTransform);
|
|
||||||
|
|
||||||
console.log('');
|
|
||||||
console.log('Listening, press Ctrl+C to stop.');
|
|
||||||
console.log('');
|
|
||||||
console.log('End (ms) Transcript Results/Status');
|
|
||||||
console.log('=========================================================');
|
|
||||||
|
|
||||||
// startStream();
|
|
||||||
// [END speech_transcribe_infinite_streaming]
|
|
||||||
}
|
|
||||||
|
|
||||||
// process.on('unhandledRejection', err => {
|
|
||||||
// console.error(err.message);
|
|
||||||
// process.exitCode = 1;
|
|
||||||
// });
|
|
||||||
|
|
||||||
main(...process.argv.slice(2));
|
|
||||||
|
|
@ -1,67 +1,11 @@
|
||||||
"use strict";
|
|
||||||
|
|
||||||
import { app, protocol, BrowserWindow, ipcMain } from "electron";
|
import { app, protocol, BrowserWindow, ipcMain } from "electron";
|
||||||
import { createProtocol } from "vue-cli-plugin-electron-builder/lib";
|
import { createProtocol } from "vue-cli-plugin-electron-builder/lib";
|
||||||
import installExtension, { VUEJS_DEVTOOLS } from "electron-devtools-installer";
|
import installExtension, { VUEJS_DEVTOOLS } from "electron-devtools-installer";
|
||||||
const isDevelopment = process.env.NODE_ENV !== "production";
|
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
const fs = require('fs');
|
|
||||||
import WebSocket from "ws";
|
import WebSocket from "ws";
|
||||||
const portAudio = require('naudiodon');
|
|
||||||
const speech = require('@google-cloud/speech');
|
|
||||||
|
|
||||||
const client = new speech.SpeechClient();
|
|
||||||
|
|
||||||
const encoding = 'LINEAR16';
|
|
||||||
const sampleRateHertz = 16000;
|
|
||||||
const languageCode = 'en-US';
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
encoding: encoding,
|
|
||||||
sampleRateHertz: sampleRateHertz,
|
|
||||||
languageCode: languageCode,
|
|
||||||
};
|
|
||||||
const request = {
|
|
||||||
config,
|
|
||||||
interimResults: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const speechCallback = (d: any) => {
|
|
||||||
console.log(d)
|
|
||||||
}
|
|
||||||
|
|
||||||
const recognizeStream = client
|
|
||||||
.streamingRecognize(request)
|
|
||||||
.on('error', err => {
|
|
||||||
if (err.code === 11) {
|
|
||||||
// restartStream();
|
|
||||||
} else {
|
|
||||||
console.error('API request error ' + err);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on('data', speechCallback);
|
|
||||||
|
|
||||||
|
|
||||||
// Create an instance of AudioIO with inOptions (defaults are as below), which will return a ReadableStream
|
const isDevelopment = process.env.NODE_ENV !== "production";
|
||||||
const audioOptions = {
|
|
||||||
channelCount: 2,
|
|
||||||
sampleFormat: portAudio.sampleFormat16Bit,
|
|
||||||
sampleRate: 16000,
|
|
||||||
deviceId: -1, // Use -1 or omit the deviceId to select the default device
|
|
||||||
closeOnError: false // Close the stream if an audio error is detected, if set false then just log the error
|
|
||||||
}
|
|
||||||
|
|
||||||
const ai = new portAudio.AudioIO({
|
|
||||||
inOptions: audioOptions
|
|
||||||
});
|
|
||||||
ai.pipe(recognizeStream);
|
|
||||||
ai.start();
|
|
||||||
|
|
||||||
|
|
||||||
// const filename = "rawAudio.wav";
|
|
||||||
// Create a write stream to write out to a raw audio file
|
|
||||||
// const writeStream = fs.createWriteStream(filename, { encoding: 'binary'});
|
|
||||||
// ai.pipe(writeStream);
|
|
||||||
|
|
||||||
// Keep a global reference of the window object, if you don't, the window will
|
// 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.
|
// be closed automatically when the JavaScript object is garbage collected.
|
||||||
|
|
@ -73,18 +17,28 @@ protocol.registerSchemesAsPrivileged([
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function createWindow() {
|
function createWindow() {
|
||||||
|
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
win = new BrowserWindow({
|
win = new BrowserWindow({
|
||||||
width: 450,
|
width: 350,
|
||||||
height: 1025,
|
height: 520,
|
||||||
resizable: true,
|
resizable: false,
|
||||||
|
maximizable: false,
|
||||||
|
|
||||||
|
// Postition at launch
|
||||||
|
x: 100,
|
||||||
|
y: 100,
|
||||||
|
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
// Use pluginOptions.nodeIntegration, leave this alone
|
devTools: false,
|
||||||
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
|
|
||||||
nodeIntegration: (process.env
|
nodeIntegration:
|
||||||
.ELECTRON_NODE_INTEGRATION as unknown) as boolean,
|
(process.env.ELECTRON_NODE_INTEGRATION as unknown) as boolean,
|
||||||
|
|
||||||
preload: path.join(__dirname, "preload.js")
|
preload: path.join(__dirname, "preload.js")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (process.env.WEBPACK_DEV_SERVER_URL) {
|
if (process.env.WEBPACK_DEV_SERVER_URL) {
|
||||||
|
|
@ -98,12 +52,17 @@ function createWindow() {
|
||||||
win.loadURL("app://./index.html");
|
win.loadURL("app://./index.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
win.webContents.on('did-finish-load', () => {
|
win.webContents.on('did-finish-load', () => {
|
||||||
|
|
||||||
|
// Define the websocket
|
||||||
const ws = new WebSocket("ws://localhost:8080");
|
const ws = new WebSocket("ws://localhost:8080");
|
||||||
|
|
||||||
|
// Send ID on open.
|
||||||
ws.on("open", function open() {
|
ws.on("open", function open() {
|
||||||
// ws.send("hernandeze2@xavier.edu");
|
ws.send("gundersena@crimata.com");
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on("message", (message: any) => {
|
ws.on("message", (message: any) => {
|
||||||
const parsed = JSON.parse(message);
|
const parsed = JSON.parse(message);
|
||||||
console.log('new message', message)
|
console.log('new message', message)
|
||||||
|
|
@ -124,21 +83,7 @@ function createWindow() {
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('update-recorder', (Event: any, record: boolean) => {
|
ipcMain.on('update-recorder', (Event: any, record: boolean) => {
|
||||||
if (record) {
|
|
||||||
// ai.start();
|
// ai.start();
|
||||||
} else {
|
|
||||||
// ai.quit();
|
|
||||||
|
|
||||||
// const audio = {
|
|
||||||
// content: fs.readFileSync(filename).toString('base64'),
|
|
||||||
// }
|
|
||||||
// const message = {
|
|
||||||
// text: "",
|
|
||||||
// audio: audioData
|
|
||||||
// }
|
|
||||||
// ws.send(JSON.stringify(message))
|
|
||||||
// audioData = "";
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
// mock messages
|
|
||||||
import Messages from "./mockMessages";
|
|
||||||
|
|
||||||
// child components
|
// child components
|
||||||
import MessageVisualizer from "./messageVisualizer/index.vue";
|
import MessageVisualizer from "./messageVisualizer/index.vue";
|
||||||
|
|
@ -23,44 +21,40 @@ import TextAudioInput from "@/components/taInput/index.vue";
|
||||||
import { Mitt } from "@/types/mitt/index";
|
import { Mitt } from "@/types/mitt/index";
|
||||||
|
|
||||||
import { defineComponent, ref, inject } from "vue";
|
import { defineComponent, ref, inject } from "vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
|
||||||
name: "Messenger",
|
name: "Messenger",
|
||||||
components: { MessageVisualizer, TextAudioInput },
|
components: { MessageVisualizer, TextAudioInput },
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const messages = Messages;
|
|
||||||
const count = ref(0);
|
const count = ref(0);
|
||||||
|
|
||||||
// imports mitt Emitter safely
|
// imports mitt Emitter safely
|
||||||
let emitter: Mitt;
|
let emitter: Mitt;
|
||||||
const emitterInject: Mitt | undefined = inject("mitt");
|
const emitterInject: Mitt | undefined = inject("mitt");
|
||||||
if (emitterInject) emitter = emitterInject;
|
|
||||||
|
|
||||||
function emittMessage() {
|
if (emitterInject) {
|
||||||
if (count.value < messages.length) {
|
emitter = emitterInject;
|
||||||
emitter.emit("renderMessage", messages[count.value]);
|
|
||||||
count.value++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render message when render-message event called.
|
||||||
window.ipcRenderer.on("render-message", (event, payload) => {
|
window.ipcRenderer.on("render-message", (event, payload) => {
|
||||||
console.log(payload.message);
|
console.log(payload.message);
|
||||||
emitter.emit("renderMessage", payload.message);
|
emitter.emit("renderMessage", payload.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
|
||||||
emittMessage,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
#messenger {
|
#messenger {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
height: 500px;
|
height: 500px;
|
||||||
background-color: #e6e6e6;
|
background-color: #e6e6e6;
|
||||||
box-shadow: 0px 3px 10px 0px rgba(0, 0, 0, 0.3);
|
box-shadow: 0px 3px 10px 0px rgba(0, 0, 0, 0.3);
|
||||||
// border-radius: 20px;
|
// border-radius: 20px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
||||||
<template>
|
|
||||||
<defs>
|
|
||||||
<radialGradient
|
|
||||||
id="radial-gradient"
|
|
||||||
cx="0.5"
|
|
||||||
cy="0.7"
|
|
||||||
:r="radius"
|
|
||||||
gradientTransform="translate(-1.489 1.5) rotate(-90) scale(1 1.989)"
|
|
||||||
gradientUnits="objectBoundingBox"
|
|
||||||
>
|
|
||||||
<stop offset="0" stop-color="#fff" />
|
|
||||||
<stop id="sound" offset="1" stop-color="rgb(230, 230, 230)" />
|
|
||||||
</radialGradient>
|
|
||||||
<filter id="Rectangle_410" x="0" y="0" width="380" height="530" filterUnits="userSpaceOnUse">
|
|
||||||
<feOffset dy="3" input="SourceAlpha" />
|
|
||||||
<feGaussianBlur stdDeviation="5" result="blur" />
|
|
||||||
<feFlood flood-opacity="0.129" />
|
|
||||||
<feComposite operator="in" in2="blur" />
|
|
||||||
<feComposite in="SourceGraphic" />
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<g transform="matrix(1, 0, 0, 1, 0, 0)" filter="url(#Rectangle_410)">
|
|
||||||
<rect
|
|
||||||
id="Rectangle_410-2"
|
|
||||||
data-name="Rectangle 410"
|
|
||||||
width="350"
|
|
||||||
height="500"
|
|
||||||
fill="url(#radial-gradient)"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { defineComponent, onMounted, ref, inject, watch } from "vue";
|
|
||||||
export default defineComponent({
|
|
||||||
name: "voiceBackground",
|
|
||||||
setup() {
|
|
||||||
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
|
||||||
const analyzer = audioCtx.createAnalyser();
|
|
||||||
analyzer.fftSize = 2048;
|
|
||||||
const xInitial = 24;
|
|
||||||
const xFinal = 640;
|
|
||||||
// analyzer.minDecibels = -90;
|
|
||||||
analyzer.smoothingTimeConstant = 0.85;
|
|
||||||
const dataArray = new Uint8Array(analyzer.frequencyBinCount);
|
|
||||||
const sampleRate = 16000;
|
|
||||||
let gradient;
|
|
||||||
const radius = ref(0);
|
|
||||||
const think = ref(false);
|
|
||||||
const aiOffset = ref(0.7);
|
|
||||||
|
|
||||||
const anime = inject("animejs");
|
|
||||||
let thinkAnimation;
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
gradient = document.getElementById("radial-gradient");
|
|
||||||
|
|
||||||
thinkAnimation = anime({
|
|
||||||
targets: gradient,
|
|
||||||
loop: true,
|
|
||||||
cy: "1.3",
|
|
||||||
direction: "alternate",
|
|
||||||
easing: "easeInOutCirc",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function convertBlock(buffer) {
|
|
||||||
// incoming data is an ArrayBuffer
|
|
||||||
const incomingData = new Uint8Array(buffer); // create a uint8 view on the ArrayBuffer
|
|
||||||
const l = incomingData.length; // length, we need this for the loop
|
|
||||||
const outputData = new Float32Array(incomingData.length); // create the Float32Array for output
|
|
||||||
for (let i = 0; i < l; i++) {
|
|
||||||
outputData[i] = (incomingData[i] - 128) / 128.0; // convert audio to float
|
|
||||||
}
|
|
||||||
return outputData; // return the Float32Array
|
|
||||||
}
|
|
||||||
|
|
||||||
window.ipcRenderer.on("render-audio", (event, payload) => {
|
|
||||||
const floatArray = convertBlock(payload.audio.buffer);
|
|
||||||
const buffer = audioCtx.createBuffer(1, floatArray.length, sampleRate);
|
|
||||||
buffer.copyToChannel(floatArray, 0, 0);
|
|
||||||
const source = audioCtx.createBufferSource();
|
|
||||||
source.buffer = buffer;
|
|
||||||
source.connect(analyzer);
|
|
||||||
source.start();
|
|
||||||
analyzer.getByteFrequencyData(dataArray);
|
|
||||||
const dataView = dataArray.slice(xInitial, xFinal);
|
|
||||||
const sum = dataView.reduce((a, b) => a + b);
|
|
||||||
const average = sum / dataView.length;
|
|
||||||
const scaled = average / 255;
|
|
||||||
const zeroed = scaled - 0.6;
|
|
||||||
if (zeroed > 0) {
|
|
||||||
radius.value = zeroed;
|
|
||||||
}
|
|
||||||
if (payload.speech === think.value) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
think.value = payload.speech;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// watch(think, async (think, prevThink) => {
|
|
||||||
// if (think) {
|
|
||||||
// anime({
|
|
||||||
// targets: gradient,
|
|
||||||
// cy: "0.7",
|
|
||||||
// });
|
|
||||||
// thinkAnimation.play();
|
|
||||||
// } else {
|
|
||||||
// thinkAnimation.pause();
|
|
||||||
// // thinkAnimation.reset();
|
|
||||||
// anime({
|
|
||||||
// targets: gradient,
|
|
||||||
// cy: "1",
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// // thinkAnimation.reset();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
return {
|
|
||||||
radius,
|
|
||||||
aiOffset,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { createApp } from "vue";
|
import { createApp } from "vue";
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import router from "./router";
|
import router from "./router";
|
||||||
import store from "./store";
|
|
||||||
|
|
||||||
// animation library
|
// animation library
|
||||||
import anime from "animejs";
|
import anime from "animejs";
|
||||||
|
|
@ -11,7 +10,6 @@ import mitt from "mitt";
|
||||||
const emitter = mitt();
|
const emitter = mitt();
|
||||||
|
|
||||||
createApp(App)
|
createApp(App)
|
||||||
.use(store)
|
|
||||||
.use(router)
|
.use(router)
|
||||||
.provide("animejs", anime)
|
.provide("animejs", anime)
|
||||||
.provide("mitt", emitter)
|
.provide("mitt", emitter)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,10 @@
|
||||||
import { createRouter, createWebHistory, createWebHashHistory, RouteRecordRaw } from "vue-router";
|
import {
|
||||||
|
createRouter,
|
||||||
|
createWebHistory,
|
||||||
|
createWebHashHistory,
|
||||||
|
RouteRecordRaw
|
||||||
|
} from "vue-router";
|
||||||
|
|
||||||
import HomeIndex from "../views/home/index.vue";
|
import HomeIndex from "../views/home/index.vue";
|
||||||
|
|
||||||
const routes: Array<RouteRecordRaw> = [
|
const routes: Array<RouteRecordRaw> = [
|
||||||
|
|
|
||||||
114
testAudio.js
114
testAudio.js
|
|
@ -1,114 +0,0 @@
|
||||||
const fs = require('fs');
|
|
||||||
// const portAudio = require('naudiodon');
|
|
||||||
// Imports the Google Cloud client library
|
|
||||||
const speech = require('@google-cloud/speech');
|
|
||||||
const portAudio = require('naudiodon');
|
|
||||||
|
|
||||||
// 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');
|
|
||||||
const WebSocket = require("ws")
|
|
||||||
// Start piping data and start streaming
|
|
||||||
const {Writable} = require('stream');
|
|
||||||
const ws = new WebSocket("ws://localhost:8080");
|
|
||||||
//const duplex = WebSocket.createWebSocketStream(ws, { encoding: 'binary' });
|
|
||||||
|
|
||||||
const audioChunks = [];
|
|
||||||
const audioInputStreamTransform = new Writable({
|
|
||||||
write(chunk, encoding, next) {
|
|
||||||
console.log(chunk)
|
|
||||||
// ws send chunk
|
|
||||||
// audioChunks.push(chunk);
|
|
||||||
// ws.send(chunk)
|
|
||||||
next();
|
|
||||||
},
|
|
||||||
|
|
||||||
final() {
|
|
||||||
console.log(audioChunks)
|
|
||||||
// ws.send("stop")
|
|
||||||
// stop ws transer
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Creates a client
|
|
||||||
const client = new speech.SpeechClient();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO(developer): Uncomment the following lines before running the sample.
|
|
||||||
*/
|
|
||||||
const filename = './rawAudio.wav';
|
|
||||||
const encoding = 'LINEAR16';
|
|
||||||
const sampleRateHertz = 44100;
|
|
||||||
const languageCode = 'en-US';
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
encoding: encoding,
|
|
||||||
sampleRateHertz: sampleRateHertz,
|
|
||||||
languageCode: languageCode,
|
|
||||||
};
|
|
||||||
const audio = {
|
|
||||||
content: fs.readFileSync(filename).toString('base64'),
|
|
||||||
};
|
|
||||||
|
|
||||||
// const request = {
|
|
||||||
// config: config,
|
|
||||||
// audio: audio,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// Detects speech in the audio file
|
|
||||||
async function test() {
|
|
||||||
const [response] = await client.recognize(request);
|
|
||||||
const transcription = response.results
|
|
||||||
.map(result => result.alternatives[0].transcript)
|
|
||||||
.join('\n');
|
|
||||||
console.log('Transcription: ', transcription);
|
|
||||||
}
|
|
||||||
|
|
||||||
// test();
|
|
||||||
|
|
||||||
const request = {
|
|
||||||
config,
|
|
||||||
interimResults: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const speechCallback = (data) => {
|
|
||||||
console.log(
|
|
||||||
`Transcription: ${data.results[0].alternatives[0].transcript}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const recognizeStream = client
|
|
||||||
.streamingRecognize(request)
|
|
||||||
.on('error', err => {
|
|
||||||
if (err.code === 11) {
|
|
||||||
// restartStream();
|
|
||||||
} else {
|
|
||||||
console.error('API request error ' + err);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on('data', speechCallback);
|
|
||||||
|
|
||||||
// Create an instance of AudioIO with inOptions (defaults are as below), which will return a ReadableStream
|
|
||||||
const audioOptions = {
|
|
||||||
channelCount: 1,
|
|
||||||
sampleFormat: portAudio.sampleFormat16Bit,
|
|
||||||
sampleRate: 16000,
|
|
||||||
deviceId: -1, // Use -1 or omit the deviceId to select the default device
|
|
||||||
closeOnError: false // Close the stream if an audio error is detected, if set false then just log the error
|
|
||||||
}
|
|
||||||
|
|
||||||
const audioData = [];
|
|
||||||
const ai = new portAudio.AudioIO({
|
|
||||||
inOptions: audioOptions
|
|
||||||
});
|
|
||||||
// ai.on('data', (d) => {
|
|
||||||
// audioData.push(d.toString('binary'))
|
|
||||||
// })
|
|
||||||
ai.pipe(audioInputStreamTransform)
|
|
||||||
ai.start();
|
|
||||||
setTimeout(() => {
|
|
||||||
ai.quit();
|
|
||||||
}, 2000)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
const WebSocket = require("ws");
|
|
||||||
|
|
||||||
const testMessage = {
|
|
||||||
content: 'Hello from test server!' ,
|
|
||||||
context: 'test message',
|
|
||||||
subContext: '',
|
|
||||||
modifiers: 'ai',
|
|
||||||
id: "0",
|
|
||||||
time: 'test'
|
|
||||||
}
|
|
||||||
const wss = new WebSocket.Server({
|
|
||||||
port: 8080
|
|
||||||
// perMessageDeflate: {
|
|
||||||
// zlibDeflateOptions: {
|
|
||||||
// // See zlib defaults.
|
|
||||||
// chunkSize: 1024,
|
|
||||||
// memLevel: 7,
|
|
||||||
// level: 3
|
|
||||||
// },
|
|
||||||
// zlibInflateOptions: {
|
|
||||||
// chunkSize: 10 * 1024
|
|
||||||
// },
|
|
||||||
// // Other options settable:
|
|
||||||
// clientNoContextTakeover: true, // Defaults to negotiated value.
|
|
||||||
// serverNoContextTakeover: true, // Defaults to negotiated value.
|
|
||||||
// serverMaxWindowBits: 10, // Defaults to negotiated value.
|
|
||||||
// // Below options specified as default values.
|
|
||||||
// concurrencyLimit: 10, // Limits zlib concurrency for perf.
|
|
||||||
// threshold: 1024 // Size (in bytes) below which messages
|
|
||||||
// // should not be compressed.
|
|
||||||
// }
|
|
||||||
});
|
|
||||||
|
|
||||||
wss.on("connection", function connection(ws, req) {
|
|
||||||
ws.on("message", function incoming(message) {
|
|
||||||
console.log(message)
|
|
||||||
// const parsed = JSON.parse(message)
|
|
||||||
// console.log(parsed)
|
|
||||||
});
|
|
||||||
|
|
||||||
const ip = req.socket.remoteAddress;
|
|
||||||
console.log("received connection from", ip);
|
|
||||||
ws.send(JSON.stringify(testMessage));
|
|
||||||
});
|
|
||||||
92
ws.py
92
ws.py
|
|
@ -1,92 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# WS server example
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import websockets
|
|
||||||
|
|
||||||
# Imports the Google Cloud client library
|
|
||||||
from google.cloud import speech
|
|
||||||
|
|
||||||
# Instantiates a client
|
|
||||||
client = speech.SpeechClient()
|
|
||||||
|
|
||||||
|
|
||||||
# config = speech.RecognitionConfig(
|
|
||||||
# encoding=speech.RecognitionConfig.AudioEncoding.LINEAR16,
|
|
||||||
# sample_rate_hertz=16000,
|
|
||||||
# language_code="en-US",
|
|
||||||
# )
|
|
||||||
|
|
||||||
# streaming_config = speech.StreamingRecognitionConfig(config=config)
|
|
||||||
|
|
||||||
# def transcribe(stream):
|
|
||||||
|
|
||||||
# requests = (
|
|
||||||
# speech.StreamingRecognizeRequest(audio_content=chunk) for chunk in stream
|
|
||||||
# )
|
|
||||||
|
|
||||||
# # Detects speech in the audio file
|
|
||||||
# responses = client.streaming_recognize(
|
|
||||||
# config=config,
|
|
||||||
# requests=requests
|
|
||||||
# )
|
|
||||||
|
|
||||||
# for response in responses:
|
|
||||||
# # Once the transcription has settled, the first result will contain the
|
|
||||||
# # is_final result. The other results will be for subsequent portions of
|
|
||||||
# # the audio.
|
|
||||||
# for result in response.results:
|
|
||||||
# print("Finished: {}".format(result.is_final))
|
|
||||||
# print("Stability: {}".format(result.stability))
|
|
||||||
# alternatives = result.alternatives
|
|
||||||
# # The alternatives are ordered from most likely to least.
|
|
||||||
# for alternative in alternatives:
|
|
||||||
# print("Confidence: {}".format(alternative.confidence))
|
|
||||||
# print(u"Transcript: {}".format(alternative.transcript))
|
|
||||||
|
|
||||||
|
|
||||||
speechtotext_client = speech.SpeechClient()
|
|
||||||
|
|
||||||
config = speech.RecognitionConfig(
|
|
||||||
audio_channel_count=1,
|
|
||||||
encoding=speech.RecognitionConfig.AudioEncoding.LINEAR16,
|
|
||||||
sample_rate_hertz=44100,
|
|
||||||
language_code="en-US",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Transcribe an audio file.
|
|
||||||
def transcribe(content):
|
|
||||||
input(content)
|
|
||||||
input(type(content))
|
|
||||||
|
|
||||||
content = speech.RecognitionAudio(content=content)
|
|
||||||
|
|
||||||
# Detects speech in the audio file
|
|
||||||
response = speechtotext_client.recognize(config=config, audio=content)
|
|
||||||
input(f"Response: {response}")
|
|
||||||
|
|
||||||
for result in response.results:
|
|
||||||
return result.alternatives[0].transcript
|
|
||||||
|
|
||||||
|
|
||||||
async def session(websocket, path):
|
|
||||||
buf = []
|
|
||||||
|
|
||||||
while True:
|
|
||||||
chunk = await websocket.recv()
|
|
||||||
|
|
||||||
if chunk == "stop":
|
|
||||||
break
|
|
||||||
|
|
||||||
buf.append(chunk)
|
|
||||||
|
|
||||||
input(f"Buffer sample: {buf[0]}")
|
|
||||||
audio = b"".join(buf)
|
|
||||||
|
|
||||||
transcribe(audio)
|
|
||||||
|
|
||||||
|
|
||||||
start_server = websockets.serve(session, "localhost", 8080)
|
|
||||||
asyncio.get_event_loop().run_until_complete(start_server)
|
|
||||||
asyncio.get_event_loop().run_forever()
|
|
||||||
Loading…
Reference in a new issue