--- /dev/null
+import useMitt from "@/modules/mitt";
+import { useIpc } from '@/modules/ipc';
+import { showRecIcon, hideRecIcon } from "./animations";
+import { onMounted, onUnmounted, ref, Ref } from "vue";
+import keyboardNameMap from "./keyBoardMaps/keyboardNameMap";
+import { renderMessage, clientMessage } from '@/modules/message';
+
+const { post, invoke } = useIpc();
+
+
+function floatTo16bPCM(output: DataView, offset: number, input: Float32Array) {
+ for (var i = 0; i < input.length; i++, offset += 2) {
+ var s = Math.max(-1, Math.min(1, input[i]));
+ output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
+ }
+}
+
+function writeFloat32 (output: DataView, offset: number, input: Float32Array) {
+ for (var i = 0; i < input.length; i++, offset += 4) {
+ output.setFloat32(offset, input[i], true)
+ }
+}
+
+function writeString(view: DataView, offset: number, string: string) {
+ for (var i = 0; i < string.length; i++) {
+ view.setUint8(offset + i, string.charCodeAt(i));
+ }
+}
+
+function encodeWAV (samples: Float32Array, format: number, sampleRate: number, numChannels: number, bitDepth: number) {
+
+ var bytesPerSample = bitDepth / 8
+ var blockAlign = numChannels * bytesPerSample
+
+ var buffer = new ArrayBuffer(44 + samples.length * bytesPerSample)
+ var view = new DataView(buffer)
+
+ /* RIFF identifier */
+ writeString(view, 0, 'RIFF')
+ /* RIFF chunk length */
+ view.setUint32(4, 36 + samples.length * bytesPerSample, true)
+ /* RIFF type */
+ writeString(view, 8, 'WAVE')
+ /* format chunk identifier */
+ writeString(view, 12, 'fmt ')
+ /* format chunk length */
+ view.setUint32(16, 16, true)
+ /* sample format (raw) */
+ view.setUint16(20, format, true)
+ /* channel count */
+ view.setUint16(22, numChannels, true)
+ /* sample rate */
+ view.setUint32(24, sampleRate, true)
+ /* byte rate (sample rate * block align) */
+ view.setUint32(28, sampleRate * blockAlign, true)
+ /* block align (channel count * bytes per sample) */
+ view.setUint16(32, blockAlign, true)
+ /* bits per sample */
+ view.setUint16(34, bitDepth, true)
+ /* data chunk identifier */
+ writeString(view, 36, 'data')
+ /* data chunk length */
+ view.setUint32(40, samples.length * bytesPerSample, true)
+ /* write data */
+ if (format === 1) {
+ floatTo16bPCM(view, 44, samples)
+ } else {
+ writeFloat32(view, 44, samples)
+ }
+
+ return buffer
+}
+
+function mergeBuffers(bufferArray: Float32Array[], recLength: number) {
+
+ // initialize array to hold all samples
+ var result = new Float32Array(recLength);
+
+ // for each array in bufferArray, append values to result
+ var offset = 0;
+
+ for (var i = 0; i < bufferArray.length; i++) {
+ result.set(bufferArray[i], offset);
+ offset += bufferArray[i].length;
+ }
+
+ return result;
+}
+
+
+function exportBuffer(recBuffer: Float32Array[], recLength: number, exportSampleRate: number) {
+ var mergedBuffers = mergeBuffers(recBuffer, recLength);
+ var encodedWav = encodeWAV(mergedBuffers, 1, exportSampleRate, 1, 16);
+ var audioBlob = new Blob([encodedWav], {type: 'audio/wav'});
+ return audioBlob;
+}
+
+function postAudioBlob(blob: Blob, uid: string) {
+ const reader = new FileReader();
+ reader.readAsDataURL(blob);
+ reader.onload = (e: ProgressEvent<FileReader>) => {
+ post("client-message", clientMessage("", reader.result as string, uid))
+ }
+}
+
+export default function useAudioInputController (typing: Ref) {
+
+ let audioContext: AudioContext;
+
+ let buffer: Float32Array[] = [];
+ let bufferLen = 0;
+ let sampleRate = 16000;
+ let numChannels = 1;
+
+ // For sending messages.
+ const { emitter } = useMitt();
+
+ const recording = ref(false);
+
+ // initiate media recorder
+ if (navigator.mediaDevices) {
+ console.log("Initializing media recorder.");
+
+ const usrOptions: any = {
+ audio: true,
+ video: false
+ }
+
+ navigator.mediaDevices.getUserMedia(usrOptions).then((stream: any) => {
+
+ audioContext = new AudioContext();
+ const source = audioContext.createMediaStreamSource(stream);
+ const processor = audioContext.createScriptProcessor(4096, 1, 1);
+
+ sampleRate = audioContext.sampleRate;
+
+ source.connect(processor);
+ processor.connect(audioContext.destination);
+
+ processor.onaudioprocess = (e: AudioProcessingEvent) => {
+
+ if (recording.value) {
+
+ const data: Float32Array = e.inputBuffer.getChannelData(0);
+
+ buffer.push(data);
+ bufferLen += data.length;
+
+ } else {
+ buffer = [];
+ bufferLen = 0;
+ }
+
+ };
+
+ });
+
+ }
+
+ const play = (dataUrl: string) => {
+ const snd = new Audio(dataUrl);
+ snd.addEventListener("canplaythrough", (event: any) => {
+ console.log("Playing");
+ snd.play();
+ });
+ }
+
+ // var play = function (blob: Blob) {
+ // // We'll use a FileReader to create and ArrayBuffer out of the audio response.
+ // var fileReader = new FileReader();
+ // fileReader.onload = function() {
+ // // Once we have an ArrayBuffer we can create our BufferSource and decode the result as an AudioBuffer.
+ // const playbackSource = audioContext.createBufferSource();
+ // audioContext.decodeAudioData(fileReader.result as Buffer, function(audioBuffer) {
+ // console.log(audioBuffer.length);
+ // console.log(audioBuffer.sampleRate);
+ // console.log(audioBuffer.numberOfChannels);
+ // console.log(audioBuffer.duration);
+
+ // // Set the source buffer as our new AudioBuffer.
+ // playbackSource.buffer = audioBuffer;
+ // // Set the destination (the actual audio-rendering device--your device's speakers).
+ // playbackSource.connect(audioContext.destination);
+ // // Add an "on ended" callback.
+ // playbackSource.onended = function(event) {
+ // console.log("Playback ended");
+ // };
+ // // Start the playback.
+ // playbackSource.start(0);
+ // });
+ // };
+ // fileReader.readAsArrayBuffer(blob);
+ // };
+
+ //---Callbacks-----------------------------------------------
+
+ const onKeyDown = (e: KeyboardEvent) => {
+ const cmd = keyboardNameMap[e.keyCode];
+
+ // Start recording on space bar.
+ if (cmd == "SPACE" && !typing.value && !recording.value) {
+
+ console.log("INPT:Starting record, filling buffer.")
+ recording.value = true;
+ showRecIcon();
+
+ }
+
+ }
+
+ const onKeyUp = async (e: KeyboardEvent) => {
+ const cmd = keyboardNameMap[e.keyCode];
+
+ // Stop recording on space up.
+ if (cmd == "SPACE" && recording.value) {
+
+ // get audio from buffer then stop recording
+ console.log("INPT:Stopping record.")
+ hideRecIcon()
+
+ // render a place holder message
+ const message = renderMessage(
+ "",
+ "",
+ "",
+ "sf"
+ );
+
+ emitter.emit("self-message", message);
+
+ const blob: Blob = exportBuffer(buffer, bufferLen, sampleRate);
+ postAudioBlob(blob, message.uid);
+ recording.value = false;
+
+ // const reader = new FileReader();
+ // reader.readAsDataURL(blob);
+ // reader.onload = (e: ProgressEvent<FileReader>) => {
+ // play(reader.result as string);
+ // }
+
+ }
+
+ }
+
+ //-----------------------------------------------------------
+
+ onMounted(() => {
+ window.addEventListener("keydown", onKeyDown);
+ window.addEventListener("keyup", onKeyUp);
+ })
+
+ onUnmounted(() => {
+ window.removeEventListener("keydown", onKeyDown);
+ window.removeEventListener("keyup", onKeyUp);
+ });
+
+ return {
+ recording
+ }
+
+}
+++ /dev/null
-import useMitt from "@/modules/mitt";
-import { useIpc } from '@/modules/ipc';
-import { showRecIcon, hideRecIcon } from "./animations";
-import { onMounted, onUnmounted, ref, Ref } from "vue";
-import keyboardNameMap from "../keyBoardMaps/keyboardNameMap";
-import { renderMessage, clientMessage } from '@/modules/message';
-import { AudioData } from "@/types";
-
-
-function arrayBufferToBase64 (buffer: any) {
- var binary = '';
- var bytes = new Uint8Array(buffer);
- var len = bytes.byteLength;
- for (var i = 0; i < len; i++) {
- binary += String.fromCharCode( bytes[ i ] );
- }
- return window.btoa( binary );
-}
-
-
-
-export default function useAudioInputController (typing: Ref) {
-
- let mediaRecorder: any;
- let buffer = '';
- let sampleRate = 44100; // default
- let channels = 1;
-
- // For sending messages.
- const { post, invoke } = useIpc();
- const { emitter } = useMitt();
-
- const recording = ref(false);
-
- // initiate media recorder
- if (navigator.mediaDevices) {
- console.log("Initializing media recorder.");
-
- const usrOptions: any = {
- audio: true,
- video: false
- }
-
- navigator.mediaDevices.getUserMedia(usrOptions).then((stream: any) => {
-
- const context = new AudioContext();
- const source = context.createMediaStreamSource(stream);
- const processor = context.createScriptProcessor(1024, 1, 1);
-
- source.connect(processor);
- processor.connect(context.destination);
-
- processor.onaudioprocess = (e: AudioProcessingEvent) => {
-
- if (recording.value) {
-
- const data = e.inputBuffer.getChannelData(0).buffer;
-
- buffer += arrayBufferToBase64(data);
-
- sampleRate = e.inputBuffer.sampleRate;
-
- } else buffer = '';
-
- };
-
- });
-
- }
-
- //---Callbacks-----------------------------------------------
-
- const onKeyDown = (e: KeyboardEvent) => {
- const cmd = keyboardNameMap[e.keyCode];
-
- // Start recording on space bar.
- if (cmd == "SPACE" && !typing.value && !recording.value) {
-
- console.log("INPT:Starting record, filling buffer.")
- recording.value = true;
- showRecIcon();
-
- }
-
- }
-
- const onKeyUp = async (e: KeyboardEvent) => {
- const cmd = keyboardNameMap[e.keyCode];
-
- // Stop recording on space up.
- if (cmd == "SPACE" && recording.value) {
-
- // get audio from buffer then stop recording
- console.log("INPT:Stopping record.")
- recording.value = false;
- hideRecIcon()
-
- // render a place holder message
- const message = renderMessage(
- "",
- "",
- "",
- "sf"
- );
-
- emitter.emit("self-message", message);
-
- // send audio to backend
- const audio: AudioData = {
- content: buffer,
- channels: channels,
- fs: sampleRate
- }
-
- console.log("INPT:Sending audio to backend");
- post("client-message", clientMessage("", audio, message.uid));
-
- }
-
- }
-
- //-----------------------------------------------------------
-
- onMounted(() => {
- window.addEventListener("keydown", onKeyDown);
- window.addEventListener("keyup", onKeyUp);
- })
-
- onUnmounted(() => {
- window.removeEventListener("keydown", onKeyDown);
- window.removeEventListener("keyup", onKeyUp);
- });
-
- return {
- recording
- }
-
-}
+++ /dev/null
-
-
-
-if (navigator.mediaDevices) {
- console.log('getUserMedia supported.');
-
- var constraints = { audio: true };
- var chunks = [];
-
- navigator.mediaDevices.getUserMedia(constraints)
- .then(function(stream) {
-
- var mediaRecorder = new MediaRecorder(stream);
-
- visualize(stream);
-
- record.onclick = function() {
- mediaRecorder.start();
- console.log(mediaRecorder.state);
- console.log("recorder started");
- record.style.background = "red";
- record.style.color = "black";
- }
-
- stop.onclick = function() {
- mediaRecorder.stop();
- console.log(mediaRecorder.state);
- console.log("recorder stopped");
- record.style.background = "";
- record.style.color = "";
- }
-
- mediaRecorder.onstop = function(e) {
- console.log("data available after MediaRecorder.stop() called.");
-
- var clipName = prompt('Enter a name for your sound clip');
-
- var clipContainer = document.createElement('article');
- var clipLabel = document.createElement('p');
- var audio = document.createElement('audio');
- var deleteButton = document.createElement('button');
-
- clipContainer.classList.add('clip');
- audio.setAttribute('controls', '');
- deleteButton.innerHTML = "Delete";
- clipLabel.innerHTML = clipName;
-
- clipContainer.appendChild(audio);
- clipContainer.appendChild(clipLabel);
- clipContainer.appendChild(deleteButton);
- soundClips.appendChild(clipContainer);
-
- audio.controls = true;
- var blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });
- chunks = [];
- var audioURL = URL.createObjectURL(blob);
- audio.src = audioURL;
- console.log("recorder stopped");
-
- deleteButton.onclick = function(e) {
- evtTgt = e.target;
- evtTgt.parentNode.parentNode.removeChild(evtTgt.parentNode);
- }
- }
-
- mediaRecorder.ondataavailable = function(e) {
- chunks.push(e.data);
- }
- })
- .catch(function(err) {
- console.log('The following error occurred: ' + err);
- })
-}
-