feat: add playhead indicator during audio playback
Add a red vertical line that tracks playback position within the selected region. The playhead shows the current position in the spectrogram as audio plays, making it easy to see what part of the signal is currently being heard. - playheadT tracks 0-1 normalized position in the selection - Red line drawn across the spectrogram viewport while playing - Semi-transparent red highlight behind the line for visibility - Reset playhead to 0 when playback stops or is stopped
This commit is contained in:
@@ -102,6 +102,10 @@ typedef struct {
|
||||
bool loaded;
|
||||
bool stftComputed;
|
||||
|
||||
// Playback state
|
||||
float playheadT; // 0-1 normalized position in selection
|
||||
float playheadElapsed; // Elapsed seconds since play started
|
||||
|
||||
// Time selection (0-1 normalized)
|
||||
float timeSelectionStart;
|
||||
float timeSelectionEnd;
|
||||
@@ -1238,6 +1242,28 @@ static void DrawSelectionDrag(Rectangle bounds)
|
||||
DrawRectangleLinesEx((Rectangle){ x, y, w, h }, 2, YELLOW);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Playhead
|
||||
// ============================================================================
|
||||
|
||||
static void DrawPlayhead(Rectangle bounds)
|
||||
{
|
||||
if (!app.isPlaying || app.playheadT < 0.0f || app.playheadT > 1.0f) return;
|
||||
|
||||
float timePos = app.timeSelectionStart + app.playheadT * (app.timeSelectionEnd - app.timeSelectionStart);
|
||||
float viewWidth = app.viewEnd - app.viewStart;
|
||||
float t = (timePos - app.viewStart) / viewWidth;
|
||||
float x = bounds.x + t * bounds.width;
|
||||
|
||||
// Clamp to bounds
|
||||
x = fmaxf(bounds.x, fminf(bounds.x + bounds.width, x));
|
||||
|
||||
// Draw vertical line
|
||||
DrawLine(x, bounds.y, x, bounds.y + bounds.height, RED);
|
||||
// Draw semi-transparent overlay to make it stand out
|
||||
DrawRectangle(x - 2, bounds.y, 4, bounds.height, (Color){ 255, 0, 0, 60 });
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PNG Export
|
||||
// ============================================================================
|
||||
@@ -1416,6 +1442,8 @@ static void DrawSidebar(void)
|
||||
if (app.isPlaying && AudioPlaybackSound.frameCount > 0) {
|
||||
StopSound(AudioPlaybackSound);
|
||||
app.isPlaying = false;
|
||||
app.playheadElapsed = 0;
|
||||
app.playheadT = 0;
|
||||
} else {
|
||||
PlaySelectedRegion();
|
||||
app.isPlaying = true;
|
||||
@@ -1665,6 +1693,12 @@ int main(int argc, char* argv[])
|
||||
app.isPlaying = false;
|
||||
app.playbackFinished = true;
|
||||
}
|
||||
// Track playhead position manually
|
||||
app.playheadElapsed += GetFrameTime();
|
||||
float selectionDuration = (app.timeSelectionEnd - app.timeSelectionStart) * app.signal.duration;
|
||||
if (selectionDuration > 0) {
|
||||
app.playheadT = app.playheadElapsed / selectionDuration;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle window resize
|
||||
@@ -1828,6 +1862,8 @@ int main(int argc, char* argv[])
|
||||
StopSound(AudioPlaybackSound);
|
||||
app.isPlaying = false;
|
||||
app.playbackFinished = false;
|
||||
app.playheadElapsed = 0;
|
||||
app.playheadT = 0;
|
||||
} else if (app.playbackFinished) {
|
||||
// Playback finished naturally - restart from beginning
|
||||
PlaySelectedRegion();
|
||||
@@ -2233,6 +2269,7 @@ int main(int argc, char* argv[])
|
||||
if (app.showGrid) DrawSpectrogramGrid(viewBounds, 10, 8, Fade(GRAY, 0.3f));
|
||||
DrawSelection(viewBounds);
|
||||
DrawSelectionDrag(viewBounds);
|
||||
DrawPlayhead(viewBounds);
|
||||
DrawLabels(viewBounds);
|
||||
float maxFreq = (float)app.signal.sampleRate / 2.0f;
|
||||
float freqMin = app.freqViewStart * maxFreq;
|
||||
|
||||
Reference in New Issue
Block a user