aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt10
-rw-r--r--atom_inspector.c27
-rw-r--r--manifest.ttl.in58
-rw-r--r--midi_inspector.c152
-rw-r--r--midi_inspector_ui.c623
-rw-r--r--osc_inspector.c154
-rw-r--r--osc_inspector_ui.c453
-rw-r--r--sherlock.c4
-rw-r--r--sherlock.h28
-rw-r--r--sherlock.ttl164
-rw-r--r--sherlock_ui.c19
11 files changed, 1662 insertions, 30 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7d7453c..4d08dff 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,6 +5,7 @@ project(sherlock.lv2)
include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_SOURCE_DIR}/libosc)
include_directories(${PROJECT_SOURCE_DIR}/eo_ui.lv2)
+include_directories(${PROJECT_SOURCE_DIR}/osc.lv2)
set(CMAKE_C_FLAGS "-std=gnu99 -Wextra -Wno-unused-parameter -ffast-math -fvisibility=hidden ${CMAKE_C_FLAGS}")
set(CMAKE_C_FLAGS "-Wshadow -Wimplicit-function-declaration -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes ${CMAKE_C_FLAGS}")
@@ -15,6 +16,7 @@ set(SHERLOCK_MINOR_VERSION 1)
set(SHERLOCK_MICRO_VERSION 1)
set(SHERLOCK_VERSION "${SHERLOCK_MAJOR_VERSION}.${SHERLOCK_MINOR_VERSION}.${SHERLOCK_MICRO_VERSION}")
add_definitions("-DSHERLOCK_VERSION=\"${SHERLOCK_VERSION}\"")
+add_definitions("-D_GNU_SOURCE=1") # asprintf
set(DEST lib/lv2/sherlock.lv2)
if(WIN32)
@@ -48,14 +50,18 @@ endif()
add_library(sherlock MODULE
sherlock.c
- atom_inspector.c)
+ atom_inspector.c
+ midi_inspector.c
+ osc_inspector.c)
target_link_libraries(sherlock ${LIBS})
set_target_properties(sherlock PROPERTIES PREFIX "")
install(TARGETS sherlock DESTINATION ${DEST})
add_library(sherlock_ui MODULE
sherlock_ui.c
- atom_inspector_ui.c)
+ atom_inspector_ui.c
+ midi_inspector_ui.c
+ osc_inspector_ui.c)
target_link_libraries(sherlock_ui ${LIBS_UI})
set_target_properties(sherlock_ui PROPERTIES PREFIX "")
install(TARGETS sherlock_ui DESTINATION ${DEST})
diff --git a/atom_inspector.c b/atom_inspector.c
index 3df5f22..c8cb63e 100644
--- a/atom_inspector.c
+++ b/atom_inspector.c
@@ -77,13 +77,6 @@ connect_port(LV2_Handle instance, uint32_t port, void *data)
}
static void
-activate(LV2_Handle instance)
-{
- handle_t *handle = (handle_t *)instance;
- //nothing
-}
-
-static void
run(LV2_Handle instance, uint32_t nsamples)
{
handle_t *handle = (handle_t *)instance;
@@ -120,6 +113,7 @@ run(LV2_Handle instance, uint32_t nsamples)
lv2_atom_forge_pad(forge, size);
}
+
if(ref)
lv2_atom_forge_pop(forge, &frame);
else
@@ -127,13 +121,6 @@ run(LV2_Handle instance, uint32_t nsamples)
}
static void
-deactivate(LV2_Handle instance)
-{
- handle_t *handle = (handle_t *)instance;
- //nothing
-}
-
-static void
cleanup(LV2_Handle instance)
{
handle_t *handle = (handle_t *)instance;
@@ -141,19 +128,13 @@ cleanup(LV2_Handle instance)
free(handle);
}
-static const void*
-extension_data(const char* uri)
-{
- return NULL;
-}
-
const LV2_Descriptor atom_inspector = {
.URI = SHERLOCK_ATOM_INSPECTOR_URI,
.instantiate = instantiate,
.connect_port = connect_port,
- .activate = activate,
+ .activate = NULL,
.run = run,
- .deactivate = deactivate,
+ .deactivate = NULL,
.cleanup = cleanup,
- .extension_data = extension_data
+ .extension_data = NULL
};
diff --git a/manifest.ttl.in b/manifest.ttl.in
index dd5853d..859fb3c 100644
--- a/manifest.ttl.in
+++ b/manifest.ttl.in
@@ -59,3 +59,61 @@ sherlock:atom_inspector_kx
a kx:Widget ;
ui:binary <sherlock_ui@LIB_EXT@> ;
rdfs:seeAlso <sherlock.ttl> .
+
+# MIDI Inspector Plugin
+sherlock:midi_inspector
+ a lv2:Plugin ;
+ lv2:minorVersion @SHERLOCK_MINOR_VERSION@ ;
+ lv2:microVersion @SHERLOCK_MICRO_VERSION@ ;
+ lv2:binary <sherlock@LIB_EXT@> ;
+ @EO_UI_WRAP@ui:ui sherlock:midi_inspector_eo ;
+ @UI_UI_WRAP@ui:ui sherlock:midi_inspector_ui ;
+ @KX_UI_WRAP@ui:ui sherlock:midi_inspector_kx ;
+ @X11_UI_WRAP@ui:ui sherlock:midi_inspector_x11 ;
+ rdfs:seeAlso <sherlock.ttl> .
+
+sherlock:midi_inspector_eo
+ a ui:EoUI ;
+ ui:binary <sherlock_ui@LIB_EXT@> ;
+ rdfs:seeAlso <sherlock.ttl> .
+sherlock:midi_inspector_ui
+ a ui:UI ;
+ ui:binary <sherlock_ui@LIB_EXT@> ;
+ rdfs:seeAlso <sherlock.ttl> .
+sherlock:midi_inspector_x11
+ a ui:X11UI ;
+ ui:binary <sherlock_ui@LIB_EXT@> ;
+ rdfs:seeAlso <sherlock.ttl> .
+sherlock:midi_inspector_kx
+ a kx:Widget ;
+ ui:binary <sherlock_ui@LIB_EXT@> ;
+ rdfs:seeAlso <sherlock.ttl> .
+
+# OSC Inspector Plugin
+sherlock:osc_inspector
+ a lv2:Plugin ;
+ lv2:minorVersion @SHERLOCK_MINOR_VERSION@ ;
+ lv2:microVersion @SHERLOCK_MICRO_VERSION@ ;
+ lv2:binary <sherlock@LIB_EXT@> ;
+ @EO_UI_WRAP@ui:ui sherlock:osc_inspector_eo ;
+ @UI_UI_WRAP@ui:ui sherlock:osc_inspector_ui ;
+ @KX_UI_WRAP@ui:ui sherlock:osc_inspector_kx ;
+ @X11_UI_WRAP@ui:ui sherlock:osc_inspector_x11 ;
+ rdfs:seeAlso <sherlock.ttl> .
+
+sherlock:osc_inspector_eo
+ a ui:EoUI ;
+ ui:binary <sherlock_ui@LIB_EXT@> ;
+ rdfs:seeAlso <sherlock.ttl> .
+sherlock:osc_inspector_ui
+ a ui:UI ;
+ ui:binary <sherlock_ui@LIB_EXT@> ;
+ rdfs:seeAlso <sherlock.ttl> .
+sherlock:osc_inspector_x11
+ a ui:X11UI ;
+ ui:binary <sherlock_ui@LIB_EXT@> ;
+ rdfs:seeAlso <sherlock.ttl> .
+sherlock:osc_inspector_kx
+ a kx:Widget ;
+ ui:binary <sherlock_ui@LIB_EXT@> ;
+ rdfs:seeAlso <sherlock.ttl> .
diff --git a/midi_inspector.c b/midi_inspector.c
new file mode 100644
index 0000000..754420c
--- /dev/null
+++ b/midi_inspector.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2015 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 <stdio.h>
+#include <stdlib.h>
+
+#include <sherlock.h>
+
+#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
+
+typedef struct _handle_t handle_t;
+
+struct _handle_t {
+ LV2_URID_Map *map;
+ const LV2_Atom_Sequence *control_in;
+ LV2_Atom_Sequence *control_out;
+ LV2_Atom_Sequence *notify;
+ LV2_Atom_Forge forge;
+
+ LV2_URID midi_event;
+};
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor, double rate,
+ const char *bundle_path, const LV2_Feature *const *features)
+{
+ int i;
+ handle_t *handle = calloc(1, sizeof(handle_t));
+ if(!handle)
+ return NULL;
+
+ for(i=0; features[i]; i++)
+ if(!strcmp(features[i]->URI, LV2_URID__map))
+ handle->map = (LV2_URID_Map *)features[i]->data;
+
+ if(!handle->map)
+ {
+ fprintf(stderr, "%s: Host does not support urid:map\n", descriptor->URI);
+ free(handle);
+ return NULL;
+ }
+
+ handle->midi_event = handle->map->map(handle->map->handle, LV2_MIDI__MidiEvent);
+
+ lv2_atom_forge_init(&handle->forge, handle->map);
+
+ return handle;
+}
+
+static void
+connect_port(LV2_Handle instance, uint32_t port, void *data)
+{
+ handle_t *handle = (handle_t *)instance;
+
+ switch(port)
+ {
+ case 0:
+ handle->control_in = (const LV2_Atom_Sequence *)data;
+ break;
+ case 1:
+ handle->control_out = (LV2_Atom_Sequence *)data;
+ break;
+ case 2:
+ handle->notify = (LV2_Atom_Sequence *)data;
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+run(LV2_Handle instance, uint32_t nsamples)
+{
+ handle_t *handle = (handle_t *)instance;
+ uint32_t capacity;
+ LV2_Atom_Forge *forge = &handle->forge;
+ LV2_Atom_Forge_Frame frame [2];
+ LV2_Atom_Forge_Ref ref;
+
+ // size of input sequence
+ size_t size = sizeof(LV2_Atom) + handle->control_in->atom.size;
+
+ // copy whole input sequence to through port
+ capacity = handle->control_out->atom.size;
+ lv2_atom_forge_set_buffer(forge, (uint8_t *)handle->control_out, capacity);
+ ref = lv2_atom_forge_raw(forge, handle->control_in, size);
+ if(!ref)
+ lv2_atom_sequence_clear(handle->control_out);
+
+ // forge whole sequence as single event
+ capacity = handle->notify->atom.size;
+ lv2_atom_forge_set_buffer(forge, (uint8_t *)handle->notify, capacity);
+ ref = lv2_atom_forge_sequence_head(forge, &frame[0], 0);
+ if(ref)
+ ref = lv2_atom_forge_frame_time(forge, 0);
+ if(ref)
+ ref = lv2_atom_forge_sequence_head(forge, &frame[1], 0);
+
+ // only serialize MIDI events to UI
+ LV2_ATOM_SEQUENCE_FOREACH(handle->control_in, ev)
+ {
+ if(ev->body.type == handle->midi_event)
+ {
+ if(ref)
+ ref = lv2_atom_forge_frame_time(forge, ev->time.frames);
+ if(ref)
+ ref = lv2_atom_forge_raw(forge, &ev->body, sizeof(LV2_Atom) + ev->body.size);
+ if(ref)
+ lv2_atom_forge_pad(forge, ev->body.size);
+ }
+ }
+
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame[1]);
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame[0]);
+ else
+ lv2_atom_sequence_clear(handle->notify);
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ handle_t *handle = (handle_t *)instance;
+
+ free(handle);
+}
+
+const LV2_Descriptor midi_inspector = {
+ .URI = SHERLOCK_MIDI_INSPECTOR_URI,
+ .instantiate = instantiate,
+ .connect_port = connect_port,
+ .activate = NULL,
+ .run = run,
+ .deactivate = NULL,
+ .cleanup = cleanup,
+ .extension_data = NULL
+};
diff --git a/midi_inspector_ui.c b/midi_inspector_ui.c
new file mode 100644
index 0000000..f0bcfbd
--- /dev/null
+++ b/midi_inspector_ui.c
@@ -0,0 +1,623 @@
+/*
+ * Copyright (c) 2015 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 <Elementary.h>
+
+#include <lv2_eo_ui.h>
+
+#define COUNT_MAX 2048 // maximal amount of events shown
+#define STRING_BUF_SIZE 2048
+#define STRING_MAX 256
+#define STRING_OFF (STRING_MAX - 4)
+
+typedef struct _UI UI;
+
+struct _UI {
+ eo_ui_t eoui;
+
+ LV2UI_Write_Function write_function;
+ LV2UI_Controller controller;
+
+ LV2_URID_Map *map;
+ LV2_Atom_Forge forge;
+ LV2_URID event_transfer;
+
+ Evas_Object *table;
+ Evas_Object *list;
+ Evas_Object *clear;
+ Evas_Object *popup;
+
+ Elm_Genlist_Item_Class *itc_midi;
+
+ char string_buf [STRING_BUF_SIZE];
+ char *logo_path;
+};
+
+#define HIL_PRE(VAL) ("<color=#bbb font=Mono style=plain><b>"VAL"</b></color> <color=#b00 font=Mono style=plain>")
+#define HIL_POST ("</color>")
+
+#define URI(VAL,TYP) ("<color=#bbb font=Mono style=plain><b>"VAL"</b></color> <color=#fff style=plain>"TYP"</color>")
+#define HIL(VAL,TYP) ("<color=#bbb font=Mono style=plain><b>"VAL"</b></color> <color=#b00 font=Mono style=plain>"TYP"</color>")
+
+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" },
+};
+
+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 char *
+_atom_stringify(UI *ui, char *ptr, char *end, int newline, const LV2_Atom *atom)
+{
+ //FIXME check for buffer overflows!!!
+
+ const uint8_t *midi = LV2_ATOM_BODY_CONST(atom);
+
+ sprintf(ptr, HIL_PRE(" "));
+ ptr += strlen(ptr);
+
+ char *barrier = ptr + STRING_OFF;
+ unsigned i;
+ for(i=0; (i<atom->size) && (ptr<barrier); i++, ptr += 3)
+ sprintf(ptr, "%02X ", midi[i]);
+
+ for( ; (i<3) && (ptr<barrier); i++, ptr += 3)
+ sprintf(ptr, " ");
+
+ const uint8_t command = midi[0] & 0xf0;
+ const uint8_t channel = midi[0] & 0x0f;
+ const midi_msg_t *command_msg = _search_command(command);
+ const char *command_str = command_msg
+ ? command_msg->key
+ : "Unknown";
+ if(command == LV2_MIDI_MSG_CONTROLLER)
+ {
+ const midi_msg_t *controller_msg = _search_controller(midi[1]);
+ const char *controller_str = controller_msg
+ ? controller_msg->key
+ : "Unknown";
+ sprintf(ptr, "(Ch %02hhu, %s, %s)", channel, command_str, controller_str);
+ }
+ else
+ {
+ sprintf(ptr, "(Ch %02hhu, %s)", channel, command_str);
+ }
+ ptr += strlen(ptr);
+
+ if(ptr >= barrier) // there would be more to print
+ {
+ ptr = barrier;
+ sprintf(ptr, "...");
+ ptr += 4;
+ }
+
+ sprintf(ptr, HIL_POST);
+
+ return ptr + strlen(ptr);
+}
+
+static char *
+_midi_label_get(void *data, Evas_Object *obj, const char *part)
+{
+ UI *ui = evas_object_data_get(obj, "ui");
+ const LV2_Atom_Event *ev = data;
+
+ if(!ui)
+ return NULL;
+
+ if(!strcmp(part, "elm.text"))
+ {
+ char *buf = ui->string_buf;
+ char *ptr = buf;
+ char *end = buf + STRING_BUF_SIZE;
+
+ ptr = _atom_stringify(ui, ptr, end, 1, &ev->body);
+
+ return ptr
+ ? strdup(buf)
+ : NULL;
+ }
+
+ return NULL;
+}
+
+static Evas_Object *
+_midi_content_get(void *data, Evas_Object *obj, const char *part)
+{
+ UI *ui = evas_object_data_get(obj, "ui");
+ const LV2_Atom_Event *ev = data;
+
+ if(!ui)
+ return NULL;
+
+ if(!strcmp(part, "elm.swallow.icon"))
+ {
+ char *buf = ui->string_buf;
+
+ sprintf(buf, "<color=#bb0 font=Mono>%04ld</color>", ev->time.frames);
+
+ Evas_Object *label = elm_label_add(obj);
+ if(label)
+ {
+ elm_object_part_text_set(label, "default", buf);
+ evas_object_show(label);
+ }
+
+ return label;
+ }
+ else if(!strcmp(part, "elm.swallow.end"))
+ {
+ char *buf = ui->string_buf;
+
+ sprintf(buf, "<color=#0bb font=Mono>%4u</color>", ev->body.size);
+
+ Evas_Object *label = elm_label_add(obj);
+ if(label)
+ {
+ elm_object_part_text_set(label, "default", buf);
+ evas_object_show(label);
+ }
+
+ return label;
+ }
+
+ return NULL;
+}
+
+static void
+_midi_del(void *data, Evas_Object *obj)
+{
+ free(data);
+}
+
+static void
+_clear_update(UI *ui, int count)
+{
+ if(!ui->clear)
+ return;
+
+ char *buf = ui->string_buf;
+ sprintf(buf, "Clear (%i of %i)", count, COUNT_MAX);
+ elm_object_text_set(ui->clear, buf);
+}
+
+static void
+_clear_clicked(void *data, Evas_Object *obj, void *event_info)
+{
+ UI *ui = data;
+
+ if(ui->list)
+ elm_genlist_clear(ui->list);
+
+ _clear_update(ui, 0);
+}
+
+static void
+_info_clicked(void *data, Evas_Object *obj, void *event_info)
+{
+ UI *ui = data;
+
+ // toggle popup
+ if(ui->popup)
+ {
+ if(evas_object_visible_get(ui->popup))
+ evas_object_hide(ui->popup);
+ else
+ evas_object_show(ui->popup);
+ }
+}
+
+static Evas_Object *
+_content_get(eo_ui_t *eoui)
+{
+ UI *ui = (void *)eoui - offsetof(UI, eoui);
+
+ ui->table = elm_table_add(eoui->win);
+ if(ui->table)
+ {
+ elm_table_homogeneous_set(ui->table, EINA_FALSE);
+ elm_table_padding_set(ui->table, 0, 0);
+
+ ui->list = elm_genlist_add(ui->table);
+ if(ui->list)
+ {
+ elm_genlist_select_mode_set(ui->list, ELM_OBJECT_SELECT_MODE_DEFAULT);
+ elm_genlist_homogeneous_set(ui->list, EINA_FALSE); // TRUE for lazy-loading
+ elm_genlist_mode_set(ui->list, ELM_LIST_SCROLL);
+ evas_object_data_set(ui->list, "ui", ui);
+ evas_object_size_hint_weight_set(ui->list, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(ui->list, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(ui->list);
+ elm_table_pack(ui->table, ui->list, 0, 0, 2, 1);
+ }
+
+ ui->clear = elm_button_add(ui->table);
+ if(ui->clear)
+ {
+ _clear_update(ui, 0);
+ evas_object_smart_callback_add(ui->clear, "clicked", _clear_clicked, ui);
+ evas_object_size_hint_weight_set(ui->clear, EVAS_HINT_EXPAND, 0.f);
+ evas_object_size_hint_align_set(ui->clear, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(ui->clear);
+ elm_table_pack(ui->table, ui->clear, 0, 1, 1, 1);
+ }
+
+ Evas_Object *info = elm_button_add(ui->table);
+ if(info)
+ {
+ evas_object_smart_callback_add(info, "clicked", _info_clicked, ui);
+ evas_object_size_hint_weight_set(info, 0.f, 0.f);
+ evas_object_size_hint_align_set(info, 1.f, EVAS_HINT_FILL);
+ evas_object_show(info);
+ elm_table_pack(ui->table, info, 1, 1, 1, 1);
+
+ Evas_Object *icon = elm_icon_add(info);
+ if(icon)
+ {
+ elm_image_file_set(icon, ui->logo_path, NULL);
+ evas_object_size_hint_min_set(icon, 20, 20);
+ evas_object_size_hint_max_set(icon, 32, 32);
+ //evas_object_size_hint_aspect_set(icon, EVAS_ASPECT_CONTROL_BOTH, 1, 1);
+ evas_object_show(icon);
+ elm_object_part_content_set(info, "icon", icon);
+ }
+ }
+
+ ui->popup = elm_popup_add(ui->table);
+ if(ui->popup)
+ {
+ elm_popup_allow_events_set(ui->popup, EINA_TRUE);
+
+ Evas_Object *hbox = elm_box_add(ui->popup);
+ if(hbox)
+ {
+ elm_box_horizontal_set(hbox, EINA_TRUE);
+ elm_box_homogeneous_set(hbox, EINA_FALSE);
+ elm_box_padding_set(hbox, 10, 0);
+ evas_object_show(hbox);
+ elm_object_content_set(ui->popup, hbox);
+
+ Evas_Object *icon = elm_icon_add(hbox);
+ if(icon)
+ {
+ elm_image_file_set(icon, ui->logo_path, NULL);
+ evas_object_size_hint_min_set(icon, 128, 128);
+ evas_object_size_hint_max_set(icon, 256, 256);
+ evas_object_size_hint_aspect_set(icon, EVAS_ASPECT_CONTROL_BOTH, 1, 1);
+ evas_object_show(icon);
+ elm_box_pack_end(hbox, icon);
+ }
+
+ Evas_Object *label = elm_label_add(hbox);
+ if(label)
+ {
+ elm_object_text_set(label,
+ "<color=#b00 shadow_color=#fff font_size=20>"
+ "Sherlock - MIDI Inspector"
+ "</color></br><align=left>"
+ "Version "SHERLOCK_VERSION"</br></br>"
+ "Copyright (c) 2015 Hanspeter Portner</br></br>"
+ "This is free and libre software</br>"
+ "Released under Artistic License 2.0</br>"
+ "By Open Music Kontrollers</br></br>"
+ "<color=#bbb>"
+ "http://open-music-kontrollers.ch/lv2/sherlock</br>"
+ "dev@open-music-kontrollers.ch"
+ "</color></align>");
+
+ evas_object_show(label);
+ elm_box_pack_end(hbox, label);
+ }
+ }
+ }
+ }
+
+ return ui->table;
+}
+
+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)
+{
+ if(strcmp(plugin_uri, SHERLOCK_MIDI_INSPECTOR_URI))
+ return NULL;
+
+ eo_ui_driver_t driver;
+ if(descriptor == &midi_inspector_eo)
+ driver = EO_UI_DRIVER_EO;
+ else if(descriptor == &midi_inspector_ui)
+ driver = EO_UI_DRIVER_UI;
+ else if(descriptor == &midi_inspector_x11)
+ driver = EO_UI_DRIVER_X11;
+ else if(descriptor == &midi_inspector_kx)
+ driver = EO_UI_DRIVER_KX;
+ else
+ return NULL;
+
+ UI *ui = calloc(1, sizeof(UI));
+ if(!ui)
+ return NULL;
+
+ eo_ui_t *eoui = &ui->eoui;
+ eoui->driver = driver;
+ eoui->content_get = _content_get;
+ eoui->w = 1024,
+ eoui->h = 480;
+
+ ui->write_function = write_function;
+ ui->controller = controller;
+
+ for(int i=0; features[i]; i++)
+ {
+ if(!strcmp(features[i]->URI, LV2_URID__map))
+ ui->map = (LV2_URID_Map *)features[i]->data;
+ }
+
+ if(!ui->map)
+ {
+ fprintf(stderr, "LV2 URID extension not supported\n");
+ free(ui);
+ return NULL;
+ }
+
+ ui->event_transfer = ui->map->map(ui->map->handle, LV2_ATOM__eventTransfer);
+ lv2_atom_forge_init(&ui->forge, ui->map);
+
+ ui->itc_midi = elm_genlist_item_class_new();
+ if(ui->itc_midi)
+ {
+ ui->itc_midi->item_style = "default_style";
+ ui->itc_midi->func.text_get = _midi_label_get;
+ ui->itc_midi->func.content_get = _midi_content_get;
+ ui->itc_midi->func.state_get = NULL;
+ ui->itc_midi->func.del = _midi_del;
+ }
+
+ sprintf(ui->string_buf, "%s/omk_logo_256x256.png", bundle_path);
+ ui->logo_path = strdup(ui->string_buf);
+
+ if(eoui_instantiate(eoui, descriptor, plugin_uri, bundle_path, write_function,
+ controller, widget, features))
+ {
+ free(ui);
+ return NULL;
+ }
+
+ return ui;
+}
+
+static void
+cleanup(LV2UI_Handle handle)
+{
+ UI *ui = handle;
+
+ eoui_cleanup(&ui->eoui);
+
+ if(ui->logo_path)
+ free(ui->logo_path);
+
+ if(ui->itc_midi)
+ elm_genlist_item_class_free(ui->itc_midi);
+
+ free(ui);
+}
+
+static void
+port_event(LV2UI_Handle handle, uint32_t i, uint32_t size, uint32_t urid,
+ const void *buf)
+{
+ UI *ui = handle;
+
+ if( (i == 2) && (urid == ui->event_transfer) && ui->list)
+ {
+ const LV2_Atom_Sequence *seq = buf;
+ int n = elm_genlist_items_count(ui->list);
+
+ LV2_ATOM_SEQUENCE_FOREACH(seq, elmnt)
+ {
+ size_t len = sizeof(LV2_Atom_Event) + elmnt->body.size;
+ LV2_Atom_Event *ev = malloc(len);
+ if(!ev)
+ continue;
+
+ memcpy(ev, elmnt, len);
+
+ // check item count
+ if(n++ >= COUNT_MAX)
+ break;
+
+ Elm_Object_Item *itm2 = elm_genlist_item_append(ui->list, ui->itc_midi,
+ ev, NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL);
+ elm_genlist_item_select_mode_set(itm2, ELM_OBJECT_SELECT_MODE_DEFAULT);
+
+ // scroll to last item
+ //elm_genlist_item_show(itm, ELM_GENLIST_ITEM_SCROLLTO_MIDDLE);
+ }
+
+ if(seq->atom.size > sizeof(LV2_Atom_Sequence_Body))
+ _clear_update(ui, n); // only update if there where any events
+ }
+}
+
+const LV2UI_Descriptor midi_inspector_eo = {
+ .URI = SHERLOCK_MIDI_INSPECTOR_EO_URI,
+ .instantiate = instantiate,
+ .cleanup = cleanup,
+ .port_event = port_event,
+ .extension_data = eoui_eo_extension_data
+};
+
+const LV2UI_Descriptor midi_inspector_ui = {
+ .URI = SHERLOCK_MIDI_INSPECTOR_UI_URI,
+ .instantiate = instantiate,
+ .cleanup = cleanup,
+ .port_event = port_event,
+ .extension_data = eoui_ui_extension_data
+};
+
+const LV2UI_Descriptor midi_inspector_x11 = {
+ .URI = SHERLOCK_MIDI_INSPECTOR_X11_URI,
+ .instantiate = instantiate,
+ .cleanup = cleanup,
+ .port_event = port_event,
+ .extension_data = eoui_x11_extension_data
+};
+
+const LV2UI_Descriptor midi_inspector_kx = {
+ .URI = SHERLOCK_MIDI_INSPECTOR_KX_URI,
+ .instantiate = instantiate,
+ .cleanup = cleanup,
+ .port_event = port_event,
+ .extension_data = eoui_kx_extension_data
+};
diff --git a/osc_inspector.c b/osc_inspector.c
new file mode 100644
index 0000000..0ead8f6
--- /dev/null
+++ b/osc_inspector.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2015 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 <stdio.h>
+#include <stdlib.h>
+
+#include <sherlock.h>
+
+#include <lv2_osc.h>
+
+typedef struct _handle_t handle_t;
+
+struct _handle_t {
+ LV2_URID_Map *map;
+ const LV2_Atom_Sequence *control_in;
+ LV2_Atom_Sequence *control_out;
+ LV2_Atom_Sequence *notify;
+ LV2_Atom_Forge forge;
+
+ osc_forge_t oforge;
+};
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor, double rate,
+ const char *bundle_path, const LV2_Feature *const *features)
+{
+ int i;
+ handle_t *handle = calloc(1, sizeof(handle_t));
+ if(!handle)
+ return NULL;
+
+ for(i=0; features[i]; i++)
+ if(!strcmp(features[i]->URI, LV2_URID__map))
+ handle->map = (LV2_URID_Map *)features[i]->data;
+
+ if(!handle->map)
+ {
+ fprintf(stderr, "%s: Host does not support urid:map\n", descriptor->URI);
+ free(handle);
+ return NULL;
+ }
+
+ osc_forge_init(&handle->oforge, handle->map);
+ lv2_atom_forge_init(&handle->forge, handle->map);
+
+ return handle;
+}
+
+static void
+connect_port(LV2_Handle instance, uint32_t port, void *data)
+{
+ handle_t *handle = (handle_t *)instance;
+
+ switch(port)
+ {
+ case 0:
+ handle->control_in = (const LV2_Atom_Sequence *)data;
+ break;
+ case 1:
+ handle->control_out = (LV2_Atom_Sequence *)data;
+ break;
+ case 2:
+ handle->notify = (LV2_Atom_Sequence *)data;
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+run(LV2_Handle instance, uint32_t nsamples)
+{
+ handle_t *handle = (handle_t *)instance;
+ uint32_t capacity;
+ LV2_Atom_Forge *forge = &handle->forge;
+ LV2_Atom_Forge_Frame frame [2];
+ LV2_Atom_Forge_Ref ref;
+
+ // size of input sequence
+ size_t size = sizeof(LV2_Atom) + handle->control_in->atom.size;
+
+ // copy whole input sequence to through port
+ capacity = handle->control_out->atom.size;
+ lv2_atom_forge_set_buffer(forge, (uint8_t *)handle->control_out, capacity);
+ ref = lv2_atom_forge_raw(forge, handle->control_in, size);
+ if(!ref)
+ lv2_atom_sequence_clear(handle->control_out);
+
+ // forge whole sequence as single event
+ capacity = handle->notify->atom.size;
+ lv2_atom_forge_set_buffer(forge, (uint8_t *)handle->notify, capacity);
+ ref = lv2_atom_forge_sequence_head(forge, &frame[0], 0);
+ if(ref)
+ ref = lv2_atom_forge_frame_time(forge, 0);
+ if(ref)
+ ref = lv2_atom_forge_sequence_head(forge, &frame[1], 0);
+
+ // only serialize OSC events to UI
+ LV2_ATOM_SEQUENCE_FOREACH(handle->control_in, ev)
+ {
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
+
+ if( osc_atom_is_bundle(&handle->oforge, obj)
+ || osc_atom_is_message(&handle->oforge, obj) )
+ {
+ if(ref)
+ ref = lv2_atom_forge_frame_time(forge, ev->time.frames);
+ if(ref)
+ ref = lv2_atom_forge_raw(forge, &ev->body, sizeof(LV2_Atom) + ev->body.size);
+ if(ref)
+ lv2_atom_forge_pad(forge, ev->body.size);
+ }
+ }
+
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame[1]);
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame[0]);
+ else
+ lv2_atom_sequence_clear(handle->notify);
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ handle_t *handle = (handle_t *)instance;
+
+ free(handle);
+}
+
+const LV2_Descriptor osc_inspector = {
+ .URI = SHERLOCK_OSC_INSPECTOR_URI,
+ .instantiate = instantiate,
+ .connect_port = connect_port,
+ .activate = NULL,
+ .run = run,
+ .deactivate = NULL,
+ .cleanup = cleanup,
+ .extension_data = NULL
+};
diff --git a/osc_inspector_ui.c b/osc_inspector_ui.c
new file mode 100644
index 0000000..50b71b5
--- /dev/null
+++ b/osc_inspector_ui.c
@@ -0,0 +1,453 @@
+/*
+ * Copyright (c) 2015 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 <Elementary.h>
+
+#include <lv2_eo_ui.h>
+#include <lv2_osc.h>
+
+#define COUNT_MAX 2048 // maximal amount of events shown
+#define STRING_BUF_SIZE 2048
+#define STRING_MAX 256
+#define STRING_OFF (STRING_MAX - 4)
+
+typedef struct _UI UI;
+
+struct _UI {
+ eo_ui_t eoui;
+
+ LV2UI_Write_Function write_function;
+ LV2UI_Controller controller;
+
+ LV2_URID_Map *map;
+ LV2_Atom_Forge forge;
+ LV2_URID event_transfer;
+ osc_forge_t oforge;
+
+ Evas_Object *table;
+ Evas_Object *list;
+ Evas_Object *clear;
+ Evas_Object *popup;
+
+ Elm_Genlist_Item_Class *itc_osc;
+
+ char string_buf [STRING_BUF_SIZE];
+ char *logo_path;
+};
+
+#define HIL_PRE(VAL) ("<color=#bbb font=Mono style=plain><b>"VAL"</b></color> <color=#b00 font=Mono style=plain>")
+#define HIL_POST ("</color>")
+
+#define URI(VAL,TYP) ("<color=#bbb font=Mono style=plain><b>"VAL"</b></color> <color=#fff style=plain>"TYP"</color>")
+#define HIL(VAL,TYP) ("<color=#bbb font=Mono style=plain><b>"VAL"</b></color> <color=#b00 font=Mono style=plain>"TYP"</color>")
+
+static char *
+_atom_stringify(UI *ui, const LV2_Atom *atom)
+{
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)atom;
+
+ if(osc_atom_is_message(&ui->oforge, obj))
+ {
+ const LV2_Atom_String *path = NULL;
+ const LV2_Atom_String *fmt = NULL;
+ const LV2_Atom_Tuple *tup = NULL;
+ osc_atom_message_unpack(&ui->oforge, obj, &path, &fmt, &tup);
+
+ char *osc = NULL;
+ asprintf(&osc, " %s ,%s", LV2_ATOM_BODY_CONST(path), LV2_ATOM_BODY_CONST(fmt));
+ //FIXME
+ return osc;
+ }
+ else if(osc_atom_is_bundle(&ui->oforge, obj))
+ {
+ //FIXME
+ }
+
+ return NULL;
+}
+
+static char *
+_osc_label_get(void *data, Evas_Object *obj, const char *part)
+{
+ UI *ui = evas_object_data_get(obj, "ui");
+ const LV2_Atom_Event *ev = data;
+
+ if(!ui)
+ return NULL;
+
+ if(!strcmp(part, "elm.text"))
+ {
+ return _atom_stringify(ui, &ev->body);
+ }
+
+ return NULL;
+}
+
+static Evas_Object *
+_osc_content_get(void *data, Evas_Object *obj, const char *part)
+{
+ UI *ui = evas_object_data_get(obj, "ui");
+ const LV2_Atom_Event *ev = data;
+
+ if(!ui)
+ return NULL;
+
+ if(!strcmp(part, "elm.swallow.icon"))
+ {
+ char *buf = ui->string_buf;
+
+ sprintf(buf, "<color=#bb0 font=Mono>%04ld</color>", ev->time.frames);
+
+ Evas_Object *label = elm_label_add(obj);
+ if(label)
+ {
+ elm_object_part_text_set(label, "default", buf);
+ evas_object_show(label);
+ }
+
+ return label;
+ }
+ else if(!strcmp(part, "elm.swallow.end"))
+ {
+ char *buf = ui->string_buf;
+
+ sprintf(buf, "<color=#0bb font=Mono>%4u</color>", ev->body.size);
+
+ Evas_Object *label = elm_label_add(obj);
+ if(label)
+ {
+ elm_object_part_text_set(label, "default", buf);
+ evas_object_show(label);
+ }
+
+ return label;
+ }
+
+ return NULL;
+}
+
+static void
+_osc_del(void *data, Evas_Object *obj)
+{
+ free(data);
+}
+
+static void
+_clear_update(UI *ui, int count)
+{
+ if(!ui->clear)
+ return;
+
+ char *buf = ui->string_buf;
+ sprintf(buf, "Clear (%i of %i)", count, COUNT_MAX);
+ elm_object_text_set(ui->clear, buf);
+}
+
+static void
+_clear_clicked(void *data, Evas_Object *obj, void *event_info)
+{
+ UI *ui = data;
+
+ if(ui->list)
+ elm_genlist_clear(ui->list);
+
+ _clear_update(ui, 0);
+}
+
+static void
+_info_clicked(void *data, Evas_Object *obj, void *event_info)
+{
+ UI *ui = data;
+
+ // toggle popup
+ if(ui->popup)
+ {
+ if(evas_object_visible_get(ui->popup))
+ evas_object_hide(ui->popup);
+ else
+ evas_object_show(ui->popup);
+ }
+}
+
+static Evas_Object *
+_content_get(eo_ui_t *eoui)
+{
+ UI *ui = (void *)eoui - offsetof(UI, eoui);
+
+ ui->table = elm_table_add(eoui->win);
+ if(ui->table)
+ {
+ elm_table_homogeneous_set(ui->table, EINA_FALSE);
+ elm_table_padding_set(ui->table, 0, 0);
+
+ ui->list = elm_genlist_add(ui->table);
+ if(ui->list)
+ {
+ elm_genlist_select_mode_set(ui->list, ELM_OBJECT_SELECT_MODE_DEFAULT);
+ elm_genlist_homogeneous_set(ui->list, EINA_FALSE); // TRUE for lazy-loading
+ elm_genlist_mode_set(ui->list, ELM_LIST_SCROLL);
+ evas_object_data_set(ui->list, "ui", ui);
+ evas_object_size_hint_weight_set(ui->list, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(ui->list, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(ui->list);
+ elm_table_pack(ui->table, ui->list, 0, 0, 2, 1);
+ }
+
+ ui->clear = elm_button_add(ui->table);
+ if(ui->clear)
+ {
+ _clear_update(ui, 0);
+ evas_object_smart_callback_add(ui->clear, "clicked", _clear_clicked, ui);
+ evas_object_size_hint_weight_set(ui->clear, EVAS_HINT_EXPAND, 0.f);
+ evas_object_size_hint_align_set(ui->clear, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(ui->clear);
+ elm_table_pack(ui->table, ui->clear, 0, 1, 1, 1);
+ }
+
+ Evas_Object *info = elm_button_add(ui->table);
+ if(info)
+ {
+ evas_object_smart_callback_add(info, "clicked", _info_clicked, ui);
+ evas_object_size_hint_weight_set(info, 0.f, 0.f);
+ evas_object_size_hint_align_set(info, 1.f, EVAS_HINT_FILL);
+ evas_object_show(info);
+ elm_table_pack(ui->table, info, 1, 1, 1, 1);
+
+ Evas_Object *icon = elm_icon_add(info);
+ if(icon)
+ {
+ elm_image_file_set(icon, ui->logo_path, NULL);
+ evas_object_size_hint_min_set(icon, 20, 20);
+ evas_object_size_hint_max_set(icon, 32, 32);
+ //evas_object_size_hint_aspect_set(icon, EVAS_ASPECT_CONTROL_BOTH, 1, 1);
+ evas_object_show(icon);
+ elm_object_part_content_set(info, "icon", icon);
+ }
+ }
+
+ ui->popup = elm_popup_add(ui->table);
+ if(ui->popup)
+ {
+ elm_popup_allow_events_set(ui->popup, EINA_TRUE);
+
+ Evas_Object *hbox = elm_box_add(ui->popup);
+ if(hbox)
+ {
+ elm_box_horizontal_set(hbox, EINA_TRUE);
+ elm_box_homogeneous_set(hbox, EINA_FALSE);
+ elm_box_padding_set(hbox, 10, 0);
+ evas_object_show(hbox);
+ elm_object_content_set(ui->popup, hbox);
+
+ Evas_Object *icon = elm_icon_add(hbox);
+ if(icon)
+ {
+ elm_image_file_set(icon, ui->logo_path, NULL);
+ evas_object_size_hint_min_set(icon, 128, 128);
+ evas_object_size_hint_max_set(icon, 256, 256);
+ evas_object_size_hint_aspect_set(icon, EVAS_ASPECT_CONTROL_BOTH, 1, 1);
+ evas_object_show(icon);
+ elm_box_pack_end(hbox, icon);
+ }
+
+ Evas_Object *label = elm_label_add(hbox);
+ if(label)
+ {
+ elm_object_text_set(label,
+ "<color=#b00 shadow_color=#fff font_size=20>"
+ "Sherlock - OSC Inspector"
+ "</color></br><align=left>"
+ "Version "SHERLOCK_VERSION"</br></br>"
+ "Copyright (c) 2015 Hanspeter Portner</br></br>"
+ "This is free and libre software</br>"
+ "Released under Artistic License 2.0</br>"
+ "By Open Music Kontrollers</br></br>"
+ "<color=#bbb>"
+ "http://open-music-kontrollers.ch/lv2/sherlock</br>"
+ "dev@open-music-kontrollers.ch"
+ "</color></align>");
+
+ evas_object_show(label);
+ elm_box_pack_end(hbox, label);
+ }
+ }
+ }
+ }
+
+ return ui->table;
+}
+
+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)
+{
+ if(strcmp(plugin_uri, SHERLOCK_OSC_INSPECTOR_URI))
+ return NULL;
+
+ eo_ui_driver_t driver;
+ if(descriptor == &osc_inspector_eo)
+ driver = EO_UI_DRIVER_EO;
+ else if(descriptor == &osc_inspector_ui)
+ driver = EO_UI_DRIVER_UI;
+ else if(descriptor == &osc_inspector_x11)
+ driver = EO_UI_DRIVER_X11;
+ else if(descriptor == &osc_inspector_kx)
+ driver = EO_UI_DRIVER_KX;
+ else
+ return NULL;
+
+ UI *ui = calloc(1, sizeof(UI));
+ if(!ui)
+ return NULL;
+
+ eo_ui_t *eoui = &ui->eoui;
+ eoui->driver = driver;
+ eoui->content_get = _content_get;
+ eoui->w = 1024,
+ eoui->h = 480;
+
+ ui->write_function = write_function;
+ ui->controller = controller;
+
+ for(int i=0; features[i]; i++)
+ {
+ if(!strcmp(features[i]->URI, LV2_URID__map))
+ ui->map = (LV2_URID_Map *)features[i]->data;
+ }
+
+ if(!ui->map)
+ {
+ fprintf(stderr, "LV2 URID extension not supported\n");
+ free(ui);
+ return NULL;
+ }
+
+ ui->event_transfer = ui->map->map(ui->map->handle, LV2_ATOM__eventTransfer);
+ lv2_atom_forge_init(&ui->forge, ui->map);
+ osc_forge_init(&ui->oforge, ui->map);
+
+ ui->itc_osc = elm_genlist_item_class_new();
+ if(ui->itc_osc)
+ {
+ ui->itc_osc->item_style = "default_style";
+ ui->itc_osc->func.text_get = _osc_label_get;
+ ui->itc_osc->func.content_get = _osc_content_get;
+ ui->itc_osc->func.state_get = NULL;
+ ui->itc_osc->func.del = _osc_del;
+ }
+
+ sprintf(ui->string_buf, "%s/omk_logo_256x256.png", bundle_path);
+ ui->logo_path = strdup(ui->string_buf);
+
+ if(eoui_instantiate(eoui, descriptor, plugin_uri, bundle_path, write_function,
+ controller, widget, features))
+ {
+ free(ui);
+ return NULL;
+ }
+
+ return ui;
+}
+
+static void
+cleanup(LV2UI_Handle handle)
+{
+ UI *ui = handle;
+
+ eoui_cleanup(&ui->eoui);
+
+ if(ui->logo_path)
+ free(ui->logo_path);
+
+ if(ui->itc_osc)
+ elm_genlist_item_class_free(ui->itc_osc);
+
+ free(ui);
+}
+
+static void
+port_event(LV2UI_Handle handle, uint32_t i, uint32_t size, uint32_t urid,
+ const void *buf)
+{
+ UI *ui = handle;
+
+ if( (i == 2) && (urid == ui->event_transfer) && ui->list)
+ {
+ const LV2_Atom_Sequence *seq = buf;
+ int n = elm_genlist_items_count(ui->list);
+
+ LV2_ATOM_SEQUENCE_FOREACH(seq, elmnt)
+ {
+ size_t len = sizeof(LV2_Atom_Event) + elmnt->body.size;
+ LV2_Atom_Event *ev = malloc(len);
+ if(!ev)
+ continue;
+
+ memcpy(ev, elmnt, len);
+
+ // check item count
+ if(n++ >= COUNT_MAX)
+ break;
+
+ Elm_Object_Item *itm2 = elm_genlist_item_append(ui->list, ui->itc_osc,
+ ev, NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL);
+ elm_genlist_item_select_mode_set(itm2, ELM_OBJECT_SELECT_MODE_DEFAULT);
+
+ // scroll to last item
+ //elm_genlist_item_show(itm, ELM_GENLIST_ITEM_SCROLLTO_MIDDLE);
+ }
+
+ if(seq->atom.size > sizeof(LV2_Atom_Sequence_Body))
+ _clear_update(ui, n); // only update if there where any events
+ }
+}
+
+const LV2UI_Descriptor osc_inspector_eo = {
+ .URI = SHERLOCK_OSC_INSPECTOR_EO_URI,
+ .instantiate = instantiate,
+ .cleanup = cleanup,
+ .port_event = port_event,
+ .extension_data = eoui_eo_extension_data
+};
+
+const LV2UI_Descriptor osc_inspector_ui = {
+ .URI = SHERLOCK_OSC_INSPECTOR_UI_URI,
+ .instantiate = instantiate,
+ .cleanup = cleanup,
+ .port_event = port_event,
+ .extension_data = eoui_ui_extension_data
+};
+
+const LV2UI_Descriptor osc_inspector_x11 = {
+ .URI = SHERLOCK_OSC_INSPECTOR_X11_URI,
+ .instantiate = instantiate,
+ .cleanup = cleanup,
+ .port_event = port_event,
+ .extension_data = eoui_x11_extension_data
+};
+
+const LV2UI_Descriptor osc_inspector_kx = {
+ .URI = SHERLOCK_OSC_INSPECTOR_KX_URI,
+ .instantiate = instantiate,
+ .cleanup = cleanup,
+ .port_event = port_event,
+ .extension_data = eoui_kx_extension_data
+};
diff --git a/sherlock.c b/sherlock.c
index 3c6bc69..340176c 100644
--- a/sherlock.c
+++ b/sherlock.c
@@ -24,6 +24,10 @@ lv2_descriptor(uint32_t index)
{
case 0:
return &atom_inspector;
+ case 1:
+ return &midi_inspector;
+ case 2:
+ return &osc_inspector;
default:
return NULL;
}
diff --git a/sherlock.h b/sherlock.h
index 8354b40..f4df7d6 100644
--- a/sherlock.h
+++ b/sherlock.h
@@ -32,20 +32,40 @@
#define SHERLOCK_URI "http://open-music-kontrollers.ch/lv2/sherlock"
-#define SHERLOCK_OBJECT_URI SHERLOCK_URI"#object"
-#define SHERLOCK_EVENT_URI SHERLOCK_URI"#frametime"
-#define SHERLOCK_FRAMETIME_URI SHERLOCK_URI"#event"
-
#define SHERLOCK_ATOM_INSPECTOR_URI SHERLOCK_URI"#atom_inspector"
#define SHERLOCK_ATOM_INSPECTOR_EO_URI SHERLOCK_URI"#atom_inspector_eo"
#define SHERLOCK_ATOM_INSPECTOR_UI_URI SHERLOCK_URI"#atom_inspector_ui"
#define SHERLOCK_ATOM_INSPECTOR_X11_URI SHERLOCK_URI"#atom_inspector_x11"
#define SHERLOCK_ATOM_INSPECTOR_KX_URI SHERLOCK_URI"#atom_inspector_kx"
+#define SHERLOCK_MIDI_INSPECTOR_URI SHERLOCK_URI"#midi_inspector"
+#define SHERLOCK_MIDI_INSPECTOR_EO_URI SHERLOCK_URI"#midi_inspector_eo"
+#define SHERLOCK_MIDI_INSPECTOR_UI_URI SHERLOCK_URI"#midi_inspector_ui"
+#define SHERLOCK_MIDI_INSPECTOR_X11_URI SHERLOCK_URI"#midi_inspector_x11"
+#define SHERLOCK_MIDI_INSPECTOR_KX_URI SHERLOCK_URI"#midi_inspector_kx"
+
+#define SHERLOCK_OSC_INSPECTOR_URI SHERLOCK_URI"#osc_inspector"
+#define SHERLOCK_OSC_INSPECTOR_EO_URI SHERLOCK_URI"#osc_inspector_eo"
+#define SHERLOCK_OSC_INSPECTOR_UI_URI SHERLOCK_URI"#osc_inspector_ui"
+#define SHERLOCK_OSC_INSPECTOR_X11_URI SHERLOCK_URI"#osc_inspector_x11"
+#define SHERLOCK_OSC_INSPECTOR_KX_URI SHERLOCK_URI"#osc_inspector_kx"
+
extern const LV2_Descriptor atom_inspector;
extern const LV2UI_Descriptor atom_inspector_eo;
extern const LV2UI_Descriptor atom_inspector_ui;
extern const LV2UI_Descriptor atom_inspector_x11;
extern const LV2UI_Descriptor atom_inspector_kx;
+extern const LV2_Descriptor midi_inspector;
+extern const LV2UI_Descriptor midi_inspector_eo;
+extern const LV2UI_Descriptor midi_inspector_ui;
+extern const LV2UI_Descriptor midi_inspector_x11;
+extern const LV2UI_Descriptor midi_inspector_kx;
+
+extern const LV2_Descriptor osc_inspector;
+extern const LV2UI_Descriptor osc_inspector_eo;
+extern const LV2UI_Descriptor osc_inspector_ui;
+extern const LV2UI_Descriptor osc_inspector_x11;
+extern const LV2UI_Descriptor osc_inspector_kx;
+
#endif // _SHERLOCK_LV2_H
diff --git a/sherlock.ttl b/sherlock.ttl
index fbf12eb..e27ebf7 100644
--- a/sherlock.ttl
+++ b/sherlock.ttl
@@ -16,6 +16,7 @@
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
@@ -26,12 +27,17 @@
@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
@prefix kx: <http://kxstudio.sf.net/ns/lv2ext/external-ui#> .
-@prefix osc: <http://opensoundcontrol.org#> .
+@prefix osc: <http://open-music-kontrollers.ch/lv2/osc#> .
@prefix lic: <http://opensource.org/licenses/> .
@prefix omk: <http://open-music-kontrollers.ch/ventosus#> .
@prefix proj: <http://open-music-kontrollers.ch/lv2/> .
@prefix sherlock: <http://open-music-kontrollers.ch/lv2/sherlock#> .
+osc:Event
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Object ;
+ rdfs:label "OSC Event (Bundle or Message)" .
+
# Maintainer
omk:me
a foaf:Person ;
@@ -162,3 +168,159 @@ sherlock:atom_inspector
lv2:symbol "notify" ;
lv2:name "Notify" ;
] .
+
+# MIDI Inspector UI
+sherlock:midi_inspector_eo
+ a ui:EoUI ;
+ ui:portNotification [
+ ui:plugin sherlock:midi_inspector ;
+ lv2:symbol "notify" ;
+ ui:protocol midi:eventTransfer ;
+ ] ;
+ lv2:requiredFeature urid:map .
+sherlock:midi_inspector_ui
+ a ui:UI ;
+ ui:portNotification [
+ ui:plugin sherlock:midi_inspector ;
+ lv2:symbol "notify" ;
+ ui:protocol midi:eventTransfer ;
+ ] ;
+ lv2:requiredFeature ui:idleInterface, urid:map ;
+ lv2:extensionData ui:idleInterface, ui:showInterface .
+sherlock:midi_inspector_x11
+ a ui:X11UI ;
+ ui:portNotification [
+ ui:plugin sherlock:midi_inspector ;
+ lv2:symbol "notify" ;
+ ui:protocol midi:eventTransfer ;
+ ] ;
+ lv2:requiredFeature ui:idleInterface, urid:map ;
+ lv2:optionalFeature ui:resize ;
+ lv2:extensionData ui:idleInterface .
+sherlock:midi_inspector_kx
+ a kx:Widget ;
+ ui:portNotification [
+ ui:plugin sherlock:midi_inspector ;
+ lv2:symbol "notify" ;
+ ui:protocol midi:eventTransfer ;
+ ] ;
+ lv2:requiredFeature kx:Host, urid:map .
+
+# MIDI Inspector Plugin
+sherlock:midi_inspector
+ a lv2:Plugin,
+ lv2:AnalyserPlugin ;
+ doap:name "Sherlock MIDI Inspector" ;
+ doap:license lic:Artistic-2.0 ;
+ lv2:project proj:sherlock ;
+ lv2:optionalFeature lv2:isLive, lv2:hardRTCapable ;
+ lv2:requiredFeature urid:map ;
+
+ lv2:port [
+ # input event port
+ a lv2:InputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports midi:MidiEvent ;
+ lv2:index 0 ;
+ lv2:symbol "control_in" ;
+ lv2:name "Control In" ;
+ lv2:designation lv2:control ;
+ ] , [
+ # output event port
+ a lv2:OutputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports midi:MidiEvent ;
+ lv2:index 1 ;
+ lv2:symbol "control_out" ;
+ lv2:name "Control Out" ;
+ lv2:designation lv2:control ;
+ ] , [
+ # output notify port
+ a lv2:OutputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports atom:Sequence ;
+ lv2:index 2 ;
+ lv2:symbol "notify" ;
+ lv2:name "Notify" ;
+ ] .
+
+# OSC Inspector UI
+sherlock:osc_inspector_eo
+ a ui:EoUI ;
+ ui:portNotification [
+ ui:plugin sherlock:osc_inspector ;
+ lv2:symbol "notify" ;
+ ui:protocol osc:eventTransfer ;
+ ] ;
+ lv2:requiredFeature urid:map .
+sherlock:osc_inspector_ui
+ a ui:UI ;
+ ui:portNotification [
+ ui:plugin sherlock:osc_inspector ;
+ lv2:symbol "notify" ;
+ ui:protocol osc:eventTransfer ;
+ ] ;
+ lv2:requiredFeature ui:idleInterface, urid:map ;
+ lv2:extensionData ui:idleInterface, ui:showInterface .
+sherlock:osc_inspector_x11
+ a ui:X11UI ;
+ ui:portNotification [
+ ui:plugin sherlock:osc_inspector ;
+ lv2:symbol "notify" ;
+ ui:protocol osc:eventTransfer ;
+ ] ;
+ lv2:requiredFeature ui:idleInterface, urid:map ;
+ lv2:optionalFeature ui:resize ;
+ lv2:extensionData ui:idleInterface .
+sherlock:osc_inspector_kx
+ a kx:Widget ;
+ ui:portNotification [
+ ui:plugin sherlock:osc_inspector ;
+ lv2:symbol "notify" ;
+ ui:protocol osc:eventTransfer ;
+ ] ;
+ lv2:requiredFeature kx:Host, urid:map .
+
+# OSC Inspector Plugin
+sherlock:osc_inspector
+ a lv2:Plugin,
+ lv2:AnalyserPlugin ;
+ doap:name "Sherlock OSC Inspector" ;
+ doap:license lic:Artistic-2.0 ;
+ lv2:project proj:sherlock ;
+ lv2:optionalFeature lv2:isLive, lv2:hardRTCapable ;
+ lv2:requiredFeature urid:map ;
+
+ lv2:port [
+ # input event port
+ a lv2:InputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports osc:Event ;
+ lv2:index 0 ;
+ lv2:symbol "control_in" ;
+ lv2:name "Control In" ;
+ lv2:designation lv2:control ;
+ ] , [
+ # output event port
+ a lv2:OutputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports osc:Event ;
+ lv2:index 1 ;
+ lv2:symbol "control_out" ;
+ lv2:name "Control Out" ;
+ lv2:designation lv2:control ;
+ ] , [
+ # output notify port
+ a lv2:OutputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports atom:Sequence ;
+ lv2:index 2 ;
+ lv2:symbol "notify" ;
+ lv2:name "Notify" ;
+ ] .
diff --git a/sherlock_ui.c b/sherlock_ui.c
index d9fe2fe..f23d279 100644
--- a/sherlock_ui.c
+++ b/sherlock_ui.c
@@ -30,6 +30,25 @@ lv2ui_descriptor(uint32_t index)
return &atom_inspector_x11;
case 3:
return &atom_inspector_kx;
+
+ case 4:
+ return &midi_inspector_eo;
+ case 5:
+ return &midi_inspector_ui;
+ case 6:
+ return &midi_inspector_x11;
+ case 7:
+ return &midi_inspector_kx;
+
+ case 8:
+ return &osc_inspector_eo;
+ case 9:
+ return &osc_inspector_ui;
+ case 10:
+ return &osc_inspector_x11;
+ case 11:
+ return &osc_inspector_kx;
+
default:
return NULL;
}