From b3eb2ea99154f253695ff85b88e8c16da11f5462 Mon Sep 17 00:00:00 2001 From: Tyler Date: Mon, 30 Mar 2026 21:38:32 -0700 Subject: [PATCH] Fix playback auto-reset, add ffmpeg conversion, fix file browser Open button click Co-authored-by: Qwen-Coder --- src/spectrogram.c | 80 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/src/spectrogram.c b/src/spectrogram.c index 03c9bd3..6563825 100644 --- a/src/spectrogram.c +++ b/src/spectrogram.c @@ -129,6 +129,7 @@ typedef struct { // Playback state bool isPlaying; + bool playbackFinished; // Track if playback completed naturally } SpectrogramApp; // ============================================================================ @@ -331,10 +332,53 @@ static void FreeSTFT(StftResult* result) // Audio Loading // ============================================================================ +// Convert audio file to WAV using ffmpeg (if available) +static bool ConvertToFFmpegWAV(const char* inputPath, char* outputPath, size_t outputSize) +{ + // Create temp WAV path + snprintf(outputPath, outputSize, "/tmp/rspektrum_temp_converted.wav"); + + // Build ffmpeg command + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "ffmpeg -y -loglevel quiet -i \"%s\" -ar 48000 -ac 1 -f wav \"%s\" 2>&1", inputPath, outputPath); + + int result = system(cmd); + + if (result == 0 && FileExists(outputPath)) { + TraceLog(LOG_INFO, "FFmpeg conversion successful: %s", outputPath); + return true; + } + + TraceLog(LOG_WARNING, "FFmpeg conversion failed or not available"); + return false; +} + static bool LoadWavFile(const char* filepath, AudioSignal* signal) { + const char* ext = GetFileExtension(filepath); + char convertedPath[512] = { 0 }; + bool isConverted = false; + + // Check if we need to convert via ffmpeg + bool isWav = ext && (strcmp(ext, ".wav") == 0 || strcmp(ext, ".WAV") == 0); + + if (!isWav) { + // Try ffmpeg conversion + if (ConvertToFFmpegWAV(filepath, convertedPath, sizeof(convertedPath))) { + filepath = convertedPath; + isConverted = true; + } else { + TraceLog(LOG_ERROR, "Unsupported format and ffmpeg not available: %s", filepath); + return false; + } + } + Wave wave = LoadWave(filepath); - if (wave.data == NULL) { TraceLog(LOG_ERROR, "Failed to open WAV file: %s", filepath); return false; } + if (wave.data == NULL) { + TraceLog(LOG_ERROR, "Failed to open WAV file: %s", filepath); + if (isConverted) remove(convertedPath); + return false; + } signal->sampleRate = wave.sampleRate; signal->channels = wave.channels; @@ -363,6 +407,13 @@ static bool LoadWavFile(const char* filepath, AudioSignal* signal) signal->numSamples = monoSamples; } UnloadWave(wave); + + // Clean up temp file if converted + if (isConverted) { + remove(convertedPath); + TraceLog(LOG_INFO, "Cleaned up temp file: %s", convertedPath); + } + TraceLog(LOG_INFO, "Loaded WAV: %d Hz, %.2f sec, %d samples", signal->sampleRate, signal->duration, signal->numSamples); return true; } @@ -708,14 +759,17 @@ static void DrawFileBrowser(void) Rectangle openBtn = { bx + bw - 160, btnY, 140, 38 }; Rectangle cancelBtn = { bx + 15, btnY, 110, 38 }; - if (CheckCollisionPointRec(GetMousePosition(), openBtn)) DrawRectangleRec(openBtn, (Color){ 80, 80, 90, 255 }); + bool openHovered = CheckCollisionPointRec(GetMousePosition(), openBtn); + bool openClicked = openHovered && IsMouseButtonPressed(MOUSE_LEFT_BUTTON); + if (openHovered) DrawRectangleRec(openBtn, (Color){ 100, 100, 120, 255 }); + else DrawRectangleRec(openBtn, (Color){ 80, 80, 90, 255 }); DrawRectangleLinesEx(openBtn, 1, WHITE); DrawText("OPEN (Enter)", openBtn.x + 25, openBtn.y + 12, 16, WHITE); DrawRectangleRec(cancelBtn, (Color){ 100, 40, 40, 255 }); DrawText("ESC Cancel", cancelBtn.x + 18, cancelBtn.y + 12, 16, WHITE); - if (IsKeyPressed(KEY_ENTER) && app.browserSelected >= 0 && app.browserFileCount > 0) LoadSelectedFile(); + if ((IsKeyPressed(KEY_ENTER) || openClicked) && app.browserSelected >= 0 && app.browserFileCount > 0) LoadSelectedFile(); if (IsKeyPressed(KEY_ESCAPE)) app.showFileBrowser = false; if (IsKeyPressed(KEY_UP) && app.browserSelected > 0 && app.browserFileCount > 0) { app.browserSelected--; @@ -1011,6 +1065,7 @@ int main(int argc, char* argv[]) app.visibleTextureValid = false; app.fftSize = FFT_SIZE_DEFAULT; app.isPlaying = false; + app.playbackFinished = false; GenerateColormapTexture(); ScanDirectory(GetWorkingDirectory()); @@ -1063,7 +1118,16 @@ int main(int argc, char* argv[]) if (app.showFileBrowser) { // Browser handles its own input } - + + // Check if playback finished naturally + if (app.isPlaying && AudioPlaybackSound.frameCount > 0) { + // Check if sound stopped playing (IsSoundPlaying returns false when done) + if (!IsSoundPlaying(AudioPlaybackSound)) { + app.isPlaying = false; + app.playbackFinished = true; + } + } + // View controls if (app.loaded && !app.showFileBrowser) { // Spectrogram area is to the right of sidebar (320px) @@ -1137,9 +1201,17 @@ int main(int argc, char* argv[]) // Keyboard shortcuts (SPACE for play/stop toggle, ESC for clear) if (IsKeyPressed(KEY_SPACE) && !app.showFileBrowser) { if (app.isPlaying && AudioPlaybackSound.frameCount > 0) { + // Currently playing - stop it StopSound(AudioPlaybackSound); app.isPlaying = false; + app.playbackFinished = false; + } else if (app.playbackFinished) { + // Playback finished naturally - restart from beginning + PlaySelectedRegion(); + app.isPlaying = true; + app.playbackFinished = false; } else { + // Not playing and didn't just finish - start playback PlaySelectedRegion(); app.isPlaying = true; }