Commit 804465d9 authored by Hanspeter Portner's avatar Hanspeter Portner

initial commit

parents
cmake_minimum_required(VERSION 2.8)
project(lua.lv2)
include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_SOURCE_DIR}/tlsf-3.0)
set(DEST lib/lv2/lua.lv2)
set(LIB_EXT ".so")
find_package(PkgConfig) # ${PKG_CONFIG_FOUND}
pkg_search_module(LV2 REQUIRED lv2>=1.10)
include_directories(${LV2_INCLUDE_DIRS})
set(LIBS ${LIBS} ${LV2_LDFLAGS})
pkg_search_module(ELM REQUIRED elementary>=1.8)
include_directories(${ELM_INCLUDE_DIRS})
set(LIBS_UI ${LIBS_UI} ${ELM_LDFLAGS})
option(USE_LUAJIT "use LuaJIT" ON)
if(USE_LUAJIT)
pkg_search_module(LUAJIT REQUIRED luajit>=2.0)
include_directories(${LUAJIT_INCLUDE_DIRS})
set(LIBS ${LIBS} ${LUAJIT_LDFLAGS})
else()
pkg_search_module(LUA REQUIRED lua)
include_directories(${LUA_INCLUDE_DIRS})
set(LIBS ${LIBS} ${LUA_LDFLAGS})
endif()
# tlsf
add_library(tlsf tlsf-3.0/tlsf.c)
set(CMAKE_C_FLAGS "-fPIC") #FIXME
add_library(lua MODULE
lua_lv2.c
lua_control.c)
target_link_libraries(lua tlsf ${LIBS})
set_target_properties(lua PROPERTIES PREFIX "")
install(TARGETS lua DESTINATION ${DEST})
add_library(lua_ui MODULE
lua_lv2_ui.c
lua_control_ui.c)
target_link_libraries(lua_ui ${LIBS_UI})
set_target_properties(lua_ui PROPERTIES PREFIX "")
install(TARGETS lua_ui DESTINATION ${DEST})
configure_file(${PROJECT_SOURCE_DIR}/manifest.ttl.in ${PROJECT_BINARY_DIR}/manifest.ttl)
find_program(EDJE_CC NAMES edje_cc)
if(EDJE_CC_NOTFOUND)
message(SEND_ERROR "edje_cc not found")
else()
message(STATUS "edje_cc found: " ${EDJE_CC})
endif()
add_custom_command(
OUTPUT ${PROJECT_BINARY_DIR}/lua.edj
COMMAND ${EDJE_CC} ARGS
-fd ${PROJECT_SOURCE_DIR}
-id ${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/lua.edc
${PROJECT_BINARY_DIR}/lua.edj
MAIN_DEPENDENCY
${PROJECT_SOURCE_DIR}/lua.edc
${PROJECT_SOURCE_DIR}/lua_control.edc)
add_custom_target(THEME ALL DEPENDS ${PROJECT_BINARY_DIR}/lua.edj)
install(FILES ${PROJECT_BINARY_DIR}/manifest.ttl DESTINATION ${DEST})
install(FILES ${PROJECT_BINARY_DIR}/lua.edj DESTINATION ${DEST})
install(FILES ${PROJECT_SOURCE_DIR}/lua.ttl DESTINATION ${DEST})
/*
* Copyright (c) 2014 Hanspeter Portner (dev@open-music-kontrollers.ch)
*
* This GUI theme and its accompanying resources are released under a
* Creative Commons Attribution Non-Commercial Share-Alike License 3.0
*/
#define LUA_URI "http://open-music-kontrollers.ch/lv2/lua"
collections {
# include <lua_control.edc>
}
@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 lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
# Maintainer
<http://open-music-kontrollers.ch#me>
a foaf:Person ;
foaf:name "Hanspeter Portner" ;
foaf:mbox <dev@open-music-kontrollers.ch> ;
foaf:homepage <http://open-music-kontrollers.ch> .
# Project
<http://open-music-kontrollers.ch/lv2/lua>
a doap:Project ;
doap:maintainer <http://open-music-kontrollers.ch#me> ;
doap:name "Lua bundle" .
# Plugin UI
<http://open-music-kontrollers.ch/lv2/lua#control/ui>
a ui:X11UI ;
lv2:requiredFeature ui:idleInterface ;
lv2:optionalFeature ui:noUserResize ;
lv2:extensionData ui:idleInterface, ui:showInterface .
# Plugin
<http://open-music-kontrollers.ch/lv2/lua#control>
a lv2:Plugin,
lv2:ConverterPlugin;
doap:name "Lua for Control Ports" ;
doap:license <http://opensource.org/licenses/isc> ;
lv2:project <http://open-music-kontrollers.ch/lv2/lua> ;
lv2:optionalFeature lv2:isLive, lv2:hardRTCapable ;
lv2:extensionData ui:stateInterface ;
# input control ports
lv2:port [
a lv2:InputPort ,
lv2:ControlPort ;
lv2:index 0 ;
lv2:symbol "input_1" ;
lv2:name "Input 1" ;
lv2:default 0.0;
] ;
lv2:port [
a lv2:InputPort ,
lv2:ControlPort ;
lv2:index 1 ;
lv2:symbol "input_2" ;
lv2:name "Input 2" ;
lv2:default 0.0;
] ;
lv2:port [
a lv2:InputPort ,
lv2:ControlPort ;
lv2:index 2 ;
lv2:symbol "input_3" ;
lv2:name "Input 3" ;
lv2:default 0.0;
] ;
lv2:port [
a lv2:InputPort ,
lv2:ControlPort ;
lv2:index 3 ;
lv2:symbol "input_4" ;
lv2:name "Input 4" ;
lv2:default 0.0;
] ;
lv2:port [
a lv2:InputPort ,
lv2:ControlPort ;
lv2:index 4 ;
lv2:symbol "input_5" ;
lv2:name "Input 5" ;
lv2:default 0.0;
] ;
lv2:port [
a lv2:InputPort ,
lv2:ControlPort ;
lv2:index 5 ;
lv2:symbol "input_6" ;
lv2:name "Input 6" ;
lv2:default 0.0;
] ;
lv2:port [
a lv2:InputPort ,
lv2:ControlPort ;
lv2:index 6 ;
lv2:symbol "input_7" ;
lv2:name "Input 7" ;
lv2:default 0.0;
] ;
lv2:port [
a lv2:InputPort ,
lv2:ControlPort ;
lv2:index 7 ;
lv2:symbol "input_8" ;
lv2:name "Input 8" ;
lv2:default 0.0;
] ;
# output control ports
lv2:port [
a lv2:OutputPort ,
lv2:ControlPort ;
lv2:index 8 ;
lv2:symbol "output_1" ;
lv2:name "Output 1" ;
] ;
lv2:port [
a lv2:OutputPort ,
lv2:ControlPort ;
lv2:index 9 ;
lv2:symbol "output_2" ;
lv2:name "Output 2" ;
] ;
lv2:port [
a lv2:OutputPort ,
lv2:ControlPort ;
lv2:index 10 ;
lv2:symbol "output_3" ;
lv2:name "Output 3" ;
] ;
lv2:port [
a lv2:OutputPort ,
lv2:ControlPort ;
lv2:index 11 ;
lv2:symbol "output_4" ;
lv2:name "Output 4" ;
] ;
lv2:port [
a lv2:OutputPort ,
lv2:ControlPort ;
lv2:index 12 ;
lv2:symbol "output_5" ;
lv2:name "Output 5" ;
] ;
lv2:port [
a lv2:OutputPort ,
lv2:ControlPort ;
lv2:index 13 ;
lv2:symbol "output_6" ;
lv2:name "Output 6" ;
] ;
lv2:port [
a lv2:OutputPort ,
lv2:ControlPort ;
lv2:index 14 ;
lv2:symbol "output_7" ;
lv2:name "Output 7" ;
] ;
lv2:port [
a lv2:OutputPort ,
lv2:ControlPort ;
lv2:index 15 ;
lv2:symbol "output_8" ;
lv2:name "Output 8" ;
] .
#include <lua_lv2.h>
#include <lauxlib.h>
typedef struct _Handle Handle;
struct _Handle {
LV2_URID_Map *map;
struct {
LV2_URID atom_string;
LV2_URID lua_chunk;
LV2_URID state_default;
} uris;
Lua_VM lvm;
const float *control_in [8];
float *control_out [8];
};
static const char *default_chunk =
"function run(...)"
"return ..."
"end";
static LV2_State_Status
state_save(LV2_Handle instance, LV2_State_Store_Function store, LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features)
{
Handle *handle = (Handle *)instance;
return store(
state,
handle->uris.lua_chunk,
handle->lvm.chunk,
strlen(handle->lvm.chunk)+1,
handle->uris.atom_string,
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
}
static LV2_State_Status
state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features)
{
Handle *handle = (Handle *)instance;
size_t size;
uint32_t type;
uint32_t flags2;
const char *chunk = retrieve(
state,
handle->uris.lua_chunk,
&size,
&type,
&flags2
);
//TODO check type, flags2
if(size && type)
{
if(handle->lvm.chunk)
free(handle->lvm.chunk);
handle->lvm.chunk = strdup(chunk);
}
// load chunk
if(luaL_dostring(handle->lvm.L, handle->lvm.chunk))
{
free(handle->lvm.chunk);
handle->lvm.chunk = strdup(default_chunk);
if(luaL_dostring(handle->lvm.L, handle->lvm.chunk))
{
fprintf(stderr, "Lua: %s\n", lua_tostring(handle->lvm.L, -1));
//return NULL; //FIXME load default chunk?
}
}
return LV2_STATE_SUCCESS;
}
static LV2_Handle
instantiate(const LV2_Descriptor* descriptor, double rate, const char *bundle_path, const LV2_Feature *const *features)
{
int i;
Handle *handle = (Handle *)calloc(1, sizeof(Handle));
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->uris.atom_string = handle->map->map(handle->map->handle, LV2_ATOM__String);
handle->uris.lua_chunk = handle->map->map(handle->map->handle, "http://lua.org#chunk");
handle->uris.state_default = handle->map->map(handle->map->handle, LV2_STATE__loadDefaultState);
if(lua_vm_init(&handle->lvm))
return NULL;
// load chunk
handle->lvm.chunk = strdup(default_chunk);
if(luaL_dostring(handle->lvm.L, handle->lvm.chunk))
{
fprintf(stderr, "Lua: %s\n", lua_tostring(handle->lvm.L, -1));
return NULL;
}
return handle;
}
static void
connect_port(LV2_Handle instance, uint32_t port, void *data)
{
Handle *handle = (Handle *)instance;
if(port < 8)
handle->control_in[port] = (const float *)data;
else
handle->control_out[port-8] = (float *)data;
}
static void
activate(LV2_Handle instance)
{
Handle *handle = (Handle *)instance;
//nothing
}
static void
run(LV2_Handle instance, uint32_t nsamples)
{
Handle *handle = (Handle *)instance;
lua_State *L = handle->lvm.L;
lua_getglobal(L, "run");
if(lua_isfunction(L, -1))
{
for(int i=0; i<8; i++)
lua_pushnumber(L, *handle->control_in[i]);
if(lua_pcall(L, 8, 8, 0))
fprintf(stderr, "Lua: %s\n", lua_tostring(L, -1));
for(int i=0; i<8; i++)
if(lua_isnumber(L, i+1))
*handle->control_out[i] = lua_tonumber(L, i+1);
else
*handle->control_out[i] = 0.f;
lua_pop(L, 8);
}
else
lua_pop(L, 1);
lua_gc(L, LUA_GCSTEP, 0);
}
static void
deactivate(LV2_Handle instance)
{
Handle *handle = (Handle *)instance;
//nothing
}
static void
cleanup(LV2_Handle instance)
{
Handle *handle = (Handle *)instance;
lua_vm_deinit(&handle->lvm);
free(handle);
}
static const void*
extension_data(const char* uri)
{
const static LV2_State_Interface state_iface = {
.save = state_save,
.restore = state_restore
};
if(!strcmp(uri, LV2_STATE__interface))
return &state_iface;
else
return NULL;
}
const LV2_Descriptor lv2_lua_control = {
.URI = LUA_CONTROL_URI,
.instantiate = instantiate,
.connect_port = connect_port,
.activate = activate,
.run = run,
.deactivate = deactivate,
.cleanup = cleanup,
.extension_data = extension_data
};
/*
* Copyright (c) 2014 Hanspeter Portner (dev@open-music-kontrollers.ch)
*
* This GUI theme and its accompanying resources are released under a
* Creative Commons Attribution Non-Commercial Share-Alike License 3.0
*/
#define LUA_CONTROL_URI LUA_URI"#control"
#define LUA_CONTROL_UI_URI LUA_CONTROL_URI"/ui"
images {
image: "encapsulation.png" COMP;
image: "omk_logo_256x256.png" COMP;
}
group {
name: LUA_CONTROL_UI_URI"/theme";
parts {
part {
name: "bg";
type: RECT;
mouse_events: 0;
description {
state: "default" 0.0;
color: 48 48 48 255;
}
}
part {
name: "encapsulation";
type: IMAGE;
mouse_events: 0;
description {
state: "default" 0.0;
image {
normal: "encapsulation.png";
border: 12 12 12 12;
middle: NONE;
scale_hint: STATIC;
}
}
}
part {
name: "content";
type: SWALLOW;
mouse_events: 1;
repeat_events: 1;
description {
state: "default" 0.0;
rel1.offset: 12 12;
rel2.offset: -12 -12;
}
}
part {
name: "logo";
type: IMAGE;
mouse_events: 0;
description {
state: "default" 0.0;
rel1.relative: 0.2 0.2;
rel2.relative: 0.8 0.8;
aspect: 1 1;
aspect_preference: BOTH;
image.normal: "omk_logo_256x256.png";
color: 255 255 255 16;
}
}
}
}
/*
* Copyright (c) 2014 Hanspeter Portner (dev@open-music-kontrollers.ch)
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*/
#include <lua_lv2.h>
#include <Elementary.h>
typedef struct _UI UI;
struct _UI {
LV2UI_Write_Function write_function;
LV2UI_Controller controller;
int w, h;
Ecore_Evas *ee;
Evas *e;
Evas_Object *theme;
Evas_Object *vbox;
Evas_Object *entry;
char theme_path [512];
};
// Idle interface
static int
idle_cb(LV2UI_Handle handle)
{
UI *ui = handle;
if(!ui)
return -1;
ecore_main_loop_iterate();
return 0;
}
static const LV2UI_Idle_Interface idle_ext = {
.idle = idle_cb
};
// Show Interface
static int
_show_cb(LV2UI_Handle handle)
{
UI *ui = handle;
if(!ui)
return -1;
ecore_evas_show(ui->ee);
return 0;
}
static int
_hide_cb(LV2UI_Handle handle)
{
UI *ui = handle;
if(!ui)
return -1;
ecore_evas_hide(ui->ee);
return 0;
}
static const LV2UI_Show_Interface show_ext = {
.show = _show_cb,
.hide = _hide_cb
};
// Resize Interface
static int
resize_cb(LV2UI_Feature_Handle handle, int w, int h)
{
UI *ui = handle;
if(!ui)
return -1;
ui->w = w;
ui->h = h;
ecore_evas_resize(ui->ee, ui->w, ui->h);
evas_object_resize(ui->theme, ui->w, ui->h);
return 0;
}
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)
{
elm_init(1, (char **)&plugin_uri);
//edje_frametime_set(0.04);
if(strcmp(plugin_uri, LUA_CONTROL_URI))
return NULL;
UI *ui = calloc(1, sizeof(UI));
if(!ui)
return NULL;
ui->w = 400;
ui->h = 400;
ui->write_function = write_function;
ui->controller = controller;
void *parent = NULL;
LV2UI_Resize *resize = NULL;
int i, j;
for(i=0; features[i]; i++)
{
if(!strcmp(features[i]->URI, LV2_UI__parent))
parent = features[i]->data;
else if (!strcmp(features[i]->URI, LV2_UI__resize))
resize = (LV2UI_Resize *)features[i]->data;
}
ui->ee = ecore_evas_gl_x11_new(NULL, (Ecore_X_Window)parent, 0, 0, ui->w, ui->h);
if(!ui->ee)
ui->ee = ecore_evas_software_x11_new(NULL, (Ecore_X_Window)parent, 0, 0, ui->w, ui->h);
if(!ui->ee)
printf("could not start evas\n");
ui->e = ecore_evas_get(ui->ee);
ecore_evas_show(ui->ee);
if(resize)
resize->ui_resize(resize->handle, ui->w, ui->h);
sprintf(ui->theme_path, "%s/lua.edj", bundle_path);
ui->theme = edje_object_add(ui->e);
edje_object_file_set(ui->theme, ui->theme_path, LUA_CONTROL_UI_URI"/theme");
evas_object_resize(ui->theme, ui->w, ui->h);
evas_object_show(ui->theme);
ui->vbox = elm_box_add(ui->theme);
evas_object_size_hint_weight_set(ui->vbox, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(ui->vbox, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_show(ui->vbox);
evas_object_resize(ui->vbox, 800, 450);
edje_object_part_swallow(ui->theme, "content", ui->vbox);
ui->entry = elm_entry_add(ui->vbox);
elm_entry_single_line_set(ui->entry, EINA_FALSE);
elm_entry_scrollable_set(ui->entry, EINA_TRUE);
elm_entry_editable_set(ui->entry, EINA_TRUE);
elm_object_focus_set(ui->entry, EINA_TRUE);
evas_object_size_hint_weight_set(ui->entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(ui->entry, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_show(ui->entry);
elm_box_pack_end(ui->vbox, ui->entry);
elm_entry_entry_set(ui->entry,
"<code>function run(...)<br/>"
"</tab>return ...<br/>"
"end</code>");
return ui;
}
static void
cleanup(LV2UI_Handle handle)
{
UI *ui = handle;
if(ui)
{
ecore_evas_hide(ui->ee);
elm_box_clear(ui->vbox);
edje_object_part_unswallow(ui->theme, ui->vbox);
evas_object_del(ui->vbox);
evas_object_del(ui->theme);
ecore_evas_free(ui->ee);
free(ui);
}
elm_shutdown();
}
static void
port_event(LV2UI_Handle handle, uint32_t i, uint32_t buffer_size, uint32_t format, const void *buffer)
{