diff options
author | Hanspeter Portner <dev@open-music-kontrollers.ch> | 2015-12-03 23:01:19 +0100 |
---|---|---|
committer | Hanspeter Portner <dev@open-music-kontrollers.ch> | 2015-12-03 23:01:19 +0100 |
commit | 54de16fcd5a1f6904eb7b89bf5aaf9b3f4e9175c (patch) | |
tree | a6029dea5a2026328fb36e6634b76201a4731e84 | |
parent | cd9b98d8931d4049e536743e842294a6b7bb93d1 (diff) | |
download | sherlock.lv2-54de16fcd5a1f6904eb7b89bf5aaf9b3f4e9175c.tar.xz |
prototype osc_inspector and midi_inspector.
-rw-r--r-- | CMakeLists.txt | 10 | ||||
-rw-r--r-- | atom_inspector.c | 27 | ||||
-rw-r--r-- | manifest.ttl.in | 58 | ||||
-rw-r--r-- | midi_inspector.c | 152 | ||||
-rw-r--r-- | midi_inspector_ui.c | 623 | ||||
-rw-r--r-- | osc_inspector.c | 154 | ||||
-rw-r--r-- | osc_inspector_ui.c | 453 | ||||
-rw-r--r-- | sherlock.c | 4 | ||||
-rw-r--r-- | sherlock.h | 28 | ||||
-rw-r--r-- | sherlock.ttl | 164 | ||||
-rw-r--r-- | sherlock_ui.c | 19 |
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 +}; @@ -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; } @@ -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; } |