diff options
author | Hanspeter Portner <dev@open-music-kontrollers.ch> | 2016-11-04 20:57:47 +0100 |
---|---|---|
committer | Hanspeter Portner <dev@open-music-kontrollers.ch> | 2016-11-04 20:57:47 +0100 |
commit | ae914e4c6072a468792a6f93f0b8875f9be94c45 (patch) | |
tree | 1d9f94cc2154680cacd3f1ee8ed794456fea135e | |
parent | 41f046e9479230eda4aaf388c9c282e7b428bfe1 (diff) | |
download | sherlock.lv2-ae914e4c6072a468792a6f93f0b8875f9be94c45.tar.xz |
prototype nuklear ui.
-rw-r--r-- | CMakeLists.txt | 34 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | atom_inspector_nk.c | 266 | ||||
-rw-r--r-- | manifest.ttl.in | 15 | ||||
-rw-r--r-- | midi_inspector_nk.c | 496 | ||||
-rw-r--r-- | osc_inspector_nk.c | 390 | ||||
-rw-r--r-- | sherlock.h | 3 | ||||
-rw-r--r-- | sherlock.ttl | 6 | ||||
-rw-r--r-- | sherlock_nk.c | 453 | ||||
-rw-r--r-- | sherlock_nk.h | 99 | ||||
-rw-r--r-- | sherlock_ui.ttl | 36 |
11 files changed, 1794 insertions, 6 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index ead9152..d75e1d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,11 +7,26 @@ include_directories(${PROJECT_SOURCE_DIR}/libosc) include_directories(${PROJECT_SOURCE_DIR}/osc.lv2) include_directories(${PROJECT_SOURCE_DIR}/sandbox_ui.lv2) include_directories(${PROJECT_SOURCE_DIR}/props.lv2) +include_directories(${PROJECT_SOURCE_DIR}/pugl) include_directories(${PROJECT_SOURCE_DIR}/symap) +set(CMAKE_C_FLAGS "-fdata-sections -ffunction-sections ${CMAKE_C_FLAGS}") set(CMAKE_C_FLAGS "-std=gnu11 -Wextra -Wno-unused-parameter -ffast-math -fvisibility=hidden ${CMAKE_C_FLAGS}") -set(CMAKE_C_FLAGS "-Wshadow -Wimplicit-function-declaration -Wmissing-prototypes -Wstrict-prototypes ${CMAKE_C_FLAGS}") -set(CMAKE_MODULE_LINKER_FLAGS "-Wl,-z,nodelete ${CMAKE_MODULE_LINKER_FLAGS}") +set(CMAKE_C_FLAGS "-Wshadow -Wimplicit-function-declaration -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes ${CMAKE_C_FLAGS}") + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + set(CMAKE_MODULE_LINKER_FLAGS "-Wl,-z,defs ${CMAKE_MODULE_LINKER_FLAGS}") + set(CMAKE_MODULE_LINKER_FLAGS "-Wl,-z,nodelete ${CMAKE_MODULE_LINKER_FLAGS}") +elseif(WIN32) + set(CMAKE_C_FLAGS "-mstackrealign ${CMAKE_C_FLAGS}") +endif() + +if(APPLE) + set(CMAKE_MODULE_LINKER_FLAGS "-Wl,-dead_strip ${CMAKE_MODULE_LINKER_FLAGS}") +else() + set(CMAKE_MODULE_LINKER_FLAGS "-Wl,--gc-sections -Wl,-s ${CMAKE_MODULE_LINKER_FLAGS}") +endif() + add_definitions("-D_GNU_SOURCE=1") # asprintf file(STRINGS "VERSION" SHERLOCK_VERSION) @@ -77,6 +92,20 @@ target_link_libraries(sherlock_eo set_target_properties(sherlock_eo PROPERTIES PREFIX "") install(TARGETS sherlock_eo DESTINATION ${DEST}) +add_definitions("-DPUGL_HAVE_GL") +add_library(sherlock_nk MODULE + sherlock_nk.c + midi_inspector_nk.c + atom_inspector_nk.c + osc_inspector_nk.c + pugl/pugl/pugl_x11.c) +target_link_libraries(sherlock_nk + ${NANOMSG_LDFLAGS} + ${SRATOM_LDFLAGS} + X11 GL m) +set_target_properties(sherlock_nk PROPERTIES PREFIX "") +install(TARGETS sherlock_nk DESTINATION ${DEST}) + add_executable(sandbox_efl ${PROJECT_SOURCE_DIR}/sandbox_ui.lv2/sandbox_slave.c ${PROJECT_SOURCE_DIR}/sandbox_ui.lv2/sandbox_efl.c @@ -92,3 +121,4 @@ install(FILES ${PROJECT_BINARY_DIR}/manifest.ttl DESTINATION ${DEST}) install(FILES ${PROJECT_SOURCE_DIR}/sherlock.ttl DESTINATION ${DEST}) install(FILES ${PROJECT_SOURCE_DIR}/sherlock_ui.ttl DESTINATION ${DEST}) install(FILES ${PROJECT_SOURCE_DIR}/omk_logo_256x256.png DESTINATION ${DEST}) +install(FILES ${PROJECT_SOURCE_DIR}/nuklear/extra_font/Cousine-Regular.ttf DESTINATION ${DEST}) @@ -1 +1 @@ -0.11.31 +0.11.39 diff --git a/atom_inspector_nk.c b/atom_inspector_nk.c new file mode 100644 index 0000000..5b92f20 --- /dev/null +++ b/atom_inspector_nk.c @@ -0,0 +1,266 @@ +/* + * 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 <ctype.h> + +#include <sherlock.h> +#include <sherlock_nk.h> + +static const uint8_t ful = 0xff; +static const uint8_t one = 0xbb; +static const uint8_t two = 0x66; +static const uint8_t non = 0x0; +static const struct nk_color white = {.r = one, .g = one, .b = one, .a = ful}; +static const struct nk_color gray = {.r = two, .g = two, .b = two, .a = ful}; +static const struct nk_color yellow = {.r = one, .g = one, .b = non, .a = ful}; +static const struct nk_color magenta = {.r = one, .g = two, .b = one, .a = ful}; +static const struct nk_color green = {.r = non, .g = one, .b = non, .a = ful}; +static const struct nk_color blue = {.r = non, .g = one, .b = one, .a = ful}; +static const struct nk_color orange = {.r = one, .g = two, .b = non, .a = ful}; +static const struct nk_color violet = {.r = two, .g = two, .b = one, .a = ful}; + +#define NS_RDF (const uint8_t*)"http://www.w3.org/1999/02/22-rdf-syntax-ns#" +#define NS_RDFS (const uint8_t*)"http://www.w3.org/2000/01/rdf-schema#" +#define NS_XSD (const uint8_t*)"http://www.w3.org/2001/XMLSchema#" +#define NS_OSC (const uint8_t*)"http://open-music-kontrollers.ch/lv2/osc#" +#define NS_XPRESS (const uint8_t*)"http://open-music-kontrollers.ch/lv2/xpress#" +#define NS_SPOD (const uint8_t*)"http://open-music-kontrollers.ch/lv2/synthpod#" +#define NS_CANVAS (const uint8_t*)"http://open-music-kontrollers.ch/lv2/canvas#" + +// copyied and adapted from libsratom +static inline char * +_sratom_to_turtle(Sratom* sratom, + LV2_URID_Unmap* unmap, + const char* base_uri, + const SerdNode* subject, + const SerdNode* predicate, + uint32_t type, + uint32_t size, + const void* body) +{ + SerdURI buri = SERD_URI_NULL; + SerdNode base = serd_node_new_uri_from_string((uint8_t *)(base_uri), NULL, &buri); + SerdEnv* env = serd_env_new(&base); + SerdChunk str = { NULL, 0 }; + + serd_env_set_prefix_from_strings(env, (const uint8_t *)"rdf", NS_RDF); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"rdfs", NS_RDFS); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"xsd", NS_XSD); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"lv2", (const uint8_t *)LV2_CORE_PREFIX); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"midi", (const uint8_t *)LV2_MIDI_PREFIX); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"atom", (const uint8_t *)LV2_ATOM_PREFIX); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"units", (const uint8_t *)LV2_UNITS_PREFIX); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"ui", (const uint8_t *)LV2_UI_PREFIX); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"time", (const uint8_t *)LV2_TIME_URI"#"); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"patch", (const uint8_t *)LV2_PATCH_PREFIX); + + serd_env_set_prefix_from_strings(env, (const uint8_t *)"osc", NS_OSC); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"xpress", NS_XPRESS); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"spod", NS_SPOD); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"canvas", NS_CANVAS); + + SerdWriter* writer = serd_writer_new( + SERD_TURTLE, + (SerdStyle)(SERD_STYLE_ABBREVIATED | + SERD_STYLE_RESOLVED | + SERD_STYLE_CURIED | + SERD_STYLE_ASCII), + env, &buri, serd_chunk_sink, &str); + + // Write @prefix directives + serd_env_foreach(env, + (SerdPrefixSink)serd_writer_set_prefix, + writer); + + sratom_set_sink(sratom, base_uri, + (SerdStatementSink)serd_writer_write_statement, + (SerdEndSink)serd_writer_end_anon, + writer); + sratom_write(sratom, unmap, SERD_EMPTY_S, + subject, predicate, type, size, body); + serd_writer_finish(writer); + + serd_writer_free(writer); + serd_env_free(env); + serd_node_free(&base); + return (char*)serd_chunk_sink_finish(&str); +} + +void +_atom_inspector_expose(struct nk_context *ctx, struct nk_rect wbounds, void *data) +{ + plughandle_t *handle = data; + + const float widget_h = 20; + bool ttl_dirty = false; + + if(nk_begin(ctx, "Window", wbounds, NK_WINDOW_NO_SCROLLBAR)) + { + nk_window_set_bounds(ctx, wbounds); + + nk_layout_row_dynamic(ctx, wbounds.h, 2); + if(nk_group_begin(ctx, "Left", NK_WINDOW_NO_SCROLLBAR)) + { + 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); + } + + nk_layout_row_dynamic(ctx, widget_h, 2); + { + if(nk_button_label(ctx, "clear")) + _clear(handle); + + int selected = 0; + for(int i = 0; i < 5; i++) + { + if(handle->state.count == max_values[i]) + { + selected = i; + break; + } + } + + selected = nk_combo(ctx, max_items, 5, selected, widget_h, + nk_vec2(wbounds.w/3, widget_h*5)); + if(handle->state.count != max_values[selected]) + { + handle->state.count = max_values[selected]; + _toggle(handle, handle->urid.count, handle->state.count, false); + } + } + + nk_layout_row_dynamic(ctx, wbounds.h - 2*widget_h, 1); + if(nk_group_begin(ctx, "Events", 0)) + { + uint32_t counter = 0; + const LV2_Atom *selected = NULL; + + LV2_ATOM_TUPLE_FOREACH((const LV2_Atom_Tuple *)handle->ser.atom, atom) + { + const LV2_Atom_Tuple *tup = (const LV2_Atom_Tuple *)atom; + const LV2_Atom_Long *offset = (const LV2_Atom_Long *)lv2_atom_tuple_begin(tup); + const LV2_Atom_Int *nsamples = (const LV2_Atom_Int *)lv2_atom_tuple_next(&offset->atom); + const LV2_Atom_Sequence *seq = (const LV2_Atom_Sequence *)lv2_atom_tuple_next(&nsamples->atom); + + nk_layout_row_dynamic(ctx, 2.f, 1); + _ruler(ctx, 2.f, gray); + + nk_layout_row_dynamic(ctx, widget_h, 3); + nk_labelf_colored(ctx, NK_TEXT_LEFT, orange, "@%"PRIi64, offset->body); + nk_labelf_colored(ctx, NK_TEXT_CENTERED, green, "-%"PRIu32"-", counter); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, violet, "%"PRIi32, nsamples->body); + + nk_layout_row_dynamic(ctx, 2.f, 1); + _ruler(ctx, 1.f, gray); + + const LV2_Atom *last = NULL; + LV2_ATOM_SEQUENCE_FOREACH(seq, ev) + { + const LV2_Atom *body = &ev->body; + const int64_t frames = ev->time.frames; + const char *uri = handle->unmap->unmap(handle->unmap->handle, body->type); + + nk_layout_row_begin(ctx, NK_DYNAMIC, widget_h, 3); + { + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_LEFT, yellow, "+%04"PRIi64, frames); + + nk_layout_row_push(ctx, 0.8); + if(nk_select_label(ctx, uri, NK_TEXT_LEFT, handle->selected == body)) + { + ttl_dirty = handle->selected != body; // has selection actually changed? + handle->selected = body; + } + + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, blue, "%"PRIu32, body->size); + } + nk_layout_row_end(ctx); + + last = body; + } + + if(handle->bottom) + { + ttl_dirty = true; + handle->selected = last; + } + + counter += 1; + } + + const struct nk_panel *panel = nk_window_get_panel(ctx); + if(handle->bottom) + { + panel->offset->y = panel->at_y; + handle->bottom = false; + + _post_redisplay(handle); + } + + nk_group_end(ctx); + } + + nk_group_end(ctx); + } + + if(nk_group_begin(ctx, "Right", NK_WINDOW_NO_SCROLLBAR)) + { + const LV2_Atom *atom = handle->selected; + + if(ttl_dirty && atom) + { + char *ttl = _sratom_to_turtle(handle->sratom, handle->unmap, + handle->base_uri, NULL, NULL, + atom->type, atom->size, LV2_ATOM_BODY_CONST(atom)); + if(ttl) + { + struct nk_str *str = &handle->edit.string; + const size_t len = strlen(ttl); + + nk_str_clear(str); + + char *from, *to; + for(from=ttl, to=strchr(from, '\t'); + to; + from=to+1, to=strchr(from, '\t')) + { + nk_str_append_text_utf8(str, from, to-from); + nk_str_append_text_utf8(str, " ", 2); + } + nk_str_append_text_utf8(str, from, strlen(from)); + + free(ttl); + } + } + + nk_layout_row_dynamic(ctx, wbounds.h, 1); + const nk_flags flags = NK_EDIT_EDITOR | NK_EDIT_READ_ONLY; + const nk_flags mode = nk_edit_buffer(ctx, flags, &handle->edit, nk_filter_default); + (void)mode; + + nk_group_end(ctx); + } + } + nk_end(ctx); +} diff --git a/manifest.ttl.in b/manifest.ttl.in index cea642e..161fadf 100644 --- a/manifest.ttl.in +++ b/manifest.ttl.in @@ -40,6 +40,7 @@ sherlock:atom_inspector ui:ui sherlock:atom_inspector_1_ui ; ui:ui sherlock:atom_inspector_2_kx ; ui:ui sherlock:atom_inspector_3_eo ; + ui:ui sherlock:atom_inspector_4_nk ; rdfs:seeAlso <sherlock.ttl> . sherlock:atom_inspector_1_ui @@ -54,6 +55,10 @@ sherlock:atom_inspector_3_eo a ui:EoUI ; ui:binary <sherlock_eo@CMAKE_SHARED_MODULE_SUFFIX@> ; rdfs:seeAlso <sherlock_ui.ttl> . +sherlock:atom_inspector_4_nk + a ui:X11UI ; + ui:binary <sherlock_nk@CMAKE_SHARED_MODULE_SUFFIX@> ; + rdfs:seeAlso <sherlock_ui.ttl> . # MIDI Inspector Plugin sherlock:midi_inspector @@ -64,6 +69,7 @@ sherlock:midi_inspector ui:ui sherlock:midi_inspector_1_ui ; ui:ui sherlock:midi_inspector_2_kx ; ui:ui sherlock:midi_inspector_3_eo ; + ui:ui sherlock:midi_inspector_4_nk ; rdfs:seeAlso <sherlock.ttl> . sherlock:midi_inspector_1_ui @@ -78,6 +84,10 @@ sherlock:midi_inspector_3_eo a ui:EoUI ; ui:binary <sherlock_eo@CMAKE_SHARED_MODULE_SUFFIX@> ; rdfs:seeAlso <sherlock_ui.ttl> . +sherlock:midi_inspector_4_nk + a ui:X11UI ; + ui:binary <sherlock_nk@CMAKE_SHARED_MODULE_SUFFIX@> ; + rdfs:seeAlso <sherlock_ui.ttl> . # OSC Inspector Plugin sherlock:osc_inspector @@ -88,6 +98,7 @@ sherlock:osc_inspector ui:ui sherlock:osc_inspector_1_ui ; ui:ui sherlock:osc_inspector_2_kx ; ui:ui sherlock:osc_inspector_3_eo ; + ui:ui sherlock:osc_inspector_4_nk ; rdfs:seeAlso <sherlock.ttl> . sherlock:osc_inspector_1_ui @@ -102,3 +113,7 @@ sherlock:osc_inspector_3_eo a ui:EoUI ; ui:binary <sherlock_eo@CMAKE_SHARED_MODULE_SUFFIX@> ; rdfs:seeAlso <sherlock_ui.ttl> . +sherlock:osc_inspector_4_nk + a ui:X11UI ; + ui:binary <sherlock_nk@CMAKE_SHARED_MODULE_SUFFIX@> ; + rdfs:seeAlso <sherlock_ui.ttl> . diff --git a/midi_inspector_nk.c b/midi_inspector_nk.c new file mode 100644 index 0000000..a0548f3 --- /dev/null +++ b/midi_inspector_nk.c @@ -0,0 +1,496 @@ +/* + * 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 <sherlock.h> +#include <sherlock_nk.h> + +static const uint8_t ful = 0xff; +static const uint8_t one = 0xbb; +static const uint8_t two = 0x66; +static const uint8_t non = 0x0; +static const struct nk_color white = {.r = one, .g = one, .b = one, .a = ful}; +static const struct nk_color gray = {.r = two, .g = two, .b = two, .a = ful}; +static const struct nk_color yellow = {.r = one, .g = one, .b = non, .a = ful}; +static const struct nk_color magenta = {.r = one, .g = two, .b = one, .a = ful}; +static const struct nk_color green = {.r = non, .g = one, .b = non, .a = ful}; +static const struct nk_color blue = {.r = non, .g = one, .b = one, .a = ful}; +static const struct nk_color orange = {.r = one, .g = two, .b = non, .a = ful}; +static const struct nk_color violet = {.r = two, .g = two, .b = one, .a = ful}; + +typedef struct _midi_msg_t midi_msg_t; + +struct _midi_msg_t { + uint8_t type; + const char *key; +}; + +#define COMMANDS_NUM 18 +static const midi_msg_t commands [COMMANDS_NUM] = { + { LV2_MIDI_MSG_NOTE_OFF , "NoteOff" }, + { LV2_MIDI_MSG_NOTE_ON , "NoteOn" }, + { LV2_MIDI_MSG_NOTE_PRESSURE , "NotePressure" }, + { LV2_MIDI_MSG_CONTROLLER , "Controller" }, + { LV2_MIDI_MSG_PGM_CHANGE , "ProgramChange" }, + { LV2_MIDI_MSG_CHANNEL_PRESSURE , "ChannelPressure" }, + { LV2_MIDI_MSG_BENDER , "Bender" }, + { LV2_MIDI_MSG_SYSTEM_EXCLUSIVE , "SystemExclusive" }, + { LV2_MIDI_MSG_MTC_QUARTER , "QuarterFrame" }, + { LV2_MIDI_MSG_SONG_POS , "SongPosition" }, + { LV2_MIDI_MSG_SONG_SELECT , "SongSelect" }, + { LV2_MIDI_MSG_TUNE_REQUEST , "TuneRequest" }, + { LV2_MIDI_MSG_CLOCK , "Clock" }, + { LV2_MIDI_MSG_START , "Start" }, + { LV2_MIDI_MSG_CONTINUE , "Continue" }, + { LV2_MIDI_MSG_STOP , "Stop" }, + { LV2_MIDI_MSG_ACTIVE_SENSE , "ActiveSense" }, + { LV2_MIDI_MSG_RESET , "Reset" }, +}; + +#define CONTROLLERS_NUM 72 +static const midi_msg_t controllers [CONTROLLERS_NUM] = { + { LV2_MIDI_CTL_MSB_BANK , "BankSelection_MSB" }, + { LV2_MIDI_CTL_MSB_MODWHEEL , "Modulation_MSB" }, + { LV2_MIDI_CTL_MSB_BREATH , "Breath_MSB" }, + { LV2_MIDI_CTL_MSB_FOOT , "Foot_MSB" }, + { LV2_MIDI_CTL_MSB_PORTAMENTO_TIME , "PortamentoTime_MSB" }, + { LV2_MIDI_CTL_MSB_DATA_ENTRY , "DataEntry_MSB" }, + { LV2_MIDI_CTL_MSB_MAIN_VOLUME , "MainVolume_MSB" }, + { LV2_MIDI_CTL_MSB_BALANCE , "Balance_MSB" }, + { LV2_MIDI_CTL_MSB_PAN , "Panpot_MSB" }, + { LV2_MIDI_CTL_MSB_EXPRESSION , "Expression_MSB" }, + { LV2_MIDI_CTL_MSB_EFFECT1 , "Effect1_MSB" }, + { LV2_MIDI_CTL_MSB_EFFECT2 , "Effect2_MSB" }, + { LV2_MIDI_CTL_MSB_GENERAL_PURPOSE1 , "GeneralPurpose1_MSB" }, + { LV2_MIDI_CTL_MSB_GENERAL_PURPOSE2 , "GeneralPurpose2_MSB" }, + { LV2_MIDI_CTL_MSB_GENERAL_PURPOSE3 , "GeneralPurpose3_MSB" }, + { LV2_MIDI_CTL_MSB_GENERAL_PURPOSE4 , "GeneralPurpose4_MSB" }, + + { LV2_MIDI_CTL_LSB_BANK , "BankSelection_LSB" }, + { LV2_MIDI_CTL_LSB_MODWHEEL , "Modulation_LSB" }, + { LV2_MIDI_CTL_LSB_BREATH , "Breath_LSB" }, + { LV2_MIDI_CTL_LSB_FOOT , "Foot_LSB" }, + { LV2_MIDI_CTL_LSB_PORTAMENTO_TIME , "PortamentoTime_LSB" }, + { LV2_MIDI_CTL_LSB_DATA_ENTRY , "DataEntry_LSB" }, + { LV2_MIDI_CTL_LSB_MAIN_VOLUME , "MainVolume_LSB" }, + { LV2_MIDI_CTL_LSB_BALANCE , "Balance_LSB" }, + { LV2_MIDI_CTL_LSB_PAN , "Panpot_LSB" }, + { LV2_MIDI_CTL_LSB_EXPRESSION , "Expression_LSB" }, + { LV2_MIDI_CTL_LSB_EFFECT1 , "Effect1_LSB" }, + { LV2_MIDI_CTL_LSB_EFFECT2 , "Effect2_LSB" }, + { LV2_MIDI_CTL_LSB_GENERAL_PURPOSE1 , "GeneralPurpose1_LSB" }, + { LV2_MIDI_CTL_LSB_GENERAL_PURPOSE2 , "GeneralPurpose2_LSB" }, + { LV2_MIDI_CTL_LSB_GENERAL_PURPOSE3 , "GeneralPurpose3_LSB" }, + { LV2_MIDI_CTL_LSB_GENERAL_PURPOSE4 , "GeneralPurpose4_LSB" }, + + { LV2_MIDI_CTL_SUSTAIN , "SustainPedal" }, + { LV2_MIDI_CTL_PORTAMENTO , "Portamento" }, + { LV2_MIDI_CTL_SOSTENUTO , "Sostenuto" }, + { LV2_MIDI_CTL_SOFT_PEDAL , "SoftPedal" }, + { LV2_MIDI_CTL_LEGATO_FOOTSWITCH , "LegatoFootSwitch" }, + { LV2_MIDI_CTL_HOLD2 , "Hold2" }, + + { LV2_MIDI_CTL_SC1_SOUND_VARIATION , "SC1_SoundVariation" }, + { LV2_MIDI_CTL_SC2_TIMBRE , "SC2_Timbre" }, + { LV2_MIDI_CTL_SC3_RELEASE_TIME , "SC3_ReleaseTime" }, + { LV2_MIDI_CTL_SC4_ATTACK_TIME , "SC4_AttackTime" }, + { LV2_MIDI_CTL_SC5_BRIGHTNESS , "SC5_Brightness" }, + { LV2_MIDI_CTL_SC6 , "SC6" }, + { LV2_MIDI_CTL_SC7 , "SC7" }, + { LV2_MIDI_CTL_SC8 , "SC8" }, + { LV2_MIDI_CTL_SC9 , "SC9" }, + { LV2_MIDI_CTL_SC10 , "SC10" }, + + { LV2_MIDI_CTL_GENERAL_PURPOSE5 , "GeneralPurpose5" }, + { LV2_MIDI_CTL_GENERAL_PURPOSE6 , "GeneralPurpose6" }, + { LV2_MIDI_CTL_GENERAL_PURPOSE7 , "GeneralPurpose7" }, + { LV2_MIDI_CTL_GENERAL_PURPOSE8 , "GeneralPurpose8" }, + { LV2_MIDI_CTL_PORTAMENTO_CONTROL , "PortamentoControl" }, + + { LV2_MIDI_CTL_E1_REVERB_DEPTH , "E1_ReverbDepth" }, + { LV2_MIDI_CTL_E2_TREMOLO_DEPTH , "E2_TremoloDepth" }, + { LV2_MIDI_CTL_E3_CHORUS_DEPTH , "E3_ChorusDepth" }, + { LV2_MIDI_CTL_E4_DETUNE_DEPTH , "E4_DetuneDepth" }, + { LV2_MIDI_CTL_E5_PHASER_DEPTH , "E5_PhaserDepth" }, + + { LV2_MIDI_CTL_DATA_INCREMENT , "DataIncrement" }, + { LV2_MIDI_CTL_DATA_DECREMENT , "DataDecrement" }, + + { LV2_MIDI_CTL_NRPN_LSB , "NRPN_LSB" }, + { LV2_MIDI_CTL_NRPN_MSB , "NRPN_MSB" }, + + { LV2_MIDI_CTL_RPN_LSB , "RPN_LSB" }, + { LV2_MIDI_CTL_RPN_MSB , "RPN_MSB" }, + + { LV2_MIDI_CTL_ALL_SOUNDS_OFF , "AllSoundsOff" }, + { LV2_MIDI_CTL_RESET_CONTROLLERS , "ResetControllers" }, + { LV2_MIDI_CTL_LOCAL_CONTROL_SWITCH , "LocalControlSwitch" }, + { LV2_MIDI_CTL_ALL_NOTES_OFF , "AllNotesOff" }, + { LV2_MIDI_CTL_OMNI_OFF , "OmniOff" }, + { LV2_MIDI_CTL_OMNI_ON , "OmniOn" }, + { LV2_MIDI_CTL_MONO1 , "Mono1" }, + { LV2_MIDI_CTL_MONO2 , "Mono2" }, +}; + +#define TIMECODES_NUM 8 +static const midi_msg_t timecodes [TIMECODES_NUM] = { + { 0 , "FrameNumber_LSB" }, + { 1 , "FrameNumber_MSB" }, + { 2 , "Second_LSB" }, + { 3 , "Second_MSB" }, + { 4 , "Minute_LSB" }, + { 5 , "Minute_MSB" }, + { 6 , "Hour_LSB" }, + { 7 , "RateAndHour_MSB" }, +}; + +static int +_cmp_search(const void *itm1, const void *itm2) +{ + const midi_msg_t *msg1 = itm1; + const midi_msg_t *msg2 = itm2; + + if(msg1->type < msg2->type) + return -1; + else if(msg1->type > msg2->type) + return 1; + + return 0; +} + +static inline const midi_msg_t * +_search_command(uint8_t type) +{ + return bsearch(&type, commands, COMMANDS_NUM, sizeof(midi_msg_t), _cmp_search); +} + +static inline const midi_msg_t * +_search_controller(uint8_t type) +{ + return bsearch(&type, controllers, CONTROLLERS_NUM, sizeof(midi_msg_t), _cmp_search); +} + +static inline const midi_msg_t * +_search_timecode(uint8_t type) +{ + return bsearch(&type, timecodes, TIMECODES_NUM, sizeof(midi_msg_t), _cmp_search); +} + +static const char *keys [12] = { + "C", "C#", + "D", "D#", + "E", + "F", "F#", + "G", "G#", + "A", "A#", + "B" +}; + +static inline const char * +_note(uint8_t val, int8_t *octave) +{ + *octave = val / 12 - 1; + + return keys[val % 12]; +} + +void +_midi_inspector_expose(struct nk_context *ctx, struct nk_rect wbounds, void *data) +{ + plughandle_t *handle = data; + + const float widget_h = 20; + + if(nk_begin(ctx, "Window", wbounds, NK_WINDOW_NO_SCROLLBAR)) + { + nk_window_set_bounds(ctx, wbounds); + + 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); + } + + nk_layout_row_dynamic(ctx, widget_h, 2); + { + if(nk_button_label(ctx, "clear")) + _clear(handle); + + int selected = 0; + for(int i = 0; i < 5; i++) + { + if(handle->state.count == max_values[i]) + { + selected = i; + break; + } + } + + selected = nk_combo(ctx, max_items, 5, selected, widget_h, + nk_vec2(wbounds.w/3, widget_h*5)); + if(handle->state.count != max_values[selected]) + { + handle->state.count = max_values[selected]; + _toggle(handle, handle->urid.count, handle->state.count, false); + } + } + + nk_layout_row_dynamic(ctx, wbounds.h - 2*widget_h, 1); + if(nk_group_begin(ctx, "Events", 0)) + { + uint32_t counter = 0; + + LV2_ATOM_TUPLE_FOREACH((const LV2_Atom_Tuple *)handle->ser.atom, atom) + { + const LV2_Atom_Tuple *tup = (const LV2_Atom_Tuple *)atom; + const LV2_Atom_Long *offset = (const LV2_Atom_Long *)lv2_atom_tuple_begin(tup); + const LV2_Atom_Int *nsamples = (const LV2_Atom_Int *)lv2_atom_tuple_next(&offset->atom); + const LV2_Atom_Sequence *seq = (const LV2_Atom_Sequence *)lv2_atom_tuple_next(&nsamples->atom); + + nk_layout_row_dynamic(ctx, 2.f, 1); + _ruler(ctx, 2.f, gray); + + nk_layout_row_dynamic(ctx, widget_h, 3); + nk_labelf_colored(ctx, NK_TEXT_LEFT, orange, "@%"PRIi64, offset->body); + nk_labelf_colored(ctx, NK_TEXT_CENTERED, green, "-%"PRIu32"-", counter); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, violet, "%"PRIi32, nsamples->body); + + nk_layout_row_dynamic(ctx, 2.f, 1); + _ruler(ctx, 1.f, gray); + + LV2_ATOM_SEQUENCE_FOREACH(seq, ev) + { + const LV2_Atom *body = &ev->body; + const int64_t frames = ev->time.frames; + const uint8_t *msg = LV2_ATOM_BODY_CONST(body); + const uint8_t cmd = (msg[0] & 0xf0) == 0xf0 + ? msg[0] + : msg[0] & 0xf0; + + const midi_msg_t *command_msg = _search_command(cmd); + const char *command_str = command_msg + ? command_msg->key + : "Unknown"; + + char tmp [16]; + nk_layout_row_begin(ctx, NK_DYNAMIC, widget_h, 7); + { + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_LEFT, yellow, "+%04"PRIi64, frames); + + nk_layout_row_push(ctx, 0.2); + const unsigned rem = body->size; + const unsigned to = rem >= 4 ? 4 : rem % 4; + for(unsigned i=0, ptr=0; i<to; i++, ptr+=3) + sprintf(&tmp[ptr], "%02"PRIX8" ", msg[i]); + tmp[to*3 - 1] = '\0'; + nk_label_colored(ctx, tmp, NK_TEXT_LEFT, white); + + nk_layout_row_push(ctx, 0.2); + nk_label_colored(ctx, command_str, NK_TEXT_LEFT, magenta); + + switch(cmd) + { + case LV2_MIDI_MSG_NOTE_OFF: + // fall-through + case LV2_MIDI_MSG_NOTE_ON: + // fall-through + case LV2_MIDI_MSG_NOTE_PRESSURE: + { + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "Ch:%02"PRIu8, + (msg[0] & 0x0f) + 1); + + nk_layout_row_push(ctx, 0.2); + int8_t octave; + const char *key = _note(msg[1], &octave); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%s%+"PRIi8, key, octave); + + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%"PRIu8, msg[2]); + } break; + case LV2_MIDI_MSG_CONTROLLER: + { + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "Ch:%02"PRIu8, + (msg[0] & 0x0f) + 1); + + const midi_msg_t *controller_msg = _search_controller(msg[1]); + const char *controller_str = controller_msg + ? controller_msg->key + : "Unknown"; + nk_layout_row_push(ctx, 0.2); + nk_label_colored(ctx, controller_str, NK_TEXT_RIGHT, white); + + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%"PRIu8, msg[2]); + } break; + case LV2_MIDI_MSG_PGM_CHANGE: + // fall-through + case LV2_MIDI_MSG_CHANNEL_PRESSURE: + { + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "Ch:%02"PRIu8, + (msg[0] & 0x0f) + 1); + + nk_layout_row_push(ctx, 0.2); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%"PRIu8, msg[1]); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + } break; + case LV2_MIDI_MSG_BENDER: + { + const int16_t bender = (((int16_t)msg[2] << 7) | msg[1]) - 0x2000; + + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "Ch:%02"PRIu8, + (msg[0] & 0x0f) + 1); + + nk_layout_row_push(ctx, 0.2); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%"PRIi16, bender); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + } break; + case LV2_MIDI_MSG_MTC_QUARTER: + { + const uint8_t msg_type = msg[1] >> 4; + const uint8_t msg_val = msg[1] & 0xf; + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + + const midi_msg_t *timecode_msg = _search_timecode(msg_type); + const char *timecode_str = timecode_msg + ? timecode_msg->key + : "Unknown"; + nk_layout_row_push(ctx, 0.2); + nk_label_colored(ctx, timecode_str, NK_TEXT_RIGHT, white); + + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%"PRIu8, msg_val); + } break; + case LV2_MIDI_MSG_SONG_POS: + { + const int16_t song_pos= (((int16_t)msg[2] << 7) | msg[1]); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + + nk_layout_row_push(ctx, 0.2); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%"PRIu16, song_pos); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + } break; + case LV2_MIDI_MSG_SONG_SELECT: + { + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + + nk_layout_row_push(ctx, 0.2); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%"PRIu8, msg[1]); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + } break; + case LV2_MIDI_MSG_SYSTEM_EXCLUSIVE: + // fall-throuh + case LV2_MIDI_MSG_TUNE_REQUEST: + // fall-throuh + case LV2_MIDI_MSG_CLOCK: + // fall-throuh + case LV2_MIDI_MSG_START: + // fall-throuh + case LV2_MIDI_MSG_CONTINUE: + // fall-throuh + case LV2_MIDI_MSG_STOP: + // fall-throuh + case LV2_MIDI_MSG_ACTIVE_SENSE: + // fall-throuh + case LV2_MIDI_MSG_RESET: + { + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + + nk_layout_row_push(ctx, 0.2); + _empty(ctx); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + } break; + } + + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, blue, "%"PRIu32, body->size); + } + nk_layout_row_end(ctx); + + for(unsigned j=4; j<body->size; j+=4) + { + nk_layout_row_begin(ctx, NK_DYNAMIC, widget_h, 7); + { + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + + nk_layout_row_push(ctx, 0.2); + const unsigned rem = body->size - j; + const unsigned to = rem >= 4 ? 4 : rem % 4; + for(unsigned i=0, ptr=0; i<to; i++, ptr+=3) + sprintf(&tmp[ptr], "%02"PRIX8" ", msg[j+i]); + tmp[to*3 - 1] = '\0'; + nk_label_colored(ctx, tmp, NK_TEXT_LEFT, white); + + nk_layout_row_push(ctx, 0.2); + _empty(ctx); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + + nk_layout_row_push(ctx, 0.2); + _empty(ctx); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + } + } + } + + counter += 1; + } + + const struct nk_panel *panel = nk_window_get_panel(ctx); + if(handle->bottom) + { + panel->offset->y = panel->at_y; + handle->bottom = false; + + _post_redisplay(handle); + } + + nk_group_end(ctx); + } + } + nk_end(ctx); +} diff --git a/osc_inspector_nk.c b/osc_inspector_nk.c new file mode 100644 index 0000000..f72376a --- /dev/null +++ b/osc_inspector_nk.c @@ -0,0 +1,390 @@ +/* + * 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 <time.h> +#include <math.h> + +#include <sherlock.h> +#include <sherlock_nk.h> + +#include <osc.lv2/util.h> + +static const uint8_t ful = 0xff; +static const uint8_t one = 0xbb; +static const uint8_t two = 0x66; +static const uint8_t non = 0x0; +static const struct nk_color white = {.r = one, .g = one, .b = one, .a = ful}; +static const struct nk_color gray = {.r = two, .g = two, .b = two, .a = ful}; +static const struct nk_color yellow = {.r = one, .g = one, .b = non, .a = ful}; +static const struct nk_color magenta = {.r = one, .g = two, .b = one, .a = ful}; +static const struct nk_color green = {.r = non, .g = one, .b = non, .a = ful}; +static const struct nk_color blue = {.r = non, .g = one, .b = one, .a = ful}; +static const struct nk_color orange = {.r = one, .g = two, .b = non, .a = ful}; +static const struct nk_color violet = {.r = two, .g = two, .b = one, .a = ful}; +static const struct nk_color red = {.r = one, .g = non, .b = non, .a = ful}; + +typedef struct _mem_t mem_t; + +struct _mem_t { + size_t size; + char *buf; +}; + +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; i<sz; i++) + _mem_printf(&mem, "%02"PRIx8, b[i]); + } break; + + case LV2_OSC_TRUE: + { + _mem_printf(&mem, "T:rue"); + } break; + case LV2_OSC_FALSE: + { + _mem_printf(&mem, "F:alse"); + } break; + case LV2_OSC_NIL: + { + _mem_printf(&mem, "N:il"); + } break; + case LV2_OSC_IMPULSE: + { + _mem_printf(&mem, "I:impulse"); + } break; + + case LV2_OSC_INT64: + { + int64_t h; + lv2_osc_int64_get(&handle->osc_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; i<sz; i++) + _mem_printf(&mem, "%02"PRIx8, m[i]); + } break; + } + + _mem_printf(&mem, " "); + } + + nk_layout_row_push(ctx, 0.4 - offset); + nk_label_colored(ctx, LV2_ATOM_BODY_CONST(path), NK_TEXT_LEFT, magenta); + + nk_layout_row_push(ctx, 0.5); + 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); +} + +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 = 20; //FIXME + + 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); + _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 = 20; + + if(nk_begin(ctx, "Window", wbounds, NK_WINDOW_NO_SCROLLBAR)) + { + nk_window_set_bounds(ctx, wbounds); + + 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); + } + + nk_layout_row_dynamic(ctx, widget_h, 2); + { + if(nk_button_label(ctx, "clear")) + _clear(handle); + + int selected = 0; + for(int i = 0; i < 5; i++) + { + if(handle->state.count == max_values[i]) + { + selected = i; + break; + } + } + + selected = nk_combo(ctx, max_items, 5, selected, widget_h, + nk_vec2(wbounds.w/3, widget_h*5)); + if(handle->state.count != max_values[selected]) + { + handle->state.count = max_values[selected]; + _toggle(handle, handle->urid.count, handle->state.count, false); + } + } + + nk_layout_row_dynamic(ctx, wbounds.h - 2*widget_h, 1); + if(nk_group_begin(ctx, "Events", 0)) + { + uint32_t counter = 0; + + LV2_ATOM_TUPLE_FOREACH((const LV2_Atom_Tuple *)handle->ser.atom, atom) + { + const LV2_Atom_Tuple *tup = (const LV2_Atom_Tuple *)atom; + const LV2_Atom_Long *offset = (const LV2_Atom_Long *)lv2_atom_tuple_begin(tup); + const LV2_Atom_Int *nsamples = (const LV2_Atom_Int *)lv2_atom_tuple_next(&offset->atom); + const LV2_Atom_Sequence *seq = (const LV2_Atom_Sequence *)lv2_atom_tuple_next(&nsamples->atom); + + nk_layout_row_dynamic(ctx, 2.f, 1); + _ruler(ctx, 2.f, gray); + + nk_layout_row_dynamic(ctx, widget_h, 3); + nk_labelf_colored(ctx, NK_TEXT_LEFT, orange, "@%"PRIi64, offset->body); + nk_labelf_colored(ctx, NK_TEXT_CENTERED, green, "-%"PRIu32"-", counter); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, violet, "%"PRIi32, nsamples->body); + + nk_layout_row_dynamic(ctx, 2.f, 1); + _ruler(ctx, 1.f, gray); + + LV2_ATOM_SEQUENCE_FOREACH(seq, 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); + nk_labelf_colored(ctx, NK_TEXT_LEFT, yellow, "+%04"PRIi64, frames); + + _osc_packet(handle, ctx, obj, off); + } + + counter += 1; + } + + const struct nk_panel *panel = nk_window_get_panel(ctx); + if(handle->bottom) + { + panel->offset->y = panel->at_y; + handle->bottom = false; + + _post_redisplay(handle); + } + + nk_group_end(ctx); + } + } + nk_end(ctx); +} @@ -42,16 +42,19 @@ #define SHERLOCK_ATOM_INSPECTOR_UI_URI SHERLOCK_URI"#atom_inspector_1_ui" #define SHERLOCK_ATOM_INSPECTOR_KX_URI SHERLOCK_URI"#atom_inspector_2_kx" #define SHERLOCK_ATOM_INSPECTOR_EO_URI SHERLOCK_URI"#atom_inspector_3_eo" +#define SHERLOCK_ATOM_INSPECTOR_NK_URI SHERLOCK_URI"#atom_inspector_4_nk" #define SHERLOCK_MIDI_INSPECTOR_URI SHERLOCK_URI"#midi_inspector" #define SHERLOCK_MIDI_INSPECTOR_UI_URI SHERLOCK_URI"#midi_inspector_1_ui" #define SHERLOCK_MIDI_INSPECTOR_KX_URI SHERLOCK_URI"#midi_inspector_2_kx" #define SHERLOCK_MIDI_INSPECTOR_EO_URI SHERLOCK_URI"#midi_inspector_3_eo" +#define SHERLOCK_MIDI_INSPECTOR_NK_URI SHERLOCK_URI"#midi_inspector_4_nk" #define SHERLOCK_OSC_INSPECTOR_URI SHERLOCK_URI"#osc_inspector" #define SHERLOCK_OSC_INSPECTOR_UI_URI SHERLOCK_URI"#osc_inspector_1_ui" #define SHERLOCK_OSC_INSPECTOR_KX_URI SHERLOCK_URI"#osc_inspector_2_kx" #define SHERLOCK_OSC_INSPECTOR_EO_URI SHERLOCK_URI"#osc_inspector_3_eo" +#define SHERLOCK_OSC_INSPECTOR_NK_URI SHERLOCK_URI"#osc_inspector_4_nk" extern const LV2_Descriptor atom_inspector; extern const LV2UI_Descriptor atom_inspector_ui; diff --git a/sherlock.ttl b/sherlock.ttl index 963670a..18593d0 100644 --- a/sherlock.ttl +++ b/sherlock.ttl @@ -147,7 +147,7 @@ sherlock:atom_inspector sherlock:count 2048 ; sherlock:overwrite false ; sherlock:block false ; - sherlock:follow false ; + sherlock:follow true ; ] . # MIDI Inspector Plugin @@ -205,7 +205,7 @@ sherlock:midi_inspector sherlock:count 2048 ; sherlock:overwrite false ; sherlock:block false ; - sherlock:follow false ; + sherlock:follow true ; ] . # OSC Inspector Plugin @@ -263,5 +263,5 @@ sherlock:osc_inspector sherlock:count 2048 ; sherlock:overwrite false ; sherlock:block false ; - sherlock:follow false ; + sherlock:follow true ; ] . diff --git a/sherlock_nk.c b/sherlock_nk.c new file mode 100644 index 0000000..76685c0 --- /dev/null +++ b/sherlock_nk.c @@ -0,0 +1,453 @@ +/* + * 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 <stdlib.h> +#include <stdio.h> +#include <math.h> +#include <time.h> +#include <limits.h> + +#include <sherlock.h> + +#define NK_PUGL_IMPLEMENTATION +#include <sherlock_nk.h> + +const char *max_items [5] = { + "1k", "2k", "4k", "8k", "16k" +}; +const int32_t max_values [5] = { + 0x400, 0x800, 0x1000, 0x2000, 0x4000 +}; + +static LV2_Atom_Forge_Ref +_sink(LV2_Atom_Forge_Sink_Handle handle, const void *buf, uint32_t size) +{ + atom_ser_t *ser = handle; + + const LV2_Atom_Forge_Ref ref = ser->offset + 1; + + const uint32_t new_offset = ser->offset + size; + if(new_offset > ser->size) + { + uint32_t new_size = ser->size << 1; + while(new_offset > new_size) + new_size <<= 1; + + if(!(ser->buf = realloc(ser->buf, new_size))) + return 0; // realloc failed + + ser->size = new_size; + } + + memcpy(ser->buf + ser->offset, buf, size); + ser->offset = new_offset; + + return ref; +} + +static LV2_Atom * +_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref) +{ + atom_ser_t *ser = handle; + + const uint32_t offset = ref - 1; + + return (LV2_Atom *)(ser->buf + offset); +} + +static bool +_ser_malloc(atom_ser_t *ser, size_t size) +{ + ser->size = size; + ser->offset = 0; + ser->buf = malloc(ser->size); + + return ser->buf != NULL; +} + +static bool +_ser_realloc(atom_ser_t *ser, size_t size) +{ + ser->size = size; + ser->offset = 0; + ser->buf = realloc(ser->buf, ser->size); + + return ser->buf != NULL; +} + +static void +_ser_free(atom_ser_t *ser) +{ + if(ser->buf) + free(ser->buf); +} + +static inline void +_discover(plughandle_t *handle) +{ + atom_ser_t ser; + + if(_ser_malloc(&ser, 512)) + { + lv2_atom_forge_set_sink(&handle->forge, _sink, _deref, &ser); + LV2_Atom_Forge_Frame frame; + + lv2_atom_forge_object(&handle->forge, &frame, 0, handle->props.urid.patch_get); + lv2_atom_forge_pop(&handle->forge, &frame); + + handle->write_function(handle->controller, 0, lv2_atom_total_size(ser.atom), + handle->event_transfer, ser.atom); + + _ser_free(&ser); + } +} + +void +_toggle(plughandle_t *handle, LV2_URID property, int32_t val, bool is_bool) +{ + atom_ser_t ser; + + if(_ser_malloc(&ser, 512)) + { + lv2_atom_forge_set_sink(&handle->forge, _sink, _deref, &ser); + LV2_Atom_Forge_Frame frame; + + lv2_atom_forge_object(&handle->forge, &frame, 0, handle->props.urid.patch_set); + lv2_atom_forge_key(&handle->forge, handle->props.urid.patch_property); + lv2_atom_forge_urid(&handle->forge, property); + + lv2_atom_forge_key(&handle->forge, handle->props.urid.patch_value); + if(is_bool) + lv2_atom_forge_bool(&handle->forge, val); + else + lv2_atom_forge_int(&handle->forge, val); + + lv2_atom_forge_pop(&handle->forge, &frame); + + handle->write_function(handle->controller, 0, lv2_atom_total_size(ser.atom), + handle->event_transfer, ser.atom); + + _ser_free(&ser); + } +} + +void +_ruler(struct nk_context *ctx, float line_thickness, struct nk_color color) +{ + struct nk_rect bounds = nk_layout_space_bounds(ctx); + const enum nk_widget_layout_states states = nk_widget(&bounds, ctx); + + if(states != NK_WIDGET_INVALID) + { + struct nk_command_buffer *canv= nk_window_get_canvas(ctx); + const float x0 = bounds.x; + const float y0 = bounds.y + bounds.h/2; + const float x1 = bounds.x + bounds.w; + const float y1 = y0; + + nk_stroke_line(canv, x0, y0, x1, y1, line_thickness, color); + } +} + +void +_empty(struct nk_context *ctx) +{ + nk_text(ctx, NULL, 0, NK_TEXT_RIGHT); +} + +void +_clear(plughandle_t *handle) +{ + atom_ser_t *ser = &handle->ser; + struct nk_str *str = &handle->edit.string; + + if(_ser_realloc(ser, 1024)) + { + lv2_atom_forge_set_sink(&handle->mem, _sink, _deref, ser); + lv2_atom_forge_tuple(&handle->mem, &handle->frame); + } + + handle->count = 0; + handle->selected = NULL; + nk_str_clear(str); +} + +void +_post_redisplay(plughandle_t *handle) +{ + nk_pugl_post_redisplay(&handle->win); +} + +static LV2UI_Handle +instantiate(const LV2UI_Descriptor *descriptor, const char *plugin_uri, + const char *bundle_path, LV2UI_Write_Function write_function, + LV2UI_Controller controller, LV2UI_Widget *widget, + const LV2_Feature *const *features) +{ + plughandle_t *handle = calloc(1, sizeof(plughandle_t)); + if(!handle) + return NULL; + + void *parent = NULL; + LV2UI_Resize *host_resize = NULL; + for(int i=0; features[i]; i++) + { + if(!strcmp(features[i]->URI, LV2_URID__map)) + handle->map = features[i]->data; + else if(!strcmp(features[i]->URI, LV2_URID__unmap)) + handle->unmap = features[i]->data; + else if(!strcmp(features[i]->URI, LV2_UI__parent)) + parent = features[i]->data; + else if(!strcmp(features[i]->URI, LV2_UI__resize)) + host_resize = features[i]->data; + } + + if(!handle->map || !handle->unmap) + { + fprintf(stderr, "LV2 URID extension not supported\n"); + free(handle); + return NULL; + } + if(!parent) + { + free(handle); + return NULL; + } + + handle->event_transfer = handle->map->map(handle->map->handle, LV2_ATOM__eventTransfer); + lv2_atom_forge_init(&handle->forge, handle->map); + lv2_atom_forge_init(&handle->mem, handle->map); + lv2_osc_urid_init(&handle->osc_urid, handle->map); + + handle->write_function = write_function; + handle->controller = controller; + + if(!props_init(&handle->props, MAX_NPROPS, plugin_uri, handle->map, handle)) + { + fprintf(stderr, "failed to allocate property structure\n"); + free(handle); + return NULL; + } + + if( !(handle->urid.count = props_register(&handle->props, &stat_count, &handle->state.count, &handle->stash.count)) + || !(handle->urid.overwrite = props_register(&handle->props, &stat_overwrite, &handle->state.overwrite, &handle->stash.overwrite)) + || !(handle->urid.block = props_register(&handle->props, &stat_block, &handle->state.block, &handle->stash.block)) + || !(handle->urid.follow = props_register(&handle->props, &stat_follow, &handle->state.follow, &handle->stash.follow)) ) + { + free(handle); + return NULL; + } + + nk_pugl_config_t *cfg = &handle->win.cfg; + cfg->height = 700; + cfg->resizable = true; + cfg->ignore = false; + cfg->class = "sherlock_inspector"; + cfg->title = "Sherlock Inspector"; + cfg->parent = (intptr_t)parent; + cfg->data = handle; + if(!strcmp(plugin_uri, SHERLOCK_MIDI_INSPECTOR_URI)) + { + cfg->width = 600; + cfg->expose = _midi_inspector_expose; + } + else if(!strcmp(plugin_uri, SHERLOCK_ATOM_INSPECTOR_URI)) + { + cfg->width = 1200; + cfg->expose = _atom_inspector_expose; + } + else if(!strcmp(plugin_uri, SHERLOCK_OSC_INSPECTOR_URI)) + { + cfg->width = 600; + cfg->expose = _osc_inspector_expose; + } + + char *path; + if(asprintf(&path, "%sCousine-Regular.ttf", bundle_path) == -1) + path = NULL; + + cfg->font.face = path; + cfg->font.size = 13; + + *(intptr_t *)widget = nk_pugl_init(&handle->win); + nk_pugl_show(&handle->win); + + if(host_resize) + host_resize->ui_resize(host_resize->handle, cfg->width, cfg->height); + + _clear(handle); + _discover(handle); + + nk_textedit_init_default(&handle->edit); + + handle->sratom = sratom_new(handle->map); + sratom_set_pretty_numbers(handle->sratom, false); + handle->base_uri = "file:///tmp/base"; + + return handle; +} + +static void +cleanup(LV2UI_Handle instance) +{ + plughandle_t *handle = instance; + + sratom_free(handle->sratom); + + nk_textedit_free(&handle->edit); + + atom_ser_t *ser = &handle->ser; + _ser_free(ser); + + nk_pugl_hide(&handle->win); + nk_pugl_shutdown(&handle->win); + + free(handle); +} + +static void +port_event(LV2UI_Handle instance, uint32_t i, uint32_t size, uint32_t urid, + const void *buf) +{ + plughandle_t *handle = instance; + + if(urid != handle->event_transfer) + return; + + switch(i) + { + case 0: + case 1: + { + atom_ser_t ser; + + if(_ser_malloc(&ser, 512)) + { + LV2_Atom_Forge_Frame frame; + lv2_atom_forge_set_sink(&handle->forge, _sink, _deref, &ser); + LV2_Atom_Forge_Ref ref = lv2_atom_forge_sequence_head(&handle->forge, &frame, 0); + + if(props_advance(&handle->props, &handle->forge, 0, buf, &ref)) + nk_pugl_post_redisplay(&handle->win); + + lv2_atom_forge_pop(&handle->forge, &frame); + + _ser_free(&ser); + } + + break; + } + case 2: + { + bool append = true; + + if(handle->state.block) + append = false; + + if(handle->count > handle->state.count) + { + if(handle->state.overwrite) + _clear(handle); + else + append = false; + } + + if(append) + { + const LV2_Atom *atom = buf; + const uint32_t sz = lv2_atom_total_size(atom); + + lv2_atom_forge_write(&handle->mem, atom, sz); + handle->count += 1; + + if(handle->state.follow) + handle->bottom = true; // signal scrolling to bottom + + nk_pugl_post_redisplay(&handle->win); + } + + break; + } + } +} + +static int +_idle(LV2UI_Handle instance) +{ + plughandle_t *handle = instance; + + return nk_pugl_process_events(&handle->win); +} + +static const LV2UI_Idle_Interface idle_ext = { + .idle = _idle +}; + +static const void * +extension_data(const char *uri) +{ + if(!strcmp(uri, LV2_UI__idleInterface)) + return &idle_ext; + + return NULL; +} + +const LV2UI_Descriptor midi_inspector_nk = { + .URI = SHERLOCK_MIDI_INSPECTOR_NK_URI, + .instantiate = instantiate, + .cleanup = cleanup, + .port_event = port_event, + .extension_data = extension_data +}; + +const LV2UI_Descriptor atom_inspector_nk = { + .URI = SHERLOCK_ATOM_INSPECTOR_NK_URI, + .instantiate = instantiate, + .cleanup = cleanup, + .port_event = port_event, + .extension_data = extension_data +}; + +const LV2UI_Descriptor osc_inspector_nk = { + .URI = SHERLOCK_OSC_INSPECTOR_NK_URI, + .instantiate = instantiate, + .cleanup = cleanup, + .port_event = port_event, + .extension_data = extension_data +}; + +#ifdef _WIN32 +__declspec(dllexport) +#else +__attribute__((visibility("default"))) +#endif +const LV2UI_Descriptor* +lv2ui_descriptor(uint32_t index) +{ + switch(index) + { + case 0: + return &midi_inspector_nk; + case 1: + return &atom_inspector_nk; + case 2: + return &osc_inspector_nk; + + default: + return NULL; + } +} diff --git a/sherlock_nk.h b/sherlock_nk.h new file mode 100644 index 0000000..66982c8 --- /dev/null +++ b/sherlock_nk.h @@ -0,0 +1,99 @@ +/* + * 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. + */ + +#ifndef _SHERLOCK_NK_H +#define _SHERLOCK_NK_H + +#include "nk_pugl.h" + +#include <osc.lv2/osc.h> +#include <sratom/sratom.h> + +typedef struct _plughandle_t plughandle_t; +typedef struct _atom_ser_t atom_ser_t; + +struct _atom_ser_t { + uint32_t size; + uint32_t offset; + union { + uint8_t *buf; + LV2_Atom *atom; + }; +}; + +struct _plughandle_t { + LV2UI_Write_Function write_function; + LV2UI_Controller controller; + + LV2_URID_Map *map; + LV2_URID_Unmap *unmap; + LV2_Atom_Forge forge; + LV2_Atom_Forge mem; + int32_t count; + bool bottom; + LV2_Atom_Forge_Frame frame; + LV2_URID event_transfer; + LV2_OSC_URID osc_urid; + + PROPS_T(props, MAX_NPROPS); + struct { + LV2_URID count; + LV2_URID overwrite; + LV2_URID block; + LV2_URID follow; + } urid; + state_t state; + state_t stash; + + nk_pugl_window_t win; + + atom_ser_t ser; + const LV2_Atom *selected; + struct nk_text_edit edit; + + Sratom *sratom; + const char *base_uri; +}; + +extern const char *max_items [5]; +extern const int32_t max_values [5]; + +void +_midi_inspector_expose(struct nk_context *ctx, struct nk_rect wbounds, void *data); + +void +_atom_inspector_expose(struct nk_context *ctx, struct nk_rect wbounds, void *data); + +void +_osc_inspector_expose(struct nk_context *ctx, struct nk_rect wbounds, void *data); + +void +_empty(struct nk_context *ctx); + +void +_toggle(plughandle_t *handle, LV2_URID property, int32_t val, bool is_bool); + +void +_clear(plughandle_t *handle); + +void +_ruler(struct nk_context *ctx, float line_thickness, struct nk_color color); + +void +_post_redisplay(plughandle_t *handle); + +#endif // _SHERLOCK_NK_H diff --git a/sherlock_ui.ttl b/sherlock_ui.ttl index 7c32a27..c0c6045 100644 --- a/sherlock_ui.ttl +++ b/sherlock_ui.ttl @@ -70,6 +70,20 @@ sherlock:atom_inspector_3_eo ] ; lv2:requiredFeature urid:map, urid:unmap . +sherlock:atom_inspector_4_nk + a ui:X11UI ; + ui:portNotification [ + ui:plugin sherlock:atom_inspector ; + lv2:symbol "notify" ; + ui:notifyType atom:Object ; + ui:notifyType osc:Event ; + ui:notifyType midi:MidiEvent ; + ui:notifyType time:Position ; + ui:notifyType patch:Message ; + ui:protocol atom:eventTransfer + ] ; + lv2:requiredFeature urid:map, urid:unmap, ui:idleInterface . + # MIDI Inspector UI sherlock:midi_inspector_1_ui a ui:UI ; @@ -102,6 +116,17 @@ sherlock:midi_inspector_3_eo ] ; lv2:requiredFeature urid:map . +sherlock:midi_inspector_4_nk + a ui:X11UI ; + ui:portNotification [ + ui:plugin sherlock:midi_inspector ; + lv2:symbol "notify" ; + ui:notifyType midi:MidiEvent ; + ui:protocol atom:eventTransfer + ] ; + lv2:optionalFeature ui:resize ; + lv2:requiredFeature urid:map, urid:unmap, ui:idleInterface . + # OSC Inspector UI sherlock:osc_inspector_1_ui a ui:UI ; @@ -133,3 +158,14 @@ sherlock:osc_inspector_3_eo ui:protocol atom:eventTransfer ] ; lv2:requiredFeature urid:map, urid:unmap . + +sherlock:osc_inspector_4_nk + a ui:X11UI ; + ui:portNotification [ + ui:plugin sherlock:osc_inspector ; + lv2:symbol "notify" ; + ui:notifyType osc:Event ; + ui:protocol atom:eventTransfer + ] ; + lv2:optionalFeature ui:resize ; + lv2:requiredFeature urid:map, urid:unmap, ui:idleInterface . |