summaryrefslogtreecommitdiff
path: root/src/fruits/cmc/ui/util
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2025-04-25 15:56:56 -0400
committerAndrew Opalach <andrew@akon.city> 2025-04-25 15:56:56 -0400
commitb1985414e54edf1eebe597551a48609ad08c6cfa (patch)
tree4a4def499348e0c9ccb52472e7c862f814993931 /src/fruits/cmc/ui/util
parent76fc9fe33dd58926e552b6bd3b79d798b452c6ed (diff)
downloadcamu-b1985414e54edf1eebe597551a48609ad08c6cfa.tar.gz
camu-b1985414e54edf1eebe597551a48609ad08c6cfa.tar.bz2
camu-b1985414e54edf1eebe597551a48609ad08c6cfa.zip
Partial cmc ui refactor
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/fruits/cmc/ui/util')
-rw-r--r--src/fruits/cmc/ui/util/notcurses_ext.h20
-rw-r--r--src/fruits/cmc/ui/util/waveform.c202
-rw-r--r--src/fruits/cmc/ui/util/waveform.h111
3 files changed, 333 insertions, 0 deletions
diff --git a/src/fruits/cmc/ui/util/notcurses_ext.h b/src/fruits/cmc/ui/util/notcurses_ext.h
new file mode 100644
index 0000000..b09754e
--- /dev/null
+++ b/src/fruits/cmc/ui/util/notcurses_ext.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <al/types.h>
+#include <notcurses/notcurses.h>
+
+// Copy-paste from notcurses.h
+static inline s32 ncplane_light_box(struct ncplane *n, u16 attr, u64 channels, s32 ystop, s32 xstop, u32 ctlword)
+{
+ s32 ret = 0;
+ nccell ul = NCCELL_TRIVIAL_INITIALIZER, ur = NCCELL_TRIVIAL_INITIALIZER;
+ nccell ll = NCCELL_TRIVIAL_INITIALIZER, lr = NCCELL_TRIVIAL_INITIALIZER;
+ nccell hl = NCCELL_TRIVIAL_INITIALIZER, vl = NCCELL_TRIVIAL_INITIALIZER;
+ if((ret = nccells_light_box(n, attr, channels, &ul, &ur, &ll, &lr, &hl, &vl)) == 0){
+ ret = ncplane_box(n, &ul, &ur, &ll, &lr, &hl, &vl, ystop, xstop, ctlword);
+ }
+ nccell_release(n, &ul); nccell_release(n, &ur);
+ nccell_release(n, &ll); nccell_release(n, &lr);
+ nccell_release(n, &hl); nccell_release(n, &vl);
+ return ret;
+}
diff --git a/src/fruits/cmc/ui/util/waveform.c b/src/fruits/cmc/ui/util/waveform.c
new file mode 100644
index 0000000..8d37543
--- /dev/null
+++ b/src/fruits/cmc/ui/util/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/util/waveform.h b/src/fruits/cmc/ui/util/waveform.h
new file mode 100644
index 0000000..763f54a
--- /dev/null
+++ b/src/fruits/cmc/ui/util/waveform.h
@@ -0,0 +1,111 @@
+/*
+notcurses.h:
+Halves
+
+โ–„ โ–€
+
+Quadrants
+
+โ–— โ– โ–– โ–€ โ–Ÿ โ–Œ โ–™
+
+Sextants
+
+๐Ÿฌ€ ๐Ÿฌ ๐Ÿฌ‚ ๐Ÿฌƒ ๐Ÿฌ„ ๐Ÿฌ… ๐Ÿฌ† ๐Ÿฌ‡ ๐Ÿฌˆ ๐Ÿฌ‰ ๐ŸฌŠ ๐Ÿฌ‹ ๐ŸฌŒ ๐Ÿฌ ๐ŸฌŽ ๐Ÿฌ ๐Ÿฌ ๐Ÿฌ‘ ๐Ÿฌ’ ๐Ÿฌ“ ๐Ÿฌ” ๐Ÿฌ• ๐Ÿฌ– ๐Ÿฌ— ๐Ÿฌ˜ ๐Ÿฌ™ ๐Ÿฌš ๐Ÿฌ› ๐Ÿฌœ ๐Ÿฌ ๐Ÿฌž
+
+https://wiki.ourworldoftext.com/wiki/Octant:
+Octants
+
+ ๐œบจ ๐œบซ ๐Ÿฎ‚ ๐œด€ โ–˜ ๐œด ๐œด‚ ๐œดƒ ๐œด„ โ– ๐œด… ๐œด† ๐œด‡ ๐œดˆ โ–€
+
+๐œด‰ ๐œดŠ ๐œด‹ ๐œดŒ ๐Ÿฏฆ ๐œด ๐œดŽ ๐œด ๐œด ๐œด‘ ๐œด’ ๐œด“ ๐œด” ๐œด• ๐œด– ๐œด—
+
+๐œด˜ ๐œด™ ๐œดš ๐œด› ๐œดœ ๐œด ๐œดž ๐œดŸ ๐Ÿฏง ๐œด  ๐œดก ๐œดข ๐œดฃ ๐œดค ๐œดฅ ๐œดฆ
+
+๐œดง ๐œดจ ๐œดฉ ๐œดช ๐œดซ ๐œดฌ ๐œดญ ๐œดฎ ๐œดฏ ๐œดฐ ๐œดฑ ๐œดฒ ๐œดณ ๐œดด ๐œดต ๐Ÿฎ…
+
+๐œบฃ ๐œดถ ๐œดท ๐œดธ ๐œดน ๐œดบ ๐œดป ๐œดผ ๐œดฝ ๐œดพ ๐œดฟ ๐œต€ ๐œต ๐œต‚ ๐œตƒ ๐œต„
+
+โ–– ๐œต… ๐œต† ๐œต‡ ๐œตˆ โ–Œ ๐œต‰ ๐œตŠ ๐œต‹ ๐œตŒ โ–ž ๐œต ๐œตŽ ๐œต ๐œต โ–›
+
+๐œต‘ ๐œต’ ๐œต“ ๐œต” ๐œต• ๐œต– ๐œต— ๐œต˜ ๐œต™ ๐œตš ๐œต› ๐œตœ ๐œต ๐œตž ๐œตŸ ๐œต 
+
+๐œตก ๐œตข ๐œตฃ ๐œตค ๐œตฅ ๐œตฆ ๐œตง ๐œตจ ๐œตฉ ๐œตช ๐œตซ ๐œตฌ ๐œตญ ๐œตฎ ๐œตฏ ๐œตฐ
+
+๐œบ  ๐œตฑ ๐œตฒ ๐œตณ ๐œตด ๐œตต ๐œตถ ๐œตท ๐œตธ ๐œตน ๐œตบ ๐œตป ๐œตผ ๐œตฝ ๐œตพ ๐œตฟ
+
+๐œถ€ ๐œถ ๐œถ‚ ๐œถƒ ๐œถ„ ๐œถ… ๐œถ† ๐œถ‡ ๐œถˆ ๐œถ‰ ๐œถŠ ๐œถ‹ ๐œถŒ ๐œถ ๐œถŽ ๐œถ
+
+โ–— ๐œถ ๐œถ‘ ๐œถ’ ๐œถ“ โ–š ๐œถ” ๐œถ• ๐œถ– ๐œถ— โ– ๐œถ˜ ๐œถ™ ๐œถš ๐œถ› โ–œ
+
+๐œถœ ๐œถ ๐œถž ๐œถŸ ๐œถ  ๐œถก ๐œถข ๐œถฃ ๐œถค ๐œถฅ ๐œถฆ ๐œถง ๐œถจ ๐œถฉ ๐œถช ๐œถซ
+
+โ–‚ ๐œถฌ ๐œถญ ๐œถฎ ๐œถฏ ๐œถฐ ๐œถฑ ๐œถฒ ๐œถณ ๐œถด ๐œถต ๐œถถ ๐œถท ๐œถธ ๐œถน ๐œถบ
+
+๐œถป ๐œถผ ๐œถฝ ๐œถพ ๐œถฟ ๐œท€ ๐œท ๐œท‚ ๐œทƒ ๐œท„ ๐œท… ๐œท† ๐œท‡ ๐œทˆ ๐œท‰ ๐œทŠ
+
+๐œท‹ ๐œทŒ ๐œท ๐œทŽ ๐œท ๐œท ๐œท‘ ๐œท’ ๐œท“ ๐œท” ๐œท• ๐œท– ๐œท— ๐œท˜ ๐œท™ ๐œทš
+
+โ–„ ๐œท› ๐œทœ ๐œท ๐œทž โ–™ ๐œทŸ ๐œท  ๐œทก ๐œทข โ–Ÿ ๐œทฃ โ–† ๐œทค ๐œทฅ โ–ˆ
+
+AAAAAAAAAAAAAAAA
+
+โ– โ–‚ ๐œถป ๐œท‹ โ–ƒ โ–„ ๐œทž ๐œทก โ–… โ–† ๐œทค ๐œทฅ โ–‡ โ–ˆ
+
+ โ–โ–ˆ
+ ๐œท•โ–ˆ
+
+ โ–Œ
+ โ–Œ
+ โ–Œ
+ ๐œท€
+
+โ–‚๐œดง๐œด
+
+
+ โ–โ–‚๐œท‹โ–ƒโ–„๐œทกโ–…โ–†๐œทฅโ–‡โ–ˆโ–ˆโ–ˆโ–ˆโ–‡๐œทฅโ–†โ–…๐œทกโ–„โ–ƒ๐œท‹โ–‚โ–
+โ–โ–‚๐œท‹โ–ƒโ–„๐œทกโ–…โ–†๐œทฅโ–‡โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‡๐œทฅโ–†โ–…๐œทกโ–„โ–ƒ๐œท‹โ–‚โ–
+
+
+โ–ˆโ–‡๐œทคโ–†โ–…๐œทžโ–„โ–ƒ๐œถปโ–‚โ–
+
+โ–Œ๐œตˆโ–๐œท€
+โ–โ–ƒโ–‚โ–โ–„
+
+0
+1 ๐œบจ
+1 << 1 ๐œบซ
+1 << 2 ๐œด€
+1 << 3 ๐œดƒ
+1 << 4 ๐œด‰
+1 << 5 ๐œด˜
+1 << 6 ๐œบฃ
+1 << 7 ๐œบ 
+
+๐œดข 0b00101011
+
+๐œบจ๐œบซ๐Ÿฎ‚๐œด€โ–˜๐œด๐œด‚๐œดƒ๐œด„โ–๐œด…๐œด†๐œด‡๐œดˆโ–€๐œด‰๐œดŠ๐œด‹๐œดŒ๐Ÿฏฆ๐œด๐œดŽ๐œด๐œด๐œด‘๐œด’๐œด“๐œด”๐œด•๐œด–๐œด—๐œด˜๐œด™๐œดš๐œด›๐œดœ๐œด๐œดž๐œดŸ๐Ÿฏง๐œด ๐œดก๐œดข๐œดฃ๐œดค๐œดฅ๐œดฆ๐œดง๐œดจ๐œดฉ๐œดช๐œดซ๐œดฌ๐œดญ๐œดฎ๐œดฏ๐œดฐ๐œดฑ๐œดฒ๐œดณ๐œดด๐œดต๐Ÿฎ…๐œบฃ๐œดถ๐œดท๐œดธ๐œดน๐œดบ๐œดป๐œดผ๐œดฝ๐œดพ๐œดฟ๐œต€๐œต๐œต‚๐œตƒ๐œต„โ––๐œต…๐œต†๐œต‡๐œตˆโ–Œ๐œต‰๐œตŠ๐œต‹๐œตŒโ–ž๐œต๐œตŽ๐œต๐œตโ–›๐œต‘๐œต’๐œต“๐œต”๐œต•๐œต–๐œต—๐œต˜๐œต™๐œตš๐œต›๐œตœ๐œต๐œตž๐œตŸ๐œต ๐œตก๐œตข๐œตฃ๐œตค๐œตฅ๐œตฆ๐œตง๐œตจ๐œตฉ๐œตช๐œตซ๐œตฌ๐œตญ๐œตฎ๐œตฏ๐œตฐ๐œบ ๐œตฑ๐œตฒ๐œตณ๐œตด๐œตต๐œตถ๐œตท๐œตธ๐œตน๐œตบ๐œตป๐œตผ๐œตฝ๐œตพ๐œตฟ๐œถ€๐œถ๐œถ‚๐œถƒ๐œถ„๐œถ…๐œถ†๐œถ‡๐œถˆ๐œถ‰๐œถŠ๐œถ‹๐œถŒ๐œถ๐œถŽ๐œถโ–—๐œถ๐œถ‘๐œถ’๐œถ“โ–š๐œถ”๐œถ•๐œถ–๐œถ—โ–๐œถ˜๐œถ™๐œถš๐œถ›โ–œ๐œถœ๐œถ๐œถž๐œถŸ๐œถ ๐œถก๐œถข๐œถฃ๐œถค๐œถฅ๐œถฆ๐œถง๐œถจ๐œถฉ๐œถช๐œถซโ–‚๐œถฌ๐œถญ๐œถฎ๐œถฏ๐œถฐ๐œถฑ๐œถฒ๐œถณ๐œถด๐œถต๐œถถ๐œถท๐œถธ๐œถน๐œถบ๐œถป๐œถผ๐œถฝ๐œถพ๐œถฟ๐œท€๐œท๐œท‚๐œทƒ๐œท„๐œท…๐œท†๐œท‡๐œทˆ๐œท‰๐œทŠ๐œท‹๐œทŒ๐œท๐œทŽ๐œท๐œท๐œท‘๐œท’๐œท“๐œท”๐œท•๐œท–๐œท—๐œท˜๐œท™๐œทšโ–„๐œท›๐œทœ๐œท๐œทžโ–™๐œทŸ๐œท ๐œทก๐œทขโ–Ÿ๐œทฃโ–†๐œทค๐œทฅโ–ˆ
+*/
+
+#include <al/types.h>
+
+#include "../../cmc.h"
+
+/*
+static const char *bar_lut[] = {
+ [0] = " ๐œบ โ–—๐œถ–โ–" ,
+ [1] = "๐œบฃโ–‚๐œท‹๐œท“๐œท•",
+ [2] = "โ––๐œถปโ–„๐œทก๐œทฅ",
+ [3] = "๐œตˆ๐œถฟ๐œทžโ–†๐œทฅ",
+ [4] = "โ–Œ๐œท€โ–™๐œทคโ–ˆ"
+};
+
+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);