Compare commits
6 Commits
367540a380
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b966e95b5 | |||
| 1df612aba7 | |||
| 936514217a | |||
| 776f71dac0 | |||
| 3eec087640 | |||
| 229d725bd1 |
@@ -73,6 +73,10 @@ import html from '../utils/html.js';
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if waveform should be shown
|
||||
const showWaveform = import.meta.env.VITE_SHOW_WAVEFORM === 'true';
|
||||
const allowSkip = import.meta.env.VITE_ALLOW_SKIP_BUTTONS === 'true';
|
||||
|
||||
display_element.innerHTML = `
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
@@ -117,14 +121,14 @@ import html from '../utils/html.js';
|
||||
|
||||
<ul class="list-disc list-inside space-y-2">
|
||||
<li>
|
||||
Press <span class="key-icon space">SPACE</span> to play/pause the audio and <span class="key-icon">ENTER</span> to mark the lift point.
|
||||
Press <span class="key-icon space">SPACE</span> to play the audio and <span class="key-icon space">SPACE</span> again to pause at the lifting point.
|
||||
</li>
|
||||
<li>
|
||||
You may adjust the lifting point by clicking and dragging the red line on the playback indicator.
|
||||
</li>
|
||||
<li>
|
||||
Once you have paused at the lifting point, press <span class="key-icon">ENTER</span> to confirm your selection before submitting.
|
||||
</li>
|
||||
</ul>
|
||||
<h2 class="text-base font-semibold text-black">Keyboard shortcuts:</h2>
|
||||
|
||||
<ul class="list-inside list-disc ml-5 mt-1 space-y-1 text-gray-600">
|
||||
<li><span class="key-icon space">SPACE</span> – Play/Pause</li>
|
||||
<li><span class="key-icon">ENTER</span> – Mark lift point</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -183,6 +187,7 @@ import html from '../utils/html.js';
|
||||
-->
|
||||
</div>
|
||||
|
||||
${allowSkip ? `
|
||||
<div style="margin: 20px 0; display: flex; gap: 10px; align-items: center; justify-content: center; flex-wrap: wrap;">
|
||||
<button id="skip-back-btn" style="
|
||||
padding: 8px 16px;
|
||||
@@ -204,6 +209,7 @@ import html from '../utils/html.js';
|
||||
cursor: pointer;
|
||||
">Skip Forward 0.1s <span class="key-icon"><i class="fas fa-arrow-right"></i></span> ⏩</button>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<!--
|
||||
<div id="current-time-display" style="
|
||||
@@ -222,6 +228,7 @@ import html from '../utils/html.js';
|
||||
-->
|
||||
|
||||
<div style="margin: 20px 0;">
|
||||
${showWaveform ? `
|
||||
<!-- Waveform Display -->
|
||||
<div style="margin-bottom: 20px;">
|
||||
<div style="display: flex; justify-content: space-between; font-size: 12px; color: #666; margin-bottom: 5px;">
|
||||
@@ -243,9 +250,43 @@ import html from '../utils/html.js';
|
||||
"></div>
|
||||
</div>
|
||||
</div>
|
||||
` : `
|
||||
<!-- Simple time display with invisible scrubbing area when waveform is disabled -->
|
||||
<div style="margin-bottom: 20px;">
|
||||
<div style="display: flex; justify-content: center; font-size: 14px; color: #666; margin-bottom: 10px;">
|
||||
<span>Current time: <span id="current-time">0:00</span> / <span id="end-time">0:00</span></span>
|
||||
</div>
|
||||
<!-- Invisible scrubbing area -->
|
||||
<div id="scrub-area" style="
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background: transparent;
|
||||
border: 2px dashed #ccc;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
">
|
||||
Click and drag the red line to adjust the lifting point.
|
||||
<div id="scrub-indicator" style="
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
background-color: #ff4444;
|
||||
z-index: 10;
|
||||
"></div>
|
||||
</div>
|
||||
</div>
|
||||
`}
|
||||
|
||||
<!-- Progress Bar (kept as fallback) -->
|
||||
<div style="margin-bottom: 20px; display: none;" id="fallback-progress">
|
||||
<div style="margin-bottom: 20px; display: ${showWaveform ? 'none' : 'block'};" id="fallback-progress">
|
||||
<div style="width: 100%; background-color: #ddd; border-radius: 10px; height: 8px; position: relative;">
|
||||
<div id="progress-bar" style="background-color: #007cba; height: 100%; border-radius: 10px; width: 0%; transition: width 0.1s ease;"></div>
|
||||
</div>
|
||||
@@ -260,7 +301,7 @@ import html from '../utils/html.js';
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
">Mark This as Lift Point <span class="key-icon">ENTER</span></button>
|
||||
">Confirm lifting point <span class="key-icon">ENTER</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -284,7 +325,7 @@ import html from '../utils/html.js';
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
margin-top: 20px;
|
||||
">Submit Lift Point</button>
|
||||
">Submit lifting point</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -294,10 +335,10 @@ import html from '../utils/html.js';
|
||||
console.log('Audio source set to blob URL:', audioData);
|
||||
console.log('Audio blob size:', audioBlob.size, 'bytes');
|
||||
|
||||
this.setupMarkingEvents(recordingData, audioBlob);
|
||||
this.setupMarkingEvents(recordingData, audioBlob, showWaveform);
|
||||
}
|
||||
|
||||
setupMarkingEvents(recordingData, audioBlob) {
|
||||
setupMarkingEvents(recordingData, audioBlob, showWaveform = true) {
|
||||
const audio = document.getElementById('playback-audio');
|
||||
const playPauseBtn = document.getElementById('play-pause-btn');
|
||||
// Progress bar elements
|
||||
@@ -314,7 +355,11 @@ import html from '../utils/html.js';
|
||||
// Waveform elements
|
||||
const waveformCanvas = document.getElementById('waveform-canvas');
|
||||
const playbackIndicator = document.getElementById('playback-indicator');
|
||||
const ctx = waveformCanvas.getContext('2d');
|
||||
const ctx = waveformCanvas ? waveformCanvas.getContext('2d') : null;
|
||||
|
||||
// Scrubbing area elements (for when waveform is disabled)
|
||||
const scrubArea = document.getElementById('scrub-area');
|
||||
const scrubIndicator = document.getElementById('scrub-indicator');
|
||||
|
||||
let audioDuration = 0;
|
||||
let liftPointTime = null;
|
||||
@@ -341,7 +386,17 @@ import html from '../utils/html.js';
|
||||
|
||||
// Setup canvas dimensions
|
||||
const setupCanvas = () => {
|
||||
if (!waveformCanvas || !ctx) {
|
||||
console.error('Canvas or context not available');
|
||||
return false;
|
||||
}
|
||||
|
||||
const rect = waveformCanvas.getBoundingClientRect();
|
||||
if (rect.width === 0 || rect.height === 0) {
|
||||
console.warn('Canvas has zero dimensions, retrying...');
|
||||
return false;
|
||||
}
|
||||
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
canvasWidth = rect.width * dpr;
|
||||
canvasHeight = rect.height * dpr;
|
||||
@@ -349,17 +404,30 @@ import html from '../utils/html.js';
|
||||
waveformCanvas.width = canvasWidth;
|
||||
waveformCanvas.height = canvasHeight;
|
||||
ctx.scale(dpr, dpr);
|
||||
return true;
|
||||
};
|
||||
|
||||
// Generate waveform data from audio
|
||||
const generateWaveform = async () => {
|
||||
try {
|
||||
// Ensure canvas is properly set up
|
||||
if (!setupCanvas()) {
|
||||
console.error('Failed to setup canvas for waveform');
|
||||
throw new Error('Canvas setup failed');
|
||||
}
|
||||
|
||||
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
const arrayBuffer = await audioBlob.arrayBuffer();
|
||||
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
||||
|
||||
const rawData = audioBuffer.getChannelData(0);
|
||||
const samples = Math.floor(canvasWidth / 2); // Sample every 2 pixels
|
||||
|
||||
// Guard against invalid sample count
|
||||
if (samples <= 0) {
|
||||
throw new Error('Invalid canvas width for waveform generation');
|
||||
}
|
||||
|
||||
const blockSize = Math.floor(rawData.length / samples);
|
||||
const filteredData = [];
|
||||
|
||||
@@ -386,22 +454,30 @@ import html from '../utils/html.js';
|
||||
const drawWaveform = () => {
|
||||
if (!waveformData || !ctx) return;
|
||||
|
||||
// Use the container's logical dimensions for consistent rendering
|
||||
const rect = waveformCanvas.getBoundingClientRect();
|
||||
const width = rect.width;
|
||||
const height = rect.height;
|
||||
const logicalWidth = rect.width;
|
||||
const logicalHeight = rect.height;
|
||||
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
// Clear the entire canvas using its actual dimensions
|
||||
ctx.clearRect(0, 0, logicalWidth, logicalHeight);
|
||||
ctx.fillStyle = '#e0e0e0';
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
ctx.fillRect(0, 0, logicalWidth, logicalHeight);
|
||||
|
||||
const barWidth = width / waveformData.length;
|
||||
// Guard against invalid dimensions
|
||||
if (logicalWidth <= 0 || logicalHeight <= 0 || waveformData.length === 0) return;
|
||||
|
||||
const barWidth = logicalWidth / waveformData.length;
|
||||
const maxAmplitude = Math.max(...waveformData);
|
||||
|
||||
// Guard against invalid amplitude data
|
||||
if (maxAmplitude <= 0) return;
|
||||
|
||||
ctx.fillStyle = '#007cba';
|
||||
for (let i = 0; i < waveformData.length; i++) {
|
||||
const barHeight = (waveformData[i] / maxAmplitude) * height * 0.8;
|
||||
const barHeight = (waveformData[i] / maxAmplitude) * logicalHeight * 0.8;
|
||||
const x = i * barWidth;
|
||||
const y = (height - barHeight) / 2;
|
||||
const y = (logicalHeight - barHeight) / 2;
|
||||
|
||||
ctx.fillRect(x, y, Math.max(1, barWidth - 1), barHeight);
|
||||
}
|
||||
@@ -420,7 +496,9 @@ import html from '../utils/html.js';
|
||||
const currentTime = audio.currentTime;
|
||||
const progress = audioDuration > 0 ? (currentTime / audioDuration * 100) : 0;
|
||||
|
||||
currentTimeDisplay.textContent = formatTime(currentTime);
|
||||
if (currentTimeDisplay) {
|
||||
currentTimeDisplay.textContent = formatTime(currentTime);
|
||||
}
|
||||
|
||||
// Update progress bar (fallback)
|
||||
if (progressBar) {
|
||||
@@ -428,11 +506,18 @@ import html from '../utils/html.js';
|
||||
}
|
||||
|
||||
// Update waveform playback indicator (only if not dragging)
|
||||
if (playbackIndicator && waveformCanvas && !isDragging && audioDuration > 0) {
|
||||
if (showWaveform && playbackIndicator && waveformCanvas && !isDragging && audioDuration > 0) {
|
||||
const rect = waveformCanvas.getBoundingClientRect();
|
||||
const indicatorPosition = (progress / 100) * rect.width;
|
||||
playbackIndicator.style.left = `${Math.max(0, Math.min(indicatorPosition, rect.width - 2))}px`;
|
||||
}
|
||||
|
||||
// Update scrub area indicator when waveform is disabled
|
||||
if (!showWaveform && scrubIndicator && scrubArea && !isDragging && audioDuration > 0) {
|
||||
const rect = scrubArea.getBoundingClientRect();
|
||||
const indicatorPosition = (progress / 100) * rect.width;
|
||||
scrubIndicator.style.left = `${Math.max(0, Math.min(indicatorPosition, rect.width - 2))}px`;
|
||||
}
|
||||
};
|
||||
|
||||
// Update submit button state
|
||||
@@ -487,8 +572,8 @@ import html from '../utils/html.js';
|
||||
// Update display to show confirmation
|
||||
markedPointDisplay.innerHTML = `
|
||||
<div style="text-align: center; padding: 10px; background: #d1edff; border: 1px solid #3498db; border-radius: 5px; margin: 10px 0;">
|
||||
<p style="margin: 5px 0; color: #2c3e50;">✓ Lift point marked at: ${formatPreciseTime(liftPointTime)}</p>
|
||||
<p style="margin: 5px 0; font-size: 14px; color: #2c3e50;">Preview completed. Press Submit to confirm, <span class="key-icon">ENTER</span> to play again, or mark a new point.</p>
|
||||
<p style="margin: 5px 0; color: #2c3e50;">✓ Lifting point confirmed at: ${formatPreciseTime(liftPointTime)}</p>
|
||||
<p style="margin: 5px 0; font-size: 14px; color: #2c3e50;">Preview completed. Press Submit to continue, <span class="key-icon">ENTER</span> to play again, or mark a new point.</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -510,31 +595,34 @@ import html from '../utils/html.js';
|
||||
|
||||
const setupAudioControls = () => {
|
||||
console.log(`Setup attempt ${retryCount + 1}, duration:`, audio.duration, 'ready state:', audio.readyState, 'stored duration:', storedDuration);
|
||||
console.log('Audio duration isFinite:', isFinite(audio.duration), 'Audio duration !== Infinity:', audio.duration !== Infinity);
|
||||
|
||||
// Use stored duration if available and valid
|
||||
if (storedDuration && isFinite(storedDuration) && storedDuration > 0) {
|
||||
audioDuration = storedDuration;
|
||||
startTimeDisplay.textContent = '0:00';
|
||||
endTimeDisplay.textContent = formatTime(audioDuration);
|
||||
if (startTimeDisplay) startTimeDisplay.textContent = '0:00';
|
||||
if (endTimeDisplay) endTimeDisplay.textContent = formatTime(audioDuration);
|
||||
updateDisplay();
|
||||
updateSubmitButton();
|
||||
// Initialize waveform
|
||||
setupCanvas();
|
||||
generateWaveform();
|
||||
// Initialize waveform only if enabled
|
||||
if (showWaveform) {
|
||||
generateWaveform();
|
||||
}
|
||||
console.log('Using stored duration:', audioDuration);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise try to get duration from audio element
|
||||
if (audio.duration && isFinite(audio.duration) && audio.duration > 0) {
|
||||
// Otherwise try to get duration from audio element (reject Infinity)
|
||||
if (audio.duration && isFinite(audio.duration) && audio.duration > 0 && audio.duration !== Infinity) {
|
||||
audioDuration = audio.duration;
|
||||
startTimeDisplay.textContent = '0:00';
|
||||
endTimeDisplay.textContent = formatTime(audioDuration);
|
||||
if (startTimeDisplay) startTimeDisplay.textContent = '0:00';
|
||||
if (endTimeDisplay) endTimeDisplay.textContent = formatTime(audioDuration);
|
||||
updateDisplay();
|
||||
updateSubmitButton();
|
||||
// Initialize waveform
|
||||
setupCanvas();
|
||||
generateWaveform();
|
||||
// Initialize waveform only if enabled
|
||||
if (showWaveform) {
|
||||
generateWaveform();
|
||||
}
|
||||
console.log('Audio controls setup complete, duration:', audioDuration);
|
||||
} else if (retryCount < maxRetries) {
|
||||
// Retry after a short delay if duration is not available
|
||||
@@ -545,13 +633,14 @@ import html from '../utils/html.js';
|
||||
// Fallback: estimate duration based on blob size (rough approximation)
|
||||
const estimatedDuration = Math.max(1, audioBlob.size / 8000); // ~8KB per second rough estimate
|
||||
audioDuration = estimatedDuration;
|
||||
startTimeDisplay.textContent = '0:00';
|
||||
endTimeDisplay.textContent = formatTime(audioDuration);
|
||||
if (startTimeDisplay) startTimeDisplay.textContent = '0:00';
|
||||
if (endTimeDisplay) endTimeDisplay.textContent = formatTime(audioDuration);
|
||||
updateDisplay();
|
||||
updateSubmitButton();
|
||||
// Initialize waveform even with estimated duration
|
||||
setupCanvas();
|
||||
generateWaveform();
|
||||
// Initialize waveform even with estimated duration (only if enabled)
|
||||
if (showWaveform) {
|
||||
generateWaveform();
|
||||
}
|
||||
console.log('Using estimated duration:', audioDuration);
|
||||
}
|
||||
};
|
||||
@@ -580,13 +669,16 @@ import html from '../utils/html.js';
|
||||
// Force load the audio
|
||||
audio.load();
|
||||
|
||||
// Handle window resize to redraw waveform
|
||||
window.addEventListener('resize', () => {
|
||||
if (waveformData) {
|
||||
setupCanvas();
|
||||
drawWaveform();
|
||||
}
|
||||
});
|
||||
// Handle window resize to redraw waveform (only if waveform is enabled)
|
||||
if (showWaveform) {
|
||||
window.addEventListener('resize', () => {
|
||||
if (waveformData) {
|
||||
if (setupCanvas()) {
|
||||
drawWaveform();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Mouse event handlers for dragging the playback indicator
|
||||
const handleMouseDown = (e) => {
|
||||
@@ -595,25 +687,41 @@ import html from '../utils/html.js';
|
||||
if (isPlaying) {
|
||||
audio.pause();
|
||||
}
|
||||
playbackIndicator.style.cursor = 'grabbing';
|
||||
if (showWaveform && playbackIndicator) {
|
||||
playbackIndicator.style.cursor = 'grabbing';
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
handleMouseMove(e); // Immediately update position
|
||||
};
|
||||
|
||||
const handleMouseMove = (e) => {
|
||||
if (!isDragging || !waveformCanvas || audioDuration === 0) return;
|
||||
if (!isDragging || audioDuration === 0) return;
|
||||
|
||||
let rect, indicator;
|
||||
if (showWaveform && waveformCanvas) {
|
||||
rect = waveformCanvas.getBoundingClientRect();
|
||||
indicator = playbackIndicator;
|
||||
} else if (!showWaveform && scrubArea) {
|
||||
rect = scrubArea.getBoundingClientRect();
|
||||
indicator = scrubIndicator;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = waveformCanvas.getBoundingClientRect();
|
||||
const x = Math.max(0, Math.min(e.clientX - rect.left, rect.width - 2));
|
||||
const progress = x / rect.width;
|
||||
const newTime = progress * audioDuration;
|
||||
|
||||
// Update indicator position immediately
|
||||
playbackIndicator.style.left = `${x}px`;
|
||||
if (indicator) {
|
||||
indicator.style.left = `${x}px`;
|
||||
}
|
||||
|
||||
// Update time display
|
||||
currentTimeDisplay.textContent = formatTime(newTime);
|
||||
if (currentTimeDisplay) {
|
||||
currentTimeDisplay.textContent = formatTime(newTime);
|
||||
}
|
||||
|
||||
// Update audio position
|
||||
seekTo(newTime);
|
||||
@@ -623,7 +731,9 @@ import html from '../utils/html.js';
|
||||
if (!isDragging) return;
|
||||
|
||||
isDragging = false;
|
||||
playbackIndicator.style.cursor = 'grab';
|
||||
if (showWaveform && playbackIndicator) {
|
||||
playbackIndicator.style.cursor = 'grab';
|
||||
}
|
||||
|
||||
// Resume playback if it was playing before drag
|
||||
if (wasPlayingBeforeDrag) {
|
||||
@@ -632,21 +742,40 @@ import html from '../utils/html.js';
|
||||
};
|
||||
|
||||
// Add event listeners for dragging
|
||||
playbackIndicator.addEventListener('mousedown', handleMouseDown);
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
if (showWaveform && playbackIndicator) {
|
||||
playbackIndicator.addEventListener('mousedown', handleMouseDown);
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
} else if (!showWaveform && scrubIndicator) {
|
||||
scrubIndicator.addEventListener('mousedown', handleMouseDown);
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
}
|
||||
|
||||
// Also allow clicking anywhere on the waveform to seek
|
||||
waveformCanvas.addEventListener('click', (e) => {
|
||||
if (isDragging) return; // Don't handle click if we're dragging
|
||||
|
||||
const rect = waveformCanvas.getBoundingClientRect();
|
||||
const x = Math.max(0, Math.min(e.clientX - rect.left, rect.width - 2));
|
||||
const progress = x / rect.width;
|
||||
const newTime = progress * audioDuration;
|
||||
|
||||
seekTo(newTime);
|
||||
});
|
||||
// Also allow clicking anywhere to seek
|
||||
if (showWaveform && waveformCanvas) {
|
||||
waveformCanvas.addEventListener('click', (e) => {
|
||||
if (isDragging) return; // Don't handle click if we're dragging
|
||||
|
||||
const rect = waveformCanvas.getBoundingClientRect();
|
||||
const x = Math.max(0, Math.min(e.clientX - rect.left, rect.width - 2));
|
||||
const progress = x / rect.width;
|
||||
const newTime = progress * audioDuration;
|
||||
|
||||
seekTo(newTime);
|
||||
});
|
||||
} else if (!showWaveform && scrubArea) {
|
||||
scrubArea.addEventListener('click', (e) => {
|
||||
if (isDragging) return; // Don't handle click if we're dragging
|
||||
|
||||
const rect = scrubArea.getBoundingClientRect();
|
||||
const x = Math.max(0, Math.min(e.clientX - rect.left, rect.width - 2));
|
||||
const progress = x / rect.width;
|
||||
const newTime = progress * audioDuration;
|
||||
|
||||
seekTo(newTime);
|
||||
});
|
||||
}
|
||||
|
||||
// Speed control buttons (commented out - using fixed 0.5x speed)
|
||||
/*
|
||||
@@ -669,18 +798,22 @@ import html from '../utils/html.js';
|
||||
});
|
||||
*/
|
||||
|
||||
// Skip buttons (0.1 second increments)
|
||||
skipBackBtn.addEventListener('click', () => {
|
||||
const skipAmount = 0.1; // 0.1 seconds
|
||||
audio.currentTime = Math.max(0, audio.currentTime - skipAmount);
|
||||
updateDisplay();
|
||||
});
|
||||
// Skip buttons (0.1 second increments) - only if waveform is enabled
|
||||
if (showWaveform && skipBackBtn) {
|
||||
skipBackBtn.addEventListener('click', () => {
|
||||
const skipAmount = 0.1; // 0.1 seconds
|
||||
audio.currentTime = Math.max(0, audio.currentTime - skipAmount);
|
||||
updateDisplay();
|
||||
});
|
||||
}
|
||||
|
||||
skipForwardBtn.addEventListener('click', () => {
|
||||
const skipAmount = 0.1; // 0.1 seconds
|
||||
audio.currentTime = Math.min(audioDuration, audio.currentTime + skipAmount);
|
||||
updateDisplay();
|
||||
});
|
||||
if (showWaveform && skipForwardBtn) {
|
||||
skipForwardBtn.addEventListener('click', () => {
|
||||
const skipAmount = 0.1; // 0.1 seconds
|
||||
audio.currentTime = Math.min(audioDuration, audio.currentTime + skipAmount);
|
||||
updateDisplay();
|
||||
});
|
||||
}
|
||||
|
||||
// Play/Pause button
|
||||
playPauseBtn.addEventListener('click', () => {
|
||||
@@ -727,19 +860,23 @@ import html from '../utils/html.js';
|
||||
break;
|
||||
|
||||
case 'ArrowLeft':
|
||||
e.preventDefault();
|
||||
// Skip back with left arrow (0.1 seconds)
|
||||
const skipBackAmount = 0.1;
|
||||
audio.currentTime = Math.max(0, audio.currentTime - skipBackAmount);
|
||||
updateDisplay();
|
||||
if (showWaveform) {
|
||||
e.preventDefault();
|
||||
// Skip back with left arrow (0.1 seconds)
|
||||
const skipBackAmount = 0.1;
|
||||
audio.currentTime = Math.max(0, audio.currentTime - skipBackAmount);
|
||||
updateDisplay();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ArrowRight':
|
||||
e.preventDefault();
|
||||
// Skip forward with right arrow (0.1 seconds)
|
||||
const skipForwardAmount = 0.1;
|
||||
audio.currentTime = Math.min(audioDuration, audio.currentTime + skipForwardAmount);
|
||||
updateDisplay();
|
||||
if (showWaveform) {
|
||||
e.preventDefault();
|
||||
// Skip forward with right arrow (0.1 seconds)
|
||||
const skipForwardAmount = 0.1;
|
||||
audio.currentTime = Math.min(audioDuration, audio.currentTime + skipForwardAmount);
|
||||
updateDisplay();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Enter':
|
||||
|
||||
Reference in New Issue
Block a user