Login
1 branch 0 tags
Ben (Desktop/Arch) .onigiri -> Onigiri 38f4776 1 month ago 74 Commits
moon / sdl_src / audio_player_sdl.c
#include <SDL.h>
#include <SDL_mixer.h>
#include <stdio.h>
#include <string.h>
#include "../src/audio_metadata.h"
#include "../src/audio_player.h"
#include "../src/moon.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[STORAGE_MAX_NAME] = {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[STORAGE_MAX_PATH + 10];
	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;
}