/* * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) * * This is free software: you can redistribute it and/or modify * it under the terms of the Artistic License 2.0 as published by * The Perl Foundation. * * This source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Artistic License 2.0 for more details. * * You should have received a copy of the Artistic License 2.0 * along the source as a COPYING file. If not, obtain it from * http://www.perlfoundation.org/artistic_license_2_0. */ #include #include #include #include #include #include typedef struct _mem_t mem_t; struct _mem_t { size_t size; char *buf; }; static inline void _shadow(struct nk_context *ctx, bool *shadow) { if(*shadow) { struct nk_style *style = &ctx->style; const struct nk_vec2 group_padding = style->window.group_padding; struct nk_command_buffer *canvas = nk_window_get_canvas(ctx); struct nk_rect b = nk_widget_bounds(ctx); b.x -= group_padding.x; b.w *= 10; b.w += 5*group_padding.x; nk_fill_rect(canvas, b, 0.f, nk_rgb(0x28, 0x28, 0x28)); } *shadow = !*shadow; } static inline void _mem_printf(mem_t *mem, const char *fmt, ...) { va_list args; va_start(args, fmt); char *src; if(vasprintf(&src, fmt, args) != -1) { const size_t sz = strlen(src); mem->buf = realloc(mem->buf, mem->size + sz); if(mem->buf) { char *dst = mem->buf + mem->size - 1; strncpy(dst, src, sz + 1); mem->size += sz; } else mem->size = 0; free(src); } va_end(args); } static void _osc_timetag(mem_t *mem, LV2_OSC_Timetag *tt) { if(tt->integral <= 1UL) { _mem_printf(mem, "t:%s", "immediate"); } else { const uint32_t us = floor(tt->fraction * 0x1p-32 * 1e6); const time_t ttime = tt->integral - 0x83aa7e80; const struct tm *ltime = localtime(&ttime); char tmp [32]; if(strftime(tmp, 32, "%d-%b-%Y %T", ltime)) _mem_printf(mem, "t:%s.%06"PRIu32, tmp, us); } } static void _osc_message(plughandle_t *handle, struct nk_context *ctx, const LV2_Atom_Object *obj, float offset) { const LV2_Atom_String *path = NULL; const LV2_Atom_Tuple *args = NULL; lv2_osc_message_get(&handle->osc_urid, obj, &path, &args); mem_t mem = { .size = 1, .buf = calloc(1, sizeof(char)) }; LV2_ATOM_TUPLE_FOREACH(args, arg) { const LV2_OSC_Type type = lv2_osc_argument_type(&handle->osc_urid, arg); switch(type) { case LV2_OSC_INT32: { int32_t i; lv2_osc_int32_get(&handle->osc_urid, arg, &i); _mem_printf(&mem, "i:%"PRIi32, i); } break; case LV2_OSC_FLOAT: { float f; lv2_osc_float_get(&handle->osc_urid, arg, &f); _mem_printf(&mem, "f:%f", f); } break; case LV2_OSC_STRING: { const char *s; lv2_osc_string_get(&handle->osc_urid, arg, &s); _mem_printf(&mem, "s:%s", s); } break; case LV2_OSC_BLOB: { uint32_t sz; const uint8_t *b; lv2_osc_blob_get(&handle->osc_urid, arg, &sz, &b); _mem_printf(&mem, "b(%"PRIu32"):", sz); for(unsigned i=0; iosc_urid, arg, &h); _mem_printf(&mem, "h:%"PRIi64, h); } break; case LV2_OSC_DOUBLE: { double d; lv2_osc_double_get(&handle->osc_urid, arg, &d); _mem_printf(&mem, "d:%lf", d); } break; case LV2_OSC_TIMETAG: { LV2_OSC_Timetag tt; lv2_osc_timetag_get(&handle->osc_urid, arg, &tt); _osc_timetag(&mem, &tt); } break; case LV2_OSC_SYMBOL: { const char *S; lv2_osc_symbol_get(&handle->osc_urid, arg, &S); _mem_printf(&mem, "S:%s", S); } break; case LV2_OSC_CHAR: { char c; lv2_osc_char_get(&handle->osc_urid, arg, &c); _mem_printf(&mem, "c:%c", c); } break; case LV2_OSC_RGBA: { uint8_t r [4]; lv2_osc_rgba_get(&handle->osc_urid, arg, r+0, r+1, r+2, r+3); _mem_printf(&mem, "r:"); for(unsigned i=0; i<4; i++) _mem_printf(&mem, "%02"PRIx8, r[i]); } break; case LV2_OSC_MIDI: { uint32_t sz; const uint8_t *m; lv2_osc_midi_get(&handle->osc_urid, arg, &sz, &m); _mem_printf(&mem, "m(%"PRIu32"):", sz); for(unsigned i=0; iatom.size); nk_layout_row_end(ctx); } static void _osc_packet(plughandle_t *handle, struct nk_context *ctx, const LV2_Atom_Object *obj, float offset); static void _osc_bundle(plughandle_t *handle, struct nk_context *ctx, const LV2_Atom_Object *obj, float offset) { const float widget_h = handle->dy; const LV2_Atom_Object *timetag = NULL; const LV2_Atom_Tuple *items = NULL; lv2_osc_bundle_get(&handle->osc_urid, obj, &timetag, &items); // format bundle timestamp LV2_OSC_Timetag tt; lv2_osc_timetag_get(&handle->osc_urid, &timetag->atom, &tt); mem_t mem = { .size = 1, .buf = calloc(1, sizeof(char)) }; nk_layout_row_push(ctx, 0.4 - offset); nk_label_colored(ctx, "#bundle", NK_TEXT_LEFT, red); nk_layout_row_push(ctx, 0.5); _osc_timetag(&mem, &tt); if(mem.buf) { nk_label_colored(ctx, mem.buf, NK_TEXT_LEFT, white); free(mem.buf); } else _empty(ctx); nk_layout_row_push(ctx, 0.1); nk_labelf_colored(ctx, NK_TEXT_RIGHT, blue, "%"PRIu32, obj->atom.size); nk_layout_row_end(ctx); offset += 0.025; LV2_ATOM_TUPLE_FOREACH(items, item) { nk_layout_row_begin(ctx, NK_DYNAMIC, widget_h, 4); nk_layout_row_push(ctx, offset); _shadow(ctx, &handle->shadow); _empty(ctx); _osc_packet(handle, ctx, (const LV2_Atom_Object *)item, offset); } } static void _osc_packet(plughandle_t *handle, struct nk_context *ctx, const LV2_Atom_Object *obj, float offset) { if(lv2_osc_is_message_type(&handle->osc_urid, obj->body.otype)) { _osc_message(handle, ctx, obj, offset); } else if(lv2_osc_is_bundle_type(&handle->osc_urid, obj->body.otype)) { _osc_bundle(handle, ctx, obj, offset); } } void _osc_inspector_expose(struct nk_context *ctx, struct nk_rect wbounds, void *data) { plughandle_t *handle = data; const float widget_h = handle->dy; struct nk_style *style = &ctx->style; const struct nk_vec2 window_padding = style->window.padding; const struct nk_vec2 group_padding = style->window.group_padding; if(nk_begin(ctx, "Window", wbounds, NK_WINDOW_NO_SCROLLBAR)) { nk_window_set_bounds(ctx, wbounds); struct nk_panel *panel= nk_window_get_panel(ctx); struct nk_command_buffer *canvas = nk_window_get_canvas(ctx); const float body_h = panel->bounds.h - 4*window_padding.y - 2*widget_h; nk_layout_row_dynamic(ctx, body_h, 1); nk_flags flags = NK_WINDOW_BORDER; if(handle->state.follow) flags |= NK_WINDOW_NO_SCROLLBAR; struct nk_list_view lview; if(nk_list_view_begin(ctx, &lview, "Events", flags, widget_h, NK_MIN(handle->n_item, MAX_LINES))) { if(handle->state.follow) { lview.end = NK_MAX(handle->n_item, 0); lview.begin = NK_MAX(lview.end - lview.count, 0); } handle->shadow = lview.begin % 2 == 0; for(int l = lview.begin; (l < lview.end) && (l < handle->n_item); l++) { item_t *itm = handle->items[l]; switch(itm->type) { case ITEM_TYPE_NONE: { // skip, was bundle payload } break; case ITEM_TYPE_FRAME: { nk_layout_row_dynamic(ctx, widget_h, 3); { struct nk_rect b = nk_widget_bounds(ctx); b.x -= group_padding.x; b.w *= 3; b.w += 4*group_padding.x; nk_fill_rect(canvas, b, 0.f, nk_rgb(0x18, 0x18, 0x18)); } nk_labelf_colored(ctx, NK_TEXT_LEFT, orange, "@%"PRIi64, itm->frame.offset); nk_labelf_colored(ctx, NK_TEXT_CENTERED, green, "-%"PRIu32"-", itm->frame.counter); nk_labelf_colored(ctx, NK_TEXT_RIGHT, violet, "%"PRIi32, itm->frame.nsamples); } break; case ITEM_TYPE_EVENT: { LV2_Atom_Event *ev = &itm->event.ev; const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body; const int64_t frames = ev->time.frames; const float off = 0.1; nk_layout_row_begin(ctx, NK_DYNAMIC, widget_h, 4); nk_layout_row_push(ctx, off); _shadow(ctx, &handle->shadow); nk_labelf_colored(ctx, NK_TEXT_LEFT, yellow, "+%04"PRIi64, frames); _osc_packet(handle, ctx, obj, off); } break; } } nk_list_view_end(&lview); } nk_layout_row_dynamic(ctx, widget_h, 3); { if(nk_checkbox_label(ctx, "overwrite", &handle->state.overwrite)) _toggle(handle, handle->urid.overwrite, handle->state.overwrite, true); if(nk_checkbox_label(ctx, "block", &handle->state.block)) _toggle(handle, handle->urid.block, handle->state.block, true); if(nk_checkbox_label(ctx, "follow", &handle->state.follow)) _toggle(handle, handle->urid.follow, handle->state.follow, true); } const bool max_reached = handle->n_item >= MAX_LINES; nk_layout_row_dynamic(ctx, widget_h, 2); if(nk_button_symbol_label(ctx, max_reached ? NK_SYMBOL_TRIANGLE_RIGHT: NK_SYMBOL_NONE, "clear", NK_TEXT_LEFT)) { _clear(handle); } nk_label(ctx, "Sherlock.lv2: "SHERLOCK_VERSION, NK_TEXT_RIGHT); } nk_end(ctx); }