diff --git a/src/spectrogram.c b/src/spectrogram.c index c5ab3a0..2b87c04 100644 --- a/src/spectrogram.c +++ b/src/spectrogram.c @@ -813,20 +813,20 @@ static void DrawInfo(Rectangle bounds) DrawText("No audio file loaded", 10, y, fontSize, RED); } - y = GetScreenHeight() - 240; + y = GetScreenHeight() - 260; DrawText("Controls:", 10, y, fontSize, LIGHTGRAY); y += 20; DrawText(" O - Open file browser", 10, y, fontSize, GRAY); y += 18; DrawText(" Drag & drop WAV file", 10, y, fontSize, GRAY); y += 18; - DrawText(" Mouse Wheel - Zoom time", 10, y, fontSize, GRAY); y += 18; - DrawText(" Alt+Drag - Pan view", 10, y, fontSize, GRAY); y += 18; - DrawText(" LMB Drag - Select time", 10, y, fontSize, GRAY); y += 18; - DrawText(" Shift+LMB - Select freq", 10, y, fontSize, GRAY); y += 18; + DrawText(" Mouse Wheel - Zoom to cursor", 10, y, fontSize, GRAY); y += 18; + DrawText(" Alt/Middle+Drag - Pan view", 10, y, fontSize, GRAY); y += 18; + DrawText(" LMB Drag - Select time region", 10, y, fontSize, GRAY); y += 18; + DrawText(" Shift+LMB - Select freq range", 10, y, fontSize, GRAY); y += 18; DrawText(" SPACE - Play selection", 10, y, fontSize, GRAY); y += 18; - DrawText(" M - Change colormap", 10, y, fontSize, GRAY); y += 18; + DrawText(" M - Cycle colormap", 10, y, fontSize, GRAY); y += 18; DrawText(" W/S - Adjust dB floor", 10, y, fontSize, GRAY); y += 18; DrawText(" G - Toggle grid", 10, y, fontSize, GRAY); y += 18; DrawText(" R - Reset selections", 10, y, fontSize, GRAY); y += 18; - DrawText(" Home/End - Full view/Zoom in", 10, y, fontSize, GRAY); y += 18; + DrawText(" Home - Full view, End - Zoom in", 10, y, fontSize, GRAY); y += 18; DrawText(" ESC - Clear selections", 10, y, fontSize, GRAY); y = GetScreenHeight() - 60; @@ -917,55 +917,77 @@ int main(int argc, char* argv[]) // View controls if (app.loaded && !app.showFileBrowser) { - // Zoom with mouse wheel - if (CheckCollisionPointRec(GetMousePosition(), (Rectangle){ 100, 50, GetScreenWidth() - 150, GetScreenHeight() - 150 })) { + Rectangle viewBounds = { 100, 50, GetScreenWidth() - 150, GetScreenHeight() - 150 }; + + // Zoom with mouse wheel (zooms to cursor position) + if (CheckCollisionPointRec(GetMousePosition(), viewBounds)) { int wheel = GetMouseWheelMove(); if (wheel != 0) { - float viewCenter = (app.viewStart + app.viewEnd) / 2; + // Calculate mouse position as normalized time (0-1) + float mouseT = (GetMousePosition().x - viewBounds.x) / viewBounds.width; + mouseT = Clamp(mouseT + app.viewStart, 0.0f, 1.0f); + float viewWidth = app.viewEnd - app.viewStart; float zoomFactor = (wheel > 0) ? 0.8f : 1.2f; float newWidth = viewWidth * zoomFactor; - if (newWidth < 0.05f) newWidth = 0.05f; + if (newWidth < 0.02f) newWidth = 0.02f; if (newWidth > 1.0f) newWidth = 1.0f; - app.viewStart = viewCenter - newWidth / 2; - app.viewEnd = viewCenter + newWidth / 2; + + // Zoom around cursor position + float leftOfMouse = mouseT - app.viewStart; + float rightOfMouse = app.viewEnd - mouseT; + + float newLeftOfMouse = leftOfMouse * (newWidth / viewWidth); + float newRightOfMouse = rightOfMouse * (newWidth / viewWidth); + + app.viewStart = mouseT - newLeftOfMouse; + app.viewEnd = mouseT + newRightOfMouse; + + // Clamp to valid range if (app.viewStart < 0) { app.viewStart = 0; app.viewEnd = newWidth; } if (app.viewEnd > 1) { app.viewEnd = 1; app.viewStart = 1 - newWidth; } + + // Invalidate texture cache + app.visibleTextureValid = false; } } - - // Pan with Alt+drag - bool altHeld = IsKeyDown(KEY_LEFT_ALT) || IsKeyDown(KEY_RIGHT_ALT); - if (altHeld && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { + + // Pan with Alt+drag or middle mouse button + bool canPan = IsKeyDown(KEY_LEFT_ALT) || IsKeyDown(KEY_RIGHT_ALT) || IsMouseButtonDown(MOUSE_BUTTON_MIDDLE); + if (canPan && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { app.isPanning = true; app.panStartPos = GetMousePosition(); app.panStartViewStart = app.viewStart; app.panStartViewEnd = app.viewEnd; } if (app.isPanning && IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { - Rectangle bounds = { 100, 50, GetScreenWidth() - 150, GetScreenHeight() - 150 }; - if (CheckCollisionPointRec(app.panStartPos, bounds)) { - float dx = (GetMousePosition().x - app.panStartPos.x) / bounds.width; - float viewWidth = app.panStartViewEnd - app.panStartViewStart; - app.viewStart = app.panStartViewStart - dx * viewWidth; - app.viewEnd = app.panStartViewEnd - dx * viewWidth; - if (app.viewStart < 0) { app.viewStart = 0; app.viewEnd = viewWidth; } - if (app.viewEnd > 1) { app.viewEnd = 1; app.viewStart = 1 - viewWidth; } - } + float dx = (GetMousePosition().x - app.panStartPos.x) / viewBounds.width; + float viewWidth = app.panStartViewEnd - app.panStartViewStart; + app.viewStart = app.panStartViewStart - dx * viewWidth; + app.viewEnd = app.panStartViewEnd - dx * viewWidth; + if (app.viewStart < 0) { app.viewStart = 0; app.viewEnd = viewWidth; } + if (app.viewEnd > 1) { app.viewEnd = 1; app.viewStart = 1 - viewWidth; } + app.visibleTextureValid = false; } if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) app.isPanning = false; - + // Home/End keys - if (IsKeyPressed(KEY_HOME)) { app.viewStart = 0.0f; app.viewEnd = 1.0f; } + if (IsKeyPressed(KEY_HOME)) { + app.viewStart = 0.0f; app.viewEnd = 1.0f; + app.visibleTextureValid = false; + } if (IsKeyPressed(KEY_END)) { float newWidth = 0.1f; app.viewStart = 0.0f; app.viewEnd = newWidth; + app.visibleTextureValid = false; } } - + // Other controls - if (IsKeyPressed(KEY_G) && !app.showFileBrowser) app.showGrid = !app.showFileBrowser; + if (IsKeyPressed(KEY_G) && !app.showFileBrowser) { + app.showGrid = !app.showGrid; + } if (IsKeyPressed(KEY_R) && !app.showFileBrowser) { app.timeSelectionStart = app.viewStart; app.timeSelectionEnd = app.viewEnd; @@ -975,16 +997,20 @@ int main(int argc, char* argv[]) if (IsKeyPressed(KEY_M) && !app.showFileBrowser) { app.colormap = (ColormapType)((app.colormap + 1) % COLORMAP_COUNT); GenerateColormapTexture(); + if (app.stftComputed) GenerateSpectrogramTexture(&app.stft, &app.spectrogramImage, &app.spectrogramTexture); + app.visibleTextureValid = false; } if (IsKeyPressed(KEY_W) && !app.showFileBrowser) { app.amplitudeFloorDb += 2.0f; if (app.amplitudeFloorDb > app.amplitudeCeilingDb - 5) app.amplitudeFloorDb = app.amplitudeCeilingDb - 5; if (app.stftComputed) GenerateSpectrogramTexture(&app.stft, &app.spectrogramImage, &app.spectrogramTexture); + app.visibleTextureValid = false; } if (IsKeyPressed(KEY_S) && !app.showFileBrowser) { app.amplitudeFloorDb -= 2.0f; if (app.amplitudeFloorDb < -100) app.amplitudeFloorDb = -100; if (app.stftComputed) GenerateSpectrogramTexture(&app.stft, &app.spectrogramImage, &app.spectrogramTexture); + app.visibleTextureValid = false; } if (IsKeyPressed(KEY_SPACE) && !app.showFileBrowser) PlaySelectedRegion();