sample-noise / index.html
Kumar Shubham
Initial push
b7becdf
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#2c3e50">
<meta name="description" content="Analyze engine sounds to identify potential mechanical issues">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<title>Engine Sound Classifier</title>
<link rel="manifest" href="/static/manifest.json">
<link rel="icon" type="image/png" href="/static/icons/icon-192x192.png">
<link rel="apple-touch-icon" href="/static/icons/icon-192x192.png">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root {
--primary-color: #2c3e50;
--secondary-color: #3498db;
--success-color: #2ecc71;
--warning-color: #f39c12;
--danger-color: #e74c3c;
--background-color: #f5f5f5;
--card-background: #ffffff;
--text-color: #2c3e50;
--border-radius: 12px;
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
line-height: 1.6;
background-color: var(--background-color);
color: var(--text-color);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
background-color: var(--primary-color);
color: white;
padding: 2rem;
margin-bottom: 2rem;
text-align: center;
box-shadow: var(--shadow);
}
.upload-container {
background-color: var(--card-background);
border-radius: var(--border-radius);
box-shadow: var(--shadow);
padding: 2rem;
margin-bottom: 2rem;
text-align: center;
}
.upload-area {
border: 2px dashed var(--secondary-color);
border-radius: var(--border-radius);
padding: 2rem;
margin: 1rem 0;
cursor: pointer;
transition: all 0.3s ease;
background-color: rgba(52, 152, 219, 0.05);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.upload-icon {
font-size: 3rem;
margin-bottom: 1rem;
color: var(--secondary-color);
}
.upload-area:hover {
background-color: rgba(52, 152, 219, 0.1);
transform: translateY(-2px);
}
.upload-area.drag-over {
background-color: rgba(52, 152, 219, 0.2);
border-color: var(--primary-color);
}
.btn {
background-color: var(--secondary-color);
color: white;
border: none;
padding: 12px 24px;
border-radius: var(--border-radius);
cursor: pointer;
font-size: 1rem;
font-weight: 500;
transition: all 0.3s ease;
}
.btn:hover {
background-color: #2980b9;
transform: translateY(-1px);
}
.btn:disabled {
background-color: #bdc3c7;
cursor: not-allowed;
}
.results-container {
background-color: var(--card-background);
border-radius: var(--border-radius);
box-shadow: var(--shadow);
padding: 2rem;
margin-bottom: 2rem;
display: none;
}
.result-card {
background-color: rgba(52, 152, 219, 0.05);
border-radius: var(--border-radius);
padding: 1.5rem;
margin-bottom: 1.5rem;
border-left: 5px solid var(--secondary-color);
}
.confidence-bar-container {
background-color: #e9ecef;
border-radius: var(--border-radius);
height: 25px;
margin: 10px 0;
overflow: hidden;
}
.confidence-bar {
height: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
color: white;
padding-right: 10px;
transition: width 1s ease-in-out;
border-radius: 0 var(--border-radius) var(--border-radius) 0;
}
.charts-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-top: 2rem;
}
.chart-box {
background-color: var(--card-background);
border-radius: var(--border-radius);
box-shadow: var(--shadow);
padding: 1.5rem;
}
.chart-title {
text-align: center;
margin-bottom: 1rem;
font-weight: 600;
}
.chart-image {
width: 100%;
height: auto;
border-radius: var(--border-radius);
}
.loading {
display: none;
text-align: center;
padding: 2rem;
}
.spinner {
width: 50px;
height: 50px;
border: 4px solid rgba(52, 152, 219, 0.1);
border-left-color: var(--secondary-color);
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 1rem;
}
.badge {
display: inline-block;
padding: 6px 12px;
border-radius: 20px;
font-size: 0.875rem;
font-weight: 600;
margin-left: 8px;
}
.badge-success { background-color: var(--success-color); color: white; }
.badge-warning { background-color: var(--warning-color); color: white; }
.badge-danger { background-color: var(--danger-color); color: white; }
@keyframes spin {
to { transform: rotate(360deg); }
}
@media (max-width: 768px) {
.container { padding: 16px; }
header { padding: 1.5rem; }
.upload-container, .results-container { padding: 1.5rem; }
.charts-container { grid-template-columns: 1fr; }
.btn { width: 100%; }
.upload-area { padding: 1.5rem 1rem; }
}
.offline-banner {
display: none;
background-color: var(--warning-color);
color: white;
text-align: center;
padding: 0.5rem;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
}
.install-prompt {
display: none;
background-color: var(--primary-color);
color: white;
padding: 1rem;
border-radius: var(--border-radius);
margin-bottom: 1rem;
justify-content: space-between;
align-items: center;
}
.install-btn {
background-color: white;
color: var(--primary-color);
border: none;
padding: 8px 16px;
border-radius: 20px;
font-weight: 600;
cursor: pointer;
}
</style>
</head>
<body>
<div class="offline-banner" id="offlineBanner">
You are currently offline. Some features may be limited.
</div>
<header>
<h1>Engine Sound Classifier</h1>
<p>Upload engine audio to identify potential issues</p>
</header>
<div class="container">
<div id="installPrompt" class="install-prompt">
<span>Install this app on your device for offline use</span>
<button id="installBtn" class="install-btn">Install</button>
</div>
<div class="upload-container">
<h2>Upload Engine Sound</h2>
<p>Upload a WAV file of engine sound to analyze and identify potential issues</p>
<div class="upload-area" id="dropArea">
<div class="upload-icon">📁</div>
<p>Drag and drop your audio file here or click to browse</p>
<input type="file" id="fileInput" accept=".wav" class="hidden" style="display: none;">
</div>
<button id="uploadBtn" class="btn" disabled>Analyze Sound</button>
</div>
<div class="loading" id="loading">
<div class="spinner"></div>
<p>Analyzing engine sound...</p>
</div>
<div class="results-container" id="results">
<div class="result-card">
<h3>Analysis Results</h3>
<div id="bestPrediction"></div>
<div id="confidence"></div>
</div>
<div class="charts-container">
<div class="chart-box">
<h4 class="chart-title">Model Confidence Comparison</h4>
<img id="confidenceChart" class="chart-image" alt="Confidence Chart">
</div>
<div class="chart-box">
<h4 class="chart-title">Model Voting Distribution</h4>
<img id="votingChart" class="chart-image" alt="Voting Chart">
</div>
</div>
</div>
</div>
<script>
// Service Worker Registration
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/static/sw.js')
.then(registration => console.log('ServiceWorker registered'))
.catch(err => console.log('ServiceWorker registration failed:', err));
});
}
// PWA Installation
let deferredPrompt;
const installPrompt = document.getElementById('installPrompt');
const installBtn = document.getElementById('installBtn');
window.addEventListener('beforeinstallprompt', (e) => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later
deferredPrompt = e;
// Show the install button
installPrompt.style.display = 'flex';
});
installBtn.addEventListener('click', (e) => {
// Hide the app provided install promotion
installPrompt.style.display = 'none';
// Show the install prompt
deferredPrompt.prompt();
// Wait for the user to respond to the prompt
deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the install prompt');
} else {
console.log('User dismissed the install prompt');
}
deferredPrompt = null;
});
});
// Hide install prompt when already installed
window.addEventListener('appinstalled', (evt) => {
installPrompt.style.display = 'none';
});
// Offline detection
const offlineBanner = document.getElementById('offlineBanner');
window.addEventListener('online', () => offlineBanner.style.display = 'none');
window.addEventListener('offline', () => offlineBanner.style.display = 'block');
// DOM Elements
const dropArea = document.getElementById('dropArea');
const fileInput = document.getElementById('fileInput');
const uploadBtn = document.getElementById('uploadBtn');
const loading = document.getElementById('loading');
const results = document.getElementById('results');
const bestPrediction = document.getElementById('bestPrediction');
const confidence = document.getElementById('confidence');
const confidenceChart = document.getElementById('confidenceChart');
const votingChart = document.getElementById('votingChart');
// Prevent default drag behaviors
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false);
document.body.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
// Highlight drop zone when dragging over it
['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false);
});
function highlight(e) {
dropArea.classList.add('drag-over');
}
function unhighlight(e) {
dropArea.classList.remove('drag-over');
}
// Handle dropped files
dropArea.addEventListener('drop', handleDrop, false);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
handleFiles(files);
}
// Handle selected files
fileInput.addEventListener('change', function() {
handleFiles(this.files);
});
dropArea.addEventListener('click', () => fileInput.click());
function handleFiles(files) {
if (files.length > 0) {
const file = files[0];
if (file.type === 'audio/wav' || file.name.endsWith('.wav')) {
uploadBtn.disabled = false;
} else {
alert('Please upload a WAV file');
uploadBtn.disabled = true;
}
}
}
// Upload and analyze
uploadBtn.addEventListener('click', async () => {
if (fileInput.files.length > 0) {
const formData = new FormData();
formData.append('file', fileInput.files[0]);
loading.style.display = 'block';
results.style.display = 'none';
uploadBtn.disabled = true;
try {
const response = await fetch('/api/predict', {
method: 'POST',
body: formData
});
if (!response.ok) throw new Error('Network response was not ok');
const data = await response.json();
displayResults(data);
} catch (error) {
console.error('Error:', error);
alert('An error occurred while analyzing the audio file');
} finally {
loading.style.display = 'none';
uploadBtn.disabled = false;
}
}
});
function displayResults(data) {
results.style.display = 'block';
// Display best prediction and confidence
const confidencePercent = (data.confidence * 100).toFixed(1);
let confidenceClass = 'badge-success';
if (confidencePercent < 70) confidenceClass = 'badge-danger';
else if (confidencePercent < 85) confidenceClass = 'badge-warning';
bestPrediction.innerHTML = `
<h4>Best Prediction: ${data.best_prediction}
<span class="badge ${confidenceClass}">${confidencePercent}% Confident</span>
</h4>
<p>Predicted by: ${data.best_model}</p>
`;
// Display charts
if (data.confidence_chart) {
confidenceChart.src = `data:image/png;base64,${data.confidence_chart}`;
}
if (data.voting_chart) {
votingChart.src = `data:image/png;base64,${data.voting_chart}`;
}
}
</script>
</body>
</html>