diff options
Diffstat (limited to 'src/fruits/cmc')
| -rw-r--r-- | src/fruits/cmc/cmc.c | 25 | ||||
| -rw-r--r-- | src/fruits/cmc/cmc.h | 8 | ||||
| -rw-r--r-- | src/fruits/cmc/meson.build | 4 | ||||
| -rw-r--r-- | src/fruits/cmc/ui/pane_list.c | 82 | ||||
| -rw-r--r-- | src/fruits/cmc/ui/pane_log.c | 44 | ||||
| -rw-r--r-- | src/fruits/cmc/ui/ui.c | 38 | ||||
| -rw-r--r-- | src/fruits/cmc/ui/ui.h | 15 | ||||
| -rw-r--r-- | src/fruits/cmc/ui/waveform.c | 202 | ||||
| -rw-r--r-- | src/fruits/cmc/ui/waveform.h | 13 |
9 files changed, 419 insertions, 12 deletions
diff --git a/src/fruits/cmc/cmc.c b/src/fruits/cmc/cmc.c index 6e62a4a..265550f 100644 --- a/src/fruits/cmc/cmc.c +++ b/src/fruits/cmc/cmc.c @@ -4,6 +4,7 @@ #include "../../server/common.h" #include "../../liana/list.h" +#include "../../codec/packet_ext.h" #include "../common.h" @@ -31,6 +32,7 @@ static struct cmc_search *get_search_by_id(struct cmc *c, s32 id) static void read_list_entry(struct nn_packet *packet, struct cmc_list_entry *entry, u32 id) { entry->id = id; + entry->node_id = nn_packet_read_u32(packet); entry->duration = nn_packet_read_u64(packet); entry->start = nn_packet_read_u64(packet); entry->paused_at = nn_packet_read_u64(packet); @@ -70,6 +72,9 @@ static void parse_initial_user_state(struct cmc *c, struct nn_packet *packet) u32 id = nn_packet_read_u32(packet); struct cmc_list_entry *entry = al_alloc_object(struct cmc_list_entry); read_list_entry(packet, entry, id); + if (list->current >= 0 && j == (u32)list->current) { + camu_client_request_visual_data(&c->client, entry->node_id); + } al_array_push(list->entries, entry); } al_array_push(c->lists, list); @@ -162,6 +167,7 @@ static void client_callback(void *userdata, u8 op, void *opaque) u32 id = nn_packet_read_u32(packet); struct cmc_list_entry *entry = al_array_at(list->entries, list->current); read_list_entry(packet, entry, id); + camu_client_request_visual_data(&c->client, entry->node_id); cmc_ui_queue_render(&c->ui); break; } @@ -193,6 +199,25 @@ static void client_callback(void *userdata, u8 op, void *opaque) } break; } + case CAMU_CLIENT_GOT_VISUAL_DATA: { + struct nn_packet *packet = (struct nn_packet *)opaque; + u32 node_id = nn_packet_read_u32(packet); + struct cmc_list *list = al_array_at(c->lists, 0); + struct cmc_list_entry *entry; + al_array_foreach(list->entries, i, entry) { + if (entry->node_id == node_id) { + if (entry->raw.data != NULL) break; + nn_packet_read_audio_format(packet, &entry->raw.fmt); + u32 size = nn_packet_read_u32(packet); + u8 *ptr; + NNWT_PACKET_READ_DATA(packet, size, ptr); + entry->raw.data = al_malloc(size); + al_memcpy(entry->raw.data, ptr, size); + entry->raw.sample_count = nn_packet_read_u32(packet); + } + } + break; + } } } diff --git a/src/fruits/cmc/cmc.h b/src/fruits/cmc/cmc.h index e2cdde3..ea714dc 100644 --- a/src/fruits/cmc/cmc.h +++ b/src/fruits/cmc/cmc.h @@ -6,6 +6,7 @@ #include "../../libclient/client.h" #include "../../portal/src/post_cache.h" +#include "../../codec/codec.h" #include "ui/ui.h" @@ -23,11 +24,18 @@ struct cmc_search { struct cmc_list_entry { u32 id; + u32 node_id; u64 duration; u64 start; u64 paused_at; u64 offset; wstr name; + struct { + struct camu_audio_format fmt; + u8 *data; + u32 sample_count; + char ***drawing; + } raw; }; struct cmc_list { diff --git a/src/fruits/cmc/meson.build b/src/fruits/cmc/meson.build index 6b7060d..a0fa097 100644 --- a/src/fruits/cmc/meson.build +++ b/src/fruits/cmc/meson.build @@ -12,8 +12,10 @@ if use_tui 'ui/ui.c', 'ui/pane_list.c', 'ui/pane_search.c', + 'ui/pane_log.c', + 'ui/waveform.c' ] - cmc_deps += [notcurses, notcurses_core] + cmc_deps += [notcurses, notcurses_core, codecs_client] endif if is_windows and meson.is_cross_build() diff --git a/src/fruits/cmc/ui/pane_list.c b/src/fruits/cmc/ui/pane_list.c index 695ff60..b62e0a6 100644 --- a/src/fruits/cmc/ui/pane_list.c +++ b/src/fruits/cmc/ui/pane_list.c @@ -7,6 +7,7 @@ #include "ui.h" #include "ext.h" +#include "waveform.h" void cmc_lp_init(struct cmc_ui *ui) { @@ -141,7 +142,7 @@ bool cmc_lp_handle_input(struct cmc_ui *ui, struct ncinput *input) CONSIDER_INPUT(); if (tab->move.active) { tab->move.active = false; - } else { + } else if (tab->selected != tab->list->current) { camu_client_skipto(&ui->c->client, &tab->list->name, tab->selected); } break; @@ -244,7 +245,7 @@ static void render_now_playing(struct cmc_ui *ui, struct cmc_list_tab *tab, u32 u64 pos = 0; u64 duration = 0; u64 remaining = 0; - u32 y_off = height - 6; + u32 y_off = height - 7; ncplane_set_styles(n, NCSTYLE_BOLD); if (entry) { @@ -288,10 +289,10 @@ static void render_now_playing(struct cmc_ui *ui, struct cmc_list_tab *tab, u32 ncplane_putstr_yx(n, y_off, 4 + offset, "โน"); if (entry) { - cmc_ui_putnwstr_yx(n, y_off, 6 + offset, MIN(entry->name.length, (u32)40), &entry->name); + cmc_ui_putnwstr_yx(n, y_off, 6 + offset, MIN(entry->name.length, (u32)60), &entry->name); } - bool show_remaining = true; + bool show_remaining = false; if (show_remaining) { offset = camu_print_time(timestr, sizeof(timestr), remaining, true, 4); } else { @@ -306,10 +307,81 @@ static void render_now_playing(struct cmc_ui *ui, struct cmc_list_tab *tab, u32 } ncplane_putstr_yx(n, y_off, width - 2 - offset, timestr); + /* ncplane_putchar_yx(n, height - 5, 2, 'A'); ncplane_putchar_yx(n, height - 4, 2, 'A'); ncplane_putchar_yx(n, height - 3, 2, 'A'); ncplane_putchar_yx(n, height - 2, 2, 'A'); + */ + + if (entry && entry->raw.data != NULL) { + u32 length = width - 2; + u32 top = height - 6; + u32 wave_height = 5; + if (entry->raw.drawing == NULL || entry != ui->lp.blitted) { + entry->raw.drawing = al_malloc(sizeof(char **) * wave_height); + for (u32 j = 0; j < wave_height; j++) { + entry->raw.drawing[j] = al_malloc(sizeof(char *) * length); + for (u32 i = 0; i < length; i++) { + entry->raw.drawing[j][i] = al_calloc(1, 6); + } + } + if (!ui->lp.w) { + struct ncplane_options opts = { 0 }; + opts.x = 1; + opts.y = top; + opts.cols = length; + opts.rows = wave_height; + ui->lp.w = ncplane_create(n, &opts); + } + /* + struct nccell ncl; + nccell_init(&ncl); + nccell_load(w, &ncl, " "); + nccell_set_bg_palindex(&ncl, 3); + ncplane_polyfill_yx(w, 0, 0, &ncl); + */ + u32 *rgba; + u32 pixel_width, pixel_height; + ncplane_pixel_geom(ui->lp.w, &pixel_height, &pixel_width, NULL, NULL, NULL, NULL); + //cmc_draw_waveform(entry, length, wave_height, false, entry->raw.drawing, &rgba); + cmc_draw_waveform(entry, pixel_width, pixel_height, false, entry->raw.drawing, &rgba); + if (ui->lp.vis) { + ncvisual_destroy(ui->lp.vis); + } + ui->lp.vis = ncvisual_from_rgba(rgba, pixel_height, pixel_width * 4, pixel_width); + } + + if (entry != ui->lp.blitted) { + ncplane_erase(ui->lp.w); + struct ncvisual_options vopts = { 0 }; + vopts.blitter = NCBLIT_PIXEL; + vopts.scaling = NCSCALE_STRETCH; + vopts.flags = NCVISUAL_OPTION_NOINTERPOLATE; + vopts.n = ui->lp.w; + vopts.x = 0; + vopts.y = 0; + ncvisual_blit(ui->nc, ui->lp.vis, &vopts); + ui->lp.blitted = entry; + } + + /* + 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); + */ + } /* char buf[24]; @@ -330,7 +402,7 @@ void cmc_lp_render(struct cmc_ui *ui) if (ui->lp.tabs.count <= ui->lp.current) return; struct cmc_list_tab *tab = &al_array_at(ui->lp.tabs, ui->lp.current); - tab->page_length = height - 8; + tab->page_length = height - 9; render_list_entries(ui, tab, tab->page_length); render_now_playing(ui, tab, tab->page_length, height); } diff --git a/src/fruits/cmc/ui/pane_log.c b/src/fruits/cmc/ui/pane_log.c new file mode 100644 index 0000000..a35cf20 --- /dev/null +++ b/src/fruits/cmc/ui/pane_log.c @@ -0,0 +1,44 @@ +#include "ui.h" + +void cmc_logp_init(struct cmc_ui *ui) +{ + al_array_init(ui->logp.messages); +} + +void cmc_logp_layout(struct cmc_ui *ui, struct ncplane *parent) +{ + if (ui->logp.n) ncplane_destroy(ui->logp.n); + struct ncplane_options nopts = { 0 }; + nopts.rows = ncplane_dim_y(parent); + nopts.cols = ncplane_dim_x(parent); + ui->logp.n = ncplane_create(parent, &nopts); +} + +void cmc_logp_push_message(struct cmc_ui *ui, char *message) +{ + al_array_push(ui->logp.messages, message); +} + +void cmc_logp_erase(struct cmc_ui *ui) +{ + ncplane_erase(ui->logp.n); +} + +void cmc_logp_render(struct cmc_ui *ui) +{ + struct ncplane *n = ui->logp.n; + + u32 max_height = ncplane_dim_y(n); + + u32 size = ui->logp.messages.count; + u32 index = (size > max_height) ? size - max_height : 0; + for (u32 i = index; i < size; i++) { + // We can't use putnstr to control the width because these messages will have wide characters. + ncplane_putstr_yx(n, i - index, 0, al_array_at(ui->logp.messages, i)); + } + + for (u32 i = 0; i < index; i++) { + al_free(al_array_at(ui->logp.messages, i)); + } + al_array_remove_range(ui->logp.messages, 0, index); +} diff --git a/src/fruits/cmc/ui/ui.c b/src/fruits/cmc/ui/ui.c index 10357c0..b830042 100644 --- a/src/fruits/cmc/ui/ui.c +++ b/src/fruits/cmc/ui/ui.c @@ -1,4 +1,5 @@ #include <al/lib.h> +#include <al/log.h> #include "ui.h" #include "ext.h" @@ -43,14 +44,23 @@ static void erase_previous_pane(struct cmc_ui *ui, u8 previous, u8 upcoming) case CMC_PANE_SEARCH: pn = ui->sp.n; break; + case CMC_PANE_LOG: + pn = ui->logp.n; + break; } ncplane_erase(pn); switch (upcoming) { case CMC_PANE_LIST: - ncplane_move_family_below(pn, ui->lp.n); + ncplane_move_top(ui->lp.n); + //ncplane_move_family_below(pn, ui->lp.n); break; case CMC_PANE_SEARCH: - ncplane_move_family_below(pn, ui->sp.n); + ncplane_move_top(ui->sp.n); + //ncplane_move_family_below(pn, ui->sp.n); + break; + case CMC_PANE_LOG: + ncplane_move_top(ui->logp.n); + //ncplane_move_family_below(pn, ui->logp.n); break; } } @@ -72,6 +82,9 @@ void cmc_ui_queue_render(struct cmc_ui *ui) case CMC_PANE_SEARCH: cmc_sp_erase(ui); break; + case CMC_PANE_LOG: + cmc_logp_erase(ui); + break; } if (ui->overlay_shown) { @@ -85,6 +98,9 @@ void cmc_ui_queue_render(struct cmc_ui *ui) case CMC_PANE_SEARCH: cmc_sp_render(ui); break; + case CMC_PANE_LOG: + cmc_logp_render(ui); + break; } if (ui->overlay_shown) { @@ -132,11 +148,12 @@ static void input_poll_callback(void *userdata, s32 revents) switch_to_pane(ui, CMC_PANE_LIST); break; case '2': - switch_to_pane(ui, CMC_PANE_SEARCH); break; case '3': + switch_to_pane(ui, CMC_PANE_SEARCH); break; case '4': + switch_to_pane(ui, CMC_PANE_LOG); break; case NCKEY_TAB: cmc_ui_toggle_overlay(ui); @@ -154,6 +171,9 @@ static void input_poll_callback(void *userdata, s32 revents) do_render |= true; } break; + case CMC_PANE_LOG: + do_render = true; + break; } if (do_render) { cmc_ui_queue_render(ui); @@ -187,6 +207,7 @@ static bool relayout(struct cmc_ui *ui) cmc_lp_layout(ui, ui->n); cmc_sp_layout(ui, ui->n); + cmc_logp_layout(ui, ui->n); if (ui->oln) ncplane_destroy(ui->oln); nopts.margin_r = MAX((u32)6, ncplane_dim_x(ui->n) / 2); @@ -206,6 +227,14 @@ static s32 resize_cb(struct ncplane *p) return 0; } +static s32 log_callback(void *userdata, u8 level, char *message) +{ + struct cmc_ui *ui = (struct cmc_ui *)userdata; + (void)level; + cmc_logp_push_message(ui, message); + return al_strnlen(message, AL_LOG_MESSAGE_SIZE); +} + bool cmc_ui_init(struct cmc_ui *ui, struct nn_event_loop *loop, struct cmc *c) { al_memset(ui, 0, sizeof(struct cmc_ui)); @@ -223,6 +252,9 @@ bool cmc_ui_init(struct cmc_ui *ui, struct nn_event_loop *loop, struct cmc *c) cmc_lp_init(ui); cmc_sp_init(ui); + cmc_logp_init(ui); + al_set_print(log_callback, ui); + ui->pending_layout = false; ui->pending_render = false; diff --git a/src/fruits/cmc/ui/ui.h b/src/fruits/cmc/ui/ui.h index b0ae730..302d735 100644 --- a/src/fruits/cmc/ui/ui.h +++ b/src/fruits/cmc/ui/ui.h @@ -10,6 +10,7 @@ enum { CMC_PANE_LIST = 0, CMC_PANE_SEARCH, + CMC_PANE_LOG }; struct cmc_list_tab { @@ -51,6 +52,9 @@ struct cmc_ui { u64 last_mouse_ts; struct { struct ncplane *n; + struct ncplane *w; + struct ncvisual *vis; + struct cmc_list_entry *blitted; array(struct cmc_list_tab) tabs; u32 current; } lp; // list pane. @@ -59,6 +63,10 @@ struct cmc_ui { array(struct cmc_search_tab) tabs; u32 current; } sp; // search pane. + struct { + struct ncplane *n; + array(char *) messages; + } logp; struct nn_poll input_poll; struct nn_timer render_timer; struct cmc *c; @@ -95,3 +103,10 @@ void cmc_sp_add_search(struct cmc_ui *ui, struct cmc_search *search); bool cmc_sp_handle_input(struct cmc_ui *ui, struct ncinput *input); void cmc_sp_erase(struct cmc_ui *ui); void cmc_sp_render(struct cmc_ui *ui); + +void cmc_logp_init(struct cmc_ui *ui); +void cmc_logp_layout(struct cmc_ui *ui, struct ncplane *parent); +void cmc_logp_push_message(struct cmc_ui *ui, char *message); +bool cmc_logp_handle_input(struct cmc_ui *ui, struct ncinput *input); +void cmc_logp_erase(struct cmc_ui *ui); +void cmc_logp_render(struct cmc_ui *ui); diff --git a/src/fruits/cmc/ui/waveform.c b/src/fruits/cmc/ui/waveform.c new file mode 100644 index 0000000..9bfc296 --- /dev/null +++ b/src/fruits/cmc/ui/waveform.c @@ -0,0 +1,202 @@ +#include <al/log.h> +#include <math.h> +#include "waveform.h" + +#define WITHIN_RMS(low, high, rms) \ + ((low >= 0 && low <= rms) || (high < 0 && high >= -rms) || (low < 0 && high > 0 && rms > 0)) + +#define OUTPUT_WAVEFORM_PNG +//#define WAVEFORM_REFERENCE_MODE +#ifdef OUTPUT_WAVEFORM_PNG +#include <nnwt/file.h> +#include <nnwt/net.h> +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include <stb_image_write.h> + +// min/max: #BC615E, #F4EAE0 +// rms: #FF928F, #E4CAAF +// +// min/max: #D93A50, #b22f41 +// rms: #f44f7a, #c84064 +// extra: #f6ac7f, #F5F4FA, #5b524b +// left channel: n == 0, right channel: n == 1. +#define COLOR_FOR_PIXEL(n, rms) rms ? \ + n == 0 ? nn_htonl(0xE4CAAF99) : nn_htonl(0xFF928FFF) : \ + n == 0 ? nn_htonl(0xF4EAE099) : nn_htonl(0xBC615EFF) + +// ss: current sample, ns: next sample. +#define HIT_WAVE(low, high, ss, ns) \ + ((ss <= low && ns > low) || (ss > high && ns <= high) || (ss > low && ns <= high)) + +static void test_write_bitmap(struct cmc_list_entry *entry, u32 width, u32 height, u32 **rgba) +{ + f64 start = nn_get_tick(); + + *rgba = al_calloc(width * height, 4); + + u32 channels = entry->raw.fmt.channel_count; + u32 samples_per_column = entry->raw.sample_count / width; + + s32 wave_min = INT16_MIN; + s32 wave_max = INT16_MAX; + s32 step = (wave_max * 2 + (height - 1)) / height; + //s32 step = (wave_max * 2 + (height / 2)) / height; + + s16 *steps = al_malloc(sizeof(s16) * (height + 1)); + for (s32 i = 0; i <= (s32)height; i++) { + steps[i] = (s16)MAX(wave_max - step * i, wave_min); + } + //steps[height] = wave_min; + al_assert(steps[height] == wave_min); + + s64 rms = 0; + s16 min, max; + s16 low, high; + s16 *data = (s16 *)entry->raw.data; + const u32 samples_stride = samples_per_column * channels; + for (u32 i = 0; i < width; i++) { + u32 f = i * samples_stride; + for (u32 n = 0; n < channels; n++) { + rms = 0; + min = wave_max, max = wave_min; + for (u32 m = 0; m <= samples_stride; m += channels) { + s16 ss = data[f + m + n]; + rms += ss * ss; + min = MIN(min, ss); + max = MAX(max, ss); + } + rms /= samples_per_column; + rms = sqrt(rms); +#ifndef WAVEFORM_REFERENCE_MODE + for (u32 j = 0; j < height; j++) { + low = steps[j + 1], high = steps[j]; + if (low < max && high >= min) { + (*rgba)[j * width + i] = COLOR_FOR_PIXEL(n, WITHIN_RMS(low, high, rms)); + } + } +#else + for (u32 m = 0; m < samples_stride; m += channels) { + s16 ss = data[f + m + n]; + s16 ns = data[f + m + n + channels]; + for (u32 j = 0; j < height; j++) { + low = steps[j + 1], high = steps[j]; + if (HIT_WAVE(low, high, ss, ns)) { + (*rgba)[j * width + i] = COLOR_FOR_PIXEL(n, WITHIN_RMS(low, high, rms)); + } + } + } +#endif + } + } + + log_info("generating waveform took: %.2fs.", nn_get_tick() - start); + + //stbi_write_png("./waveform.png", width, height, 4, *rgba, width * 4); + + /* + struct nn_file file; + nn_file_open(&file, &al_str_c("./raw_samples.raw"), NNWT_FILE_CREATE); + size_t bytes = entry->raw.sample_count * entry->raw.fmt.channel_count * camu_audio_format_bytes_per_sample(&entry->raw.fmt); + nn_file_write(&file, entry->raw.data, bytes); + nn_file_close(&file); + */ +} +#endif + +static const char *octant_lut[] = { + " ", "๐บจ", "๐บซ", "๐ฎ", "๐ด", "โ", "๐ด", "๐ด", "๐ด", "๐ด", "โ", "๐ด
", "๐ด", "๐ด", "๐ด", "โ", + "๐ด", "๐ด", "๐ด", "๐ด", "๐ฏฆ", "๐ด", "๐ด", "๐ด", "๐ด", "๐ด", "๐ด", "๐ด", "๐ด", "๐ด", "๐ด", "๐ด", + "๐ด", "๐ด", "๐ด", "๐ด", "๐ด", "๐ด", "๐ด", "๐ด", "๐ฏง", "๐ด ", "๐ดก", "๐ดข", "๐ดฃ", "๐ดค", "๐ดฅ", "๐ดฆ", + "๐ดง", "๐ดจ", "๐ดฉ", "๐ดช", "๐ดซ", "๐ดฌ", "๐ดญ", "๐ดฎ", "๐ดฏ", "๐ดฐ", "๐ดฑ", "๐ดฒ", "๐ดณ", "๐ดด", "๐ดต", "๐ฎ
", + "๐บฃ", "๐ดถ", "๐ดท", "๐ดธ", "๐ดน", "๐ดบ", "๐ดป", "๐ดผ", "๐ดฝ", "๐ดพ", "๐ดฟ", "๐ต", "๐ต", "๐ต", "๐ต", "๐ต", + "โ", "๐ต
", "๐ต", "๐ต", "๐ต", "โ", "๐ต", "๐ต", "๐ต", "๐ต", "โ", "๐ต", "๐ต", "๐ต", "๐ต", "โ", + "๐ต", "๐ต", "๐ต", "๐ต", "๐ต", "๐ต", "๐ต", "๐ต", "๐ต", "๐ต", "๐ต", "๐ต", "๐ต", "๐ต", "๐ต", "๐ต ", + "๐ตก", "๐ตข", "๐ตฃ", "๐ตค", "๐ตฅ", "๐ตฆ", "๐ตง", "๐ตจ", "๐ตฉ", "๐ตช", "๐ตซ", "๐ตฌ", "๐ตญ", "๐ตฎ", "๐ตฏ", "๐ตฐ", + "๐บ ", "๐ตฑ", "๐ตฒ", "๐ตณ", "๐ตด", "๐ตต", "๐ตถ", "๐ตท", "๐ตธ", "๐ตน", "๐ตบ", "๐ตป", "๐ตผ", "๐ตฝ", "๐ตพ", "๐ตฟ", + "๐ถ", "๐ถ", "๐ถ", "๐ถ", "๐ถ", "๐ถ
", "๐ถ", "๐ถ", "๐ถ", "๐ถ", "๐ถ", "๐ถ", "๐ถ", "๐ถ", "๐ถ", "๐ถ", + "โ", "๐ถ", "๐ถ", "๐ถ", "๐ถ", "โ", "๐ถ", "๐ถ", "๐ถ", "๐ถ", "โ", "๐ถ", "๐ถ", "๐ถ", "๐ถ", "โ", + "๐ถ", "๐ถ", "๐ถ", "๐ถ", "๐ถ ", "๐ถก", "๐ถข", "๐ถฃ", "๐ถค", "๐ถฅ", "๐ถฆ", "๐ถง", "๐ถจ", "๐ถฉ", "๐ถช", "๐ถซ", + "โ", "๐ถฌ", "๐ถญ", "๐ถฎ", "๐ถฏ", "๐ถฐ", "๐ถฑ", "๐ถฒ", "๐ถณ", "๐ถด", "๐ถต", "๐ถถ", "๐ถท", "๐ถธ", "๐ถน", "๐ถบ", + "๐ถป", "๐ถผ", "๐ถฝ", "๐ถพ", "๐ถฟ", "๐ท", "๐ท", "๐ท", "๐ท", "๐ท", "๐ท
", "๐ท", "๐ท", "๐ท", "๐ท", "๐ท", + "๐ท", "๐ท", "๐ท", "๐ท", "๐ท", "๐ท", "๐ท", "๐ท", "๐ท", "๐ท", "๐ท", "๐ท", "๐ท", "๐ท", "๐ท", "๐ท", + "โ", "๐ท", "๐ท", "๐ท", "๐ท", "โ", "๐ท", "๐ท ", "๐ทก", "๐ทข", "โ", "๐ทฃ", "โ", "๐ทค", "๐ทฅ", "โ" +}; + +#define RMS_BIT (1 << 8) + +void cmc_draw_waveform(struct cmc_list_entry *entry, u32 width, u32 height, bool include_rms, char ***buf, u32 **rgba) +{ +#ifdef OUTPUT_WAVEFORM_PNG + test_write_bitmap(entry, width, height, rgba); + return; +#endif + + u32 channels = entry->raw.fmt.channel_count; + u32 samples_per_column = entry->raw.sample_count / (width * 2); + + s32 pixel_height = height * 4; + s32 wave_min = INT16_MIN; + s32 wave_max = INT16_MAX; + s32 step = (wave_max * 2 + (pixel_height - 1)) / pixel_height; + + s16 *steps = al_malloc(sizeof(s16) * (pixel_height + 1)); + for (s32 i = 0; i <= (s32)pixel_height; i++) { + steps[i] = (s16)MAX(wave_max - step * i, wave_min); + } + al_assert(steps[pixel_height] == wave_min); + + s64 rms = 0; + s16 min, max; + s16 low, high; + s16 *data = (s16 *)entry->raw.data; + const u32 samples_stride = samples_per_column * channels; + u16 masks_for_chars[height]; + //u32 slen[height]; + //al_memset(slen, 0, sizeof(u32) * height); + for (u32 i = 0; i < width; i++) { + al_memset(masks_for_chars, 0, sizeof(u16) * height); + for (u32 k = 0; k < 2; k++) { + u32 f = (i * 2 + k) * samples_stride; + for (u32 n = 0; n < channels; n++) { + rms = 0; + min = wave_max, max = wave_min; + for (u32 m = 0; m <= samples_stride; m += channels) { + s16 ss = data[f + m + n]; + rms += ss * ss; + min = MIN(min, ss); + max = MAX(max, ss); + } + rms /= samples_per_column; + rms = sqrt(rms); + for (u32 j = 0; j < height; j++) { + for (u32 l = 0; l < 4; l++) { + u32 step = j * 4 + l; + low = steps[step + 1], high = steps[step]; + if (low < max && high >= min) { + if (include_rms && WITHIN_RMS(low, high, rms)) { + masks_for_chars[j] |= RMS_BIT; + } else { + masks_for_chars[j] |= 1 << (l * 2 + k); + } + } + } + } + } + } + for (u32 j = 0; j < height; j++) { + if (masks_for_chars[j] & RMS_BIT) { + buf[j][i][0] = 'R'; + masks_for_chars[j] &= ~RMS_BIT; + } + u32 len = masks_for_chars[j] == 0 ? 2 : 5; + al_memcpy(buf[j][i] + 1, octant_lut[masks_for_chars[j]], len); + //slen[j] += len; + } + } + + /* + for (u32 j = 0; j < height; j++) { + buf[j][slen[j]] = '\0'; + } + */ +} diff --git a/src/fruits/cmc/ui/waveform.h b/src/fruits/cmc/ui/waveform.h index f3803dd..464d589 100644 --- a/src/fruits/cmc/ui/waveform.h +++ b/src/fruits/cmc/ui/waveform.h @@ -1,5 +1,4 @@ /* - notcurses.h: Halves @@ -84,9 +83,15 @@ AAAAAAAAAAAAAAAA ๐ดข 0b00101011 +๐บจ๐บซ๐ฎ๐ดโ๐ด๐ด๐ด๐ดโ๐ด
๐ด๐ด๐ดโ๐ด๐ด๐ด๐ด๐ฏฆ๐ด๐ด๐ด๐ด๐ด๐ด๐ด๐ด๐ด๐ด๐ด๐ด๐ด๐ด๐ด๐ด๐ด๐ด๐ด๐ฏง๐ด ๐ดก๐ดข๐ดฃ๐ดค๐ดฅ๐ดฆ๐ดง๐ดจ๐ดฉ๐ดช๐ดซ๐ดฌ๐ดญ๐ดฎ๐ดฏ๐ดฐ๐ดฑ๐ดฒ๐ดณ๐ดด๐ดต๐ฎ
๐บฃ๐ดถ๐ดท๐ดธ๐ดน๐ดบ๐ดป๐ดผ๐ดฝ๐ดพ๐ดฟ๐ต๐ต๐ต๐ต๐ตโ๐ต
๐ต๐ต๐ตโ๐ต๐ต๐ต๐ตโ๐ต๐ต๐ต๐ตโ๐ต๐ต๐ต๐ต๐ต๐ต๐ต๐ต๐ต๐ต๐ต๐ต๐ต๐ต๐ต๐ต ๐ตก๐ตข๐ตฃ๐ตค๐ตฅ๐ตฆ๐ตง๐ตจ๐ตฉ๐ตช๐ตซ๐ตฌ๐ตญ๐ตฎ๐ตฏ๐ตฐ๐บ ๐ตฑ๐ตฒ๐ตณ๐ตด๐ตต๐ตถ๐ตท๐ตธ๐ตน๐ตบ๐ตป๐ตผ๐ตฝ๐ตพ๐ตฟ๐ถ๐ถ๐ถ๐ถ๐ถ๐ถ
๐ถ๐ถ๐ถ๐ถ๐ถ๐ถ๐ถ๐ถ๐ถ๐ถโ๐ถ๐ถ๐ถ๐ถโ๐ถ๐ถ๐ถ๐ถโ๐ถ๐ถ๐ถ๐ถโ๐ถ๐ถ๐ถ๐ถ๐ถ ๐ถก๐ถข๐ถฃ๐ถค๐ถฅ๐ถฆ๐ถง๐ถจ๐ถฉ๐ถช๐ถซโ๐ถฌ๐ถญ๐ถฎ๐ถฏ๐ถฐ๐ถฑ๐ถฒ๐ถณ๐ถด๐ถต๐ถถ๐ถท๐ถธ๐ถน๐ถบ๐ถป๐ถผ๐ถฝ๐ถพ๐ถฟ๐ท๐ท๐ท๐ท๐ท๐ท
๐ท๐ท๐ท๐ท๐ท๐ท๐ท๐ท๐ท๐ท๐ท๐ท๐ท๐ท๐ท๐ท๐ท๐ท๐ท๐ท๐ทโ๐ท๐ท๐ท๐ทโ๐ท๐ท ๐ทก๐ทขโ๐ทฃโ๐ทค๐ทฅโ */ -static char *bar_lut[] = { +#include <al/types.h> + +#include "../cmc.h" + +/* +static const char *bar_lut[] = { [0] = " ๐บ โ๐ถโ" , [1] = "๐บฃโ๐ท๐ท๐ท", [2] = "โ๐ถปโ๐ทก๐ทฅ", @@ -94,11 +99,13 @@ static char *bar_lut[] = { [4] = "โ๐ทโ๐ทคโ" }; -static char *bar_bottom_lut[] = { +static const char *bar_bottom_lut[] = { [0] = " ๐บซโ๐ดกโ" , [1] = "๐บจ๐ฎ๐ด
๐ดข๐ถ", [2] = "โ๐ดโ๐ดฆโ", [3] = "๐ด๐ด๐ด๐ฎ
๐ถซ", [4] = "โ๐ตโ๐ตฐโ" }; +*/ +void cmc_draw_waveform(struct cmc_list_entry *entry, u32 width, u32 height, bool include_rms, char ***buf, u32 **rgba); |