{process.env.REACT_APP_SHOW_HELP_VIDEO === "true" && (
@@ -783,6 +938,7 @@ const Assesment = ({ discoverStart }) => {
backgroundImage={practicebg}
{...{
setOpenLangModal,
+ setOpenTestModal,
lang,
points,
}}
diff --git a/src/components/Assesment/AudioDiagnosticTool.jsx b/src/components/Assesment/AudioDiagnosticTool.jsx
new file mode 100644
index 00000000..790e6792
--- /dev/null
+++ b/src/components/Assesment/AudioDiagnosticTool.jsx
@@ -0,0 +1,547 @@
+import React, { useState, useRef } from "react";
+import loaderGif from "../.././assets/Hourglass.gif";
+import record from "../.././assets/mic.png";
+import { StopButton } from "../../utils/constants";
+import { Box } from "@mui/material";
+import { Line } from "react-chartjs-2";
+import {
+ Chart as ChartJS,
+ CategoryScale,
+ LinearScale,
+ LineElement,
+ PointElement,
+ Title,
+ Tooltip,
+ Legend,
+} from "chart.js";
+
+// Registering Chart.js components
+ChartJS.register(
+ CategoryScale,
+ LinearScale,
+ LineElement,
+ PointElement,
+ Title,
+ Tooltip,
+ Legend
+);
+
+function AudioDiagnosticTool() {
+ const [isRecording, setIsRecording] = useState(false);
+ const [audioBlob, setAudioBlob] = useState(null);
+ const [audioUrl, setAudioUrl] = useState(null);
+ const [latencyData, setLatencyData] = useState([]);
+ const [latency, setLatency] = useState(null);
+ const [testResults, setTestResults] = useState([]);
+ const [testIndex, setTestIndex] = useState(0);
+ const [loading, setLoading] = useState(false);
+ const mediaRecorderRef = useRef(null);
+ const audioChunksRef = useRef([]);
+ const [latencyStart, setLatencyStart] = useState(0);
+
+ //console.log('inxxx', testIndex);
+
+ const startRecording = () => {
+ //console.log('Starting recording...');
+ audioChunksRef.current = [];
+ const stream = navigator.mediaDevices.getUserMedia({ audio: true });
+
+ stream
+ .then((mediaStream) => {
+ mediaRecorderRef.current = new MediaRecorder(mediaStream);
+ mediaRecorderRef.current.ondataavailable = (event) => {
+ audioChunksRef.current.push(event.data);
+ };
+ mediaRecorderRef.current.onstop = () => {
+ const audioBlob = new Blob(audioChunksRef.current, {
+ type: "audio/wav",
+ });
+ setAudioBlob(audioBlob);
+ setAudioUrl(URL.createObjectURL(audioBlob));
+ //console.log('Recording stopped, audioBlob set.');
+ };
+ mediaRecorderRef.current.start();
+ setIsRecording(true);
+ setLatencyStart(Date.now());
+ })
+ .catch((err) => {
+ console.error("Error accessing audio devices:", err);
+ });
+ };
+
+ const stopRecording = () => {
+ mediaRecorderRef.current.stop();
+ setIsRecording(false);
+
+ // Capture latency time
+ const latency = Date.now() - latencyStart;
+ //console.log(`Latency for this recording: ${latency} ms`);
+ setLatencyData((prev) => [...prev, latency]);
+ setLatency(latency);
+ };
+
+ const nextTest = () => {
+ setLoading(true); // Start loader
+
+ setTimeout(() => {
+ setLoading(false); // Stop loader
+
+ if (testIndex === 0) {
+ analyzeLatencyTest(latency);
+ } else if (testIndex === 1) {
+ analyzeNoiseTest();
+ } else if (testIndex === 2) {
+ analyzeOtherTest();
+ }
+ }, 2500);
+ setTestIndex((prev) => prev + 1);
+ setAudioBlob(null);
+ setAudioUrl(null);
+ };
+
+ const analyzeLatencyTest = (latency) => {
+ //console.log('Analyzing latency test...');
+ setTestResults((prev) => [
+ ...prev,
+ { test: "Latency", result: `${latency} ms` },
+ ]);
+ //setTestIndex(1); // Move to the next test (Noise Test)
+ };
+
+ const analyzeNoiseTest = () => {
+ //console.log('Analyzing noise test...');
+ blobToAudioBuffer(audioBlob)
+ .then(analyzeAudioBuffer)
+ .then((analysisReport) => {
+ setTestResults((prev) => [
+ ...prev,
+ { test: "Noise Level", result: analysisReport.noiseLevelDescription },
+ ]);
+ //setTestIndex(2); // Move to the next test (Other Test)
+ })
+ .catch(console.error);
+ };
+
+ const analyzeOtherTest = () => {
+ //console.log('Analyzing other test...');
+ blobToAudioBuffer(audioBlob)
+ .then(analyzeAudioBuffer)
+ .then((analysisReport) => {
+ const audioQualityDescription =
+ getAudioQualityDescription(analysisReport);
+ setTestResults((prev) => [
+ ...prev,
+ { test: "Audio Quality", result: audioQualityDescription },
+ ]);
+ //setTestIndex(3); // All tests completed
+ })
+ .catch(console.error);
+ };
+
+ const getAudioQualityDescription = (analysisReport) => {
+ const noiseLevel = analysisReport.noiseLevel;
+ if (noiseLevel >= 8) return "Excellent";
+ if (noiseLevel >= 6) return "Good";
+ if (noiseLevel >= 4) return "Average";
+ return "Poor";
+ };
+
+ const blobToAudioBuffer = (blob) => {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.readAsArrayBuffer(blob);
+ reader.onloadend = () => {
+ const audioContext = new (window.AudioContext ||
+ window.webkitAudioContext)();
+ audioContext.decodeAudioData(reader.result, resolve, reject);
+ };
+ reader.onerror = reject;
+ });
+ };
+ const analyzeAudioBuffer = (audioBuffer) => {
+ const channelData = audioBuffer.getChannelData(0); // Assuming mono audio
+ const noiseLevel = getNoiseLevel(channelData);
+ const noiseLevelRating = normalizeNoiseLevel(noiseLevel);
+ const noiseLevelDescription = getDescription(
+ noiseLevelRating,
+ "noise level"
+ );
+ return {
+ noiseLevel: noiseLevelRating,
+ noiseLevelDescription,
+ };
+ };
+
+ const getNoiseLevel = (channelData) => {
+ const noise = channelData.filter((value) => Math.abs(value) < 0.01).length;
+ return noise / channelData.length;
+ };
+
+ const normalizeNoiseLevel = (noiseLevel) => {
+ return Math.min(10, ((1 - noiseLevel) / 0.2) * 10);
+ };
+
+ const getDescription = (rating, type) => {
+ if (type === "noise level") {
+ if (rating >= 8) return "Very quiet";
+ if (rating >= 6) return "Quiet";
+ if (rating >= 4) return "Moderate noise";
+ return "Noisy";
+ }
+ };
+
+ // Chart data and options for latency graph
+ const latencyChartData = {
+ labels: latencyData.map((_, index) => `Test ${index + 1}`),
+ datasets: [
+ {
+ label: "Latency (ms)",
+ data: latencyData,
+ borderColor: "rgba(75, 192, 192, 1)",
+ fill: false,
+ },
+ ],
+ };
+
+ const latencyChartOptions = {
+ responsive: true,
+ scales: {
+ y: {
+ beginAtZero: true,
+ },
+ },
+ };
+
+ // Display final results when all tests are complete
+ const displayFinalResults = () => {
+ const idealRanges = [
+ { test: "Latency", idealRange: "0 - 150 ms" },
+ { test: "Noise Level", idealRange: "0 - 4 (lower is better)" },
+ { test: "Audio Quality", idealRange: "Excellent - Poor" },
+ ];
+
+ if (testIndex === 3) {
+ return (
+
+
+ Audio Performance Test Results
+
+
+ Audio Quality Test
+
+
+ {testResults.map((result, index) => (
+ -
+
+ {result.test}: {result.result}
+
+
+ ))}
+
+
+ Audio Latency Test
+
+
+ {latencyData.length > 0 && (
+
+
+
+ )}
+
+
+
+
+ Test
+ |
+
+ Ideal Range
+ |
+
+
+
+ {idealRanges.map((range, index) => (
+
+
+ {range.test}
+ |
+
+ {range.idealRange}
+ |
+
+ ))}
+
+
+
+
+ );
+ }
+ };
+
+ const TestSection = ({
+ title,
+ isRecording,
+ startRecording,
+ stopRecording,
+ record,
+ testIndex,
+ currentIndex,
+ loading,
+ }) => {
+ if (testIndex !== currentIndex || loading) return null;
+
+ return (
+
+
+ {title}
+
+ {!isRecording && (
+
+ )}
+ {isRecording && (
+
+
+
+ )}
+
+ );
+ };
+
+ const RecordingButton = ({ startRecording, record }) => (
+ {
+ if (e.key === "Enter") {
+ startRecording();
+ }
+ }}
+ >
+
+
+ );
+
+ return (
+
+ {/* Show record button or next step button based on testIndex */}
+
+
+
+
+
+ {loading && (
+
+
+
+ )}
+ {audioUrl && (
+
+ )}
+ {!loading && audioBlob && (
+
+
+
+ {"Next"}
+
+
+
+ )}
+
+ {!loading && displayFinalResults()}
+
+ );
+}
+
+export default AudioDiagnosticTool;
diff --git a/src/components/Layouts.jsx/MainLayout.jsx b/src/components/Layouts.jsx/MainLayout.jsx
index 93a17ff5..a180c1b3 100644
--- a/src/components/Layouts.jsx/MainLayout.jsx
+++ b/src/components/Layouts.jsx/MainLayout.jsx
@@ -114,6 +114,7 @@ const MainLayout = (props) => {
progressData,
showProgress,
setOpenLangModal,
+ setOpenTestModal,
lang,
handleBack,
disableScreen,
@@ -222,7 +223,14 @@ const MainLayout = (props) => {
return (
{LEVEL && (
@@ -1179,6 +1187,7 @@ MainLayout.propTypes = {
isShowCase: PropTypes.bool,
showProgress: PropTypes.bool,
setOpenLangModal: PropTypes.func,
+ setOpenTestModal: PropTypes.func,
points: PropTypes.number,
handleNext: PropTypes.any,
enableNext: PropTypes.bool,
diff --git a/src/utils/VoiceAnalyser.js b/src/utils/VoiceAnalyser.js
index 1884ab47..ea21d0a2 100644
--- a/src/utils/VoiceAnalyser.js
+++ b/src/utils/VoiceAnalyser.js
@@ -77,8 +77,6 @@ function VoiceAnalyser(props) {
);
const [isMatching, setIsMatching] = useState(false);
- //console.log('audio', recordedAudio, isMatching);
-
useEffect(() => {
if (!props.enableNext) {
setRecordedAudio("");
@@ -389,13 +387,17 @@ function VoiceAnalyser(props) {
}
}
- if (responseText.toLowerCase() === originalText.toLowerCase()) {
- setIsMatching(true);
- } else {
- setIsMatching(false);
- }
+ //console.log('dataaaa', data);
- //console.log('textss', recordedAudio, isMatching, responseText, originalText);
+ // if (responseText.toLowerCase() === originalText.toLowerCase()) {
+ // setIsMatching(true);
+ // } else {
+ // setIsMatching(false);
+ // }
+
+ setIsMatching(
+ data?.createScoreData?.session?.count_diff?.character === 0
+ );
const responseEndTime = new Date().getTime();
const responseDuration = Math.round(
@@ -663,8 +665,6 @@ function VoiceAnalyser(props) {
});
};
- //console.log('textss', recordedAudio, isMatching);
-
return (
{loader ? (
diff --git a/yarn.lock b/yarn.lock
index a82e4198..67ac55d8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2439,6 +2439,11 @@
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
+"@kurkle/color@^0.3.0":
+ version "0.3.4"
+ resolved "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz"
+ integrity sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==
+
"@leichtgewicht/ip-codec@^2.0.1":
version "2.0.5"
resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz"
@@ -5136,6 +5141,13 @@ character-error-rate@^1.1.4:
resolved "https://registry.npmjs.org/character-error-rate/-/character-error-rate-1.1.4.tgz"
integrity sha512-VDVylpiUdLOqY9aowYsz9M3zKdeQAdFF76PI1lv3oBQANQ6bc6V7pGEqar9nx6c37x0UMu5EHg32Sus3fQ1XxQ==
+chart.js@^4.4.6:
+ version "4.4.6"
+ resolved "https://registry.npmjs.org/chart.js/-/chart.js-4.4.6.tgz"
+ integrity sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==
+ dependencies:
+ "@kurkle/color" "^0.3.0"
+
check-types@^11.2.3:
version "11.2.3"
resolved "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz"
@@ -11332,6 +11344,11 @@ react-audio-player@^0.17.0:
dependencies:
prop-types "^15.7.2"
+react-chartjs-2@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz"
+ integrity sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==
+
react-confetti@^6.1.0:
version "6.1.0"
resolved "https://registry.npmjs.org/react-confetti/-/react-confetti-6.1.0.tgz"