From 36924ddfaefdad8417100c81d55118a8d19b507e Mon Sep 17 00:00:00 2001 From: angel Date: Sun, 7 Dec 2025 15:39:55 -0600 Subject: [PATCH] feat: refactor album art handling, implement progress bar display, and update application version to dev63 --- Makefile | 2 +- include/gui.h | 14 +++- include/main.h | 4 +- include/metadata.h | 14 +--- source/gui.c | 159 ++++++++++++--------------------------------- source/main.c | 25 ++++--- source/metadata.c | 135 ++------------------------------------ 7 files changed, 79 insertions(+), 274 deletions(-) diff --git a/Makefile b/Makefile index f9c0dbc..1668993 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ SOURCE_DIRS := source EXTRA_OUTPUT_FILES := LIBRARY_DIRS := $(DEVKITPRO)/libctru $(DEVKITPRO)/portlibs/armv6k $(DEVKITPRO)/portlibs/3ds -LIBRARIES := citro2d citro3d png z sidplay mpg123 vorbisidec opusfile opus ogg ctru m +LIBRARIES := citro2d citro3d sidplay mpg123 vorbisidec opusfile opus ogg ctru m BUILD_FLAGS := -Wall -Wextra -I$(DEVKITPRO)/libctru/include -I$(DEVKITPRO)/portlibs/armv6k/include/opus -I$(DEVKITPRO)/portlibs/3ds/include/opus -O3 -g3 -ffunction-sections -fdata-sections # -O0 -g3 -fstack-protector-strong -fsanitize=undefined -fsanitize-trap diff --git a/include/gui.h b/include/gui.h index 7f7d28b..b602146 100644 --- a/include/gui.h +++ b/include/gui.h @@ -109,10 +109,18 @@ void guiDisplayVersion(const char* version); void guiDrawText(gfxScreen_t screen, float x, float y, const char* text, u32 color, float scale); /** - * Display album art on top screen + * Display progress bar on top screen * - * \param metadata Pointer to metadata structure with album art + * \param position Current position in seconds + * \param duration Total duration in seconds */ -void guiDisplayAlbumArt(struct metadata_t* metadata); +void guiDisplayProgressBar(float position, float duration); + +/** + * Display current directory path on bottom screen + * + * \param path Current directory path + */ +void guiDisplayCurrentPath(const char* path); #endif diff --git a/include/main.h b/include/main.h index d3cc0f0..96030bd 100644 --- a/include/main.h +++ b/include/main.h @@ -1,6 +1,6 @@ /** * mice - 3DS Music Player - * Copyright (C) 2016 Mahyar Koshkouei + * Copyright (C) 2016 sillyangel * * This program comes with ABSOLUTELY NO WARRANTY and is free software. You are * welcome to redistribute it under certain conditions; for details see the @@ -13,7 +13,7 @@ #define mice_main_h /* Application version */ -#define MICE_VERSION "dev50" +#define MICE_VERSION "dev63" /* Default folder */ #define DEFAULT_DIR "sdmc:/" diff --git a/include/metadata.h b/include/metadata.h index 160072f..85b92d4 100644 --- a/include/metadata.h +++ b/include/metadata.h @@ -14,12 +14,7 @@ struct metadata_t char artist[METADATA_ARTIST_MAX]; char album[METADATA_ALBUM_MAX]; - /* Album art */ - uint8_t* albumArt; - size_t albumArtSize; - int albumArtWidth; - int albumArtHeight; - bool hasAlbumArt; + }; /** @@ -46,11 +41,4 @@ void clearMetadata(struct metadata_t* metadata); */ void displayMetadata(struct metadata_t* metadata, const char* filename); -/** - * Display album art on top screen if available - * - * \param metadata Pointer to metadata structure containing album art - */ -void displayAlbumArt(struct metadata_t* metadata); - #endif \ No newline at end of file diff --git a/source/gui.c b/source/gui.c index aab016f..80bc3d5 100644 --- a/source/gui.c +++ b/source/gui.c @@ -4,7 +4,6 @@ #include #include #include -#include #include "gui.h" #include "metadata.h" @@ -225,10 +224,10 @@ void guiDisplayFileList(const char** files, int count, int selected, int scroll) C2D_SceneBegin(bottomTarget); C2D_Text text; - float y = 10.0f; + float y = 18.0f; /* Start below path display */ float scale = 0.5f; float lineHeight = 16.0f; - int maxLines = 14; + int maxLines = 13; /* One less line due to path at top */ C2D_TextBufClear(textBuf); @@ -339,128 +338,56 @@ void guiDisplayVersion(const char* version) } /** - * PNG read callback for memory buffer - */ -static void pngReadCallback(png_structp png_ptr, png_bytep data, png_size_t length) -{ - uint8_t** buffer_ptr = (uint8_t**)png_get_io_ptr(png_ptr); - memcpy(data, *buffer_ptr, length); - *buffer_ptr += length; -} /** - * Display album art on top screen + * Display progress bar on top screen */ -void guiDisplayAlbumArt(struct metadata_t* metadata) +void guiDisplayProgressBar(float position, float duration) { - if(!metadata || !metadata->hasAlbumArt || !metadata->albumArt) + if(duration <= 0) return; C2D_SceneBegin(topTarget); - /* Decode PNG image */ - png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if(!png_ptr) - return; + /* Progress bar at bottom of top screen */ + float barY = 205.0f; + float barX = 10.0f; + float barWidth = 380.0f; + float barHeight = 6.0f; - png_infop info_ptr = png_create_info_struct(png_ptr); - if(!info_ptr) + /* Background bar */ + C2D_DrawRectSolid(barX, barY, 0.5f, barWidth, barHeight, C2D_Color32(50, 50, 60, 255)); + + /* Progress fill */ + float progress = position / duration; + if(progress > 1.0f) progress = 1.0f; + if(progress < 0.0f) progress = 0.0f; + + float fillWidth = barWidth * progress; + if(fillWidth > 0) { - png_destroy_read_struct(&png_ptr, NULL, NULL); - return; + C2D_DrawRectSolid(barX, barY, 0.5f, fillWidth, barHeight, GUI_COLOR_ACCENT); } - - if(setjmp(png_jmpbuf(png_ptr))) - { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - return; - } - - /* Set up custom read function */ - uint8_t* buffer_ptr = metadata->albumArt; - png_set_read_fn(png_ptr, &buffer_ptr, pngReadCallback); - - /* Read PNG info */ - png_read_info(png_ptr, info_ptr); - - int width = png_get_image_width(png_ptr, info_ptr); - int height = png_get_image_height(png_ptr, info_ptr); - png_byte color_type = png_get_color_type(png_ptr, info_ptr); - png_byte bit_depth = png_get_bit_depth(png_ptr, info_ptr); - - /* Convert to RGBA8 */ - if(bit_depth == 16) - png_set_strip_16(png_ptr); - if(color_type == PNG_COLOR_TYPE_PALETTE) - png_set_palette_to_rgb(png_ptr); - if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) - png_set_expand_gray_1_2_4_to_8(png_ptr); - if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) - png_set_tRNS_to_alpha(png_ptr); - if(color_type == PNG_COLOR_TYPE_RGB || - color_type == PNG_COLOR_TYPE_GRAY || - color_type == PNG_COLOR_TYPE_PALETTE) - png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); - if(color_type == PNG_COLOR_TYPE_GRAY || - color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_gray_to_rgb(png_ptr); - - png_read_update_info(png_ptr, info_ptr); - - /* Allocate image buffer */ - png_bytep* row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height); - if(!row_pointers) - { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - return; - } - - for(int y = 0; y < height; y++) - { - row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png_ptr, info_ptr)); - if(!row_pointers[y]) - { - for(int i = 0; i < y; i++) - free(row_pointers[i]); - free(row_pointers); - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - return; - } - } - - /* Read image data */ - png_read_image(png_ptr, row_pointers); - - /* Scale to fit in top-right corner (max 60x60) */ - int displayWidth = width; - int displayHeight = height; - if(width > 60 || height > 60) - { - float scale = 60.0f / (width > height ? width : height); - displayWidth = (int)(width * scale); - displayHeight = (int)(height * scale); - } - - /* Draw the image pixel by pixel */ - float startX = 330.0f; /* Top right corner */ - float startY = 10.0f; - - for(int y = 0; y < displayHeight; y++) - { - int srcY = (y * height) / displayHeight; - for(int x = 0; x < displayWidth; x++) - { - int srcX = (x * width) / displayWidth; - png_bytep px = &(row_pointers[srcY][srcX * 4]); - u32 color = C2D_Color32(px[0], px[1], px[2], px[3]); - C2D_DrawRectSolid(startX + x, startY + y, 0.5f, 1, 1, color); - } - } - - /* Clean up */ - for(int y = 0; y < height; y++) - free(row_pointers[y]); - free(row_pointers); - - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); +} + +/** + * Display current directory path on bottom screen + */ +void guiDisplayCurrentPath(const char* path) +{ + if(!path || !textBuf) + return; + + C2D_SceneBegin(bottomTarget); + + C2D_Text text; + C2D_TextBufClear(textBuf); + + /* Display path at top of bottom screen */ + char pathBuf[64]; + snprintf(pathBuf, sizeof(pathBuf), "%.55s", path); + + C2D_TextParse(&text, textBuf, pathBuf); + C2D_TextOptimize(&text); + C2D_DrawText(&text, C2D_WithColor, 5.0f, 2.0f, 0.5f, 0.35f, 0.35f, GUI_COLOR_TEXT_DIM); } diff --git a/source/main.c b/source/main.c index 3199e8a..5a3e929 100644 --- a/source/main.c +++ b/source/main.c @@ -1,6 +1,6 @@ /** * mice - 3DS Music Player - * Copyright (C) 2016 Mahyar Koshkouei + * Copyright (C) 2016 sillyangel * * This program comes with ABSOLUTELY NO WARRANTY and is free software. You are * welcome to redistribute it under certain conditions; for details see the @@ -614,18 +614,24 @@ int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) /* Display metadata if we have any */ if(currentMetadata.title[0] || currentMetadata.artist[0] || currentMetadata.album[0]) { - const char* currentFile = (fileNum > 0 && fileNum <= dirList.dirNum + dirList.fileNum) ? - (fileNum > dirList.dirNum ? dirList.files[fileNum - dirList.dirNum - 1] : "..") : ""; - guiDisplayMetadata(¤tMetadata, currentFile); - guiDisplayAlbumArt(¤tMetadata); + const char* currentFile = (fileNum > 0 && fileNum <= dirList.dirNum + dirList.fileNum) ? + (fileNum > dirList.dirNum ? dirList.files[fileNum - dirList.dirNum - 1] : "..") : ""; + guiDisplayMetadata(¤tMetadata, currentFile); } - /* Calculate scroll position to keep selection visible (14 lines visible) */ + /* Calculate scroll position to keep selection visible (13 lines visible with path) */ int scroll = from; if(fileNum < scroll) scroll = fileNum; - else if(fileNum >= scroll + 14) - scroll = fileNum - 13; + else if(fileNum >= scroll + 13) + scroll = fileNum - 12; + + /* Display current directory path */ + char currentPath[256]; + if(getcwd(currentPath, sizeof(currentPath))) + { + guiDisplayCurrentPath(currentPath); + } /* Display file list on bottom screen */ guiDisplayFileList(fileList, fileListCount, fileNum, scroll); @@ -633,11 +639,12 @@ int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) /* Display logs on top screen */ guiDisplayLog((const char**)logMessages, logMessageCount, logScroll); - /* Display playback status */ + /* Display playback status and progress bar */ if(playbackInfo.samples_per_second > 0) { float position = (float)playbackInfo.samples_played / playbackInfo.samples_per_second; float duration = (float)playbackInfo.samples_total / playbackInfo.samples_per_second; + guiDisplayProgressBar(position, duration); guiDisplayPlaybackStatus(isPlaying(), isPaused(), position, duration); } diff --git a/source/metadata.c b/source/metadata.c index 7f5033b..9bedf49 100644 --- a/source/metadata.c +++ b/source/metadata.c @@ -83,16 +83,7 @@ void clearMetadata(struct metadata_t* metadata) memset(metadata->artist, 0, METADATA_ARTIST_MAX); memset(metadata->album, 0, METADATA_ALBUM_MAX); - if(metadata->albumArt) - { - free(metadata->albumArt); - metadata->albumArt = NULL; - } - - metadata->albumArtSize = 0; - metadata->albumArtWidth = 0; - metadata->albumArtHeight = 0; - metadata->hasAlbumArt = false; + } /** @@ -104,19 +95,6 @@ void displayMetadata(struct metadata_t* metadata, const char* filename) guiDisplayMetadata(metadata, filename); } -/** - * Display album art on top screen if available - */ -void displayAlbumArt(struct metadata_t* metadata) -{ - if(!metadata || !metadata->hasAlbumArt || !metadata->albumArt) - return; - - /* For now, just indicate that album art is available */ - /* Full implementation would require image decoding and display */ - printf("🖼️ Album Art: %dx%d\n", metadata->albumArtWidth, metadata->albumArtHeight); -} - /** * Extract ID3v2 metadata from MP3 file */ @@ -278,51 +256,8 @@ static int extractId3v2Metadata(FILE* fp, struct metadata_t* metadata) } else if(strncmp(frameId, "APIC", 4) == 0) /* Attached Picture */ { - /* Extract album art data */ - if(frameSize > 10 && !metadata->hasAlbumArt) - { - uint8_t* frameData = malloc(frameSize); - if(frameData && fread(frameData, 1, frameSize, fp) == frameSize) - { - /* ID3v2 APIC frame format: - * - Text encoding (1 byte) - * - MIME type (null-terminated string) - * - Picture type (1 byte) - * - Description (null-terminated string) - * - Picture data - */ - uint8_t* ptr = frameData; - ptr++; /* Skip text encoding */ - - /* Skip MIME type */ - while(*ptr != 0 && (ptr - frameData) < (int)frameSize) ptr++; - ptr++; /* Skip null terminator */ - - ptr++; /* Skip picture type */ - - /* Skip description */ - while(*ptr != 0 && (ptr - frameData) < (int)frameSize) ptr++; - ptr++; /* Skip null terminator */ - - /* Remaining data is the image */ - size_t imageSize = frameSize - (ptr - frameData); - if(imageSize > 0) - { - metadata->albumArt = malloc(imageSize); - if(metadata->albumArt) - { - memcpy(metadata->albumArt, ptr, imageSize); - metadata->albumArtSize = imageSize; - metadata->hasAlbumArt = true; - } - } - } - if(frameData) free(frameData); - } - else - { - fseek(fp, frameSize, SEEK_CUR); - } + /* Skip album art data */ + fseek(fp, frameSize, SEEK_CUR); } else { @@ -496,68 +431,8 @@ static int extractFlacMetadata(FILE* fp, struct metadata_t* metadata) if(blockType == 6) /* PICTURE */ { - /* Extract album art from FLAC picture block */ - if(!metadata->hasAlbumArt && blockSize > 32) - { - uint8_t* pictureData = malloc(blockSize); - if(pictureData && fread(pictureData, 1, blockSize, fp) == blockSize) - { - /* FLAC picture block format: - * - Picture type (4 bytes BE) - * - MIME type length (4 bytes BE) - * - MIME type string - * - Description length (4 bytes BE) - * - Description string - * - Width (4 bytes BE) - * - Height (4 bytes BE) - * - Depth (4 bytes BE) - * - Colors (4 bytes BE) - * - Picture data length (4 bytes BE) - * - Picture data - */ - uint32_t offset = 4; /* Skip picture type */ - - /* Skip MIME type */ - uint32_t mimeLen = (pictureData[offset] << 24) | (pictureData[offset+1] << 16) | - (pictureData[offset+2] << 8) | pictureData[offset+3]; - offset += 4 + mimeLen; - - /* Skip description */ - if(offset + 4 <= blockSize) - { - uint32_t descLen = (pictureData[offset] << 24) | (pictureData[offset+1] << 16) | - (pictureData[offset+2] << 8) | pictureData[offset+3]; - offset += 4 + descLen; - } - - /* Skip width, height, depth, colors (16 bytes) */ - offset += 16; - - /* Get picture data length */ - if(offset + 4 <= blockSize) - { - uint32_t picLen = (pictureData[offset] << 24) | (pictureData[offset+1] << 16) | - (pictureData[offset+2] << 8) | pictureData[offset+3]; - offset += 4; - - if(offset + picLen <= blockSize && picLen > 0) - { - metadata->albumArt = malloc(picLen); - if(metadata->albumArt) - { - memcpy(metadata->albumArt, pictureData + offset, picLen); - metadata->albumArtSize = picLen; - metadata->hasAlbumArt = true; - } - } - } - } - if(pictureData) free(pictureData); - } - else - { - fseek(fp, blockSize, SEEK_CUR); - } + /* Skip picture block */ + fseek(fp, blockSize, SEEK_CUR); } else if(blockType == 4) /* VORBIS_COMMENT */ {