Merge branch 'main' into jose/ui

This commit is contained in:
Jose B
2023-08-03 12:41:52 -05:00
12 changed files with 200 additions and 85 deletions

View File

@@ -18,81 +18,81 @@ class CustomRecordPlugin extends RecordPlugin {
return new CustomRecordPlugin(options || {});
}
render(stream) {
if (!this.wavesurfer) return () => undefined
if (!this.wavesurfer) return () => undefined;
const container = this.wavesurfer.getWrapper()
const canvas = document.createElement('canvas')
canvas.width = container.clientWidth
canvas.height = container.clientHeight
canvas.style.zIndex = '10'
container.appendChild(canvas)
const container = this.wavesurfer.getWrapper();
const canvas = document.createElement("canvas");
canvas.width = container.clientWidth;
canvas.height = container.clientHeight;
canvas.style.zIndex = "10";
container.appendChild(canvas);
const canvasCtx = canvas.getContext('2d')
const audioContext = new AudioContext()
const source = audioContext.createMediaStreamSource(stream)
const analyser = audioContext.createAnalyser()
analyser.fftSize = 2 ** 5
source.connect(analyser)
const bufferLength = analyser.frequencyBinCount
const dataArray = new Uint8Array(bufferLength)
const canvasCtx = canvas.getContext("2d");
const audioContext = new AudioContext();
const source = audioContext.createMediaStreamSource(stream);
const analyser = audioContext.createAnalyser();
analyser.fftSize = 2 ** 5;
source.connect(analyser);
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
let animationId, previousTimeStamp;
const BUFFER_SIZE = 2 ** 8
const dataBuffer = new Array(BUFFER_SIZE).fill(canvas.height)
const BUFFER_SIZE = 2 ** 8;
const dataBuffer = new Array(BUFFER_SIZE).fill(canvas.height);
const drawWaveform = (timeStamp) => {
if (!canvasCtx) return
if (!canvasCtx) return;
analyser.getByteTimeDomainData(dataArray)
canvasCtx.clearRect(0, 0, canvas.width, canvas.height)
canvasCtx.fillStyle = '#cc3347'
analyser.getByteTimeDomainData(dataArray);
canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
canvasCtx.fillStyle = "#cc3347";
if (previousTimeStamp === undefined) {
previousTimeStamp = timeStamp
dataBuffer.push(Math.min(...dataArray))
dataBuffer.splice(0, 1)
previousTimeStamp = timeStamp;
dataBuffer.push(Math.min(...dataArray));
dataBuffer.splice(0, 1);
}
const elapsed = timeStamp - previousTimeStamp;
if (elapsed > 10) {
previousTimeStamp = timeStamp
dataBuffer.push(Math.min(...dataArray))
dataBuffer.splice(0, 1)
previousTimeStamp = timeStamp;
dataBuffer.push(Math.min(...dataArray));
dataBuffer.splice(0, 1);
}
// Drawing
const sliceWidth = canvas.width / dataBuffer.length
let x = 0
const sliceWidth = canvas.width / dataBuffer.length;
let x = 0;
for (let i = 0; i < dataBuffer.length; i++) {
const valueNormalized = dataBuffer[i] / canvas.height
const y = valueNormalized * canvas.height / 2
const sliceHeight = canvas.height + 1 - y * 2
const valueNormalized = dataBuffer[i] / canvas.height;
const y = (valueNormalized * canvas.height) / 2;
const sliceHeight = canvas.height + 1 - y * 2;
canvasCtx.fillRect(x, y, sliceWidth * 2 / 3, sliceHeight)
x += sliceWidth
canvasCtx.fillRect(x, y, (sliceWidth * 2) / 3, sliceHeight);
x += sliceWidth;
}
animationId = requestAnimationFrame(drawWaveform)
}
animationId = requestAnimationFrame(drawWaveform);
};
drawWaveform()
drawWaveform();
return () => {
if (animationId) {
cancelAnimationFrame(animationId)
cancelAnimationFrame(animationId);
}
if (source) {
source.disconnect()
source.mediaStream.getTracks().forEach((track) => track.stop())
source.disconnect();
source.mediaStream.getTracks().forEach((track) => track.stop());
}
if (audioContext) {
audioContext.close()
audioContext.close();
}
canvas?.remove()
}
canvas?.remove();
};
}
startRecording(stream) {
this.preventInteraction();

View File

@@ -1,7 +1,10 @@
import { Mulberry32 } from "../utils.js";
import React, { useState, useEffect } from "react";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faChevronRight, faChevronDown } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faChevronRight,
faChevronDown,
} from "@fortawesome/free-solid-svg-icons";
export function Dashboard({
isRecording,
@@ -24,7 +27,8 @@ export function Dashboard({
};
const handleScroll = (e) => {
const bottom = e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight;
const bottom =
e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight;
if (!bottom && autoscrollEnabled) {
setAutoscrollEnabled(false);
} else if (bottom && !autoscrollEnabled) {
@@ -44,10 +48,18 @@ export function Dashboard({
</div>
<div
className={`absolute top-7 right-5 w-10 h-10 ${autoscrollEnabled ? 'hidden' : 'flex'} justify-center items-center text-2xl cursor-pointer opacity-70 hover:opacity-100 transition-opacity duration-200 animate-bounce rounded-xl border-slate-400 bg-[#3c82f638] text-[#3c82f6ed]`}
className={`absolute top-7 right-5 w-10 h-10 ${
autoscrollEnabled ? "hidden" : "flex"
} justify-center items-center text-2xl cursor-pointer opacity-70 hover:opacity-100 transition-opacity duration-200 animate-bounce rounded-xl border-slate-400 bg-[#3c82f638] text-[#3c82f6ed]`}
onClick={scrollToBottom}
>&#11015;</div>
<div id="topics-div" className="py-2 overflow-y-auto" onScroll={handleScroll}>
>
&#11015;
</div>
<div
id="topics-div"
className="py-2 overflow-y-auto"
onScroll={handleScroll}
>
{topics.map((item, index) => (
<div key={index} className="border-b-2 py-2 hover:bg-[#8ec5fc30]">
<div
@@ -64,7 +76,9 @@ export function Dashboard({
</div>
</div>
{openIndex === index && (
<div className="p-2 mt-2 -mb-2 bg-slate-50 rounded">{item.transcript}</div>
<div className="p-2 mt-2 -mb-2 bg-slate-50 rounded">
{item.transcript}
</div>
)}
</div>
))}

View File

@@ -7,27 +7,28 @@ import "react-dropdown/style.css";
import CustomRecordPlugin from "./CustomRecordPlugin";
const AudioInputsDropdown = (props) => {
const [ddOptions, setDdOptions] = useState([]);
useEffect(() => {
const init = async () => {
// Request permission to use audio inputs
await navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => stream.getTracks().forEach((t) => t.stop()))
await navigator.mediaDevices
.getUserMedia({ audio: true })
.then((stream) => stream.getTracks().forEach((t) => t.stop()));
const devices = await navigator.mediaDevices.enumerateDevices()
const devices = await navigator.mediaDevices.enumerateDevices();
const audioDevices = devices
.filter((d) => d.kind === "audioinput" && d.deviceId != "")
.map((d) => ({ value: d.deviceId, label: d.label }))
.map((d) => ({ value: d.deviceId, label: d.label }));
if (audioDevices.length < 1) return console.log("no audio input devices")
if (audioDevices.length < 1) return console.log("no audio input devices");
setDdOptions(audioDevices)
props.setDeviceId(audioDevices[0].value)
}
init()
}, [])
setDdOptions(audioDevices);
props.setDeviceId(audioDevices[0].value);
};
init();
}, []);
const handleDropdownChange = (e) => {
props.setDeviceId(e.value);
@@ -40,8 +41,8 @@ const AudioInputsDropdown = (props) => {
value={ddOptions[0]}
disabled={props.disabled}
/>
)
}
);
};
export default function Recorder(props) {
const waveformRef = useRef();

View File

@@ -64,7 +64,7 @@ const useWebRTC = (stream) => {
duration: serverData.duration,
summary: serverData.summary,
},
text: ''
text: "",
}));
break;
default:

View File

@@ -17,9 +17,8 @@ export default function RootLayout({ children }) {
<title>Test</title>
</Head>
<body className={roboto.className + " flex flex-col min-h-screen"}>
{children}
</body>
{children}
</body>
</html>
);
}

View File

@@ -12,7 +12,8 @@ const App = () => {
// transcription, summary, etc
const serverData = useWebRTC(stream);
const sendStopCmd = () => serverData?.peer?.send(JSON.stringify({ cmd: "STOP" }))
const sendStopCmd = () =>
serverData?.peer?.send(JSON.stringify({ cmd: "STOP" }));
return (
<div className="flex flex-col items-center h-[100svh] bg-gradient-to-r from-[#8ec5fc30] to-[#e0c3fc42]">