Login
1 branch 0 tags
Ben (T14/NixOS) Optimized ESP32 ST7735 driver and lvgl integration 67debdd 1 month ago 50 Commits
moon / sdl_src / audio_player_sdl.c
#include "../src/audio_player.h"
#include "../src/audio_metadata.h"
#include <SDL.h>
#include <SDL_mixer.h>
#include <string.h>
#include <stdio.h>

static Mix_Music *current_music = NULL;
static audio_state_t current_state = AUDIO_STATE_STOPPED;
static uint8_t current_volume = 100;
static char current_filename[64] = {0};
static audio_metadata_t current_metadata;
static char current_display_title[128];

bool audio_player_init(void) {
    if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) {
        fprintf(stderr, "SDL_mixer init failed: %s\n", Mix_GetError());
        return false;
    }
    return true;
}

bool audio_player_play(const char *path) {
    audio_player_stop();

    // Build path relative to sdcard directory
    char full_path[256];
    snprintf(full_path, sizeof(full_path), "./sdcard%s", path);

    current_music = Mix_LoadMUS(full_path);
    if (!current_music) {
        fprintf(stderr, "Failed to load %s: %s\n", full_path, Mix_GetError());
        return false;
    }

    // Extract filename for display
    const char *name = strrchr(path, '/');
    name = name ? name + 1 : path;
    strncpy(current_filename, name, sizeof(current_filename) - 1);
    current_filename[sizeof(current_filename) - 1] = '\0';

    // Parse metadata for duration and tags
    memset(&current_metadata, 0, sizeof(current_metadata));
    current_display_title[0] = '\0';
    if (audio_metadata_get(full_path, &current_metadata)) {
        if (current_metadata.artist[0] && current_metadata.title[0]) {
            snprintf(current_display_title, sizeof(current_display_title), "%.60s - %.60s",
                     current_metadata.artist, current_metadata.title);
        } else if (current_metadata.title[0]) {
            snprintf(current_display_title, sizeof(current_display_title), "%.127s",
                     current_metadata.title);
        }
    }

    // Set volume and play
    Mix_VolumeMusic((int)(current_volume * MIX_MAX_VOLUME / 100));
    if (Mix_PlayMusic(current_music, 0) < 0) {
        fprintf(stderr, "Failed to play: %s\n", Mix_GetError());
        Mix_FreeMusic(current_music);
        current_music = NULL;
        return false;
    }

    current_state = AUDIO_STATE_PLAYING;
    return true;
}

void audio_player_stop(void) {
    if (current_music) {
        Mix_HaltMusic();
        Mix_FreeMusic(current_music);
        current_music = NULL;
    }
    current_state = AUDIO_STATE_STOPPED;
    current_filename[0] = '\0';
    current_display_title[0] = '\0';
    memset(&current_metadata, 0, sizeof(current_metadata));
}

void audio_player_pause(void) {
    if (current_state == AUDIO_STATE_PLAYING) {
        Mix_PauseMusic();
        current_state = AUDIO_STATE_PAUSED;
    }
}

void audio_player_resume(void) {
    if (current_state == AUDIO_STATE_PAUSED) {
        Mix_ResumeMusic();
        current_state = AUDIO_STATE_PLAYING;
    }
}

audio_state_t audio_player_get_state(void) {
    // Check if music finished playing
    if (current_state == AUDIO_STATE_PLAYING && !Mix_PlayingMusic()) {
        current_state = AUDIO_STATE_STOPPED;
        if (current_music) {
            Mix_FreeMusic(current_music);
            current_music = NULL;
        }
        current_filename[0] = '\0';
    }
    return current_state;
}

void audio_player_set_volume(uint8_t volume) {
    current_volume = volume > 100 ? 100 : volume;
    Mix_VolumeMusic((int)(current_volume * MIX_MAX_VOLUME / 100));
}

const char *audio_player_get_filename(void) {
    return current_filename[0] ? current_filename : NULL;
}

const char *audio_player_get_title(void) {
    if (current_display_title[0]) return current_display_title;
    return current_filename[0] ? current_filename : NULL;
}

void audio_player_update(void) {
    // Check if music finished
    audio_player_get_state();
}

uint32_t audio_player_get_position_ms(void) {
    if (!current_music || current_state == AUDIO_STATE_STOPPED) return 0;
    double pos = Mix_GetMusicPosition(current_music);
    if (pos < 0) return 0;
    return (uint32_t)(pos * 1000.0);
}

uint32_t audio_player_get_duration_ms(void) {
    return current_metadata.duration_ms;
}

uint8_t audio_player_get_progress(void) {
    uint32_t duration = current_metadata.duration_ms;
    if (duration == 0) return 0;
    uint32_t position = audio_player_get_position_ms();
    uint32_t progress = (position * 100) / duration;
    return (uint8_t)(progress > 100 ? 100 : progress);
}

bool audio_player_seek_ms(uint32_t position_ms) {
    if (!current_music || current_state == AUDIO_STATE_STOPPED) return false;
    return Mix_SetMusicPosition(position_ms / 1000.0) == 0;
}