#include #include #include "../../../../liana/list.h" #include "../../../../util/print_time.h" #include "../../cmc.h" #include "../util/waveform.h" #include "../util/notcurses_ext.h" #include "now_playing.h" void cmc_now_playing_init(struct cmc_now_playing *np) { al_array_init(np->visuals); } bool cmc_now_playing_layout(struct cmc_now_playing *np, struct ncplane *parent, u32 y) { if (np->n) ncplane_destroy(np->n); u32 width = ncplane_dim_x(parent); u32 height = ncplane_dim_y(parent); struct ncplane_options nopts = { 0 }; nopts.x = 0; nopts.y = y; nopts.cols = width; nopts.rows = height - y; np->n = ncplane_create(parent, &nopts); if (np->wave) ncplane_destroy(np->wave); nopts.x = 1; nopts.y = 3; nopts.rows = 5; nopts.cols = width - 2; np->wave = ncplane_create(np->n, &nopts); np->blitted = NULL; struct cmc_now_playing_visual *visual; al_array_foreach_ptr(np->visuals, i, visual) { if (visual->visual) ncvisual_destroy(visual->visual); } np->visuals.count = 0; return true; } static void get_entry_duration_and_pos(struct cmc_list_entry *entry, u64 *duration, u64 *pos) { *pos = entry->offset; *duration = entry->duration; u64 now = nn_get_timestamp(); bool paused = entry->start == LIANA_TIMESTAMP_INVALID; if (!paused && now > entry->start) { *pos += now - entry->start; } *pos = MIN(*pos, *duration); } bool cmc_now_playing_tick(struct cmc_now_playing *np, struct cmc_list_entry *entry) { return true; u64 duration = 0, pos = 0; if (entry) get_entry_duration_and_pos(entry, &duration, &pos); u32 width = ncplane_dim_x(np->n); f32 percent = 1.f; if (duration > 0) percent = MIN(pos / (f32)duration, 1.f); u32 segments = (width - 2) * percent; if (segments != np->segment_count) { np->segment_count = segments; return true; } else { // @TODO: Avoiding judder: Implement and utilize tick timing change. if (pos < np->last_pos || pos - np->last_pos > 1000000) { np->last_pos = pos; return true; } } return false; } void cmc_now_playing_erase(struct cmc_now_playing *np) { ncplane_erase(np->n); } void cmc_now_playing_clear(struct cmc_now_playing *np) { cmc_now_playing_erase(np); ncplane_erase(np->wave); np->blitted = NULL; } void cmc_now_playing_render(struct cmc_now_playing *np, struct cmc_list_entry *entry) { u64 duration = 0, pos = 0; if (entry) get_entry_duration_and_pos(entry, &duration, &pos); u64 remaining = duration - pos; ncplane_set_styles(np->n, NCSTYLE_BOLD); if (remaining == 0) { ncplane_putstr_yx(np->n, 1, 2, "∨"); } else { al_assert(entry); if (remaining == 0) { ncplane_putstr_yx(np->n, 1, 2, "∨"); } else { bool paused = entry->start == LIANA_TIMESTAMP_INVALID; ncplane_putstr_yx(np->n, 1, 2, paused ? "^": ">"); } } ncplane_set_styles(np->n, NCSTYLE_NONE); u32 width = ncplane_dim_x(np->n); u32 height = ncplane_dim_y(np->n); // ━ ╸╺ ╼ ╾ ╴╶ f32 percent = 1.f; if (duration > 0) percent = MIN(pos / (f32)duration, 1.f); u32 bar_width = width - 1; u32 segments = roundf((bar_width * 4) * percent); u32 head = segments % 4; segments /= 4; for (u32 i = 0; i < segments; i++) { ncplane_putstr_yx(np->n, 2, i + 1, "━"); } char *head_segment; switch (head) { case 0: head_segment = " "; break; case 1: head_segment = "╴"; break; case 2: head_segment = "╸"; break; case 3: head_segment = "╾"; break; } ncplane_putstr_yx(np->n, 2, segments + 1, head_segment); ncplane_set_fg_default(np->n); char timestr[16] = { 0 }; // max = 829128280:01:49\0 s32 offset = camu_print_time(timestr, sizeof(timestr), pos, true, 4); ncplane_putstr_yx(np->n, 1, 4, timestr); ncplane_putstr_yx(np->n, 1, 4 + offset, "⎹"); if (entry) { char *c_str = al_str_to_c_str(&entry->brief); ncplane_putnstr_yx(np->n, 1, 6 + offset, MIN(al_strlen(c_str), (size_t)60), c_str); al_free(c_str); } bool show_remaining = false; if (show_remaining) { offset = camu_print_time(timestr, sizeof(timestr), remaining, true, 4); } else { offset = camu_print_time(timestr, sizeof(timestr), duration, true, 4); } ncplane_putstr_yx(np->n, 1, width - 4 - offset, "⎸"); // "=" Duration, "+" Segment, "-" Remaining. if (show_remaining) { ncplane_putstr_yx(np->n, 1, width - 3 - offset, "-"); } else { ncplane_putstr_yx(np->n, 1, width - 3 - offset, "="); } ncplane_putstr_yx(np->n, 1, width - 2 - offset, timestr); if (entry) { bool visual_cached = false; struct cmc_now_playing_visual *visual; al_array_foreach_ptr(np->visuals, i, visual) { if (visual->entry == entry) { visual_cached = true; break; } } if (!visual_cached) { if (entry->raw.data != NULL) { al_array_push(np->visuals, (struct cmc_now_playing_visual){}); visual = &al_array_last(np->visuals); visual->entry = entry; u32 *rgba; u32 pixel_width, pixel_height; ncplane_pixel_geom(np->wave, &pixel_height, &pixel_width, NULL, NULL, NULL, NULL); cmc_draw_waveform(entry, pixel_width, pixel_height, false, entry->raw.drawing, &rgba); visual->visual = ncvisual_from_rgba(rgba, pixel_height, pixel_width * 4, pixel_width); } else { visual = NULL; } } if (visual && entry != np->blitted) { ncplane_erase(np->wave); struct ncvisual_options vopts = { 0 }; vopts.blitter = NCBLIT_PIXEL; vopts.scaling = NCSCALE_STRETCH; vopts.flags = NCVISUAL_OPTION_NOINTERPOLATE; vopts.n = np->wave; vopts.x = 0; vopts.y = 0; ncvisual_blit(ncplane_notcurses(np->wave), visual->visual, &vopts); np->blitted = entry; } } u64 c = 0; ncchannels_set_fg_default(&c); ncchannels_set_bg_default(&c); ncchannels_set_bg_alpha(&c, 0); ncchannels_set_fg_alpha(&c, 255); ncplane_cursor_move_yx(np->n, 0, 0); ncplane_light_box(np->n, NCSTYLE_NONE, c, height - 1, width - 1, 0); if (segments > 0 || head > 0) { if (segments == 0 && head == 1) { ncplane_putstr_yx(np->n, 2, 0, "├"); } else { ncplane_putstr_yx(np->n, 2, 0, "┝"); } if (segments == bar_width || (segments == bar_width - 1 && head > 0)) { if (head == 1) { ncplane_putstr_yx(np->n, 2, width - 1, "┤"); } else { ncplane_putstr_yx(np->n, 2, width - 1, "┥"); } } } ncplane_set_fg_default(np->n); } /* Text waveform stuff. struct nccell ncl; nccell_init(&ncl); nccell_load(w, &ncl, " "); nccell_set_bg_palindex(&ncl, 3); ncplane_polyfill_yx(w, 0, 0, &ncl); ncplane_set_fg_palindex(n, 5); for (u32 j = 0; j < wave_height; j++) { for (u32 i = 0; i < length; i++) { char *cell = entry->raw.drawing[j][i]; if (cell[0] == 'R') { ncplane_set_bg_palindex(n, 2); } ncplane_putstr_yx(n, top + j, 1 + i, cell + 1); if (cell[0] == 'R') { ncplane_set_bg_default(n); } } } ncplane_set_fg_default(n); */