aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt34
-rw-r--r--VERSION2
-rw-r--r--atom_inspector_nk.c266
-rw-r--r--manifest.ttl.in15
-rw-r--r--midi_inspector_nk.c496
-rw-r--r--osc_inspector_nk.c390
-rw-r--r--sherlock.h3
-rw-r--r--sherlock.ttl6
-rw-r--r--sherlock_nk.c453
-rw-r--r--sherlock_nk.h99
-rw-r--r--sherlock_ui.ttl36
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})
diff --git a/VERSION b/VERSION
index 487397b..1aa4246 100644
--- a/VERSION
+++ b/VERSION
@@ -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);
+}
diff --git a/sherlock.h b/sherlock.h
index 8626151..91d8409 100644
--- a/sherlock.h
+++ b/sherlock.h
@@ -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 .