#include "config.h" #include "library.h" #include "plat.h" #include #include #include "dr_flac.h" #include "stb_image.h" static unsigned hash(const char* s) { const unsigned char* p = (const unsigned char*)s; unsigned h = 2166136261; while (*p) h = (h ^ *p++) * 16777619; return h; } Song* find_song(Library* l, const char* path) { int i = hash(path) % l->cap; int c; Song* s; for (c = 0; c < l->cap; c++) { s = &l->songs[i]; if (!s->path[0] || !strcmp(path, s->path)) return s; i++; i %= l->cap; } return 0; } static void cap_iter( void* uptr, const char* path ) { Library* lib = uptr; (void)path; lib->cap++; } static void adder_iter( void* uptr, const char* path ) { Song* s; Library* lib = uptr; s = find_song(lib, path); if (get_song_meta(path, s)) { strcpy(s->path, path); lib->indices[lib->cnt++] = s - lib->songs; } } void build_library( Arena* a, Library* lib, const char* path ) { int i; lib->cnt = lib->cap = 0; iter_dir(path, cap_iter, lib); lib->cap += 16; lib->songs = arena_alloc( a, lib->cap * sizeof *lib->songs ); lib->indices = arena_alloc( a, lib->cap * sizeof *lib->indices ); for (i = 0; i < lib->cap; i++) lib->songs[i].path[0] = 0; iter_dir(path, adder_iter, lib); } void parse_vorbis_comment( Song* s, const char* com, int len ) { /* this is not safe xDDDD idrc */ if (len > 64) { len = 64; } if (!memcmp(com, "ARTIST=", 7)) { len -= 7; memcpy(s->artist, com + 7, len); s->artist[len] = 0; } else if (!memcmp(com, "TITLE=", 6)) { len -= 6; memcpy(s->name, com + 6, len); s->name[len] = 0; } else if (!memcmp(com, "ALBUM=", 6)) { len -= 6; memcpy(s->album, com + 6, len); s->album[len] = 0; } } void flac_meta( void* uptr, drflac_metadata* m ) { Song* s = uptr; drflac_vorbis_comment_iterator i; drflac_uint32 len; const char* c; switch (m->type) { case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: drflac_init_vorbis_comment_iterator( &i, m->data.vorbis_comment.commentCount, m->data.vorbis_comment.pComments ); while ((c = drflac_next_vorbis_comment( &i, &len ))) { parse_vorbis_comment(s, c, len); } break; } } void conv_art( Player* p, unsigned char* pixels, int w, int h ) { int tx, ty, i, j, k; float rx, ry; unsigned px; rx = (float)w / (float)album_cover_w; ry = (float)h / (float)album_cover_h; for (j = 0; j < album_cover_h; j++) { ty = (int)((float)j * ry); for (i = 0; i < album_cover_w; i++) { tx = (int)((float)i * rx); px = 0; px |= 0xff << 24; k = (tx + ty * w) * 4; px |= pixels[k + 0] << 16; px |= pixels[k + 1] << 8; px |= pixels[k + 2]; p->cover[i + j * album_cover_w] = px; } } p->has_art = 1; } void load_cover(Player* p, const unsigned char* data, int size) { unsigned char* pixels; int w, h, c, rc = 4; pixels = stbi_load_from_memory( data, size, &w, &h, &c, rc ); conv_art(p, pixels, w, h); stbi_image_free(pixels); } void art_meta( void* uptr, drflac_metadata* m ) { switch (m->type) { case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: if ( m->data.picture.type == DRFLAC_PICTURE_TYPE_COVER_FRONT ) { load_cover( uptr, m->data.picture.pPictureData, m->data.picture.pictureDataSize ); } break; } } int get_song_meta(const char* path, Song* s) { drflac* f; f = drflac_open_file_with_metadata( path, flac_meta, s, 0 ); if (f) { drflac_close(f); return 1; } s->path[0] = 0; return 0; } int sound_mix( void* uptr, unsigned char* buf, int size ) { Player* p; drflac* f; int r; (void)size; p = uptr; if (!p->play) return 0; f = p->f; r = drflac_read_pcm_frames_s32( f, size / p->channels / 4, (int*)buf ); if (!r) p->play = 0; p->cs += r; ui_update_seek(p); return r * p->channels * 4; } void init_player(Player* p) { p->seek = 0; p->song = 0; p->play = 0; } void try_load_art(Player* p) { const char* const tt[] = { "cover.jpg", "cover.png", "Cover.jpg", "Cover.png", "folder.jpg", "Folder.jpg", "folder.png", "Folder.png", "../cover.jpg", "../cover.png", "../Cover.jpg", "../Cover.png", "../folder.jpg", "../Folder.jpg", "../folder.png", "../Folder.png" }; unsigned char* pixels; int w, h, c, rc = 4, end, i, len; char buf[256]; char* start; strcpy(buf, p->song->path); len = strlen(buf); while (len > 0 && buf[len] != '/') { buf[len] = 0; len--; } end = sizeof tt / sizeof *tt; for (i = 0; i < end; i++) { start = buf + len; strcat(start, tt[i]); pixels = stbi_load( buf, &w, &h, &c, rc ); if (pixels) { conv_art(p, pixels, w, h); stbi_image_free(pixels); return; } buf[len + 1] = 0; } } void play_song(Player* p, Song* song) { drflac* f; int sr, channels; p->has_art = 0; f = drflac_open_file_with_metadata( song->path, art_meta, p, 0 ); /* todo errors and stuff */ if (!f) return; if (p->f) drflac_close(p->f); stop_audio(); p->song = song; channels = f->channels; sr = f->sampleRate; p->f = f; p->channels = channels; p->play = 1; p->cs = 0; p->ms = f->totalPCMFrameCount; if (!p->has_art) try_load_art(p); init_audio( p, sr, channels ); } void play_seek(Player* p, float v) { unsigned long long fi; if (!p->song) return; /* probably want to protect against overflow/error */ fi = (unsigned long long)((float)p->ms * v); lock_audio(); p->cs = fi; drflac_seek_to_pcm_frame(p->f, fi); unlock_audio(); } void deinit_player(Player* p) { if (p->song) { stop_audio(); wait_audio(); drflac_close(p->f); } } #define DR_FLAC_IMPLEMENTATION #include "dr_flac.h" #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h"